Linux PCI驱动的编写

时间:2010-05-20

  作者:曹忠明,华清远见嵌入式学院讲师。

  PCI是外围设备互联的简称(Peripheral Component Interconnect)的简称,作为一种通用的总线接口标准,他在计算机系统中得到了广泛的使用。PCI的速度能够达到132M/s。在这里简单的介绍一下 linux 下PCI驱动的实现。

  在编写一个PCI驱动的时候我们先得确定系统中是否有我们的设备。我们可以通过lspci查看PCI设备。

  [root@localhost ~]# lspci

  00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)

  00:01.0 PCI bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX AGP bridge (rev 01)

  00:07.0 ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 08)

  00:07.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)

  00:07.2 USB Controller: Intel Corporation 82371AB/EB/MB PIIX4 USB

  00:07.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)

  00:0f.0 VGA compatible controller: VMware Inc Abstract SVGA II Adapter

  00:10.0 SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)

  00:11.0 PCI bridge: VMware Inc PCI bridge (rev 02)

  02:00.0 Ethernet controller: Advanced Micro Devices [AMD] 79c970 [PCnet32 LANCE] (rev 10)

  02:01.0 Multimedia audio controller: Ensoniq ES1371 [AudioPCI-97] (rev 02)

  02:02.0 USB Controller: VMware Inc Abstract USB2 EHCI Controller

  确定有设备以后,我们就可以开始我们的PCI设备驱动的编写了。

  1、 首先我们介绍几个必须了解的结构体

  pci_driver:这个结构体定义在include/linux/pci.h,这里我们关注的是id_table、probe和remove。id_table是一个结构体数组,用来存放驱动程序适用的设备信息,probe用于检测设备,remove为设备卸载时调用。

  struct pci_driver {

  struct list_head node;

  char *nAME;

  const struct pci_device_id *id_table;        /* must be non-NULL for probe to be called */

  int (*probe) (struct pci_dev *dev, const struct pci_device_id *id);        /* New device inserted */

  void (*remove) (struct pci_dev *dev);        /* Device removed (NULL if not a hot-plug capable driver) */

  int (*suspend) (struct pci_dev *dev, pm_message_t state);        /* Device suspended */

  int (*resume) (struct pci_dev *dev);                /* Device woken up */

  int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable);        /* Enable wake event */

  void (*shutdown) (struct pci_dev *dev);

  struct pci_error_handlers *err_handler;

  struct device_driver driver;

  struct pci_dynids dynids;

  };

  pci_dev:这个结构体同样也是定义在include/linux/pci.h,它详细的定义了PCI的设备的信息。这些信息我们可以通过查看proc及sys目录先相应文件得到。

  struct pci_dev {

  struct list_head global_list;        /* node in list of all PCI devices */

  struct list_head bus_list;        /* node in per-bus list */

  struct pci_bus *bus;        /* bus this device is on */

  struct pci_bus *subordinate;        /* bus this device bridges to */

  void        *sysdata;        /* hook for sys-specific extension */

  struct proc_dir_entry *procent;        /* device entry in /proc/bus/pci */

  unsigned int        devfn;        /* encoded device & function index */

  unsigned short vendor;

  unsigned short device;

  unsigned short subsystem_vendor;

  unsigned short subsystem_device;

  unsigned int        class;        /* 3 bytes: (base,sub,prog-if) */

  u8         hdr_type; /* PCI header type (`multi' flag masked out) */

  u8         rom_base_reg; /* which config register controls the ROM */

  u8         pin;        /* which interrupt pin this device uses */

  struct pci_driver *driver; /* which driver has allocated this device */

  u64         dma_mask;         /* Mask of the bits of bus address this

  device implements. Normally this is

  0xffffffff. You only need to change

  this if your device has broken DMA

  or supports 64-bit transfers. */

  pci_power_t        current_state;        /* Current operating state. In ACPI-speak,

  this is D0-D3, D0 being fully functional,

  and D3 being off. */

  pci_channel_state_t error_state;        /* current connectivity state */

  struct        device        dev;        /* Generic device interface */

  /* device is compatible with these IDs */

  unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];

  unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];

  int         cfg_size; /* Size of configuration space */

  /*

  * Instead of touching interrupt line and base address registers

  * directly, use the values stored here. They might be different!

  */

  unsigned int irq;

  struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */

  /* These fields are used by common fixups */

  unsigned int        transparent:1;                /* Transparent PCI bridge */

  unsigned int        multifunction:1;        /* Part of multi-function device */

  /* keep track of device state */

  unsigned int is_enabled:1;        /* pci_enable_device has been called */

  unsigned int        is_busmaster:1;         /* device is busmaster */

  unsigned int        no_msi:1;         /* device may not use msi */

  unsigned int        no_d1d2:1;        /* only allow d0 or d3 */

  unsigned int        block_ucfg_access:1;        /* userspace config space access is blocked */

  unsigned int        broken_parity_status:1;        /* Device generates false positive parity */

  unsigned int        msi_enabled:1;

  unsigned int        msix_enabled:1;

  #ifndef __GENKSYMS__

  unsigned int is_managed:1;

  #endif

  u32        saved_config_space[16]; /* config space saved at suspend time */

  struct hlist_head saved_cap_space;

  struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */

  int rom_attr_enabled;        /* has display of the rom attribute been enabled? */

  struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */

  #ifndef __GENKSYMS__

  u8        revision;        /* PCI revision, low byte of class word */

  #endif

  };

  2、 这里我们开始编写一个简单的PCI驱动

  ●    LICENSE的声明必不可少:

  MODULE_LICENSE ("GPL");

  ●    pci_driver的定义:

  #define PCI_MODULE_NAME "dsp_pci_module"

  static struct pci_driver new_pci_driver = {

  name:        PCI_MODULE_NAME,

  id_table:new_pci_tbl,

  probe:        pci_probe,

  remove: pci_remove,

  };

  pci_driver中对应的pci_tbl定义:

  #define NEW_PCI_VENDOR_ID 0x15ad

  #define NEW_PCI_DEVICE_ID 0x0405

  static struct pci_device_id new_pci_tbl[] __initdata = {

  {NEW_PCI_VENDOR_ID, NEW_PCI_DEVICE_ID,

  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},

  {0,}

  };

  MODULE_DEVICE_TABLE (pci, new_pci_tbl);

  ●        probe函数的声明:

  static int __devinit pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)

  {

  /*在这里我们可以对PCI设备进行初始化及IO的注册等操作*/

  return 0;

  }

  ●        remove函数的声明:

  static void __devexit pci_remove(struct pci_dev *pci_dev)

  {

  /*对资源释放*/

  }

  ●        module_init和module_exit这两个函数在驱动中必不可少,分别在驱动被加载和卸载时调用:

  static int __init pci_init_module (void)

  {

  return pci_register_driver(&new_pci_driver);

  }

  static void __exit pci_cleanup_module (void)

  {

  pci_unregister_driver(&new_pci_driver);

  }

  module_init (pci_init_module);

  module_exit (pci_cleanup_module);

  下面我们说一下这个驱动的执行过程:

  系统加载模块是调用pci_init_module函数,在这个函数中我们通过pci_register_driver把new_pci_driver注册到系统中,这个函数首先检测id_table中定义的PCI信息是否和系统中的PCI信息有匹配,如果有则返回0,匹配成功后调用probe函数对PCI设备进行进一步的操作。同样在卸载模块时调用pci_cleanup_module,这个函数中通过pci_unregister_driver对new_pci_driver进行注销,这个会调用到remove函数。

  “本文由华清远见https://www.embedu.org/index.htm提供”



  
上一篇:如何将一维数组作为函数形参来使用
下一篇:Linux 进程创建

免责声明: 凡注明来源本网的所有作品,均为本网合法拥有版权或有权使用的作品,欢迎转载,注明出处。非本网作品均来自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

相关技术资料