《Linux设备驱动模型之platform总线深入浅出.doc》由会员分享,可在线阅读,更多相关《Linux设备驱动模型之platform总线深入浅出.doc(30页珍藏版)》请在三一办公上搜索。
1、Linux设备驱动模型之platform总线深入浅出在Linux2.6以后的设备驱动模型中,需关心总线,设备和驱动这三种实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。对于依附在USB、PCI、I2C、SPI等物理总线来 这些都不是问题。但是在嵌入式系统里面,在Soc系统中集成的独立外设控制器,挂接在Soc内存空间的外设等却不依附在此类总线。基于这一背景,Linux发明了一种总线,称为platform。相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出
2、来的总线,实际中并不存在这样的总线。platform总线相关代码:driverbaseplatform.c 文件相关结构体定义:includelinuxplatform_device.h 文件中platform总线管理下最重要的两个结构体是platform_device和platform_driver分别表示设备和驱动在Linux中的定义如下一:platform_driver/includelinuxplatform_device.h struct platform_driver int (*probe)(struct platform_device *); /探测函数,在注册平台设备时被调用
3、 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 (*r
4、esume)(struct platform_device *);/恢复函数,在开机时被调用 struct device_driver driver;/设备驱动结构;1234567891011struct device_driver const char * name; struct bus_type * bus; struct kobject kobj; struct klist klist_devices; struct klist_node knode_bus; struct module * owner; const char * mod_name; /* used for built
5、-in modules */ struct module_kobject * mkobj; int (*probe) (struct device * dev); int (*remove) (struct device * dev); void (*shutdown) (struct device * dev); int (*suspend) (struct device * dev, pm_message_t state); int (*resume) (struct device * dev);12345678910111213141516171819二:platform_devices
6、truct platform_device const char * name;/设备名称 u32 id;/取-1 struct device dev;/设备结构 u32 num_resources;/ resource结构个数 struct resource * resource;/设备资源;1234567resource结构体也是描述platform_device的一个重要结构体 该元素存入了最为重要的设备资源信息struct resource resource_size_t start; resource_size_t end; const char *name; unsigned lo
7、ng flags; struct resource *parent, *sibling, *child;1234567我们通常关心start,end,flags。它们分别标明了资源的开始值,结束值和类型,flags可以为IORESOURCE_IO,IORESOURCE_MEM,IORESOURCE_IRQ,IORESOURCE_DMA等,start,end的含义会随着flags变更,如当flags为IORESOURCE_MEM时,start,end分别表示该platform_device占据的内存的开始地址和结束地址;当flags为,IORESOURCE_IRQ时,start,end分别表示该
8、platform_device使用的中断号的开始值和结束值,如果只使用了1个中断号,开始和结束值相同。对于同种类型的资源而言,可以有多份,例如某设备占据了两个内存区域,则可以定义两个IORESOURCE_MEM资源。 对resource的定义也通常在BSP的板文件中运行,而在具体的设备驱动中通过platform_get_resource()这样的API来获取,此API的原型为:struct resource *platform_get_resource(struct platform_device *dev, unsigned int type,unsigned int num)12例如在ar
9、charmmach-at91Board-sam9261ek.c板文件中为DM9000网卡定义了如下的resourcestatic struct resource at91sam9261_dm9000_resource = 0 = .start = AT91_CHIPSELECT_2, .end = AT91_CHIPSELECT_2 + 3, .flags = IORESOURCE_MEM , 1 = .start = AT91_CHIPSELECT_2 + 0x44, .end = AT91_CHIPSELECT_2 + 0xFF, .flags = IORESOURCE_MEM , 2 =
10、 .start = AT91_PIN_PC11, .end = AT91_PIN_PC11, .flags = IORESOURCE_IRQ ;123456789101112131415161718在DM9000网卡驱动中则是通过如下办法拿到这3份资源db-addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);db-data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);db-irq_res = platform_get_resource(pdev, IORESOURCE_
11、IRQ, 0);123对于irq而言platform_get_resource()还有一个进行了封装的变体platform_get_irq(), api原型是int platform_get_irq(struct platform_device *dev, unsigned int num) struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num); return r ? r-start : -ENXIO;12345实际上这个函数也是调用platform_get_resource(dev, IORESOURCE_I
12、RQ, num)来获取资源设备除了可以在BSP中定义资源以外,还可以附加一些数据信息,因为对设备的硬件描述除了中断,内存等标准资源以外,可能还会有一些配置信息,这些配置信息也依赖于板,不适宜直接放置在设备驱动上,因此,platform也提供了platform_data的支持,例如对于dm9000staTIc struct dm9000_plat_data dm9000_platdata = .flags = DM9000_PLATF_16BITONLY,;staTIc struct platform_device at91sam9261_dm9000_device = .name = dm90
13、00, .id = 0, .num_resources = ARRAY_SIZE(at91sam9261_dm9000_resource), .resource = at91sam9261_dm9000_resource, .dev = .platform_data = 12345678910111213获取platform_data的API是dev_get_platdata()struct dm9000_plat_data *pdata = dev_get_platdata(1三:platform总线 系统为platform总线定义了一个bus_type的实例platform_bus_typ
14、e,其定义位于drivers/base/platform.c下struct bus_type platform_bus_type = .name = platform, .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .pm = 1234567最重要的是match函数,真是此成员函数确定了platform_device和platform_driver之间如何匹配的staTIc int platform_match(struct device *dev, struct dev
15、ice_driver *drv) struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); /* Attempt an OF style match first */ if (of_driver_match_device(dev, drv) return 1; /* Then try to match against the id table */ if (pdrv-id_table) return platform_match_
16、id(pdrv-id_table, pdev) != NULL; /* fall-back to driver name match */ return (strcmp(pdev-name, drv-name) = 0);12345678910111213141516可知有3中情况下platform_device和platform_driver匹配 1.基于设备树风格的匹配 2.匹配ID表(即platform_device设备名是否出现在platform_driver的id表内) 3.匹配platform_device设备名和驱动的名字 在4.0还有一种情况是基于ACPI风格的匹配对于设备驱动
17、的开发 其设计顺序为定义 platform_device - 注册 platform_device- 定义 platform_driver- 注册 platform_driver 。 这里选用的例子是q40kbd,在/drivers/input/serio目录里。这是一个键盘控制器驱动 这里直接分析初始化函数staTIc int _init q40kbd_init(void) int error; if (!MACH_IS_Q40) return -EIO; /* platform总线驱动的注册 */ error = platform_driver_register( if (error) r
18、eturn error; /* 分配一个platform设备 */ q40kbd_device = platform_device_alloc(q40kbd, -1); if (!q40kbd_device) goto err_unregister_driver; /* platform设备注册 */ error = platform_device_add(q40kbd_device); if (error) goto err_free_device; return 0; err_free_device: platform_device_put(q40kbd_device); err_unre
19、gister_driver: platform_driver_unregister( return error;123456789101112131415161718192021222324252627驱动注册函数是 platform_driver_register()函数int platform_driver_register(struct platform_driver *drv) /把驱动的总线设置为platform总线 drv-driver.bus = /依次设置驱动的各个函数指针 if (drv-probe) drv-driver.probe = platform_drv_probe
20、; 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; /调用driver_register注册驱动 return driver_register(123456789101112
21、131415161718/* 初始化后调用bus_add_driver函数执行注册过程 */int driver_register(struct device_driver * drv) if (drv-bus-probe %s needs updating - please use bus_type methodsn, drv-name); klist_init( return bus_add_driver(drv);123456789101112为总线增加一个驱动int bus_add_driver(struct device_driver *drv) struct bus_type *
22、bus = get_bus(drv-bus); int error = 0; if (!bus) return -EINVAL; pr_debug(bus %s: add driver %sn, bus-name, drv-name); /* 为kobject结构设置名字 */ error = kobject_set_name( if (error) goto out_put_bus; drv-kobj.kset = /* 为sysfs文件系统创建设备的相关文件 */ if (error = kobject_register( if (drv-bus-drivers_autoprobe) /*
23、 驱动加入总线的驱动列表 */ error = driver_attach(drv); if (error) goto out_unregister; /* 在sysfs创建驱动的属性文件和模块 */ klist_add_tail( 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) fai
24、ledn, _FUNCTION_, drv-name); error = add_bind_files(drv); if (error) /* Ditto */ printk(KERN_ERR %s: add_bind_files(%s) failedn, _FUNCTION_, drv-name); return error;out_unregister: kobject_unregister(out_put_bus: put_bus(bus); return error;123456789101112131415161718192021222324252627282930313233343
25、53637383940414243444546474849执行驱动加载int driver_attach(struct device_driver * drv) return bus_for_each_dev(drv-bus, NULL, drv, _driver_attach);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; in
26、t error = 0; if (!bus) return -EINVAL; /* 初始化一个klist_iter结构 目的是在双向链表中定位一个成员 */ klist_iter_init_node( while (dev = next_device( klist_iter_exit( return error;1234567891011121314151617181920212223242526static int _driver_attach(struct device * dev, void * data) struct device_driver * drv = data; /* *
27、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 didnt support the device. * * driver_probe_device() will spit a warning if there * is an error. */ if (dev-parent)
28、 /* Needed for USB */ down( /* 获取设备的锁 */ down( if (!dev-driver) driver_probe_device(drv, dev); up( if (dev-parent) up( return 0;1234567891011121314151617181920212223242526int driver_probe_device(struct device_driver * drv, struct device * dev) int ret = 0; if (!device_is_registered(dev) return -ENOD
29、EV; /* 调用总线配置的match函数 */ if (drv-bus-match pr_debug(%s: Matched Device %s with Driver %sn, drv-bus-name, dev-bus_id, drv-name); /* 调用really_probe函数 */ ret = really_probe(dev, drv);done: return ret;static int really_probe(struct device *dev, struct device_driver *drv) int ret = 0; atomic_inc( pr_debu
30、g(%s: Probing driver %s with device %sn, drv-bus-name, drv-name, dev-bus_id); WARN_ON(!list_empty( dev-driver = drv; if (driver_sysfs_add(dev) printk(KERN_ERR %s: driver_sysfs_add(%s) failedn, _FUNCTION_, dev-bus_id); goto probe_failed; /* 调用总线的probe函数 */ if (dev-bus-probe) ret = dev-bus-probe(dev);
31、 if (ret) goto probe_failed; /* 如果驱动提供了Probe函数,则调用驱动的probe函数 */ else if (drv-probe) ret = drv-probe(dev); if (ret) goto probe_failed; /* 设备发现了驱动 通过sysfs创建一些文件 和设备做符号链接 */ driver_bound(dev); ret = 1; pr_debug(%s: Bound Device %s to Driver %sn, drv-bus-name, dev-bus_id, drv-name); goto done;probe_fail
32、ed: devres_release_all(dev); driver_sysfs_remove(dev); dev-driver = NULL; if (ret != -ENODEV /* * Ignore errors returned by -probe so that the next driver can try * its luck. */ ret = 0;done: atomic_dec( wake_up( return ret;1234567891011121314151617181920212223242526272829303132333435363738394041424
33、3444546474849505152535455565758596061626364656667686970717273driver_probe_device() 这个函数实际上做了两件事 1.调用总线提供的match函数。如果检查通过 说明设备和驱动是匹配的 设备所指的驱动指针要赋值为当前驱动 2.探测(probe) 首先调用总线提供的probe函数,如果驱动有自己的Probe函数,还要调用驱动的probe函数platform总线的match函数在上文有介绍 三种情况均可匹配 platform总线的probe函数就是platform_drv_probe() 这个函数是个封装函数 它只是简单
34、的调用了驱动的Probe函数 驱动的Probe函数就是q40kbd_probestatic int _devinit q40kbd_probe(struct platform_device *dev) q40kbd_port = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!q40kbd_port) return -ENOMEM; q40kbd_port-id.type = SERIO_8042; q40kbd_port-open = q40kbd_open; q40kbd_port-close = q40kbd_close; q40kbd_p
35、ort-dev.parent = strlcpy(q40kbd_port-name, Q40 Kbd Port, sizeof(q40kbd_port-name); strlcpy(q40kbd_port-phys, Q40, sizeof(q40kbd_port-phys); /注册serio结构变量 serio_register_port(q40kbd_port); printk(KERN_INFO serio: Q40 kbd registeredn); return 0;123456789101112131415161718综上所述,platform总线驱动注册就是遍历各个设备 检查是
36、否和驱动匹配接下来分析platform设备的注册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_type */ pdev-dev.bus = if (pdev-id != -1) snprintf(pdev-dev.bus_id, BUS_ID_SIZE, %s.%u, pdev-name,
37、 pdev-id); else strlcpy(pdev-dev.bus_id, pdev-name, BUS_ID_SIZE); /* 把设备IO端口和IO内存资源注册到系统 */ for (i = 0; i num_resources; i+) struct resource *p, *r = if (r-name = NULL) r-name = pdev-dev.bus_id; p = r-parent; if (!p) if (r-flags else if (r-flags if (p ret = -EBUSY; goto failed; pr_debug(Registering
38、platform device %s. Parent at %sn, pdev-dev.bus_id, pdev-dev.parent-bus_id); ret = device_add( if (ret = 0) return ret; failed: while (-i = 0) if (pdev-resourcei.flags return ret;123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354int device_add(struct
39、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 = %sn, dev-bus_id); /* 获得父设备 */ parent = get_device(dev-parent); error =
40、 setup_parent(dev, parent); if (error) goto Error; /* first, register with generic layer. */ /* 这是kobject的名字 */ kobject_set_name( /* 在sys目录生成设备目录 */ error = kobject_add( if (error) goto Error; /* notify platform of device entry */ if (platform_notify) platform_notify(dev); /* notify clients of devic
41、e entry (new way) */ if (dev-bus) blocking_notifier_call_chain( /* 设置uevent属性 */ dev-uevent_attr.attr.name = uevent; dev-uevent_attr.attr.mode = S_IRUGO | S_IWUSR; if (dev-driver) dev-uevent_attr.attr.owner = dev-driver-owner; dev-uevent_attr.store = store_uevent; dev-uevent_attr.show = show_uevent;
42、 error = device_create_file(dev, 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 =
43、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( /* If this is not a fake compatible device, then create the * symlink from the class to the device. */
44、 if (dev-kobj.parent != if (parent) sysfs_create_link(#ifdef CONFIG_SYSFS_DEPRECATED class_name = make_class_name(dev-class-name, if (class_name) sysfs_create_link(#endif /* 设备的能源管理 */ if (error = device_add_attrs(dev) goto AttrsError; if (error = device_pm_add(dev) goto PMError; if (error = bus_add
45、_device(dev) goto BusError; kobject_uevent( bus_attach_device(dev); /* 设备加入父设备的链表 */ if (parent) klist_add_tail( if (dev-class) down( /* tie the class to the device */ list_add_tail( /* notify any interfaces that the device is here */ list_for_each_entry(class_intf, up( Done: kfree(class_name); put_device(dev); return error; BusError: device_pm_remove(dev); PMError: if (dev-bus) blocking_notifier_call_chain( device_remove_attrs(dev); AttrsError: if (dev-devt_attr) device_remov