Linux驱动分析
总的来说,linux
2.6以后的内核,其驱动部分,可以分为三大主要部分,分别为,1.linux 驱动模型,2.linux 设备,3.linux
驱动 1.linux
驱动模型的相应数据结构,主要包括,底层的,用来组成/sys/文件系统的,kobject,
kset,subsystem.和上层用来抽象设备,驱动,总线的,device,device_driver,bus.等
等。
接下来,我们主要分析,linux 是如何建立驱动模型的。在
init.c:
/** * driver_init - initialize driver model.
* * Call the driver model init functions to initialize their
* subsystems. Called early from init/main.c.
*/
void __init driver_init(void){
/* These are the core pieces */
devices_init();
buses_init();
classes_init();
firmware_init();
/* These are also core pieces, but must come after the
* core core pieces. */
platform_bus_init();
system_bus_init();
cpu_dev_init();
attribute_container_init();}
2.linux 设备 为例在2.6内核中将每个设备的资源用结构platform_device来描述,该结构体定义在kernel\include\linux\platform_device.h中,
struct platform_device
{
const char * name;
u32 id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义在kernel\include\linux\ioport.h中,
struct resource {
const char *name;
unsigned long start, end;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
下面举个例子来说明一下:
在kernel\arch\arm\mach-pxa\pxa27x.c定义了
tatic struct resource pxa27x_ohci_resources[] = {
[0] = {
.start = 0x4C000000,
.end = 0x4C00ff6f,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_USBH1,
.end = IRQ_USBH1,
.flags = IORESOURCE_IRQ,
},
};
这里定义了两组resource,它描述了一个usb host设备的资源,第1组描述了这个usb host设备所占用的
总线地址范围,IORESOURCE_MEM表示第1组描述的是内存类型的资源信息,第2组描述了这个usb host设备
的中断号,IORESOURCE_IRQ表示第2组描述的是中断资源信息。设备驱动会根据flags来获取相应的资源信息。
有了resource信息,就可以定义platform_device了:
static struct platform_device ohci_device =
{
.name = "pxa27x-ohci",
.id = -1,
.dev = {
.dma_mask = &pxa27x_dmamask,
.coherent_dma_mask = 0xffffffff,
},
.num_resources = ARRAY_SIZE(pxa27x_ohci_resources),
.resource = pxa27x_ohci_resources,
};
有了platform_device就可以调用函数platform_add_devices向系统中添加该设备了,这里的实现是
static int __init pxa27x_init(void)
{
return platform_add_devices(devices, ARRAY_SIZE(devices));
}
platform_add_devices 的情景分析
platform_add_devices=>device_register(&pdev->dev);
device_register=>device_add(dev);
device_add=>bus_add_device(dev);
接下来,我们详细看一下 bus_add_device 这个函数
list_add_tail(&dev->bus_list, &dev->bus->devices.list);
device_attach(dev);
up_write(&dev->bus->subsys.rwsem);
device_add_attrs(bus, dev);
sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id);
bus_add_device=>device_attach
if (dev->driver) {
device_bind_driver(dev);
return 1;
}
if (bus->match) {
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);
}
}
device_attach=>device_bind_driver:
list_add_tail(&dev->driver_list, &dev->driver->devices);
sysfs_create_link(&dev->driver->kobj, &dev->kobj,
kobject_name(&dev->kobj));
sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver");
我以 platform device serial_8250 为例 说明一下两个 sysfs_create_link 函数,反映在 sysfs文件系统上实质现象。
steven@steven-laptop:/sys/bus/platform/drivers/serial8250$ ls -l
total 0
--w------- 1 root root 4096 2009-06-29 00:08 bind
lrwxrwxrwx 1 root root 0 2009-06-29 00:08 serial8250 -> ../../../../devices/platform/serial8250
--w------- 1 root root 4096 2009-06-29 00:08 uevent
--w------- 1 root root 4096 2009-06-29 00:08 unbind
steven@steven-laptop:/sys/bus/platform/drivers/serial8250$ cd /sys/devices/platform/serial8250/
steven@steven-laptop:/sys/devices/platform/serial8250$ ls
driver modalias power subsystem tty uevent
steven@steven-laptop:/sys/devices/platform/serial8250$ ls -l
total 0
lrwxrwxrwx 1 root root 0 2009-06-29 00:13 driver -> ../../../bus/platform/drivers/serial8250
-r--r--r-- 1 root root 4096 2009-06-29 00:13 modalias
drwxr-xr-x 2 root root 0 2009-06-29 00:13 power
lrwxrwxrwx 1 root root 0 2009-06-29 00:13 subsystem -> ../../../bus/platform
drwxr-xr-x 6 root root 0 2009-06-29 00:13 tty
-rw-r--r-- 1 root root 4096 2009-06-29 00:13 uevent
steven@steven-laptop:/sys/devices/platform/serial8250$
steven@steven-laptop:/sys/bus/platform/devices$ ls -l
total 0
lrwxrwxrwx 1 root root 0 2009-07-04 23:32 eisa.0 -> ../../../devices/platform/eisa.0
lrwxrwxrwx 1 root root 0 2009-07-04 23:32 i8042 -> ../../../devices/platform/i8042
lrwxrwxrwx 1 root root 0 2009-07-04 23:32 iTCO_wdt -> ../../../devices/platform/iTCO_wdt
lrwxrwxrwx 1 root root 0 2009-07-04 23:32 pcspkr -> ../../../devices/platform/pcspkr
lrwxrwxrwx 1 root root 0 2009-07-04 23:32 serial8250 -> ../../../devices/platform/serial8250
这里的pxa27x_init必须在设备驱动加载之前被调用,可以把它放到
subsys_initcall(pxa27x_init);
关于 subsys_initcall 参考 说说subsys_initcall3.linux 驱动
阅读(837) | 评论(0) | 转发(0) |