好记性不如铅笔头

kernel, linux, 操作系统

Linux设备模型简单笔记

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

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

CONTENTS

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代码。