欢迎来到三一办公! | 帮助中心 三一办公31ppt.com(应用文档模板下载平台)
三一办公
全部分类
  • 办公文档>
  • PPT模板>
  • 建筑/施工/环境>
  • 毕业设计>
  • 工程图纸>
  • 教育教学>
  • 素材源码>
  • 生活休闲>
  • 临时分类>
  • ImageVerifierCode 换一换
    首页 三一办公 > 资源分类 > DOC文档下载  

    Linux设备驱动模型之platform总线深入浅出.doc

    • 资源ID:4842359       资源大小:105KB        全文页数:30页
    • 资源格式: DOC        下载积分:10金币
    快捷下载 游客一键下载
    会员登录下载
    三方登录下载: 微信开放平台登录 QQ登录  
    下载资源需要10金币
    邮箱/手机:
    温馨提示:
    用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)
    支付方式: 支付宝    微信支付   
    验证码:   换一换

    加入VIP免费专享
     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    Linux设备驱动模型之platform总线深入浅出.doc

    Linux设备驱动模型之platform总线深入浅出在Linux2.6以后的设备驱动模型中,需关心总线,设备和驱动这三种实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。 对于依附在USB、PCI、I2C、SPI等物理总线来 这些都不是问题。但是在嵌入式系统里面,在Soc系统中集成的独立外设控制器,挂接在Soc内存空间的外设等却不依附在此类总线。基于这一背景,Linux发明了一种总线,称为platform。 相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。 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 *); /探测函数,在注册平台设备时被调用 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;/设备驱动结构;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-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_devicestruct 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 long 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分别表示该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例如在archarmmach-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 = .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_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_IRQ, num)来获取资源设备除了可以在BSP中定义资源以外,还可以附加一些数据信息,因为对设备的硬件描述除了中断,内存等标准资源以外,可能还会有一些配置信息,这些配置信息也依赖于板,不适宜直接放置在设备驱动上,因此,platform也提供了platform_data的支持,例如对于dm9000staTIc struct dm9000_plat_data dm9000_platdata = .flags = DM9000_PLATF_16BITONLY,;staTIc struct platform_device at91sam9261_dm9000_device = .name = "dm9000", .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_type,其定义位于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 device_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_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风格的匹配对于设备驱动的开发  其设计顺序为定义 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) return 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_unregister_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; 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(123456789101112131415161718/* 初始化后调用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 * 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) /* 驱动加入总线的驱动列表 */ 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) failedn", _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;12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849执行驱动加载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; int 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; /* * 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) /* 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 -ENODEV; /* 调用总线配置的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_debug("%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); 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_failed: 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;12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273driver_probe_device() 这个函数实际上做了两件事  1.调用总线提供的match函数。如果检查通过 说明设备和驱动是匹配的 设备所指的驱动指针要赋值为当前驱动  2.探测(probe) 首先调用总线提供的probe函数,如果驱动有自己的Probe函数,还要调用驱动的probe函数platform总线的match函数在上文有介绍 三种情况均可匹配  platform总线的probe函数就是platform_drv_probe() 这个函数是个封装函数 它只是简单的调用了驱动的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_port->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总线驱动注册就是遍历各个设备 检查是否和驱动匹配接下来分析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, 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 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 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 = 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 device 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; 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 = 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. */ 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_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

    注意事项

    本文(Linux设备驱动模型之platform总线深入浅出.doc)为本站会员(sccc)主动上传,三一办公仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知三一办公(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    备案号:宁ICP备20000045号-2

    经营许可证:宁B2-20210002

    宁公网安备 64010402000987号

    三一办公
    收起
    展开