1. 设备,LDM中用device结构体来描述设备
- struct device {
- /* 父设备,通常是某种总线或者是宿主设备 */
- struct device *parent;
- /* 私有数据指针:子设备链表,父设备链表节点,驱动程序链表节点,总线链表节点 */
- struct device_private *p;
- struct kobject kobj; /* 连接到结构体系中的kobject */
- const char *init_name; /* initial name of the device */
- const struct device_type *type; /* 设备类型 */
- struct bus_type *bus; /* 依附的总线 */
- struct device_driver *driver; /* 关联的驱动程序 */
- void *platform_data; /* Platform specific data*/
- dev_t devt; /* dev_t, creates the sysfs"dev" */
- struct klist_node knode_class; /* */
- struct class *class; /* 设备所属的类 */
- const struct attribute_group **groups; /* optional groups */
- void (*release)(structdevice *dev); /* 当引用计数(kobject->kref)减为0时调用 */
- };
我们在注册一个设备时,通常要初始化parent,name,bus_id和bus这四个成员。
2. 设备的注册和注销
- int device_register(struct device *dev)
- void device_unregister(struct device *dev)
注册函数分析,注册函数分为两个部分device_initialize和device_add,具体分析一下device_add
- int device_add(structdevice *dev)
- {
- struct device *parent = NULL;
- struct class_interface *class_intf;
- int error = -EINVAL;
- /* 引用计数加1 */
- dev = get_device(dev);
- /*
- * for statically allocated devices, whichshould all be converted
- * some day, we need to initialize the name. We prevent reading back
- * the name, and force the use of dev_name()
- */
- if (dev->init_name) {
- dev_set_name(dev,"%s", dev->init_name);
- dev->init_name = NULL;
- }
- /* 增加父设备的引用计数 */
- parent = get_device(dev->parent);
- setup_parent(dev, parent);
- /* 把内嵌的kobject注册到设备模型中 */
- error = kobject_add(&dev->kobj,dev->kobj.parent, NULL);
- /*创建属性文件uevent */
- error = device_create_file(dev,&uevent_attr);
- /* 如果定义了devt,则产生dev属性,并在/dev目录下产生设备节点文件 */
- if (MAJOR(dev->devt)) {
- error = device_create_file(dev,&devt_attr);
- }
- /* 创建属性文件 */
- error = device_add_class_symlinks(dev);
- error = device_add_attrs(dev);
- /* 把设备添加到总线上,这个是重点,稍后分析*/
- error = bus_add_device(dev);
- /*产生KOBJ_ADD uevent */
- kobject_uevent(&dev->kobj,KOBJ_ADD);
- /* 给设备探测相应的驱动 */
- bus_probe_device(dev);
- /* 如果设备有父设备,将它加入parent的子设备链中 */
- if (parent)
- klist_add_tail(&dev->p->knode_parent,
- &parent->p->klist_children);
- /* 如果设备附属于某个类,则需完成相应工作*/
- if (dev->class) {
- /* 把设备添加到class的设备链表中,完成关联*/
- klist_add_tail(&dev->knode_class,
- &dev->class->p->klist_devices);
- /* notify any interfaces thatthe device is here */
- list_for_each_entry(class_intf,
- &dev->class->p->class_interfaces, node)
- if(class_intf->add_dev)
- class_intf->add_dev(dev,class_intf);
- }
- }
- int bus_add_device(struct device *dev)
- {
- /* 引用计数加一 */
- struct bus_type *bus =bus_get(dev->bus);
- if (bus) {
- /* 创建相应的属性文件 */
- error = device_add_attrs(bus,dev);
- error =sysfs_create_link(&bus->p->devices_kset->kobj,
- &dev->kobj,dev_name(dev));
- error =sysfs_create_link(&dev->kobj,
- &dev->bus->p->subsys.kobj,"subsystem");
- /* 把设备加入到总线的设备链中,这步才是重点*/
- klist_add_tail(&dev->p->knode_bus,&bus->p->klist_devices);
- }
- }
bus_probe_device()->device_attach()
- int device_attach(struct device *dev)
- {
- if (dev->driver) {
- /* 如果设备已经依附于某个驱动,进行绑定 */
- ret =device_bind_driver(dev);
- } else {
- /* 遍历bus->p->klist_drivers寻找匹配的驱动,然后执行回调函数__device_attach */
- ret = bus_for_each_drv(dev->bus, NULL, dev,__device_attach);
- }
- out_unlock:
- device_unlock(dev);
- return ret;
- }
__device_attach()->driver_probe_device()->really_probe()->bus->probe()->drv->probe() 总线中定义的probe函数会优先执行,如果总线中没有定义probe才会执行驱动中定义的probe
3. 设备属性
设备属性由device_attribute来表示
- structdevice_attribute {
- struct attribute attr; /* 属性 */
- ssize_t (*show)(struct device *dev,struct device_attribute *attr,
- char *buf); /* 读取属性 */
- ssize_t (*store)(struct device *dev,struct device_attribute *attr,
- const char *buf, size_t count); /* 写入属性 */
- };
初始化设备属性:
- DEVICE_ATTR(_name,_mode, _show, _store)
在sysfs目录下产生和移除属性文件
- int device_create_file(struct device *device,
- conststruct device_attribute *entry);
- void device_remove_file(struct device *dev,
- const struct device_attribute*attr);
4. 实例解析
创建一个设备和它的属性version,并将此设备挂接到之前创建的总线上
- /*
- * for learn device
- */
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/device.h>
- extern struct bus_type scbus_type;
- extern struct device scbus;
- static char *Version = "revision1.0";
- /* 析构函数,引用计数减为0时调用 */
- void screlease(struct device *dev)
- {
- printk("scbusrelease\n");
- }
- struct device scdevice = {
- .parent = &scbus, /* 父设备,此处为依附的总线 */
- .init_name = "scdevice0",
- .bus = &scbus_type, /* 依附的总线类型 */
- .release = screlease,
- };
- /*
- * export device attribute
- */
- static ssize_t show_device_version(structdevice *dev,
- structdevice_attribute *attr, char *buf)
- {
- returnsprintf(buf, "%s\n", Version);
- }
- DEVICE_ATTR(version, 0666,show_device_version, NULL);
- static int __init scdevice_init(void)
- {
- intret;
- /*注册设备 */
- ret= device_register(&scdevice);
- if(ret)
- returnret;
- /*创建属性 */
- ret= device_create_file(&scdevice, &dev_attr_version);
- if(ret)
- gotoerr_create;
- printk("Createa scdevice");
- return0;
- err_create:
- device_unregister(&scdevice);
- returnret;
- }
- static void __exit scdevice_exit(void)
- {
- device_remove_file(&scdevice,&dev_attr_version);
- device_unregister(&scdevice);
- printk("Removea scdevice");
- }
- module_init(scdevice_init);
- module_exit(scdevice_exit);
- MODULE_LICENSE("Dual BSD/GPL");
- MODULE_AUTHOR("CJOK<cjok.liao@gmail.com>");
试验结果: