Chinaunix首页 | 论坛 | 博客
  • 博客访问: 149375
  • 博文数量: 55
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 25
  • 用 户 组: 普通用户
  • 注册时间: 2013-03-22 14:28
文章分类

全部博文(55)

文章存档

2013年(55)

我的朋友

分类: LINUX

2013-03-22 15:46:39

原文地址:I2C设备与驱动的关联 作者:zhiqiang0071

Linux操作系统中,驱动程序的加载分为两种:内核启动时自动加载和用户手动加载;硬件设备也可以采用两种方式添加到系统中:在系统启动前及系统运行时的热插拨。下面,我们以arm体系结构下的at91处理器中的I2C控制器为例,介绍一下硬件设备及相关的驱动程序是如何绑定及松绑的。
  1. 平台驱动注册过程
1.1 at91_i2c_init()函数
在文件drivers/i2c/busses/i2c-at91.c中,定义了结构体struct platform_driver并进行了初始化,通过使用module_init()宏进行声明,当模块被加载到内核时会调用 at91_i2c_init()函数。在此函数中,调用了platform_driver_register()函数来完成注册。

static struct platform_driver at91_i2c_driver = {
	.probe		= at91_i2c_probe,
	.remove		= __devexit_p(at91_i2c_remove),
	.suspend	= at91_i2c_suspend,
	.resume		= at91_i2c_resume,
	.driver		= {
		.name	= "at91_i2c",
		.owner	= THIS_MODULE,
	},
};

static int __init at91_i2c_init(void)
{
	return platform_driver_register(&at91_i2c_driver);
}
1.2 platform_driver_register()函数
在文件drivers/base/platform.c中,实现并导出了platform_driver_register()函数,以便使其他模块中的函数可以调用此函数。它在完成简单的包装后,调用了driver_register()函数,完成了从平台实现到Linux内核实现的过渡。
    在此,我们需要关注一下platform_match()platform_drv_probe()函数。platform_match() 函数确定驱动与设备的关联,而platform_drv_probe()函数会在随后介绍的函数中被调用。
//比较驱动信息中的name与设备信息中的name两者是否一致
static int platform_match(struct device * dev, struct device_driver * drv)
{
struct platform_device *pdev = container_of(dev, struct platform_device, dev);

	return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}

struct bus_type platform_bus_type = { 
	.name		= "platform", 
	.dev_attrs	= platform_dev_attrs, 
	.match		= platform_match, 
	.uevent		= platform_uevent, 
	.suspend	= platform_suspend, 
	.suspend_late	= platform_suspend_late, 
	.resume_early	= platform_resume_early, 
	.resume		= platform_resume, 
}; 
EXPORT_SYMBOL_GPL(platform_bus_type); 

/** 
 *	platform_driver_register 
 *	@drv: platform driver structure 
 */ 
int platform_driver_register(struct platform_driver *drv) 
{ 
	drv->driver.bus = &platform_bus_type; 
	//really_probe函数中,回调了platform_drv_probe函数
	if (drv->probe) 
		drv->driver.probe = platform_drv_probe; 
	if (drv->remove) 
		drv->driver.remove = platform_drv_remove; 
	if (drv->shutdown) 
		drv->driver.shutdown = platform_drv_shutdown; 
	if (drv->suspend) 
		drv->driver.suspend = platform_drv_suspend; 
	if (drv->resume) 
		drv->driver.resume = platform_drv_resume; 
	return driver_register(&drv->driver); 
} 
EXPORT_SYMBOL_GPL(platform_driver_register); 

1.3 driver_register()函数
在文件drivers/base/driver.c中,实现了driver_register()函数。在此函数中,初始化结构体struct device_driver中的klist_deviceunloaded字段,通过klist_device字段,可以保存此驱动支持的设备链表,通过“完成”接口机制,完成线程间的同步。链表和“完成”接口的详细信息可以参考文献[1]。返回bus_add_driver()函数的运行结果。

/** 
 *	driver_register - register driver with bus 
 *	@drv:	driver to register 
 * 
 *	We pass off most of the work to the bus_add_driver() call, 
 *	since most of the things we have to do deal with the bus 
 *	structures. 
 * 
 *	The one interesting aspect is that we setup @drv->unloaded 
 *	as a completion that gets complete when the driver reference 
 *	count reaches 0. 
 */ 
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); 
	init_completion(&drv->unloaded); 
	return bus_add_driver(drv); 
} 

1.4 bus_add_driver()函数
在文件drivers/base/bus.c中实现了bus_add_driver()函数,它通过语句klist_add_tail(&drv->knode_bus, &bus->klist_drivers); 将驱动信息保存到总线结构中,在设备注册过程中,我们就可以明白此语句的作用了。在此语句之前,调用了driver_attach()函数。
/** 
 *	bus_add_driver - Add a driver to the bus. 
 *	@drv:	driver. 
 * 
 */ 
int bus_add_driver(struct device_driver *drv) 
{ 
	struct bus_type * bus = get_bus(drv->bus); 
	int error = 0; 

	if (!bus) 
		return 0; 

	pr_debug("bus %s: add driver %s\n", bus->name, drv->name); 
	error = kobject_set_name(&drv->kobj, "%s", drv->name); 
	if (error) 
		goto out_put_bus; 
	drv->kobj.kset = &bus->drivers; 
	if ((error = kobject_register(&drv->kobj))) 
		goto out_put_bus; 

	error = driver_attach(drv); 
	if (error) 
		goto out_unregister; 
	klist_add_tail(&drv->knode_bus, &bus->klist_drivers); 
	module_add_driver(drv->owner, drv); 

	error = driver_add_attrs(bus, drv); 
	if (error) { 
		/* How the hell do we get out of this pickle? Give up */ 
		printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", 
			__FUNCTION__, drv->name); 
	} 
	error = add_bind_files(drv); 
	if (error) { 
		/* Ditto */ 
		printk(KERN_ERR "%s: add_bind_files(%s) failed\n", 
			__FUNCTION__, drv->name); 
	} 

	return error; 
out_unregister: 
	kobject_unregister(&drv->kobj); 
out_put_bus: 
	put_bus(bus); 
	return error; 
} 

1.5 dd.c文件
在文件drivers/base/dd.c中,实现了设备与驱动交互的核心函数。
1.5.1 driver_attach()函数
函数driver_attach()返回bus_for_each_dev()函数的运行结果。bus_for_each_dev()函数的原型如下:
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, 
	int (*fn) (struct device *, void *));
该函数迭代了在总线上的每个设备,将相关的device结构传递给fn,同时传递data值。如果startNULL,将从总线上的第一个设备开始迭代;否则将从start后的第一个设备开始迭代。如果fn返回一个非零值,将停止迭代,而这个值也会从该函数返回(摘自<设备驱动程序>>第三版)。
该函数是如何知道总线上的每个设备的呢?在设备注册过程中,我会详细介绍。
/* 
 *	drivers/base/dd.c - The core device/driver interactions. 
 * 
 * 	This file contains the (sometimes tricky) code that controls the 
 *	interactions between devices and drivers, which primarily includes 
 *	driver binding and unbinding. 
 */

/** 
 *	driver_attach - try to bind driver to devices. 
 *	@drv:	driver. 
 * 
 *	Walk the list of devices that the bus has on it and try to 
 *	match the driver with each one.  If driver_probe_device() 
 *	returns 0 and the @dev->driver is set, we've found a 
 *	compatible pair. 
 */ 
int driver_attach(struct device_driver * drv) 
{ 
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); 
} 
1.5.2 __driver_attach()函数
函数__driver_attach()在调用driver_probe_device()函数前,需要进行线程间的互斥处理。
static int __driver_attach(struct device * dev, void * data) 
{ 
	struct device_driver * drv = data; 

	/* 
	 * Lock device and try to bind to it. We drop the error 
	 * here and always return 0, because we need to keep trying 
	 * to bind to devices and some drivers will return an error 
	 * simply if it didn't support the device. 
	 * 
	 * driver_probe_device() will spit a warning if there 
	 * is an error. 
	 */ 

	if (dev->parent)	/* Needed for USB */ 
		down(&dev->parent->sem); 
	down(&dev->sem); 
	if (!dev->driver) 
		driver_probe_device(drv, dev); 
	up(&dev->sem); 
	if (dev->parent) 
		up(&dev->parent->sem); 

	return 0; 
} 
1.5.3 driver_probe_device()函数
driver_probe_device()函数中,调用了match函数platform_match(),如果它返回0,表示驱动与设备不一致,函数返回;否则,调用really_probe()函数。
/** 
 * driver_probe_device - attempt to bind device & driver together 
 * @drv: driver to bind a device to 
 * @dev: device to try to bind to the driver 
 * 
 * First, we call the bus's match function, if one present, which should 
 * compare the device IDs the driver supports with the device IDs of the 
 * device. Note we don't do this ourselves because we don't know the 
 * format of the ID structures, nor what is to be considered a match and 
 * what is not. 
 * 
 * This function returns 1 if a match is found, an error if one occurs 
 * (that is not -ENODEV or -ENXIO), and 0 otherwise. 
 * 
 * This function must be called with @dev->sem held.  When called for a 
 * USB interface, @dev->parent->sem must be held as well. 
 */ 
int driver_probe_device(struct device_driver * drv, struct device * dev) 
{ 
	struct stupid_thread_structure *data; 
	struct task_struct *probe_task; 
	int ret = 0; 

	if (!device_is_registered(dev)) 
		return -ENODEV; 
	if (drv->bus->match && !drv->bus->match(dev, drv)) 
		goto done; 

	pr_debug("%s: Matched Device %s with Driver %s\n", 
		 drv->bus->name, dev->bus_id, drv->name); 

	data = kmalloc(sizeof(*data), GFP_KERNEL); 
	if (!data) 
		return -ENOMEM; 
	data->drv = drv; 
	data->dev = dev; 

	if (drv->multithread_probe) { 
		probe_task = kthread_run(really_probe, data, 
					 "probe-%s", dev->bus_id); 
		if (IS_ERR(probe_task)) 
			ret = really_probe(data); 
	} else 
		ret = really_probe(data); 

done: 
	return ret; 
} 

struct stupid_thread_structure { 
	struct device_driver *drv; 
	struct device *dev; 
}; 

1.5.4 really_probe()函数
really_probe()函数中,实现了设备与驱动的绑定。语句如下:dev->driver = drv;
ret = drv->probe(dev); probe()函数的实现如下:
include/linux/platform_device.h

#define to_platform_device(x) container_of((x), struct platform_device, dev)

drivers/base/platform.c

#define to_platform_driver(drv)	(container_of((drv), struct platform_driver, driver))


static int platform_drv_probe(struct device *_dev) 
{ 
	struct platform_driver *drv = to_platform_driver(_dev->driver); 
	struct platform_device *dev = to_platform_device(_dev); 
 
	return drv->probe(dev); 
} 
在此函数中,回调了我们在i2c-at91.c文件中实现的探测函数at91_i2c_probe(),至此,平台驱动的注册过程结束。

static atomic_t probe_count = ATOMIC_INIT(0); 
static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue); 

static int really_probe(void *void_data) 
{ 
	struct stupid_thread_structure *data = void_data; 
	struct device_driver *drv = data->drv; 
	struct device *dev = data->dev; 
	int ret = 0; 

	atomic_inc(&probe_count); 
	pr_debug("%s: Probing driver %s with device %s\n", 
		 drv->bus->name, drv->name, dev->bus_id); 
	WARN_ON(!list_empty(&dev->devres_head)); 

	dev->driver = drv; 
	if (driver_sysfs_add(dev)) { 
		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", 
			__FUNCTION__, dev->bus_id); 
		goto probe_failed; 
	} 

	if (dev->bus->probe) { 
		ret = dev->bus->probe(dev); 
		if (ret) 
			goto probe_failed; 
	} else if (drv->probe) { 
		ret = drv->probe(dev); 
		if (ret) 
			goto probe_failed; 
	} 
	//设备与驱动绑定后,对系统中已注册的组件进行事件通知。
	driver_bound(dev); 
	ret = 1; 
	pr_debug("%s: Bound Device %s to Driver %s\n", 
		 drv->bus->name, dev->bus_id, drv->name); 
	goto done; 

probe_failed: 
	devres_release_all(dev); 
	driver_sysfs_remove(dev); 
	dev->driver = NULL; 

	if (ret != -ENODEV && ret != -ENXIO) { 
		/* driver matched but the probe failed */ 
		printk(KERN_WARNING 
		       "%s: probe of %s failed with error %d\n", 
		       drv->name, dev->bus_id, ret); 
	} 
	/* 
	 * Ignore errors returned by ->probe so that the next driver can try 
	 * its luck. 
	 */ 
	ret = 0; 
done: 
	kfree(data); 
	atomic_dec(&probe_count); 
	wake_up(&probe_waitqueue); 
	return ret; 
} 

















  1. 平台驱动卸载过程
平台驱动卸载过程是注册的逆过程,详细信息可参考注册过程进行分析。
2.1 at91_i2c_exit()函数
static void __exit at91_i2c_exit(void)
{
	platform_driver_unregister(&at91_i2c_driver);
}

2.2 platform_driver_unregister()函数

/** 
 *	platform_driver_unregister 
 *	@drv: platform driver structure 
 */ 
void platform_driver_unregister(struct platform_driver *drv) 
{ 
	driver_unregister(&drv->driver); 
} 
EXPORT_SYMBOL_GPL(platform_driver_unregister);

2.3 driver_unregister()函数

/** 
 *	driver_unregister - remove driver from system. 
 *	@drv:	driver. 
 * 
 *	Again, we pass off most of the work to the bus-level call. 
 * 
 *	Though, once that is done, we wait until @drv->unloaded is completed. 
 *	This will block until the driver refcount reaches 0, and it is 
 *	released. Only modular drivers will call this function, and we 
 *	have to guarantee that it won't complete, letting the driver 
 *	unload until all references are gone. 
 */ 

void driver_unregister(struct device_driver * drv) 
{ 
	bus_remove_driver(drv); 
	/* 
	 * If the driver is a module, we are probably in 
	 * the module unload path, and we want to wait 
	 * for everything to unload before we can actually 
	 * finish the unload. 
	 */ 
	if (drv->owner) 
		wait_for_completion(&drv->unloaded); 
} 

2.4 bus_remove_driver()函数
/** 
 *	bus_remove_driver - delete driver from bus's knowledge. 
 *	@drv:	driver. 
 * 
 *	Detach the driver from the devices it controls, and remove 
 *	it from its bus's list of drivers. Finally, we drop the reference 
 *	to the bus we took in bus_add_driver(). 
 */ 

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); 
} 

2.5 driver_detach()函数
/** 
 * driver_detach - detach driver from all devices it controls. 
 * @drv: driver. 
 */ 
void driver_detach(struct device_driver * drv) 
{ 
	struct device * dev; 

	for (;;) { 
		spin_lock(&drv->klist_devices.k_lock); 
		if (list_empty(&drv->klist_devices.k_list)) { 
			spin_unlock(&drv->klist_devices.k_lock); 
			break; 
		} 
		dev = list_entry(drv->klist_devices.k_list.prev, 
				struct device, knode_driver.n_node); 
		get_device(dev); 
		spin_unlock(&drv->klist_devices.k_lock); 

		if (dev->parent)	/* Needed for USB */ 
			down(&dev->parent->sem); 
		down(&dev->sem); 
		if (dev->driver == drv) 
			__device_release_driver(dev); 
		up(&dev->sem); 
		if (dev->parent) 
			up(&dev->parent->sem); 
		put_device(dev); 
	} 
} 

2.6 __device_release_driver()函数
/** 
 *	device_release_driver - manually detach device from driver. 
 *	@dev:	device. 
 * 
 *	Manually detach device from driver. 
 * 
 *	__device_release_driver() must be called with @dev->sem held. 
 *	When called for a USB interface, @dev->parent->sem must be held 
 *	as well. 
 */ 

static void __device_release_driver(struct device * dev) 
{ 
	struct device_driver * drv; 

	drv = dev->driver; 
	if (drv) { 
		get_driver(drv); 
		driver_sysfs_remove(dev); 
		sysfs_remove_link(&dev->kobj, "driver"); 
		klist_remove(&dev->knode_driver); 

		if (dev->bus) 
			blocking_notifier_call_chain(&dev->bus->bus_notifier, 
						     BUS_NOTIFY_UNBIND_DRIVER, 
						     dev); 

		if (dev->bus && dev->bus->remove) 
			dev->bus->remove(dev); 
		else if (drv->remove) 
			drv->remove(dev); 
		devres_release_all(dev); 
		dev->driver = NULL; 
		put_driver(drv); 
	} 
} 

2.7 platform_drv_remove()函数
static int platform_drv_remove(struct device *_dev) 
{ 
	struct platform_driver *drv = to_platform_driver(_dev->driver); 
	struct platform_device *dev = to_platform_device(_dev); 

	return drv->remove(dev); 
}

在此函数中,回调了我们在i2c-at91.c文件中实现的移除函数at91_i2c_remove(),至此,平台驱动的卸载过程结束。

在注册和卸载过程中,Linux采用了一些变量来保存相关的信息,比如引用计数、通知链等,感兴趣的人员,可以详细的阅读此部分内容。































  1. 设备注册过程
3.1 at91_add_device_i2c()函数
在文件arch/arm/mach-at91/at91sam9263_devices.c中,对I2C设备进行了定义。
/* -------------------------------------------------------------------- 
 *  TWI (i2c) 
 * -------------------------------------------------------------------- */ 

#if defined(CONFIG_I2C_AT91) || defined(CONFIG_I2C_AT91_MODULE) 

static struct resource twi_resources[] = { 
	[0] = { 
		.start	= AT91SAM9263_BASE_TWI, 
		.end	= AT91SAM9263_BASE_TWI + SZ_16K - 1, 
		.flags	= IORESOURCE_MEM, 
	}, 
	[1] = { 
		.start	= AT91SAM9263_ID_TWI, 
		.end	= AT91SAM9263_ID_TWI, 
		.flags	= IORESOURCE_IRQ, 
	}, 
}; 

static struct platform_device at91sam9263_twi_device = { 
	//此名字与驱动信息中的名字"at91_i2c"是一致的,在match()函数调用中,会返回1
	.name		= "at91_i2c", 
	.id		= -1, 
	.resource	= twi_resources, 
	.num_resources	= ARRAY_SIZE(twi_resources), 
}; 

void __init at91_add_device_i2c(void) 
{ 
	/* pins used for TWI interface */ 
	at91_set_A_periph(AT91_PIN_PB4, 0);		/* TWD */ 
	at91_set_multi_drive(AT91_PIN_PB4, 1); 

	at91_set_A_periph(AT91_PIN_PB5, 0);		/* TWCK */ 
	at91_set_multi_drive(AT91_PIN_PB5, 1); 

	//对设备进行注册
	platform_device_register(&at91sam9263_twi_device); 
} 
#else 
void __init at91_add_device_i2c(void) {} 
#endif
3.2 platform.c文件
在文件drivers/base/platform.c中,实现了下面的函数。
3.2.1 platform_device_register()函数
/** 
 *	platform_device_register - add a platform-level device 
 *	@pdev:	platform device we're adding 
 * 
 */ 
int platform_device_register(struct platform_device * pdev) 
{ 
	device_initialize(&pdev->dev); 
	return platform_device_add(pdev); 
} 
EXPORT_SYMBOL_GPL(platform_device_register);
3.2.2 platform_device_add()函数
/** 
 *	platform_device_add - add a platform device to device hierarchy 
 *	@pdev:	platform device we're adding 
 * 
 *	This is part 2 of platform_device_register(), though may be called 
 *	separately _iff_ pdev was allocated by platform_device_alloc(). 
 */ 
int platform_device_add(struct platform_device *pdev) 
{ 
	int i, ret = 0; 

	if (!pdev) 
		return -EINVAL; 

	if (!pdev->dev.parent) 
		pdev->dev.parent = &platform_bus; 

	pdev->dev.bus = &platform_bus_type; 

	if (pdev->id != -1) 
		snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id); 
	else 
		strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE); 

	for (i = 0; i < pdev->num_resources; i++) { 
		struct resource *p, *r = &pdev->resource[i]; 

		if (r->name == NULL) 
			r->name = pdev->dev.bus_id; 

		p = r->parent; 
		if (!p) { 
			if (r->flags & IORESOURCE_MEM) 
				p = &iomem_resource; 
			else if (r->flags & IORESOURCE_IO) 
				p = &ioport_resource; 
		} 

		if (p && insert_resource(p, r)) { 
			printk(KERN_ERR 
			       "%s: failed to claim resource %d\n", 
			       pdev->dev.bus_id, i); 
			ret = -EBUSY; 
			goto failed; 
		} 
	} 

	pr_debug("Registering platform device '%s'. Parent at %s\n", 
		 pdev->dev.bus_id, pdev->dev.parent->bus_id); 

	ret = device_add(&pdev->dev); 
	if (ret == 0) 
		return ret; 

 failed: 
	while (--i >= 0) 
		if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO)) 
			release_resource(&pdev->resource[i]); 
	return ret; 
} 
EXPORT_SYMBOL_GPL(platform_device_add); 

3.3 device_add()函数
在文件drivers/base/core.c中实现了device_add()函数。
/** 
 *	device_add - add device to device hierarchy. 
 *	@dev:	device. 
 * 
 *	This is part 2 of device_register(), though may be called 
 *	separately _iff_ device_initialize() has been called separately. 
 * 
 *	This adds it to the kobject hierarchy via kobject_add(), adds it 
 *	to the global and sibling lists for the device, then 
 *	adds it to the other relevant subsystems of the driver model. 
 */ 
int device_add(struct device *dev) 
{ 
	struct device *parent = NULL; 
	char *class_name = NULL; 
	struct class_interface *class_intf; 
	int error = -EINVAL; 

	dev = get_device(dev); 
	if (!dev || !strlen(dev->bus_id)) 
		goto Error; 

	pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id); 

	parent = get_device(dev->parent); 

	error = setup_parent(dev, parent); 
	if (error) 
		goto Error; 

	/* first, register with generic layer. */ 
	kobject_set_name(&dev->kobj, "%s", dev->bus_id); 
	error = kobject_add(&dev->kobj); 
	if (error) 
		goto Error; 

	/* notify platform of device entry */ 
	if (platform_notify) 
		platform_notify(dev); 

	/* notify clients of device entry (new way) */ 
	if (dev->bus) 
		blocking_notifier_call_chain(&dev->bus->bus_notifier, 
					     BUS_NOTIFY_ADD_DEVICE, dev); 

	dev->uevent_attr.attr.name = "uevent"; 
	dev->uevent_attr.attr.mode = S_IWUSR; 
	if (dev->driver) 
		dev->uevent_attr.attr.owner = dev->driver->owner; 
	dev->uevent_attr.store = store_uevent; 
	error = device_create_file(dev, &dev->uevent_attr); 
	if (error) 
		goto attrError; 

	if (MAJOR(dev->devt)) { 
		struct device_attribute *attr; 
		attr = kzalloc(sizeof(*attr), GFP_KERNEL); 
		if (!attr) { 
			error = -ENOMEM; 
			goto ueventattrError; 
		} 
		attr->attr.name = "dev"; 
		attr->attr.mode = S_IRUGO; 
		if (dev->driver) 
			attr->attr.owner = dev->driver->owner; 
		attr->show = show_dev; 
		error = device_create_file(dev, attr); 
		if (error) { 
			kfree(attr); 
			goto ueventattrError; 
		} 

		dev->devt_attr = attr; 
	} 

	if (dev->class) { 
		sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj, 
				  "subsystem"); 
		/* If this is not a "fake" compatible device, then create the 
		 * symlink from the class to the device. */ 
		if (dev->kobj.parent != &dev->class->subsys.kset.kobj) 
			sysfs_create_link(&dev->class->subsys.kset.kobj, 
					  &dev->kobj, dev->bus_id); 
		if (parent) { 
			sysfs_create_link(&dev->kobj, &dev->parent->kobj, 
							"device"); 
#ifdef CONFIG_SYSFS_DEPRECATED 
			class_name = make_class_name(dev->class->name, 
							&dev->kobj); 
			if (class_name) 
				sysfs_create_link(&dev->parent->kobj, 
						  &dev->kobj, class_name); 
#endif 
		} 
	} 

	if ((error = device_add_attrs(dev))) 
		goto AttrsError; 
	if ((error = device_add_groups(dev))) 
		goto GroupError; 
	if ((error = device_pm_add(dev))) 
		goto PMError; 
	if ((error = bus_add_device(dev))) 
		goto BusError; 
	if (!dev->uevent_suppress) 
		kobject_uevent(&dev->kobj, KOBJ_ADD); 
	if ((error = bus_attach_device(dev))) 
		goto AttachError; 
	if (parent) 
		klist_add_tail(&dev->knode_parent, &parent->klist_children); 

	if (dev->class) { 
		down(&dev->class->sem); 
		/* tie the class to the device */ 
		list_add_tail(&dev->node, &dev->class->devices); 

		/* notify any interfaces that the device is here */ 
		list_for_each_entry(class_intf, &dev->class->interfaces, node) 
			if (class_intf->add_dev) 
				class_intf->add_dev(dev, class_intf); 
		up(&dev->class->sem); 
	} 
 Done: 
 	kfree(class_name); 
	put_device(dev); 
	return error; 
 AttachError: 
	bus_remove_device(dev); 
 BusError: 
	device_pm_remove(dev); 
 PMError: 
	if (dev->bus) 
		blocking_notifier_call_chain(&dev->bus->bus_notifier, 
					     BUS_NOTIFY_DEL_DEVICE, dev); 
	device_remove_groups(dev); 
 GroupError: 
	device_remove_attrs(dev); 
 AttrsError: 
	if (dev->devt_attr) { 
		device_remove_file(dev, dev->devt_attr); 
		kfree(dev->devt_attr); 
	} 

	if (dev->class) { 
		sysfs_remove_link(&dev->kobj, "subsystem"); 
		/* If this is not a "fake" compatible device, remove the 
		 * symlink from the class to the device. */ 
		if (dev->kobj.parent != &dev->class->subsys.kset.kobj) 
			sysfs_remove_link(&dev->class->subsys.kset.kobj, 
					  dev->bus_id); 
		if (parent) { 
#ifdef CONFIG_SYSFS_DEPRECATED 
			char *class_name = make_class_name(dev->class->name, 
							   &dev->kobj); 
			if (class_name) 
				sysfs_remove_link(&dev->parent->kobj, 
						  class_name); 
			kfree(class_name); 
#endif 
			sysfs_remove_link(&dev->kobj, "device"); 
		} 

		down(&dev->class->sem); 
		/* notify any interfaces that the device is now gone */ 
		list_for_each_entry(class_intf, &dev->class->interfaces, node) 
			if (class_intf->remove_dev) 
				class_intf->remove_dev(dev, class_intf); 
		/* remove the device from the class list */ 
		list_del_init(&dev->node); 
		up(&dev->class->sem); 
	} 
 ueventattrError: 
	device_remove_file(dev, &dev->uevent_attr); 
 attrError: 
	kobject_uevent(&dev->kobj, KOBJ_REMOVE); 
	kobject_del(&dev->kobj); 
 Error: 
	if (parent) 
		put_device(parent); 
	goto Done; 
} 
3.4 bus_attach_device()函数
在文件drivers/base/bus.c中实现了bus_attach_device()函数。

/**
 *	bus_attach_device - add device to bus
 *	@dev:	device tried to attach to a driver
 *
 *	- Add device to bus's list of devices.
 *	- Try to attach to driver.
 */
int bus_attach_device(struct device * dev)
{
	struct bus_type *bus = dev->bus;
	int ret = 0;

	if (bus) {
		dev->is_registered = 1;
		ret = device_attach(dev);
		if (ret >= 0) {
//将设备信息存储在bus结构中。在驱动注册后,利用bus_for_each_dev()函数可以得到所有设备的
//信息,完成驱动与设备之间的绑定。			
			klist_add_tail(&dev->knode_bus, &bus->klist_devices);
			ret = 0;
		} else
			dev->is_registered = 0;
	}
	return ret;
}
3.5 dd.c文件
在文件drivers/base/dd.c中实现了device_attach()函数。
3.5.1 device_attach()函数
/** 
 *	device_attach - try to attach device to a driver. 
 *	@dev:	device. 
 * 
 *	Walk the list of drivers that the bus has and call 
 *	driver_probe_device() for each pair. If a compatible 
 *	pair is found, break out and return. 
 * 
 *	Returns 1 if the device was bound to a driver; 
 *	0 if no matching device was found; error code otherwise. 
 * 
 *	When called for a USB interface, @dev->parent->sem must be held. 
 */ 
int device_attach(struct device * dev) 
{ 
	int ret = 0; 
 
	down(&dev->sem); 
	if (dev->driver) { 
		ret = device_bind_driver(dev); 
		if (ret == 0) 
			ret = 1; 
	} else 
		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); 
	up(&dev->sem); 
	return ret; 
} 

static int __device_attach(struct device_driver * drv, void * data) 
{ 
	struct device * dev = data; 
	//在此之后,利用驱动信息来探测设备,参考第一部分的介绍
	return driver_probe_device(drv, dev); 
}
3.5.2 device_bind_driver()函数
/** 
 *	device_bind_driver - bind a driver to one device. 
 *	@dev:	device. 
 * 
 *	Allow manual attachment of a driver to a device. 
 *	Caller must have already set @dev->driver. 
 * 
 *	Note that this does not modify the bus reference count 
 *	nor take the bus's rwsem. Please verify those are accounted 
 *	for before calling this. (It is ok to call with no other effort 
 *	from a driver's probe() method.) 
 * 
 *	This function must be called with @dev->sem held. 
 */ 
int device_bind_driver(struct device *dev) 
{ 
	int ret; 

	ret = driver_sysfs_add(dev); 
	if (!ret) 
		driver_bound(dev); //通知链
	return ret; 
}

到此,设备与驱动的两种绑定方式:在设备注册时进行绑定及在驱动注册时进行绑定就介绍完了。






参考文献
1Linux设备驱动程序 第三版
2Linux 2.6.21.5内核原代码
阅读(889) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~