6/26/2011 7:27:13 PM
DMA engine
static int __init dma_bus_init(void)
{
mutex_init(&dma_list_mutex);
return class_register(&dma_devclass);
}
struct class_device_attribute {
struct attribute attr;
ssize_t (*show)(struct class_device *, char * buf);
ssize_t (*store)(struct class_device *, const char * buf, size_t count);
};
类设备属性
static struct class_device_attribute dma_class_attrs[] = {
__ATTR(memcpy_count, S_IRUGO, show_memcpy_count, NULL),
__ATTR(bytes_transferred, S_IRUGO, show_bytes_transferred, NULL),
__ATTR(in_use, S_IRUGO, show_in_use, NULL),
__ATTR_NULL
};
拷贝计数和字节传输
Linux驱动模型从面向对象的观点,提供了通用的、一致性的数据模型,用来描述总线和设备以及它们的层次结构。
Linux驱动模型抽象出统一的总线、设备接口、桥接口、驱动程序等模型,形成驱动程序的基类。这些基类抽象出共有特性和方法函数,
具体的设备驱动程序从基类继承产生派生类。例如:PCI设备结构从基类device继承,继承方法如下:
struct pci_dev {
...
struct device device;
};
通过对象化处理后,设备驱动程序之间层次结构关系转换成类对象之间的层次关系,驱动程序的基类从kobject继承,这样,驱动程序类对象之间的层次关系反映到文件系统sysfs中,用户空间的应用程序通过sysfs可以查看或操作类对象的数据。
通常,计算机的周边设备都是通过总线与处理器连接。在Linux内核中,每个总线类型(如:PCI、USB等)应该声明一个静态的总线类型对象实例,并初始化该对象的部分成员,这些成员一般在驱动程序的运行过程中无变化。总线类型对象也简称总线对象。
当总线驱动程序初始化时,它调用函数bus_register注册总线类型实例:初始化总线类型实例中的易变化的成员,将对象插入总线类型对象的全局链表中。一旦注册了总线对象,总线驱动程序就可以使用该对象的成员。
每一种总线类型都用一个总线类型对象结构bust_type进行描述,它包括了一类总线的通用操作函数、总线属性、总线类型私有数据等。其列出如下(在include/linux/device.h中):
注册总线类型实例
struct bus_type {
const char *name;
/*总线属性,用于在文件系统sysfs显示总线的属性*/
struct bus_attribute *bus_attrs;
struct device_attribute *dev_attrs; /*设备属性,用于在sysfs中显示*/
struct driver_attribute *drv_attrs;/*驱动程序属性,用于在sysfs中显示*/
/*通过把总线驱动程序支持的设备ID与特定设备ID进行比较,来决定总线是否支持这个设备。当总线注册了驱动程序时,系统遍历总线的设备链表,并且每个没有相关设备驱动程序的设备会调用match回调函数*/
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev); /*探测设备是否在工作状态*/
int (*remove)(struct device *dev); /*移去设备*/
void (*shutdown)(struct device *dev); /*关闭设备*/
/*用于总线上的设备的电源管理*/
int (*suspend)(struct device *dev, pm_message_t state); /*挂起设备时的操作*/
int (*suspend_late)(struct device *dev, pm_message_t state); /*挂起设备后的操作*/
int (*resume_early)(struct device *dev); /*恢复设备早期的操作*/
int (*resume)(struct device *dev); /*恢复设备时的操作*/
struct pm_ext_ops *pm; /*电源管理其他的操作*/
struct bus_type_private *p; /*总线类型私有数据*/
};
结构bus_type_private含有总线类型对象的私有数据,含有实际的内核对象信息,允许静态分配结构bus_type。驱动程序核心代码使用该结构,用户编写的驱动程序不许使用该结构。
总线属性 设备属性 驱动属性
struct bus_type_private {
struct kset subsys; /*主kobject,定义此总线的kset*/
struct kset *drivers_kset; /*与此总线相关的驱动程序的链表*/
struct kset *devices_kset; /*与此总线相关的设备的链表*/
struct klist klist_devices; /*用于在devices_kset上遍历的klist*/
struct klist klist_drivers; /*用于在drivers_kset上遍历的klist*/
struct blocking_notifier_head bus_notifier; /*总线通知链表,用于总线所关心的事件*/
unsigned int drivers_autoprobe:1;
struct bus_type *bus; /*用于回指总线类型对象*/
};
bus_type_private 描述了挂载在bus上的设备和驱动等
当一个总线驱动程序被初始化时,它调用函数bus_register初始化了总线类型对象,并将对象插入到内核对象树中。函数bus_unregister用来注销bus。两个函数原型列出如下:
int bus_register(struct bus_type * bus);
extern void bus_unregister(struct bus_type *bus);
总线注册和取消注册
内核分别存在结构devices的链表和结构device_drivers的链表,利用下面的辅助函数可以遍历这两个链表,这两个函数遍历链表,并调用每个设备或驱动程序的回调函数。在回调函数调用之前,链表中的对象的引用计数加1,在下一个对象已获得之后,它个计数减1。
int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data,
int (*fn)(struct device *, void *));
int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
void * data, int (*fn)(struct device_driver *, void *));
遍历总线上的所有设备和驱动
在sysfs文件系统的bus目录下,每个总线获得一个目录。在总线驱动程序目录drivers里,每个与总线注册在一起的驱动程序有一个目录。在总线中发现的每个设备在bus/devices目录里有一个符号链接指向物体体系中的设备的目录。
PCI总线在文件系统sysfs中显示的内容列出如下:
^-^$ tree /sys/bus/pci
/sys/bus/pci /*对应总线属性结构*/
|-- devices /*对应设备属性结构*/
| |-- 0000:00:00.0 -> ../../../devices/pci0000:00/0000:00:00.0
| |-- 0000:00:00.1 -> ../../../devices/pci0000:00/0000:00:00.1
| ……
| `-- 0000:04:09.0 -> ../../../devices/pci0000:00/0000:00:10.0/0000:04:09.0
|-- drivers /*对应驱动程序属性结构*/
| |-- 8139cp
| | |-- bind
| | |-- module -> ../../../../module/8139cp
| | |-- new_id
| | |-- uevent
| | `-- unbind
| ……
| `-- yenta_cardbus
| |-- bind
| |-- module -> ../../../../module/yenta_socket
| |-- new_id
| |-- uevent
| `-- unbind
|-- drivers_autoprobe
|-- drivers_probe
|-- slots
`-- uevent
查看总线和总线上的设备