分类: LINUX
2010-11-04 19:57:20
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.7platform_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);
returndrv->remove(dev);
}
在此函数中,回调了我们在i2c-at91.c文件中实现的移除函数at91_i2c_remove(),至此,平台驱动的卸载过程结束。
在注册和卸载过程中,Linux采用了一些变量来保存相关的信息,比如引用计数、通知链等,感兴趣的人员,可以详细的阅读此部分内容。
设备注册过程
3.1at91_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_deviceat91sam9263_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.2platform.c文件
在文件drivers/base/platform.c中,实现了下面的函数。
3.2.1platform_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);
returnplatform_device_add(pdev);
}
EXPORT_SYMBOL_GPL(platform_device_register);
3.2.2platform_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().
*/
intplatform_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.3device_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.
*/
intdevice_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.4bus_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.1device_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.2device_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;
}
到此,设备与驱动的两种绑定方式:在设备注册时进行绑定及在驱动注册时进行绑定就介绍完了。