/*********************************************************************************** * * mdev,bus,device,driver,platform * * 声明: * 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会 * 不对齐,从而影响阅读. * 2. 由于本人水平有限,很难阐述清楚bus device driver platform的关系 * 所以强烈要求您详细参考本次提供的预热文章. * * * 2015-3-21 晴 深圳 尚观 Opt 曾剑锋 **********************************************************************************/ \\\\\\\\\\\--*目录*--////////// | 一. 预热文章: | 二. mdev 原理及配置: | 三. bus device driver接口: | 四. platform bus接口 \\\\\\\\\\\\\\\//////////////// 一. 预热文章: 1. linux设备驱动模型(上): http://m.blog.csdn.net/blog/zhuzongwei1988/5785461 2. [嵌入式Linux学习七步曲之第四篇 Linux内核移植]详解Linux2.6内核中 基于platform机制的驱动模型: http://blog.csdn.net/sailor_8318/article/details/5267698 3. [嵌入式Linux学习七步曲之第五篇 Linux内核移植]PowerPC+Linux2.6.25平台 下的I2C驱动架构分析: http://blog.csdn.net/sailor_8318/article/details/5905988 4. [嵌入式Linux学习七步曲之第五篇 Linux内核移植]PowerPC+Linux2.6.25平台 下的SPI驱动架构分析: http://blog.csdn.net/sailor_8318/article/details/5977733 二. mdev 原理及配置: 1. 在/etc/init.d/rcS中最后执行命令: #采用设备模型进行创建设备节点的必须加上,热插拔处理 echo "/sbin/mdev" > /proc/sys/kernel/hotplug mdev -s 2. mdev扫描/sys/lock(块设备保存在/sys/block目录下,2.6.25版本以后,块设备也保存在 /sys/class/block目录下.mdev扫描/sys/block是为了实现向后兼容和/sys/class两个 目录下的dev属性文件,从该dev属性文件中获取设备编号(dev属性文件以"major:minor\n" 形式保存设备编号),并以包含该dev属性文件的目录名称作为设备名device_name(即包含 dev属性文件的目录为device_name,而/sys/class和/device_name之间的那部分目录称为 subsystem,也就是每个dev属性文件所在的路径都可表示/sys/class/subsystem/device_name/dev), 3. 并在/dev目录下创建相应的设备文件.例如: cat /sys/class/tty/tty0/dev会得到4:0,subsystem为tty,device_name为tty0. 4. 系统运行起来以后,每次创建新的节点的时候都会调用mdev,并根据/etc/mdev.conf文件 做相应的事,如果配置中没有对应的配置,那就按常规处理: cat > /etc/mdev.conf << EOF misc_dev 0:0 0600 =test/my_device event.* 0:0 0600 =input/ mice 0:0 0600 =input/ mouse0 0:0 0600 =input/ dsp 0:0 0600 =sound/ sdb[0-9] 0:0 0644 * /sbin/auto_mount EOF 配置解析: 1. 格式: <device regex> <uid>:<gid> <octal permissins> [<@|$|*> <command>] 1. @在创建设备节点后运行命令; 2. $在删除设备节点前运行命令; 3. *创建设备节点后和创建设干杯节点前都会运行命令; 2. =input: 表示将mice放在/dev/input目录下; 3. =test/my_device: 表示将misc_dev改名字为my_device,并放在/dev/test目录下; 5. 按照上面的操作,可以实现U盘的自动挂载. 三. bus device driver接口: 1. 总线注册: struct bus_type bus; bus_register(&bus); 2. 总线注销: bus_unregister(&bus); 3. 设备注册: struct device dev; device_register(&dev); 4. 设备注销: device_unregister(&dev); 5. 驱动注册: struct device_driver drv; driver_register(&drv); 6. 驱动注销: driver_unregister(&drv); 7. bus device接口实例Demo: ... //总线通过match函数决定总线匹配规则,返回0代表匹配失败 int up_match(struct device *dev, struct device_driver *drv) { printk("try to match!\n"); /** * 通过名字匹配设备和驱动,init_name中的值会赋给kobj.name * 并且init_name中的值会变成NULL,所以如果要通过名字匹配 * 设备驱动,需要比较的是dev->kobj.name和drv->name的值. * 如下方式是错误的: * return !strcmp(dev->init_name, drv->name); */ return !strcmp(dev->kobj.name, drv->name); } struct bus_type up_bus = { .name = "niubi_bus", .match = up_match, }; EXPORT_SYMBOL(up_bus); void test_release(struct device *dev) { } struct device up_dev = { .init_name = "bus_device", .release = test_release, }; EXPORT_SYMBOL(up_dev); int __init test_init(void) { int ret; ret = bus_register(&up_bus); if(ret) { printk("bus_register FAILED!\n"); goto err0; } ret = device_register(&up_dev); if(ret) { printk("device_register FAILED!\n"); goto err1; } return ret; err1: bus_unregister(&up_bus); err0: return ret; } void __exit test_exit(void) { bus_unregister(&up_bus); } ... 8. device_driver接口实例Demo: ... extern struct bus_type up_bus; //dev指向匹配的设备结构 static int up_probe(struct device *dev) { printk("Probe.\n"); return 0; } static int up_remove(struct device *dev) { printk("Remove.\n"); return 0; } struct device_driver drv = { .owner = THIS_MODULE, .name = "niubi_dev", //match的时候要用到 .bus = &up_bus, .probe = up_probe, .remove = up_remove, }; int __init test_init(void) { int ret; ret = driver_register(&drv); if(ret) printk("driver_register FAILED!\n"); return ret; } void __exit test_exit(void) { driver_unregister(&drv); } ... 四. platform bus接口 1. 注册平台设备: struct platform_device pdev; platform_device_register(&dev); 2. 注销平台设备: platform_device_unregister(&dev); 3. 注册平台驱动: struct platform_driver pdrv; platform_driver_register(&pdrv); 4. 注销平台驱动: platform_driver_unregister(&pdrv); 5. platform_device接口Demo: #include <linux/module.h> #include <linux/platform_device.h> //定义自己的平台数据结构 struct my_platform_data { int w; int h; char name[20]; }; struct my_platform_data pdata = { .w = 800, .h = 480, .name = "lcd_screen", }; struct resource res[] = { [0] = { .start = 0x10000000, .end = 0x10000000 + SZ_128 - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = 0x20000000, .end = 0x20000000 + SZ_128 - 1, .flags = IORESOURCE_MEM, }, [2] = { .start = 0x30, .end = 0x30, .flags = IORESOURCE_IRQ, }, }; void test_release(struct device *dev) { } //设备的resource保存在设备结构里 struct platform_device pdev = { .name = "device_v3", .id = -1, .dev = { .release = test_release, .platform_data = &pdata, }, .num_resources = ARRAY_SIZE(res), .resource = res, }; int __init test_init(void) { int ret; ret = platform_device_register(&pdev); if(ret) printk("platform_device_register FAILED!\n"); return ret; } void __exit test_exit(void) { platform_device_unregister(&pdev); } module_init(test_init); module_exit(test_exit); MODULE_LICENSE("GPL"); 6. platform_device接口Demo: #include <linux/module.h> #include <linux/platform_device.h> //定义自己的平台数据结构 struct my_platform_data { int w; int h; char name[20]; }; static int up_probe(struct platform_device *pdev) { int i; struct my_platform_data *pdata = pdev->dev.platform_data; for(i = 0; i < pdev->num_resources; i++) { printk("start = %x, end = %x\n", pdev->resource[i].start, pdev->resource[i].end); } printk("==========================\n"); printk("platform data.\n"); printk("width = %d, height = %d\n%s\n", pdata->w, pdata->h, pdata->name); return 0; } static int up_remove(struct platform_device *pdev) { printk("In %s func.\n", __func__); return 0; } //最后一个元素清0 struct platform_device_id up_ids[] = { {"device_v1", 1}, {"device_v2", 2}, {"device_v3", 3}, {"device_v4", 4}, {"device_v5", 5}, {}, }; //使用id_table和设备匹配 struct platform_driver pdrv = { .probe = up_probe, .remove = up_remove, .driver = { .name = "xxxxxxx", .owner = THIS_MODULE, }, .id_table = up_ids, }; int __init test_init(void) { int ret; ret = platform_driver_register(&pdrv); if(ret) printk("platform_driver_register FAILED!\n"); return ret; } void __exit test_exit(void) { platform_driver_unregister(&pdrv); } module_init(test_init); module_exit(test_exit); MODULE_LICENSE("GPL");