全部博文(86)
分类: LINUX
2015-07-21 18:38:31
copied from: http://blog.csdn.net/pillarbuaa/article/details/7789341
Kernel driver model
Peter xu, 2012.07
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Agenda
What is device/driver register?
Platform device-driver model
I2C device-driver model
Device/Driver register
There are three key entities including bus, device, driver.
Declare there is one special device/driver on the BUS
The name and ID of device/driver must be exclusive
The process of register is only to add the device/driver kobject into the kobject list(kset), not verify whether are the details right or not.
The bus driver will match the device and driver according to name or id.
One device must have only one driver; one driver may have more devices
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Platform device-driver model
Platform bus
Platform device
Platform driver
Platform device&driver case
Register ab8500_i2c device&driver
Register ab8500_fg device&driver
Register power_supply class and ab8500_fg device
Create capacity attribute
Register path
sys/devices/platform/xxxx
Mapping path: sys/bus/platform/devices/xxxx
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Platform Bus
Bus is the channel between processor and devices.
New driver management and register from Linux 2.6.
This pseudo-bus is used to connect devices on busses with minimal infrastructure, like those used to integrate peripherals on many system-on-chip processors.
More easily plant
Important struct @kernel/drivers/base/platform.c
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match, //compare device name with driver name for attaching together
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
struct device platform_bus = {
.init_name = "platform",
};
struct bus_type_private {
struct kset subsys;
struct kset *drivers_kset; // the list of drivers associated with this bus
struct kset *devices_kset; // the list of devices associated with this bus
struct klist klist_devices; // the klist to iterate over the @devices_kset
struct klist klist_drivers; //the klist to iterate over the @drivers_kset
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
};
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Platform Bus (cont’)
Important function @kernel/drivers/base/platform.c
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* match against the id table first */
if (pdrv->id_table) //in general ,null
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0); //compare names
}
int __init platform_bus_init(void)
{…
error = bus_register(&platform_bus_type); //register platform bus, /sys/bus/platform
…
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Platform device
Platform devices are devices that typically appear as autonomous entities in the system. This includes legacy port-based devices and host bridges to peripheral buses, and most controllers integrated into system-on-chip platforms. What they usually have in common is direct addressing from a CPU bus. Rarely, a platform device will be connected through a segment of some other kind of bus; but its registers will still be directly addressable.
Platform devices are given a name, used in driver binding, and a list of resources such as addresses and IRQs.
struct platform_device { //special device, derive from device
const char *name; //the name must be same with the name of counterpart driver
u32 id;
struct device dev;
u32 num_resources;
struct resource *resource; //CPU HW resource, such as MEM,IO,IRQ,DMA
};
int platform_device_register(struct platform_device *pdev)
int platform_device_add(struct platform_device *pdev){
…
pdev->dev.bus = &platform_bus_type;
…
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Platform device (cont’)
@drivers/base/platform.c
int platform_device_register(struct platform_device *pdev){
device_initialize(&pdev->dev); //init device structure
return platform_device_add(pdev);
}
int platform_device_add(struct platform_device *pdev)
{…
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus; //platform node
pdev->dev.bus = &platform_bus_type; //platform bus type
…
ret = device_add(&pdev->dev); //add a platform device to device hierarchy
}
int __init platform_bus_init(void)
{…
error = device_register(&platform_bus); //register platform device, /sys/devices/platform
…
}
static int platform_match(struct device *dev, struct device_driver *drv)
{…
return (strcmp(pdev->name, drv->name) == 0); //compare device name and driver name
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Platform device (cont’)
device_add(struct device *dev)@kernel/drivers/base/core.c
àbus_probe_device(dev) @kernel/drivers/base/bus.c
àdevice_attach(dev)@kernel/drivers/base/dd.c
àbus_for_each_drv(dev->bus, NULL, dev, __device_attach) //compare all drivers in bus to the device
à__device_attach@ kernel/drivers/base/bus.c
àdriver_match_device@kernel/drivers/base/base.h
àdrv->bus->match(dev,drv)
àplatform_match@kernel/drivers/base/platform.c //compare driver name and device name
find the matched driver
àdriver_probe_device(drv, dev) @kernel/drivers/base
àreally_probe(dev, drv) @kernel/drivers/base
{…
if (dev->bus->probe) { //=null
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) { //call platform driver probe function
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
…
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Platform driver
Platform drivers follow the standard driver model convention, where discovery/enumeration is handled outside the drivers, and drivers provide probe() and remove() methods. They support power management and shutdown notifications using the standard conventions.
struct platform_driver {//special driver, derive from device_driver
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
Platform drivers register themselves the normal way
int platform_driver_register(struct platform_driver *drv);
{
drv->driver.bus = &platform_bus_type;
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;
return driver_register(&drv->driver);
}
In common situations where the device is known not to be hot-pluggable, the probe() routine can live in an init section to reduce the driver's runtime memory footprint:
int platform_driver_probe(struct platform_driver *drv, int (*probe)(struct platform_device *))
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Platform driver (cont’)
driver_register(&drv->driver) @kernel/drivers/base/driver.c
àbus_add_driver(drv) @kernel/drivers/base/bus.c
àdriver_attach(drv) @kernel/drivers/base/dd.c
àbus_for_each_dev(drv->bus, NULL, drv, __driver_attach); //compare all devices in bus to the driver
à__driver_attach@kernel/drivers/base/dd.c
àdriver_match_device(drv, dev) @kernel/drivers/base/base.h
àdrv->bus->match(dev,drv)
àplatform_match@kernel/drivers/base/platform.c //compare driver name and device name
find the matched device
àdriver_probe_device(drv, dev) @kernel/drivers/base
àreally_probe(dev, drv) @kernel/drivers/base
{…
if (dev->bus->probe) { //=null
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) { //call platform driver probe function
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
…
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Platform device case: ab8500_i2c on Riogrande
@kernel/arch/arm/mach-ux500/board-rio-grande.c
Platform device structure
static struct platform_device ux500_ab8500_device = {
.name = "ab8500-i2c",
.id = 0,
.dev = {
.platform_data = &ab8500_platdata,
},
.num_resources = 1,
.resource = ab8500_resources,
};
Platform device resource
static struct resource ab8500_resources[] = {
[0] = {
.start = IRQ_DB8500_AB8500,
.end = IRQ_DB8500_AB8500,
.flags = IORESOURCE_IRQ //interrupt resource
}
};
Register the “ab8500-i2c” device platform
platform_device_register(&ux500_ab8500_device);//@kernel/drivers/base/platform.c
platform_device_add(…){
…
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
}
The “ab8500-i2c” device has been registered under the node of “platform”
sys/devices/platform/ab8500-i2c.0
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Platform driver case: ab8500_i2c on Riogrande
@kernel/drivers/mfd/ab8500-i2c.c
Platform driver structure
static struct platform_driver ab8500_i2c_driver = {
.driver = {
.name = "ab8500-i2c",
.owner = THIS_MODULE,
},
.probe = ab8500_i2c_probe,
.remove = __devexit_p(ab8500_i2c_remove)
};
Register the “ab8500-i2c” driver platform
platform_driver_register(&ab8500_i2c_driver);
Run probe
ab8500_i2c_probe
ab8500_init(ab8500);@kernel/drivers/mfd/ab8500-core.c
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Platform device case: ab8500_fg on Riogrande
@kernel/drivers/mfd/ab8500-core.c
Platform device structure
mfd: multifunction device drivers
struct mfd_cell@kernel/linux/mfd/core.h
static struct mfd_cell __devinitdata ab8500_bm_devs[] =
...
{
.name = "ab8500-fg",
.num_resources = ARRAY_SIZE(ab8500_fg_resources),
.resources = ab8500_fg_resources,
}
Platform resource
static struct resource __devinitdata ab8500_fg_resources[] = {
{
.name = "NCONV_ACCU",
.start = AB8500_INT_CCN_CONV_ACC,
.end = AB8500_INT_CCN_CONV_ACC,
.flags = IORESOURCE_IRQ, //interrupt resource
},
Register the “ab8500-fg” device platform
mfd_add_devices(ab8500->dev..,ab8500_bm_devs,…); // @kernel/drivers/mfd/mfd-core.c
platform_device_add(..); // @kernel/driver/base/platform.c
The “ab8500-fg” device has been registered under the node of “ab8500-i2c”
sys/devices/platform/ab8500-i2c.0/ab8500-fg.0
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Platform driver case: power_supply class on Riogrande
@kernel/drivers/power/power_supply_core.c
static int __init power_supply_class_init(void)
{
power_supply_class = class_create(THIS_MODULE, "power_supply");
if (IS_ERR(power_supply_class))
return PTR_ERR(power_supply_class);
power_supply_class->dev_uevent = power_supply_uevent;
power_supply_init_attrs(&power_supply_dev_type);
return 0;
}
power_supply_register(struct device *parent, struct power_supply *psy)
{
…
dev->class = power_supply_class;
dev->parent = parent; //the node of “sys/devices/platform/ab8500-i2c.0/ab8500-fg.0”
rc = kobject_set_name(&dev->kobj, "%s", psy->name); //name = “ab8500_fg”
rc = device_add(dev);
…
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Platform driver case: ab8500_fg on Riogrande
@kernel/drivers/power/ab8500_fg.c
Platform driver structure
static struct platform_driver ab8500_fg_driver = {
.probe = ab8500_fg_probe,
.remove = __devexit_p(ab8500_fg_remove),
.suspend = ab8500_fg_suspend,
.resume = ab8500_fg_resume,
.driver = {
.name = "ab8500-fg",
.owner = THIS_MODULE,
},
};
Register the “ab8500-fg” driver platform
platform_driver_register(&ab8500_fg_driver);
Run probe function
Init HW parameters
static int __devinit ab8500_fg_probe(struct platform_device *pdev) {
…
di->fg_psy.name = "ab8500_fg";
…
ret = power_supply_register(di->dev, &di->fg_psy);
..
}
power_supply_register@kernel/drivers/power/power_supply_core.c
The “power_supply/ab8500_fg” device has been registered under the node of “ab8500-fg.0”
sys/devices/platform/ab8500-i2c.0/ab8500-fg.0/power_supply/ab8500_fg
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Platform driver attribute case: ab8500_fg on Riogrande
@kernel/drivers/power/ab8500_fg.c
static int __devinit ab8500_fg_probe(struct platform_device *pdev)
{
…
di->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
di->fg_psy.properties = ab8500_fg_props;
di->fg_psy.num_properties = ARRAY_SIZE(ab8500_fg_props);
di->fg_psy.get_property = ab8500_fg_get_property;
…
ret = ab8500_fg_sysfs_psy_create_attrs(di->fg_psy.dev);
}
static struct device_attribute ab8500_fg_sysfs_psy_attrs[] = {
__ATTR(capacity, S_IRUGO, show_capacity, NULL),
};
static enum power_supply_property ab8500_fg_props[] = {
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
POWER_SUPPLY_PROP_ENERGY_FULL,
POWER_SUPPLY_PROP_ENERGY_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
};
The attributes of capacity, voltage_now, etc have been created under the node of “ab8500_fg”
sys/devices/platform/ab8500-i2c.0/ab8500-fg.0/power_supply/ab8500_fg/capacity
Platform register case workflow chart:
sys/devices/platform/ab8500-i2c.0/ab8500-fg.0/power_supply/ab8500_fg/capacity
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------I2CI2C device-driver model
I2C device-driver model architecture
I2C device-driver case
I2C device case “nmk-i2c” on Riogrande
I2C device adapter&client case on Riogrande
I2C driver case “as3677” on Riogrande
I2C device-driver model
I2C is the name for a two-wire serial bus protocol(SDA,SCL) originally developed by Phillips. It commonly is used in embedded systems so different components can communicate;
The I2C kernel code is broken up into a number of logical pieces: the I2C core, I2C bus drivers, I2C algorithm drivers and I2C chip drivers.
I2C Core
I2C Bus Drivers
An I2C bus driver is described by a struct named i2c_adapter
I2C Algorithm Drivers
An I2C algorithm is used by the I2C bus driver to talk to the I2C bus. Most I2C bus drivers define their own I2C algorithms and use them, as they are tied closely to how the bus driver talks to that specific type of hardware.
I2C Chip Drivers
Register Path:
/sys/devices/platform/
Mapping path: /sys/bus/i2c/devices/xxxx
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------I2CI2C device-driver model architecture
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------I2CI2C device case “nmk-i2c” on Riogrande
@kernel/arch/arm/mach-ux500/board-rio-grande.c
i2c_board_info
static struct i2c_board_info __initdata pdp_i2c2_devices[] =
{
I2C_BOARD_INFO("as3677", 0x80 >> 1), //i2c name=“as3677”,address=0x40
.platform_data = &as3677_pdata,
.irq = 92,
}
pdp_i2c_init
db8500_add_i2c2(&pdp_i2c2_data) ,there are four I2C buses on DB8500
dbx500_add_i2c(2, U8500_I2C2_BASE, IRQ_DB8500_I2C2, pdata)
dbx500_add_platform_device_4k1irq("nmk-i2c", id, base, irq, pdata);
{
struct resource resources[] = {
[0] = { //4k memory map
.start = base,
.end = base + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = { //interrupt IRQ_DB8500_I2C2
.start = irq,
.end = irq,
.flags = IORESOURCE_IRQ,
}
};
return dbx500_add_platform_device(name, id, pdata, resources, ARRAY_SIZE(resources));
}
platform_device_add: add device “nmk-i2c” under platform node
i2c_register_board_info(2, ARRAY_AND_SIZE(pdp_i2c2_devices));
Add pdp_i2c2_devices into __i2c_board_list, busnum=2 (I2CBUS)
Register I2C device
/sys/devices/platform/nmk-i2c.2/
I2C driver case “nmk-i2c” on Riogrande
@kernel/drivers/i2c/busses/i2c-nomadik.c
Platform driver struct
static struct platform_driver nmk_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME, //name="nmk-i2c"
.pm = &nmk_i2c_pm,
},
.probe = nmk_i2c_probe,
.remove = __devexit_p(nmk_i2c_remove),
};
platform_driver_register(&nmk_i2c_driver);
Find the I2C device by name of “nmk-i2c”
Register I2C driver
/sys/devices/platform/nmk-i2c.2
I2C device adapter&client case on Riogrande
Run probe function: nmk_i2c_probe@kernel/drivers/i2c/busses/i2c-nomadik.c
adap->nr = pdev->id; //=2
Add adapter
i2c_add_numbered_adapter(adap)@kernel/drivers/i2c/i2c-core.c
i2c_register_adapter(adap)@kernel/drivers/i2c/i2c-core.c
dev_set_name(&adap->dev, "i2c-%d", adap->nr); //name=”i2c-2”
adap->dev.bus = &i2c_bus_type;
device_register(&adap->dev);
Register I2C adapter device
/sys/devices/platform/nmk-i2c.2/i2c-2
i2c_scan_static_board_info(adap)
Compare __i2c_board_list.id and adap.nr,One I2C bus device must be matched one I2C adapter
i2c_new_device
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
dev_set_name(&client->dev, “%d-%04x”, i2c_adapter_id(adap),client->addr); //I2C BUS ID=2, I2C device address=0x40 for “as3677”
device_register(&client->dev);
Register I2C client device
/sys/devices/platform/nmk-i2c.2/i2c-2/2-0040
I2C driver case “as3677” on Riogrande
@kernel/drivers/leds/led-class.c
leds_init : create class
leds_class = class_create(THIS_MODULE, "leds");
led_classdev_register //register a new object of the class
@kernel/drivers/leds/leds-as3677.c
I2C driver struct
static struct i2c_driver as3677_driver = {
.driver = {
.name = "as3677",
#ifdef CONFIG_PM
.pm = &as3677_pm,
#endif
},
.probe = as3677_probe,
.remove = as3677_remove,
.shutdown = as3677_shutdown,
.id_table = as3677_id,
};
i2c_add_driver(&as3677_driver);à i2c_register_driver(THIS_MODULE, driver);
driver_register(&drv->driver)@kernel/drivers/base/driver.c
bus_add_driver@kernel/drivers/base/bus.c //Add the driver into i2c_bus_type I2C BUS(adapter)
bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver); //attach the driver to matched adapter
as3677_probe
as3677_configure(client, data, as3677_pdata)
led_classdev_register(&client->dev, &led->ldev);
Register “3677” led device
/sys/devices/platform/nmk-i2c.2/i2c-2/2-0040/leds
device_add_attributes(led->ldev.dev,as3677_led_attributes); //add attribute
/sys/devices/platform/nmk-i2c.2/i2c-2/2-0040/leds/lcd-backlight