免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 781 | 回复: 0
打印 上一主题 下一主题

Linux设备驱动编程之复杂设备驱动 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-11-02 11:50 |只看该作者 |倒序浏览
  这里所说的复杂设备驱动涉及到PCI、USB、网络设备、块设备等(严格意义而言,这些设备在概念上并不并列,例如与块设备并列的是字符设备,而PCI、USB设备等都可能属于字符设备),这些设备的驱动中又涉及到一些与特定设备类型相关的较为复杂的数据结构和程序结构。本文将不对这些设备驱动的细节进行过多的介绍,仅仅进行轻描淡写的叙述。
  PCI 是The Peripheral Component Interconnect -Bus的缩写,CPU使用PCI桥chipset与PCI设备通信,PCI桥chipset处理了PCI子系统与内存子系统间的所有数据交互,PCI设备完全被从内存子系统分离出来。下图呈现了PCI子系统的原理:

  每个PCI设备都有一个256字节的设备配置块,其中前64字节作为设备的ID和基本配置信息,Linux中提供了一组函数来处理PCI配置块。在PCI设备能得以使用前,Linux驱动程序需要从PCI设备配置块中的信息决定设备的特定参数,进行相关设置以便能正确操作该PCI设备。
  一般的PCI设备初始化函数处理流程为:
  (1)检查内核是否支持PCI-Bios;
  (2)检查设备是否存在,获得设备的配置信息;
  1~2这两步的例子如下:
int pcidata_read_proc(char *buf, char **start, off_t offset, int len, int *eof,void *data)
{
 int i, pos = 0;
 int bus, devfn;
 if (!pcibios_present())
  return sprintf(buf, "No PCI bios present\n");
 /*
 * This code is derived from "drivers/pci/pci.c". This means that
 * the GPL applies to this source file and credit is due to the
 * original authors (Drew Eckhardt, Frederic Potter, David
 * Mosberger-Tang)
 */
 for (bus = 0; !bus; bus++)
 {
  /* only bus 0 :-) */
  for (devfn = 0; devfn
  其中使用的pci_find_slot()函数定义为:
struct pci_dev *pci_find_slot (unsigned int bus,
unsigned int devfn)
{
 struct pci_dev *pptr = kmalloc(sizeof(*pptr), GFP_KERNEL);
 int index = 0;
 unsigned short vendor;
 int ret;
 if (!pptr) return NULL;
 pptr->index = index; /* 0 */
 ret = pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor);
 if (ret /* == PCIBIOS_DEVICE_NOT_FOUND or whatever error */
|| vendor==0xffff || vendor==0x0000) {
  kfree(pptr); return NULL;
 }
 printk("ok (%i, %i %x)\n", bus, devfn, vendor);
 /* fill other fields */
 pptr->bus = bus;
 pptr->devfn = devfn;
 pcibios_read_config_word(pptr->bus, pptr->devfn,PCI_VENDOR_ID, &pptr->vendor);
 pcibios_read_config_word(pptr->bus, pptr->devfn,PCI_DEVICE_ID, &pptr->device);
 return pptr;
}
  (3)根据设备的配置信息申请I/O空间及IRQ资源;
  这里所说的复杂设备驱动涉及到PCI、USB、网络设备、块设备等(严格意义而言,这些设备在概念上并不并列,例如与块设备并列的是字符设备,而PCI、USB设备等都可能属于字符设备),这些设备的驱动中又涉及到一些与特定设备类型相关的较为复杂的数据结构和程序结构。本文将不对这些设备驱动的细节进行过多的介绍,仅仅进行轻描淡写的叙述。
  PCI 是The Peripheral Component Interconnect -Bus的缩写,CPU使用PCI桥chipset与PCI设备通信,PCI桥chipset处理了PCI子系统与内存子系统间的所有数据交互,PCI设备完全被从内存子系统分离出来。下图呈现了PCI子系统的原理:

  每个PCI设备都有一个256字节的设备配置块,其中前64字节作为设备的ID和基本配置信息,Linux中提供了一组函数来处理PCI配置块。在PCI设备能得以使用前,Linux驱动程序需要从PCI设备配置块中的信息决定设备的特定参数,进行相关设置以便能正确操作该PCI设备。
  一般的PCI设备初始化函数处理流程为:
  (1)检查内核是否支持PCI-Bios;
  (2)检查设备是否存在,获得设备的配置信息;
  1~2这两步的例子如下:
int pcidata_read_proc(char *buf, char **start, off_t offset, int len, int *eof,void *data)
{
 int i, pos = 0;
 int bus, devfn;
 if (!pcibios_present())
  return sprintf(buf, "No PCI bios present\n");
 /*
 * This code is derived from "drivers/pci/pci.c". This means that
 * the GPL applies to this source file and credit is due to the
 * original authors (Drew Eckhardt, Frederic Potter, David
 * Mosberger-Tang)
 */
 for (bus = 0; !bus; bus++)
 {
  /* only bus 0 :-) */
  for (devfn = 0; devfn
  其中使用的pci_find_slot()函数定义为:
struct pci_dev *pci_find_slot (unsigned int bus,
unsigned int devfn)
{
 struct pci_dev *pptr = kmalloc(sizeof(*pptr), GFP_KERNEL);
 int index = 0;
 unsigned short vendor;
 int ret;
 if (!pptr) return NULL;
 pptr->index = index; /* 0 */
 ret = pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor);
 if (ret /* == PCIBIOS_DEVICE_NOT_FOUND or whatever error */
|| vendor==0xffff || vendor==0x0000) {
  kfree(pptr); return NULL;
 }
 printk("ok (%i, %i %x)\n", bus, devfn, vendor);
 /* fill other fields */
 pptr->bus = bus;
 pptr->devfn = devfn;
 pcibios_read_config_word(pptr->bus, pptr->devfn,PCI_VENDOR_ID, &pptr->vendor);
 pcibios_read_config_word(pptr->bus, pptr->devfn,PCI_DEVICE_ID, &pptr->device);
 return pptr;
}
  (3)根据设备的配置信息申请I/O空间及IRQ资源;
 块驱动程序最终必须提供完成实际块 I/O 操作的机制,在 Linux中,用于这些 I/O 操作的方法称为"request(请求)"。在块设备的注册过程中,需要初始化request队列,这一动作通过blk_init_queue来完成,blk_init_queue函数建立队列,并将该驱动程序的 request 函数关联到队列。在模块的清除阶段,应调用 blk_cleanup_queue 函数。看看mtdblock的例子:
static void handle_mtdblock_request(void)
{
 struct request *req;
 struct mtdblk_dev *mtdblk;
 unsigned int res;
 for (;;) {
  INIT_REQUEST;
  req = CURRENT;
  spin_unlock_irq(QUEUE_LOCK(QUEUE));
  mtdblk = mtdblks[minor(req->rq_dev)];
  res = 0;
  if (minor(req->rq_dev) >= MAX_MTD_DEVICES)
   panic("%s : minor out of bound", __FUNCTION__);
  if (!IS_REQ_CMD(req))
   goto end_req;
  if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9))
   goto end_req;
  // Handle the request
  switch (rq_data_dir(req))
  {
   int err;
   case READ:
    down(&mtdblk->cache_sem);
    err = do_cached_read (mtdblk, req->sector current_nr_sectors buffer);
    up(&mtdblk->cache_sem);
    if (!err)
     res = 1;
     break;
   case WRITE:
    // Read only device
    if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) )
     break;
     // Do the write
     down(&mtdblk->cache_sem);
     err = do_cached_write (mtdblk, req->sector current_nr_sectors buffer);
     up(&mtdblk->cache_sem);
    if (!err)
     res = 1;
     break;
  }
  end_req:
   spin_lock_irq(QUEUE_LOCK(QUEUE));
   end_request(res);
 }
}
int __init init_mtdblock(void)
{
 int i;
 spin_lock_init(&mtdblks_lock);
 /* this lock is used just in kernels >= 2.5.x */
 spin_lock_init(&mtdblock_lock);
 #ifdef CONFIG_DEVFS_FS
 if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops))
 {
  printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",MTD_BLOCK_MAJOR);
  return -EAGAIN;
 }
 devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL);
 register_mtd_user(&notifier);
 #else
 if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
  printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",MTD_BLOCK_MAJOR);
  return -EAGAIN;
 }
 #endif
 /* We fill it in at open() time. */
 for (i=0; i

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/23204/showart_194005.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP