/***********************************************************************************
*
* mdev,bus,device,driver,platform
*
* 声明:
* 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会
* 不对齐,从而影响阅读.
* 2. 由于本人水平有限,很难阐述清楚bus device driver platform的关系
* 所以强烈要求您详细参考本次提供的预热文章.
*
*
* 2015-3-21 晴 深圳 尚观 Opt 曾剑锋
**********************************************************************************/ \\\\\\\\\\\--*目录*--//////////
| 一. 预热文章:
| 二. mdev 原理及配置:
| 三. bus device driver接口:
| 四. platform bus接口
\\\\\\\\\\\\\\\//////////////// 一. 预热文章:
. linux设备驱动模型(上):
http://m.blog.****.net/blog/zhuzongwei1988/5785461
. [嵌入式Linux学习七步曲之第四篇 Linux内核移植]详解Linux2.6内核中
基于platform机制的驱动模型:
http://blog.****.net/sailor_8318/article/details/5267698
. [嵌入式Linux学习七步曲之第五篇 Linux内核移植]PowerPC+Linux2..25平台
下的I2C驱动架构分析:
http://blog.****.net/sailor_8318/article/details/5905988
. [嵌入式Linux学习七步曲之第五篇 Linux内核移植]PowerPC+Linux2..25平台
下的SPI驱动架构分析:
http://blog.****.net/sailor_8318/article/details/5977733 二. mdev 原理及配置:
. 在/etc/init.d/rcS中最后执行命令:
#采用设备模型进行创建设备节点的必须加上,热插拔处理
echo "/sbin/mdev" > /proc/sys/kernel/hotplug
mdev -s
. 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),
. 并在/dev目录下创建相应的设备文件.例如:
cat /sys/class/tty/tty0/dev会得到4:,subsystem为tty,device_name为tty0.
. 系统运行起来以后,每次创建新的节点的时候都会调用mdev,并根据/etc/mdev.conf文件
做相应的事,如果配置中没有对应的配置,那就按常规处理:
cat > /etc/mdev.conf << EOF
misc_dev : =test/my_device
event.* : =input/
mice : =input/
mouse0 : =input/
dsp : =sound/
sdb[-] : * /sbin/auto_mount
EOF
配置解析:
. 格式: <device regex> <uid>:<gid> <octal permissins> [<@|$|*> <command>]
. @在创建设备节点后运行命令;
. $在删除设备节点前运行命令;
. *创建设备节点后和创建设干杯节点前都会运行命令;
. =input: 表示将mice放在/dev/input目录下;
. =test/my_device: 表示将misc_dev改名字为my_device,并放在/dev/test目录下;
. 按照上面的操作,可以实现U盘的自动挂载. 三. bus device driver接口:
. 总线注册:
struct bus_type bus;
bus_register(&bus);
. 总线注销:
bus_unregister(&bus);
. 设备注册:
struct device dev;
device_register(&dev);
. 设备注销:
device_unregister(&dev);
. 驱动注册:
struct device_driver drv;
driver_register(&drv);
. 驱动注销:
driver_unregister(&drv);
. 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);
}
... . device_driver接口实例Demo:
...
extern struct bus_type up_bus; //dev指向匹配的设备结构
static int up_probe(struct device *dev)
{
printk("Probe.\n"); return ;
} static int up_remove(struct device *dev)
{
printk("Remove.\n"); return ;
} 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接口
. 注册平台设备:
struct platform_device pdev;
platform_device_register(&dev);
. 注销平台设备:
platform_device_unregister(&dev);
. 注册平台驱动:
struct platform_driver pdrv;
platform_driver_register(&pdrv);
. 注销平台驱动:
platform_driver_unregister(&pdrv); . platform_device接口Demo: #include <linux/module.h>
#include <linux/platform_device.h> //定义自己的平台数据结构
struct my_platform_data {
int w;
int h;
char name[];
}; struct my_platform_data pdata = {
.w = ,
.h = ,
.name = "lcd_screen",
}; struct resource res[] = {
[] = {
.start = 0x10000000,
.end = 0x10000000 + SZ_128 - ,
.flags = IORESOURCE_MEM,
},
[] = {
.start = 0x20000000,
.end = 0x20000000 + SZ_128 - ,
.flags = IORESOURCE_MEM,
},
[] = {
.start = 0x30,
.end = 0x30,
.flags = IORESOURCE_IRQ,
},
}; void test_release(struct device *dev)
{
} //设备的resource保存在设备结构里
struct platform_device pdev = {
.name = "device_v3",
.id = -,
.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"); . platform_device接口Demo: #include <linux/module.h>
#include <linux/platform_device.h> //定义自己的平台数据结构
struct my_platform_data {
int w;
int h;
char name[];
}; static int up_probe(struct platform_device *pdev)
{
int i;
struct my_platform_data *pdata = pdev->dev.platform_data; for(i = ; 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 ;
} static int up_remove(struct platform_device *pdev)
{
printk("In %s func.\n", __func__); return ;
} //最后一个元素清0
struct platform_device_id up_ids[] = {
{"device_v1", },
{"device_v2", },
{"device_v3", },
{"device_v4", },
{"device_v5", },
{},
}; //使用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");