linux/include/linux/device.h
struct device { struct klist klist_children; struct klist_node knode_parent; /* node in sibling list */ struct klist_node knode_driver; struct klist_node knode_bus; struct device *parent; struct kobject kobj; char bus_id[BUS_ID_SIZE]; /* position on parent bus */ struct device_type *type; unsigned is_registered:1; unsigned uevent_suppress:1; struct semaphore sem; /* semaphore to synchronize calls to * its driver.*/
struct bus_type * bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */
void *driver_data; /* data private to the driver */ void *platform_data; /* Platform specific data, device core doesn’t touch it */
struct dev_pm_info power;
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
u64 *dma_mask; /* dma mask (if dma’able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all
hardware supports 64 bit addresses for consistent allocations such descriptors. */
struct list_head dma_pools; /* dma pools (if dma’ble) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ /* arch specific additions */ struct dev_archdata archdata; spinlock_t devres_lock; struct list_head devres_head; /* class_device migration path */ struct list_head node; struct class *class; dev_t devt; /* dev_t, creates the sysfs “dev” */ struct attribute_group **groups; /* optional groups */ void (*release)(struct device * dev); };
二、作用: 用于描述设备相关的信息设备之间的层次关系,以及设备与总线、驱动的关系。 三、详解: 1、struct klist klist_children; struct klist被定义在linux/include/linux/klist.h中,原型是: struct klist { spinlock_t k_lock; struct list_head k_list; void (*get)(struct klist_node *); void (*put)(struct klist_node *); };
可见它是对struct list_head的扩展,在此它的作用是连接设备列表中的孩子列表。 2、struct klist_node knode_parent; /* node in sibling list */ struct klist_node被定义在linux/include/linux/klist.h,原型是: struct klist_node { struct klist * n_klist; struct list_head n_node; struct kref n_ref; struct completion n_removed; };
在此它的作用是表示它的兄弟节点。 3、struct klist_node knode_driver; 表示它的驱动节点。 4、struct klist_node knode_bus; 表示总线节点。 5、struct device *parent; 指向其父设备。 6、struct kobject kobj; 这里http://blog.chinaunix.net/u1/55599/showart.php?id=1086478 有对kobject的解释,此处
它是内嵌的一个kobject对象。 7、char bus_id[BUS_ID_SIZE]; bus_id表示其在父总线上的位置。BUS_ID_SIZE被定义为:
#define KOBJ_NAME_LEN 20 /*linux/include/linux/kobject.h*/
#define BUS_ID_SIZE KOBJ_NAME_LEN /*linux/include/linux/device.h*/
所以表示位置的字符串长度不能超过20。 8、struct device_type *type; 被定义在/linux/include/linux/device.h中,原型是: struct device_type { const char *name; struct attribute_group **groups; int (*uevent)(struct device *dev, char **envp, int num_envp,char *buffer, int
buffer_size); void (*release)(struct device *dev); int (*suspend)(struct device * dev, pm_message_t state); int (*resume)(struct device * dev); };
device_type结构表示设备的类型。一个设备类或者总线可以包含不同类型的设备,例如“分区
”和“磁盘” , “鼠标”和“事件” 。device_type就可以标识某个设备类型和该设备的特有
信息,它就等同于kobject结构中的kobj_type一样。如果name数据成员被指定,那么uevent成员
函数就会把它包含在DEVTYPE变量中。 9、unsigned is_registered:1; 标识该设备是否已经被注册过。is_registered:1这样的形式表示is_registered这个变量只有一
位。在32位linux系统下,unsigned是4字节32位,而经过is_registered:1这样的限制后,变量
is_registered只有一位,其取值只能是1或者0,相当于声明了一个boolean类型的变量。在此种
用法中,后面指定数据宽度的值只能大于0且小于本身的数据宽度。 10、struct bus_type * bus; 指向所连接总线的指针。 11、struct device_driver *driver; 指向被分配到该设备的设备驱动。 12、u64 *dma_mask; /*指向设备DMA屏蔽字。*/ u64 coherent_dma_mask;/*设备一致性DMA的屏蔽字。*/ struct list_head dma_pools; /*聚集的DMA缓冲池。*/ struct dma_coherent_mem *dma_mem; /*指向设备所使用的一致性DMA存储器描述符的指针*/ 13、spinlock_t devres_lock; 定义一个设备自旋锁,用于互斥访问设备。关于自旋锁的详细讲解参考:http://www.deansys.com/doc/ldd3/ch05s05.html 14、void (*release)(struct device * dev); 释放设备描述符的回调函数。 四、操作: linux内核系统了一系列完整的对device操作的函数。 1、其中device_register()函数用来将一个新的device对象插入设备模型。它在
linux/drivers/base/core.c中被实现: int device_register(struct device *dev){ device_initialize(dev);
return device_add(dev);}
该函数首先是调用device_initialize()初始化device结构,具体是初始化嵌入的kobject结构
dev->kobj,初始化列表中的孩子列表kobj->klist_children,初始化DMA缓冲池dev->dma_pools,
初始化自旋锁dev->devres_lock等。接下来device_add()函数才真正将该device对象dev插入设
备模型中。device_add()函数首先是通过kboject_add()函数将它添加到kobject层次,再把它添
加都全局和兄弟链表中,最后添加到其他相关的子系统的驱动程序模型,完成device对象的注册
。
2、device_unregister()完成相反的过程:/linux/drivers/base/core.c void device_unregister(struct device * dev){ pr_debug(“DEV: Unregistering device. ID = ‘%s’\n”, dev->bus_id); device_del(dev); put_device(dev); }
它会先以KERN_DEBUG级别打印注销设备的信息,然后才真正删除设备,减少设备对象的引用计数
。 3、get_device()和put_device()分别是增加和减少设备对象的引用计数。这两个函数都定义在
:/linux/drivers/base/core.c中。具体是应用在注册device对象时,device_add()函数会调用
get_device()增加对该device对象的引用计数。在注销设备对象时,device_unregister()函数
直接调用put_device()函数减少对该device对象的引用计数。
device_driver
系统的每一个驱动程序都由struct device_driver,定义在/include/linux/device.h: struct device_driver { const char * name;
/*设备驱动程序的名称。在调用driver_register()往设备驱动程序模型中插入一个新的device_driver对象时,driver_register()函数会调用bus_add_driver()函数,bus_add_driver()调用kobject_set_name()函数将name赋给drv>kobj.name或者drv->kobj.k_name。注:drv为要调用driver_register()注册的device_driver类型的对象。*/ struct bus_type * bus; //指向总线描述符的指针,总线连接所支持的设备 struct kobject kobj; //内嵌的kobject结构,主要用于计数 struct klist klist_devices; struct klist_node knode_bus; struct module * owner; const char * mod_name; struct module_kobject * mkobj; 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 (*resume) (struct device * dev); //将挂起的驱动恢复运行 };
调用函数:
1、driver_register()函数:/linux/drivers/base/driver.c int driver_register(struct device_driver * drv) {if ((drv->bus->probe && drv->probe) ||(drv->bus->remove && drv->remove) ||(drv->bus->shutdown && drv->shutdown)) {printk(KERN_WARNING “Driver ‘%s’ needs updating - please use bus_type methods/n”, drv->name);} klist_init(&drv->klist_devices, NULL, NULL); return bus_add_driver(drv);} 函数首先是检查device_driver类型的drv对象和其bus域的回调函数是否已经被赋值,如果为空,则打印警告。核心的注册是调用bus_add_driver()函数实现。在bus_add_driver()函数中,首先是检查drv->bus,如果为空可立即返回EINVAL(无效的变量),注册失败,可见bus域必须提前初始化好才行。接下来是对kobj域进行初始化,检查bus域等。最后调用add_bind_files()函数(实际上是由add_bind_files()函数调用driver_create_file()函数)在sys文件系统下为其创建一个新的目录。
2、driver_unregister()函数:/linux/drivers/base/driver.c/linux/drivers/base/driver.c void driver_unregister(struct device_driver * drv) { bus_remove_driver(drv); } 调用bus_remove_driver在/linux/drivers/base/bus.c: void bus_remove_driver(struct device_driver * drv) { if (!drv->bus) return;
remove_bind_files(drv); driver_remove_attrs(drv->bus, drv); klist_remove(&drv->knode_bus); pr_debug(“bus %s: remove driver %s/n”, drv->bus->name, drv->name); driver_detach(drv); module_remove_driver(drv); kobject_unregister(&drv->kobj); put_bus(drv->bus); }
在bus_remove_driver()函数中首先是检查要卸载的device_driver类型的对象drv的bus域,如果为空则返回。此种情况会发生在调用driver_register()函数注册drv时没有检查返回值,注册失败,但程序依然向下运行,到driver_unregister()时就会到688行处返回。接下来会删除在调用driver_register()函数注册时在sys文件系统中创建的目录,然后删除drv对象的属性,(属性是记录在文件中的,删除属性其实是删除记录drv->bus属性的文件),删除驱动模块,减少对drv->bus的引用计数等。
3、get_driver()和put_driver()函数:分别是增加和减少对该device_drvier类型的对象的引用计数。
4、int driver_create_file(struct device_driver * drv, struct driver_attribute * attr);
void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr);
分别是在sys文件系统中为device_driver对象创建和删除目录