PCI设备驱动

PCI有3种地址空间:I/O空间,内存地址空间和配置空间

CPU可以访问所有的地址空间。

I/O空间和内存地址空间由设备驱动程序使用。

PCI配置空间由linux内核中的PCI初始代码使用,这些代码用于配置PCI设备,比如中断号,I/O或内存基地址。

1. PCI 的配置空间,及其软件表示

/include /linux /pci_regs.h文件定义了PCI配置空间寄存器。

1.1 一个PCI的配置空间至少有256字节,布局如下图所示:


1.2 Kernel通常使用 pci_device_id 来让PCI设备匹配上相应的PCI驱动:

USB设备驱动中定义的usb_device_id一样,定义了pci_device_id结构体,并将它导出到用户空间。include/ linux/ mod_devicetable.h中。

struct pci_device_id {

       __u32 vendor, device;           /* Vendor and device ID or PCI_ANY_ID*/

       __u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */

       __u32 class, class_mask;      /* (class,subclass,prog-if) triplet */

       kernel_ulong_t driver_data;    /* Data private to the driver */

};
参数如下:

--- Vendor ID : 占16bit, 也就是2个字节,用来标识硬件厂商

A central authority governs the registry of vendor IDs,  and hardware manufactures must apply to this body when they would like a new vendor id.
--- Device ID :  占16bit,标识是哪类设备的ID,

The special value of PCI_ANY_ID functions as a wildcard.
--- subVendor ID
--- subDevice ID :
各占16bit, 标识 PCI 子系统中的厂商号和设备号。
These are typically set to PCI_ANY_ID , since matching vendor_id and device_id is usually sufficient for most purposes.
--- Class
--- Class mask : 
描述设备类型。
--- driver data : PCI 设备驱动私有数据,

1.3 内核提供了下面几个宏,来帮助生成 pci_device_id:

PCI_DEVICE(vend, dev);   // 用来指定 设备厂商ID和设备ID

/**
 * PCI_DEVICE - macro used to describe a specific pci device
 * @vend: the 16 bit PCI Vendor ID
 * @dev: the 16 bit PCI Device ID
 *
 * This macro is used to create a struct pci_device_id that matches a
 * specific device.  The subvendor and subdevice fields will be set to
 * PCI_ANY_ID.
 */
#define PCI_DEVICE(vend,dev) \
	.vendor = (vend), .device = (dev), \
	.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID

For example, the following manifest identifies the VIA 82C561 chipset:
    PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561) 
PCI_ANY_ID 定义如下:

#define PCI_ANY_ID (~0)
PCI_VDEVICE(vendor, device);  //这里传输的是vendor name, 通过宏PCI_VENDOR_ID, 来查出对应的vendor ID, 这点与 PCI_DEVICE()不同。

/**
 * PCI_VDEVICE - macro used to describe a specific pci device in short form
 * @vendor: the vendor name
 * @device: the 16 bit PCI Device ID
 *
 * This macro is used to create a struct pci_device_id that matches a
 * specific PCI device.  The subvendor, and subdevice fields will be set
 * to PCI_ANY_ID. The macro allows the next field to follow as the device
 * private data.
 */

#define PCI_VDEVICE(vendor, device)        \
    PCI_VENDOR_ID_##vendor, (device),    \       
    PCI_ANY_ID, PCI_ANY_ID, 0, 0

For example, the following matches the VIA 6410 chipset, and populates the driver_data field with the value of 1 .
Although driver_data isn’t used by the PCI core, your driver can use it for its own purposes:
    { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_6410),1 } 
PCI_DEVICE_CLASS(dev_class, dev_class_mask);  //用来指定 PCI 设备类型
/**
 * PCI_DEVICE_CLASS - macro used to describe a specific pci device class
 * @dev_class: the class, subclass, prog-if triple for this device
 * @dev_class_mask: the class mask for this device
 *
 * This macro is used to create a struct pci_device_id that matches a
 * specific PCI class.  The vendor, device, subvendor, and subdevice
 * fields will be set to PCI_ANY_ID.
 */
#define PCI_DEVICE_CLASS(dev_class,dev_class_mask) \
	.class = (dev_class), .class_mask = (dev_class_mask), \
	.vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
	.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
For example:
    PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) 
PCI驱动使用 宏MODULE_DEVICE_TABLE 来导出一系列 pci_device_id 结构,也就是PCI设备表到用户空间,
如下所示:
static struct pci_device_id netdrv_pci_tbl[] = {

       {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },

       {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NETDRV_CB },

       {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMC1211TX },

/*    {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MPX5030 },*/

       {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DELTA8139 },

       {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADDTRON8139 },

       {0,}

};

MODULE_DEVICE_TABLE (pci, netdrv_pci_tbl);

宏 DEFINE_PCI_DEVICE_TABLE() ;   ---用来便捷的定义一系列 pci_device_id

/**
 * DEFINE_PCI_DEVICE_TABLE - macro used to describe a pci device table
 * @_table: device table name
 *
 * This macro is used to create a struct pci_device_id array (a device table)
 * in a generic manner.
 */
#define DEFINE_PCI_DEVICE_TABLE(_table) \
	const struct pci_device_id _table[] __devinitconst
At build time, this information gets included into databases that user-space facilities like udev and modprobe can use to load appropriate drivers when matching devices are inserted
下图所示:


2. PCI 设备驱动

2.1  pci_driver 结构体

pci_driver 结构体用来定义PCI驱动。

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  (*suspend_late) (struct pci_dev *dev, pm_message_t state);

       int  (*resume_early) (struct pci_dev *dev);

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

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

       struct pci_error_handlers *err_handler;

       struct device_driver       driver;   //设备驱动,

       struct pci_dynids dynids;

};

参数:

--- name : 驱动名,通常设置成模块名

--- *id_table :  指向 PCI驱动对应的一系列 pci_device_id 表的指针。

--- *probe : 当PCI core 发现一个设备与 *id_table 中的选项配对上时,该函数会被调用。

--- *remove : 当设备移除或驱动被卸载时,会调用该函数。

2.2 pci_register_driver(struct pci_driver *driver);  --- 注册 PCI 驱动。

定义如下:

/*
 * pci_register_driver must be a macro so that KBUILD_MODNAME can be expanded
 */
#define pci_register_driver(driver)		\
	__pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
/**
 * __pci_register_driver - register a new pci driver
 * @drv: the driver structure to register
 * @owner: owner module of drv
 * @mod_name: module name string
 * 
 * Adds the driver structure to the list of registered drivers.
 * Returns a negative value on error, otherwise 0. 
 * If no error occurred, the driver remains registered even if 
 * no device was claimed during registration.
 */
int __pci_register_driver(struct pci_driver *drv, struct module *owner,
			  const char *mod_name)
{
	int error;

	/* initialize common driver fields */
	drv->driver.name = drv->name;
	drv->driver.bus = &pci_bus_type;     //指明是PCI bus 类型
	drv->driver.owner = owner;
	drv->driver.mod_name = mod_name;

	spin_lock_init(&drv->dynids.lock);
	INIT_LIST_HEAD(&drv->dynids.list);

	/* register with core */
	error = driver_register(&drv->driver);     //注册设备驱动
	if (error)
		goto out;

	error = pci_create_newid_file(drv);
	if (error)
		goto out_newid;

	error = pci_create_removeid_file(drv);
	if (error)
		goto out_removeid;
out:
	return error;

out_removeid:
	pci_remove_newid_file(drv);
out_newid:
	driver_unregister(&drv->driver);
	goto out;
}
pci_create_newid_file(struct pci_driver *drv)
static int
pci_create_newid_file(struct pci_driver *drv)
{
	int error = 0;
	if (drv->probe != NULL)
		error = driver_create_file(&drv->driver, &driver_attr_new_id);
	return error;
}

pci_create_removeid_file(struct pci_driver *drv);

static int
pci_create_removeid_file(struct pci_driver *drv)
{
	int error = 0;
	if (drv->probe != NULL)
		error = driver_create_file(&drv->driver,&driver_attr_remove_id);
	return error;
}
static void pci_remove_newid_file(struct pci_driver *drv);

static void pci_remove_newid_file(struct pci_driver *drv)
{
	driver_remove_file(&drv->driver, &driver_attr_new_id);
}

void pci_unregister_driver(struct pci_driver *drv);     --- 卸载 pci_driver

/**
 * pci_unregister_driver - unregister a pci driver
 * @drv: the driver structure to unregister
 * 
 * Deletes the driver structure from the list of registered PCI drivers,
 * gives it a chance to clean up by calling its remove() function for
 * each device it was responsible for, and marks those devices as
 * driverless.
 */

void
pci_unregister_driver(struct pci_driver *drv)
{
	pci_remove_removeid_file(drv);
	pci_remove_newid_file(drv);
	driver_unregister(&drv->driver);
	pci_free_dynids(drv);
}
例子:

static struct pci_driver mydev_driver = {
    .name     = “mydev”,
    .id_table = mydev_devices,
    .probe    = mydev_probe,
    .remove = mydev_remove,
};

static int __init
mydev_init(void)
{
	return pci_register_driver(&mydev_driver);
}
	
static void __exit
mydev_exit(void)
{
	pci_unregister_driver(&driver);
}
2.3 使能/禁止 PCI设备:

int pci_enable_device(struct pci_dev *dev); ---

/**
 * pci_enable_device - Initialize device before it's used by a driver.
 * @dev: PCI device to be initialized
 *
 *  Initialize device before it's used by a driver. Ask low-level code
 *  to enable I/O and memory. Wake up the device if it was suspended.
 *  Beware, this function can fail.
 *
 *  Note we don't actually enable the device many times if we call
 *  this function repeatedly (we just increment the count).
 */
int pci_enable_device(struct pci_dev *dev)
{
	return __pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);
}
void pci_disable_device(struct pci_dev *dev);

/**
 * pci_disable_device - Disable PCI device after use
 * @dev: PCI device to be disabled
 *
 * Signal to the system that the PCI device is not in use by the system
 * anymore.  This only involves disabling PCI bus-mastering, if active.
 *
 * Note we don't actually disable the device until all callers of
 * pci_enable_device() have called pci_disable_device().
 */
void
pci_disable_device(struct pci_dev *dev)
{
	struct pci_devres *dr;

	dr = find_pci_dr(dev);
	if (dr)
		dr->enabled = 0;

	if (atomic_sub_return(1, &dev->enable_cnt) != 0)
		return;

	do_pci_disable_device(dev);

	dev->is_busmaster = 0;
}
pci  probe() 中通常会用到,pci_enable_device(), 如下所示:

static int __devinit
mydev_probe(struct pci_dev *pdev,
            const struct pci_device_id *id)
{
	int rc = pci_enable_device(pdev);    //使能pci设备, Must enable the device before we can access any of its memory resources.
	u8 irq; 

	if (rc) { /* handle error */
		return rc; 
	} 

	rc = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); //Read the IRQ from the device’s configuration space,
                                                                                                             //using one of the provided functions for reading/writing to/from the device configuration space.
       if (rc) { /* handle error */ 
	   	return rc; 
	} 

	rc = request_irq(irq, mydev_interrupt, IRQF_SHARED, “mydev”, NULL); //With IRQ known, we can proceed as usual. … 
return 0; 
} 






1. PCI总线。

PCI总线体系结构中,PCI桥设备比较重要,它将父总线与子总线连接在一起。

 

PCI桥一种特殊的PCI设备,包括以下几种:

     -----Host/PCI桥,用于连接CPUPCI根总线,第一个根总线的编号为0

      -----PCI-to-PCI桥,用于连接PCI父总线与子总线。

       -----PCI/ISA桥,用于连接老式的ISA总线。

 

PCI总线用pci_bus结构来描述。在kernel/ include/ linux / pci.h

struct pci_bus {

       struct list_head node;            /* node in list of buses */

       struct pci_bus *parent;   /* parent bus this bridge is on */

       struct list_head children;       /* list of child buses */

       struct list_head devices; /* list of devices on this bus */

       struct pci_dev *self;             /* bridge device as seen by parent */

       struct resource       *resource[PCI_BUS_NUM_RESOURCES];

                                   /* address space routed to this bus */

 

       struct pci_ops *ops;             /* configuration access functions */

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

       struct proc_dir_entry *procdir;     /* directory entry in /proc/bus/pci */

 

       unsigned char number;         /* bus number */

       unsigned char primary;  /* number of primary bridge */

       unsigned char secondary;      /* number of secondary bridge */

       unsigned char subordinate;    /* max number of subordinate buses */

 

       char        name[48];

 

       unsigned short  bridge_ctl;   /* manage NO_ISA/FBB/et al behaviors */

       pci_bus_flags_t bus_flags;    /* Inherited by child busses */

       struct device          *bridge;

       struct class_device class_dev;

       struct bin_attribute *legacy_io; /* legacy I/O for this bus */

       struct bin_attribute *legacy_mem; /* legacy mem */

};

2. PCI设备

所有类型的PCI设备都可以用pci_dev结构描述。在在kernel/ include/ linux / pci.h

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           revision;  /* PCI revision, low byte of class word */

       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_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;

       unsigned int    is_managed:1;

       atomic_t  enable_cnt;     /* pci_enable_device has been called */

 

       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 */

#ifdef CONFIG_PCI_MSI

       struct list_head msi_list;

#endif

};

3.PCI 配置空间的访问。

 

PCI有3种地址空间:I/O空间,内存地址空间和配置空间

CPU可以访问所有的地址空间。

I/O空间和内存地址空间由设备驱动程序使用。

PCI配置空间由linux内核中的PCI初始代码使用,这些代码用于配置PCI设备,比如中断号,I/O或内存基地址。

/include /linux /pci_regs.h文件定义了PCI配置空间寄存器。

pci_bus结构体中的pci_ops指针成员,指向PCI总线所使用的配置空间的访问操作

struct pci_ops {

       int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val);

       int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val);

};
也在在 kernel/ include/ linux / pci.h中, 具体访问函数如下:

int pci_bus_read_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 *val);

int pci_bus_read_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 *val);

int pci_bus_read_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 *val);

int pci_bus_write_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 val);

int pci_bus_write_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 val);

int pci_bus_write_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 val);

 

static inline int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val)

{

       return pci_bus_read_config_byte (dev->bus, dev->devfn, where, val);

}

static inline int pci_read_config_word(struct pci_dev *dev, int where, u16 *val)

{

       return pci_bus_read_config_word (dev->bus, dev->devfn, where, val);

}

static inline int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val)

{

       return pci_bus_read_config_dword (dev->bus, dev->devfn, where, val);

}

static inline int pci_write_config_byte(struct pci_dev *dev, int where, u8 val)

{

       return pci_bus_write_config_byte (dev->bus, dev->devfn, where, val);

}

static inline int pci_write_config_word(struct pci_dev *dev, int where, u16 val)

{

       return pci_bus_write_config_word (dev->bus, dev->devfn, where, val);

}

static inline int pci_write_config_dword(struct pci_dev *dev, int where, u32 val)

{

       return pci_bus_write_config_dword (dev->bus, dev->devfn, where, val);

}
4. PCI 设备驱动结构。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值