赞
踩
总线-设备-驱动 又称为 设备驱动模型。
总线(bus):负责管理挂载对应总线的设备以及驱动;
设备(device):挂载在某个总线的物理设备;
驱动(driver):与特定设备相关的软件,负责初始化该设备以及提供一些操作该设备的操作方式;
类(class):对于具有相同功能的设备,归结到一种类别,进行分类管理;
以下只说 总线-设备-驱动 模式下的操作
总线管理着两个链表:设备链表 和 驱动链表。
当我们向内核注册一个设备时,便插入到总线的设备链表。
当我们向内核注册一个驱动时,便插入到总线的驱动链表。
在插入的同时,总线会执行一个 bus_type 结构体中的 match 方法对新插入的 设备/驱动 进行匹配。(例如以名字的方式匹配。方式有很多总,下面再详细分析。)
匹配成功后,会调用 驱动 device_driver 结构体中的 probe 方法。(通常在 probe 中获取设备资源。具体有开发人员决定。)
在移除设备或驱动时,会调用 device_driver 结构体中的 remove 方法。
定义好要用到的硬件资源赋值到resource结构体进行注册,例如我的板子的用户LED为GROUP_PIN(5,3)。
static struct resource resources[] = {
{
.start = GROUP_PIN(5,3),
.flags = IORESOURCE_IRQ,
.name = "100ask_led_pin",
},
{
.start = GROUP_PIN(5,8),
.flags = IORESOURCE_IRQ,
.name = "100ask_led_pin",
},
};
把刚才注册好的设备资源放到platform_device进行二次注册。
static struct platform_device board_A_led_dev = {
.name = "100ask_led",
.num_resources = ARRAY_SIZE(resources),
.resource = resources,
.dev = {
.release = led_dev_release,
},
};
在注册设备入口函数进行设备注册,就是在总线的设备列表能找到刚才注册的设备。
static int __init led_dev_init(void)
{
int err;
err = platform_device_register(&board_A_led_dev);
return 0;
}
将实体函数chip_demo_gpio_probe、chip_demo_gpio_remove注册到platform_driver结构体
static struct platform_driver chip_demo_gpio_driver = {
.probe = chip_demo_gpio_probe,
.remove = chip_demo_gpio_remove,
.driver = {
.name = "100ask_led",
},
};
通过匹配原则在驱动注册时进行设备驱动匹配,匹配成功后会调用刚才的probe函数,这个函数会将刚才匹配的节点进行注册( led_class_create_device(g_ledcnt)),probe 函数中根据 platform_device 的资源确定了引脚,probe 将g_ledpins 数组填充,将来设备层调用的资源g_ledpins[g_ledcnt]都是来自于这。
我们用platform_device结构体来指定设备信息时,platform_drive是直接从platform_device中拿资源的,如下platform_get_resource函数。
static int chip_demo_gpio_probe(struct platform_device *pdev) { struct resource *res; int i = 0; while (1) { res = platform_get_resource(pdev, IORESOURCE_IRQ, i++); if (!res) break; g_ledpins[g_ledcnt] = res->start; led_class_create_device(g_ledcnt); g_ledcnt++; } return 0; }
platform_drive从platform_device中拿资源时正式之前注册设备时的资源。
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num)
{
int i;
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
if (type == resource_type(r) && num-- == 0)
return r;
}
return NULL;
}
驱动层没有变化,注册app对应到的实体函数,写好入口函数。
static struct file_operations led_drv = { .owner = THIS_MODULE, .open = led_drv_open, .read = led_drv_read, .write = led_drv_write, .release = led_drv_close, }; /* 1. 把file_operations结构体告诉内核:注册驱动程序 */ /* 2. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */ static int __init led_init(void) { int err; printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); major = register_chrdev(0, "100ask_led", &led_drv); /* /dev/led */ led_class = class_create(THIS_MODULE, "100ask_led_class"); err = PTR_ERR(led_class); if (IS_ERR(led_class)) { printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); unregister_chrdev(major, "led"); return -1; } return 0; }
举个例子,驱动层实体函数led_drv_write 会被app层write调用,led_drv_write中则调用注册好的ctl函数
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
int err;
char status;
struct inode *inode = file_inode(file);
int minor = iminor(inode);
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
err = copy_from_user(&status, buf, 1);
/* 根据次设备号和status控制LED */
p_led_opr->ctl(minor, status);
return 1;
}
板级的ctl函数源码如下,GROUP(g_ledpins[which])中用到的g_ledpins资源就是刚才设备驱动匹配时probe函数中的g_ledpins资源。
static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */ { //printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off"); printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which])); switch(GROUP(g_ledpins[which])) { case 0: { printk("set pin of group 0 ...\n"); break; } case 1: { printk("set pin of group 1 ...\n"); break; } case 2: { printk("set pin of group 2 ...\n"); break; } case 3: { printk("set pin of group 3 ...\n"); break; } case 4: { printk("set pin of group 2 ...\n"); break; } case 5: { //板载对应的port printk("set pin of group 3 ...\n"); break; } } return 0; } static struct led_operations board_demo_led_opr = { .init = board_demo_led_init, .ctl = board_demo_led_ctl, };
总线设备驱动,实现了设备和驱动的分离,匹配算法和设备树中的方法是一致的,这种模型虽然易于扩展,但是,冗余代码太多, 修改引脚时设备端的代码需要重新编译。更换引脚时, 上图中的 led_drv.c 基本不用改, 但是需要修改 板级的指定硬件资源的结构体(如下图),这样的话一个板子对应一个C文件,会使得Linux内核非常庞大,垃圾文件太多,所以还需要继续用设备树的方式进行改进。
static struct resource resources[] = {
{
.start = GROUP_PIN(5,3),
.flags = IORESOURCE_IRQ,
.name = "100ask_led_pin",
},
{
.start = GROUP_PIN(5,8),
.flags = IORESOURCE_IRQ,
.name = "100ask_led_pin",
},
};
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。