文章的分析基於linux2.6.22,本文僅分析了註冊過程
PART A 先分析驅動側文件:\drive\leds\Leds-s3c24xx.c
1.
static int __init s3c24xx_led_init(void)
{
return platform_driver_register(&s3c24xx_led_driver);
}
static struct platform_driver s3c24xx_led_driver = {
.probe = s3c24xx_led_probe,
.remove = s3c24xx_led_remove,
.suspend = s3c24xx_led_suspend,
.resume = s3c24xx_led_resume,
.driver = {
.name = "s3c24xx_led",
.owner = THIS_MODULE,
},
};
2.
/**
* platform_driver_register
* @drv: platform driver structure
*/
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
......
return driver_register(&drv->driver);
}
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,
};
3.
/**
* 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.
*/
int driver_register(struct device_driver * drv)
{
......
klist_init(&drv->klist_devices, NULL, NULL);
return bus_add_driver(drv);
}
4.
/**
* bus_add_driver - Add a driver to the bus.
* @drv: driver.
*
*/
int bus_add_driver(struct device_driver *drv)
{
.......
if (drv->bus->drivers_autoprobe) {
error = driver_attach(drv);
.......
}
........
}
5.
/**
* 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);
}
6.
/**
* bus_for_each_dev - device iterator.
* @bus: bus type.
* @start: device to start iterating from.
* @data: data for the callback.
* @fn: function to be called for each device.
*
* Iterate over @bus's list of devices, and call @fn for each,
* passing it @data. If @start is not NULL, we use that device to
* begin iterating from.
*
* We check the return of @fn each time. If it returns anything
* other than 0, we break out and return that value.
*
* NOTE: The device that returns a non-zero value is not retained
* in any way, nor is its refcount incremented. If the caller needs
* to retain this data, it should do, and increment the reference
* count in the supplied callback.
*/
int bus_for_each_dev(struct bus_type * bus, struct device * start,
void * data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device * dev;
.......
klist_iter_init_node(&bus->klist_devices, &i,
(start ? &start->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
......
}
7.
static int __driver_attach(struct device * dev, void * data)
{
struct device_driver * drv = data;
.......
if (!dev->driver)
driver_probe_device(drv, dev);
.......
}
8.
/**
* 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, -ENODEV if the device is
* not registered, 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)
{
......
if (drv->bus->match && !drv->bus->match(dev, drv))
......
ret = really_probe(dev, drv);
......
}
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);
}
9.
static int really_probe(struct device *dev, struct device_driver *drv)
{
.......
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
.......
} else if (drv->probe) {
ret = drv->probe(dev);
.......
}
.......
}
10.
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);
}
#define to_platform_driver(drv) (container_of((drv), struct platform_driver, driver))
#define to_platform_device(x) container_of((x), struct platform_device, dev)
11.
static int s3c24xx_led_probe(struct platform_device *dev)
{
......
}
PART B: 接着分析設備側文件:arch\arm\plat-s3c24xx\Common-smdk.c
1.
static struct s3c24xx_led_platdata smdk_pdata_led4 = {
.gpio = S3C2410_GPF4,
.flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
.name = "led4",
.def_trigger = "timer",
};
.......
static struct platform_device smdk_led4 = {
.name = "s3c24xx_led",
.id = 0,
.dev = {
.platform_data = &smdk_pdata_led4,
},
};
......
static struct platform_device __initdata *smdk_devs[] = {
&s3c_device_nand,
&smdk_led4,
&smdk_led5,
&smdk_led6,
&smdk_led7,
};
void __init smdk_machine_init(void)
{
......
platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));
......
}
2.
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]);
.......
}
return ret;
}
3.
int platform_device_register(struct platform_device * pdev)
{
device_initialize(&pdev->dev);
return platform_device_add(pdev);
}
4.
void device_initialize(struct device *dev)
{
kobj_set_kset_s(dev, devices_subsys);
kobject_init(&dev->kobj);
klist_init(&dev->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
INIT_LIST_HEAD(&dev->node);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
set_dev_node(dev, -1);
}
int platform_device_add(struct platform_device *pdev)
{
......
pdev->dev.bus = &platform_bus_type;
......
ret = device_add(&pdev->dev);
......
}
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,
};
5.
int device_add(struct device *dev)
{
......
bus_attach_device(dev);
......
}
6.
/**
* 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.
*/
void bus_attach_device(struct device * dev)
{
struct bus_type *bus = dev->bus;
......
if (bus) {
dev->is_registered = 1;
if (bus->drivers_autoprobe)
ret = device_attach(dev);
......
if (ret >= 0)
klist_add_tail(&dev->knode_bus, &bus->klist_devices);
else
dev->is_registered = 0;
}
}
7.
/**
* 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;
* -ENODEV if the device is not registered.
*
* When called for a USB interface, @dev->parent->sem must be held.
*/
int device_attach(struct device * dev)
{
int ret = 0;
.......
if (dev->driver) {
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else {
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
}
.......
}
8.
/**
* bus_for_each_drv - driver iterator
* @bus: bus we're dealing with.
* @start: driver to start iterating on.
* @data: data to pass to the callback.
* @fn: function to call for each driver.
*
* This is nearly identical to the device iterator above.
* We iterate over each driver that belongs to @bus, and call
* @fn for each. If @fn returns anything but 0, we break out
* and return it. If @start is not NULL, we use it as the head
* of the list.
*
* NOTE: we don't return the driver that returns a non-zero
* value, nor do we leave the reference count incremented for that
* driver. If the caller needs to know that info, it must set it
* in the callback. It must also be sure to increment the refcount
* so it doesn't disappear before returning to the caller.
*/
int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
void * data, int (*fn)(struct device_driver *, void *))
{
struct klist_iter i;
struct device_driver * drv;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->klist_drivers, &i,
start ? &start->knode_bus : NULL);
while ((drv = next_driver(&i)) && !error)
error = fn(drv, data);
klist_iter_exit(&i);
return error;
}
9.
static int __device_attach(struct device_driver * drv, void * data)
{
struct device * dev = data;
return driver_probe_device(drv, dev);
}
10.
/**
* 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, -ENODEV if the device is
* not registered, 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)
{
int ret = 0;
if (!device_is_registered(dev))
.......
if (drv->bus->match && !drv->bus->match(dev, drv))
......
......
ret = really_probe(dev, drv);
......
}
/**
* platform_match - bind platform device to platform driver.
* @dev: device.
* @drv: driver.
*
* Platform device IDs are assumed to be encoded like this:
* "<name><instance>", where <name> is a short description of the
* type of device, like "pci" or "floppy", and <instance> is the
* enumerated instance of the device, like '0' or '42'.
* Driver IDs are simply "<name>".
* So, extract the <name> from the platform_device structure,
* and compare it against the name of the driver. Return whether
* they match or not.
*/
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);
}
11.
static int really_probe(struct device *dev, struct device_driver *drv)
{
......
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
......
} else if (drv->probe) {
ret = drv->probe(dev);
......
}
......
}
12.
至此又去調用drv->probe(dev),參見PART A的10和11的分析