歡迎您光臨本站 註冊首頁

Linux設備驅動編程之複雜設備驅動

←手機掃碼閱讀     火星人 @ 2014-03-26 , reply:0

  這裡所說的複雜設備驅動涉及到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這兩步的例子如下:
QUOTE:
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這兩步的例子如下:
QUOTE:
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的例子:
QUOTE:
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 << 9, req->current_nr_sectors << 9,
req->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 << 9,req->current_nr_sectors << 9,
req->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(¬ifier);
 #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< MAX_MTD_DEVICES; i++) {
  mtd_sizes = 0;
  mtd_blksizes = BLOCK_SIZE;
 }
 init_waitqueue_head(&thr_wq);
 /* Allow the block size to default to BLOCK_SIZE. */
 blksize_size[MAJOR_NR] = mtd_blksizes;
 blk_size[MAJOR_NR] = mtd_sizes;

 BLK_INIT_QUEUE(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request, &mtdblock_lock);

 kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
 return 0;
}

static void __exit cleanup_mtdblock(void)
{
 leaving = 1;
 wake_up(&thr_wq);
 down(&thread_sem);
 #ifdef CONFIG_DEVFS_FS
  unregister_mtd_user(¬ifier);
  devfs_unregister(devfs_dir_handle);
  devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME);
 #else
  unregister_blkdev(MAJOR_NR,DEVICE_NAME);
 #endif
 blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
 blksize_size[MAJOR_NR] = NULL;
 blk_size[MAJOR_NR] = NULL;
}


[火星人 ] Linux設備驅動編程之複雜設備驅動已經有695次圍觀

http://coctec.com/docs/linux/show-post-189203.html