Chinaunix首页 | 论坛 | 博客
  • 博客访问: 274383
  • 博文数量: 81
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 878
  • 用 户 组: 普通用户
  • 注册时间: 2014-07-25 23:20
文章分类

全部博文(81)

文章存档

2017年(45)

2016年(20)

2015年(2)

2014年(14)

我的朋友

分类: C#/.net

2017-01-29 21:06:32

最近分析了下Linux设备管理相关的代码【linux/drivers】,本来想偷个懒,直接转一篇Linux设备模型的文章呢,结果找了半天发现还不如自己笔记呢。。哎。。越来越懒了。。

这里我们以Linux2.6.11的源码为基础,简单的分析下设备模型框架相关的代码和实现。由于代码比较多,这里只简单的笔记下主干流程代码。

bus 

Linux设备驱动模型是以总线bus为基础框架的, 各种设备和驱动均挂载到指定的bus上 

bus的数据类型定义:

device.h: struct bus_type { char * name; struct subsystem	subsys; /* bus实际上是以subsys为基础构建的 */ struct kset		drivers;/* bus上可以挂载多个driver */ struct kset		devices;/* bus上可以挂载多个device */ 。。。。。 。。。。。 };

当添加一个新的bus时,调用函数bus_register

bus.c /* 这里声明了bus管理子系统bus_subsys,用来管理所有的bus实例。*/ decl_subsys(bus, &ktype_bus, NULL); /* 注册一个bus到kernel中,这里可以搜索下,会发现有很多地方调用了该函数 */ int bus_register(struct bus_type * bus) { int retval; retval = kobject_set_name(&bus->subsys.kset.kobj, "%s", bus->name); if (retval) goto out; /* 将bus实例添加到bus_subsys中 */ subsys_set_kset(bus, bus_subsys); retval = subsystem_register(&bus->subsys); if (retval) goto out; /* bus实例下管理的devices,这里也放到subsys中 */ kobject_set_name(&bus->devices.kobj, "devices"); bus->devices.subsys = &bus->subsys; retval = kset_register(&bus->devices); if (retval) goto bus_devices_fail; /* bus实例下管理的drivers,这里也放到subsys中 */ kobject_set_name(&bus->drivers.kobj, "drivers"); bus->drivers.subsys = &bus->subsys; bus->drivers.ktype = &ktype_driver; retval = kset_register(&bus->drivers); if (retval) goto bus_drivers_fail; bus_add_attrs(bus); pr_debug("bus type '%s' registered\n", bus->name); return 0; bus_drivers_fail: kset_unregister(&bus->devices); bus_devices_fail: subsystem_unregister(&bus->subsys); out: return retval; }

简单的分析完bus_register函数,我们以PCI总线为例,看下Linux:

#/sys/bus目录代表了bus_subsys #/sys/bus/pci目录代表了pci目录注册实例 [root@MyCloudServer pci]# pwd /sys/bus/pci [root@MyCloudServer pci]# ls #devices  drivers为两个目录,对应于bus_register函数 devices  drivers  drivers_autoprobe  drivers_probe  rescan  resource_alignment  slots  uevent

device 

分析完bus,我们来看下device,device的数据类型定义:

device.h: struct device { 。。。。。 。。。。。 /*
device挂在bus上,一个device只能对应一个driver,
*/ struct bus_type * bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this
					   device */ 。。。。。 。。。。。 };

当添加一个新的设备时,会调用函数device_register

core.c /*
这里声明了device管理子系统devices_subsys,用来管理所有的devices实例。同时由于device有挂在bus上,
因此bus上也会有一个device实例,是以超链接的形式链接的。
*/ decl_subsys(devices, &ktype_device, &device_hotplug_ops); int device_register(struct device *dev) { device_initialize(dev); return device_add(dev); } void device_initialize(struct device *dev) { /* 将device放到devices_subsys中 */ kobj_set_kset_s(dev, devices_subsys); kobject_init(&dev->kobj); INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->children); INIT_LIST_HEAD(&dev->driver_list); INIT_LIST_HEAD(&dev->bus_list); INIT_LIST_HEAD(&dev->dma_pools); } int device_add(struct device *dev) { struct device *parent = NULL; int error = -EINVAL; 。。。。。 。。。。。 goto Error; if ((error = device_pm_add(dev))) goto PMError; /* 将device挂到bus上 */ if ((error = bus_add_device(dev))) goto BusError; down_write(&devices_subsys.rwsem); 。。。。。 。。。。。 } bus.c: int bus_add_device(struct device * dev) { struct bus_type * bus = get_bus(dev->bus); int error = 0; if (bus) { down_write(&dev->bus->subsys.rwsem); pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id); list_add_tail(&dev->bus_list, &dev->bus->devices.list); device_attach(dev); up_write(&dev->bus->subsys.rwsem); device_add_attrs(bus, dev); /* 通过超链接的形式,将device挂到bus上 */ sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id); } return error; }

在Linux上找个例子看下:

[root@MyCloudServer devices]# pwd /sys/bus/xen/devices [root@MyCloudServer devices]# ls -l
total 0 #可以看到,这里是超链接的形式 lrwxrwxrwx 1 root root 0 Dec 12 23:40 vbd-51712 -> ../../../devices/vbd-51712 lrwxrwxrwx 1 root root 0 Dec 12 23:40 vbd-51728 -> ../../../devices/vbd-51728 lrwxrwxrwx 1 root root 0 Dec 12 23:40 vif-0 -> ../../../devices/vif-0

driver 

接着分析driver,driver数据类型定义:

device.h: struct device_driver { char * name; /* driver也是挂在bus上 */ struct bus_type * bus; struct semaphore	unload_sem; struct kobject		kobj; /* 一个driver可以对应多个device,比如一个USB驱动可以同时对应多个USB设备 */ struct list_head	devices; struct module * owner; 。。。。。 。。。。。 };

当添加一个新的driver时,会调用函数driver_register

driver.c int driver_register(struct device_driver * drv) { INIT_LIST_HEAD(&drv->devices); init_MUTEX_LOCKED(&drv->unload_sem); return bus_add_driver(drv); } bus.c: int bus_add_driver(struct device_driver * drv) { struct bus_type * bus = get_bus(drv->bus); int error = 0; if (bus) { pr_debug("bus %s: add driver %s\n", bus->name, drv->name); error = kobject_set_name(&drv->kobj, "%s", drv->name); if (error) { put_bus(bus); return error; } /* 将driver挂到bus上 */ drv->kobj.kset = &bus->drivers; if ((error = kobject_register(&drv->kobj))) { put_bus(bus); return error; } down_write(&bus->subsys.rwsem); driver_attach(drv); up_write(&bus->subsys.rwsem); module_add_driver(drv->owner, drv); driver_add_attrs(bus, drv); } return error; }

那么device和driver是如何相互绑定的呢?这里简单的笔记几个函数:

bus.c: /* 尝试将一个device和一个driver绑定 */ int driver_probe_device(struct device_driver * drv, struct device * dev) { /* 总线如果有前期方法,先过滤下看能不能绑定 */ if (drv->bus->match && !drv->bus->match(dev, drv)) return -ENODEV; dev->driver = drv; /* driver如果有自己的绑定方法,也尝试绑定下 */ if (drv->probe) { int error = drv->probe(dev); if (error) { dev->driver = NULL; return error; } } device_bind_driver(dev); return 0; } /* 绑定device实例对应的driver */ int device_attach(struct device * dev) { struct bus_type * bus = dev->bus; struct list_head * entry; int error; /* 如果device实例已经声明了要绑定的driver了,那就没啥好说的了。*/ if (dev->driver) { device_bind_driver(dev); return 1; } if (bus->match) { /* 遍历当前bus上所有的driver,一个个尝试和device绑定,有一个成功就OK */ list_for_each(entry, &bus->drivers.list) { struct device_driver * drv = to_drv(entry); error = driver_probe_device(drv, dev); if (!error) /* success, driver matched */ return 1; if (error != -ENODEV && error != -ENXIO) /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d\n", drv->name, dev->bus_id, error); } } return 0; } /* 一个新的driver进来了,看下当前device有没有需要绑定的 */ void driver_attach(struct device_driver * drv) { struct bus_type * bus = drv->bus; struct list_head * entry; int error; if (!bus->match) return; list_for_each(entry, &bus->devices.list) { /* 遍历当前bus上的device,如果有device还没有绑定driver,就尝试和本driver绑定下,
当前bus上所有的device都要遍历一遍。
 */ struct device * dev = container_of(entry, struct device, bus_list); if (!dev->driver) { error = driver_probe_device(drv, dev); if (error && (error != -ENODEV)) /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d\n", drv->name, dev->bus_id, error); } } } 

class 

除了bus,device,driver这种竖向结构,Linxu设备模型还有一种横向结构,class。class是将不同bus下的同一种类型的device管理起来,比如:

[root@MyCloudServer net]# pwd /sys/class/net [root@MyCloudServer net]# ls -l
total 0 lrwxrwxrwx 1 root root 0 Dec 12 23:53 eth0 -> ../../devices/vif-0/net/eth0
lrwxrwxrwx 1 root root 0 Dec 12 23:53 lo -> ../../devices/virtual/net/lo

class数据结构如下:

device.h: struct class { char * name; struct subsystem	subsys; struct list_head	children; struct list_head	interfaces; struct class_attribute * class_attrs; struct class_device_attribute * class_dev_attrs; int (*hotplug)(struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size); void (*release)(struct class_device *dev); void (*class_release)(struct class *class); };

当添加一个新的class时,会调用函数class_register。

class.c /* 这里声明了class管理子系统class_subsys,用来管理所有的class实例。*/ static decl_subsys(class, &ktype_class, NULL); int class_register(struct class * cls) { int error; pr_debug("device class '%s': registering\n", cls->name); INIT_LIST_HEAD(&cls->children); INIT_LIST_HEAD(&cls->interfaces); error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name); if (error) return error; subsys_set_kset(cls, class_subsys); /* 将class注册到class_subsys上 */ error = subsystem_register(&cls->subsys); if (!error) { error = add_class_attrs(class_get(cls)); class_put(cls); } return error; }

class和device通过class_device结构体产生联系。

device.h: struct class_device { struct list_head	node; struct kobject		kobj; struct class * class; /* required */ struct device * dev; /* not necessary, but nice to have */ void * class_data; /* class-specific data */ char class_id[BUS_ID_SIZE]; /* unique to this class */ };

如果要绑定一个class和一个device,调用函数class_device_register。

class.c int class_device_register(struct class_device *class_dev) { class_device_initialize(class_dev); return class_device_add(class_dev); } int class_device_add(struct class_device *class_dev) { 。。。。。。 。。。。。。 /* now take care of our own registration */ if (parent) { down_write(&parent->subsys.rwsem); list_add_tail(&class_dev->node, &parent->children); /* 此处循环是在添加device到class前进行前期处理 */ list_for_each_entry(class_intf, &parent->interfaces, node) if (class_intf->add) class_intf->add(class_dev); up_write(&parent->subsys.rwsem); } class_device_add_attrs(class_dev); /* 在class目录下创建链接 */ class_device_dev_link(class_dev); class_device_driver_link(class_dev); register_done: if (error && parent) class_put(parent); class_device_put(class_dev); return error; }

在分析class_device_add时可以看到代码中有一个前期处理循环,这里是用class_interface来定义的。

device.h: struct class_interface { struct list_head	node; struct class *class; int (*add) (struct class_device *); void (*remove) (struct class_device *); };

假如我们需要在device加入class前需要额外处理下,就可以通过调用class_interface_register函数来注册一个回调函数。

class.c: int class_interface_register(struct class_interface *class_intf) { struct class * parent; struct class_device * class_dev; if (!class_intf || !class_intf->class) return -ENODEV; parent = class_get(class_intf->class); if (!parent) return -EINVAL; down_write(&parent->subsys.rwsem); list_add_tail(&class_intf->node, &parent->interfaces); if (class_intf->add) { list_for_each_entry(class_dev, &parent->children, node) class_intf->add(class_dev); } up_write(&parent->subsys.rwsem); return 0; }

platform_device 

最后在简单的笔记下platform_device结构体。

device.h: /*
个人认为platform_device就是一个device,不过多了一个resource管理功能。
*/ struct platform_device { char * name; u32		id; struct device	dev; u32		num_resources; struct resource * resource; };

不过platform_device有两个特点: 
1 platform_device全部挂在struct bus_type platform_bus_type总线上。 
2 platform_device实例在device目录下有一个共同的父设备(文件夹)struct device platform_bus 。

具体platform_device就不笔记了,可以参考platform.c代码。

阅读(1646) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~