Linux驱动修炼之道-platform

本文详细解析了Linux平台总线的初始化过程、平台设备与驱动的注册与匹配机制,并通过示例代码展示了如何创建和注册平台设备及驱动。
AI助手已提取文章相关产品:

努力成为linux kernel hacker的人李万鹏原创作品,为梦而战。转载请标明出处

http://blog.youkuaiyun.com/woshixingaaa/archive/2011/05/21/6436172.aspx

首先看一下我的系统中都有什么设备挂在了platform虚拟总线上:

hacker@hacker:~/linux-2.6.30.4$ cd /sys/bus/platform/ hacker@hacker:/sys/bus/platform$ tree . |-- devices | |-- Fixed MDIO bus.0 -> ../../../devices/platform/Fixed MDIO bus.0 | |-- eisa.0 -> ../../../devices/platform/eisa.0 | |-- i8042 -> ../../../devices/platform/i8042 | |-- pcspkr -> ../../../devices/platform/pcspkr | |-- rtc_cmos -> ../../../devices/platform/rtc_cmos | `-- serial8250 -> ../../../devices/platform/serial8250 |-- drivers | |-- dsa | | |-- bind | | |-- uevent | | `-- unbind | |-- i8042 | | |-- bind | | |-- i8042 -> ../../../../devices/platform/i8042 | | |-- uevent | | `-- unbind | |-- mdio-gpio | | |-- bind | | |-- uevent | | `-- unbind | |-- parport_pc | | |-- bind | | |-- module -> ../../../../module/parport_pc | | |-- uevent | | `-- unbind | |-- rtc_cmos | | |-- bind | | |-- rtc_cmos -> ../../../../devices/platform/rtc_cmos | | |-- uevent | | `-- unbind | |-- serial8250 | | |-- bind | | |-- serial8250 -> ../../../../devices/platform/serial8250 | | |-- uevent | | `-- unbind | `-- twl4030_reg | |-- bind | |-- uevent | `-- unbind |-- drivers_autoprobe |-- drivers_probe `-- uevent 19 directories, 24 files

platform的初始化:首先系统启动的时候会调用platform_bus_init来初始化这个虚拟总线,让后向虚拟总线注册即将挂载这条总线上的设备。platform_bus_type部分是内核为我们实现好的,我们只关系platform_device与platform_driver就行了。

struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .pm = PLATFORM_PM_OPS_PTR, }; EXPORT_SYMBOL_GPL(platform_bus_type); int __init platform_bus_init(void) { int error; early_platform_cleanup(); error = device_register(&platform_bus); if (error) return error; error = bus_register(&platform_bus_type); if (error) device_unregister(&platform_bus); return error; }

记住总线也是一种设备,所以首先注册总线设备,然后注册总线。

static struct platform_device *smdk2410_devices[] __initdata = { &s3c_device_usb, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, &s3c_device_iis, };

把设备挂到platform总线上:

static void __init smdk2410_init(void) { s3c_i2c0_set_platdata(NULL); platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices)); smdk_machine_init(); }

首先来看一个重要的数据结构:

struct resource { resource_size_t start; /*资源的起始物理地址*/ resource_size_t end; /*资源的结束物理地址*/ const char *name; /*资源的名称*/ unsigned long flags; /*资源的类型*/ struct resource *parent, *sibling, *child; /*资源的链表指针*/ }; struct platform_device { const char * name; /*设备名*/ int id; /*设备编号,配合设备名使用*/ struct device dev; u32 num_resources; struct resource * resource; /*设备资源*/ struct platform_device_id *id_entry; }; 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; struct platform_device_id *id_table; };

设备的分配:

struct platform_device *platform_device_alloc(const char *name, int id); //name:设备名,id:设备id,一般为-1

设备的注册:

int platform_device_add(struct platform_device *pdev);

获取资源:

struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);

/*dev:资源所属的设备,type:获取的资源类型,num:获取的资源数*/
这里详述platform_device与platform_driver是怎样匹配上的,这里跟踪函数的执行过程,首先是platform_driver_register:

int platform_driver_register(struct platform_driver *drv) { 。。。。。。。。。。 return driver_register(&drv->driver); } int driver_register(struct device_driver *drv) { 。。。。。。。。。。。 ret = bus_add_driver(drv); 。。。。。。。。。。。 } int bus_add_driver(struct device_driver *drv) { 。。。。。。。。。。。。 if (drv->bus->p->drivers_autoprobe) { error = driver_attach(drv); if (error) goto out_unregister; } 。。。。。。。。。。。。 } int driver_attach(struct device_driver *drv) { return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); }

这里来看__driver_attach这个函数,其中分别调用了driver_match_device,driver_probe_device函数。如果匹配成果调用probe函数,否则返回。

static 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 didn't support the device. * * driver_probe_device() will spit a warning if there * is an error. */ if (!driver_match_device(drv, dev)) return 0; if (dev->parent) /* Needed for USB */ down(&dev->parent->sem); down(&dev->sem); if (!dev->driver) driver_probe_device(drv, dev); up(&dev->sem); if (dev->parent) up(&dev->parent->sem); return 0; }

匹配的时候调用的bus的match函数。

struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .pm = PLATFORM_PM_OPS_PTR, };

找到platform_match:

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) return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */ return (strcmp(pdev->name, drv->name) == 0); }

最后一行可以看到通过pdev->name与drv->name进行匹配,也就是说是通过设备与驱动的名字进行匹配。匹配成功后调用驱动的probe函数。

int driver_probe_device(struct device_driver *drv, struct device *dev) { 。。。。。。。。。 ret = really_probe(dev, drv); 。。。。。。。。 } static int really_probe(struct device *dev, struct device_driver *drv) { 。。。。。。。。 if (dev->bus->probe) { ret = dev->bus->probe(dev); if (ret) goto probe_failed; } else if (drv->probe) { ret = drv->probe(dev); if (ret) goto probe_failed; } 。。。。。。。。 }

由relly_probe函数可以看出,如果bus定义了probe函数,则调用bus的probe函数;如果bus,没有定义而driver定义了probe函数,则调用driver的probe函数。由上边的platform_bus_type可以看出bus并没有定义probe函数,所以调用driver的probe函数。

测试程序:

device.c

#include <linux/module.h> #include <linux/init.h> #include <linux/device.h> #include <linux/string.h> #include <linux/module.h> #include <linux/platform_device.h> static struct platform_device *my_device; static int __init platform_dev_init(void) { int ret; //分配结构 my_device = platform_device_alloc("my_dev", -1); //注册设备 ret = platform_device_add(my_device); if(ret) printk("platform_device_add failed!/n"); return ret; } static void __exit platform_dev_exit(void) { platform_device_unregister(my_device);//卸载设备 } module_init(platform_dev_init); module_exit(platform_dev_exit); MODULE_LICENSE("GPL");

driver.c

#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/device.h> #include <linux/string.h> #include <linux/module.h> #include <linux/platform_device.h> static int my_probe(struct device *dev) { printk("Driver found device!/n"); return 0; } static int my_remove(struct device *dev) { printk("Driver found device unpluged!/n"); return 0; } //定义platform_driver结构体 static struct platform_driver my_driver = { .probe = my_probe, .remove = my_remove, .driver = { .owner = THIS_MODULE, .name = "my_dev", }, }; static int __init my_driver_init(void) { return platform_driver_register(&my_driver); } static void __exit my_driver_exit(void) { platform_driver_unregister(&my_driver); } module_init(my_driver_init); module_exit(my_driver_exit); MODULE_LICENSE("GPL");

测试效果:

root@hacker:/home/hacker/platform# insmod driver.ko root@hacker:/home/hacker/platform# insmod device.ko root@hacker:/home/hacker/platform# dmesg [ 4499.724439] Driver found device! root@hacker:/home/hacker/platform# rmmod driver.ko root@hacker:/home/hacker/platform# dmesg [ 4499.724439] Driver found device! [ 4513.368712] Driver found device unpluged! root@hacker:/home/hacker/platform# rmmod device.ko root@hacker:/home/hacker/platform# insmod device.ko root@hacker:/home/hacker/platform# insmod driver.ko root@hacker:/home/hacker/platform# dmesg [ 4540.509227] Driver found device! root@hacker:/home/hacker/platform# rmmod device.ko root@hacker:/home/hacker/platform# dmesg [ 4540.509227] Driver found device! [ 4545.786076] Driver found device unpluged! root@hacker:/home/hacker/platform# rmmod driver.ko root@hacker:/home/hacker/platform# dmesg [ 4540.509227] Driver found device! [ 4545.786076] Driver found device unpluged!

您可能感兴趣的与本文相关内容

Linux常见驱动源码分析(kernel hacker修炼)--李万鹏 李万鹏 IBM Linux Technology Center kernel team 驱动资料清单内容如下: Linux设备模型(中)之上层容器.pdf Linux设备模型(上)之底层模型.pdf Linux驱动修炼-驱动中一些常见的宏.pdf Linux驱动修炼-内存映射.pdf Linux驱动修炼-看门狗框架源码分析.pdf Linux驱动修炼-触摸屏驱动之s3c2410_ts源码分析.pdf Linux驱动修炼-SPI驱动框架源码分析(中).pdf Linux驱动修炼-SPI驱动框架源码分析(下).pdf Linux驱动修炼-SPI驱动框架源码分析(上).pdf Linux驱动修炼-RTC子系统框架源码分析.pdf Linux驱动修炼-platform.pdf Linux驱动修炼-LCD背光gpio控制.pdf Linux驱动修炼-INPUT子系统(下).pdf Linux驱动修炼-INPUT子系统(上).pdf Linux驱动修炼-framebuffer(中).pdf Linux驱动修炼-framebuffer(下).pdf Linux驱动修炼-framebuffer(上).pdf Linux驱动修炼-DMA框架源码分析(下).pdf Linux驱动修炼-DMA框架源码分析(上).pdf Linux驱动修炼-DM9000A网卡驱动框架源码分析(中).pdf Linux驱动修炼-DM9000A网卡驱动框架源码分析(下).pdf Linux驱动修炼-DM9000A网卡驱动框架源码分析(上).pdf Linux驱动修炼-clock框架.pdf Linux驱动修炼-ADC驱动.pdf Linux内核访问外设I O资源的方式.pdf LINUX内核USB子系统学习笔记之初识USB.pdf kernel hacker修炼驱动-流水灯.pdf kernel hacker修炼驱动-混杂设备.pdf kernel hacker修炼驱动-按键.pdf kernel hacker修炼之PCI subsystem(五).pdf kernel hacker修炼之PCI subsystem(四).pdf kernel hacker修炼之PCI subsystem(三).pdf kernel hacker修炼之PCI subsystem(六).pdf kernel hacker修炼之PCI subsystem(二).pdf
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值