驱动分层/分离,总线驱动设备模型

先介绍一下驱动的分层/分离。如下图
这里写图片描述

input.c为应用程序提供了接口,是核心层,而在核心层下面的那一层由两方面组成,一个是纯软件的,里面是很稳定的代码,还有一个是与硬件相关的代码,一般只需要通过修改与硬件相关的代码而达到我们的目的。

再介绍一个概念,总线-驱动-设备模型。如下图
这里写图片描述
bus、driver、device实际上都只是一个结构体。
左边的device是与硬件相关的代码,实现下面的功能:
①通过device_add把device放入bus的dev链表中;
②从bus的drv链表中取出每一个drv,用bus的match函数判断drv能否支持dev;
③若可以支持,调用drv的probe函数。

而右边的driver是比较稳定的代码,实现下面的功能:
①通过driver_register把driver放入bus的drv链表中;
②从bus的dev链表中取出每一个dev,用bus的match函数(通过名字)判断drv能否支持dev;
③若可以支持,调用probe函数。

但要注意的是,这只不过是一种使左右两边建立联系的机制,在probe函数中做什么由你自己决定,或者注册一个字符设备,或者注册一个input_dev结构体,都可以。

下面介绍一下具体怎么实现,先从device开始。
首先要注册一个平台设备

static struct platform_device led_dev = {
    .name         = "myled",
    .id       = -1,
    .num_resources    = ARRAY_SIZE(led_resource),
    .resource     = led_resource,
};

接下来就是对resource的设定

static struct resource led_resource[] = {
    [0] = {//代表寄存器的地址
        .start = 0x56000010,
        .end   = 0x56000010 + 8 - 1,
        .flags = IORESOURCE_MEM,
    },
    [1] = {//代表选定的寄存器的具体哪一位,这里选择了pin5
        .start = 5,
        .end   = 5,
        .flags = IORESOURCE_IRQ,
        .dev = {
        .release = led_release,
        },
};

其中的flag在linux/ioport.h中定义。
当然还要在入口函数里注册平台设备:platform_device_register(&led_dev);
出口函数里卸载平台设备:platform_device_unregister(&led_dev);
还要用module_init对入口和出口函数进行修饰。

这样,一个平台设备就完成了。

接下来要注册一个平台驱动。
首先要定义一个平台驱动

struct platform_driver led_drv = {
    .probe      = led_probe,
    .remove     = led_remove,
    .driver     = {
        .name   = "myled",
    }
};

要注意的是,.name一定要和之前注册的平台设备相同,这样才能通过match函数找到彼此。至于入口函数、出口函数和修饰,与之前的平台设备基本一致。
接下来就是构造led_probe和led_remove了,先从简单的做起,在这两个函数里我们先不实现点灯的功能,只是打印一些信息

static int led_probe(struct platform_device * pdev)
{
    printk("led_probe,found led\n");
    return 0;
}

static int led_remove(struct platform_device * pdev)
{
    printk("led_remove,remove led\n");
    return 0;
}

这样,我们就完成了一个简单的例子,把平台驱动和平台设备编译加载,可以看到打印出“led_probe,found led”,然后卸载的时候又打印出“led_remove,remove led”。与预期一致。

接下来加入复杂的代码。
在probe函数中,先要根据platform_device的资源进行ioremap,也就是得到之前在平台设备里的led_resource[],这就要用platform_get_resource(),然后再用ioremap得到具体的寄存器地址以及引脚。具体的代码如下:

    struct resource *res;
    /*根据platform_device的资源进行ioremap*/

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    gpio_con = ioremap(res->start,res->end - res->start +1);  
    gpio_dat = gpio_con + 1;

    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    pin = res->start;

然后还需要在probe函数里注册字符设备驱动,这都是老一套了,跟以前我们学的过程完全一样(先构造一个file_operations结构体,然后设置系统自动分配主设备号……)具体的代码如下:

 major = register_chrdev(0, "myled", &led_fops);

    cls = class_create(THIS_MODULE, "myled");

    class_device_create(cls, NULL, MKDEV(major, 0), NULL, "myled");

接下来就是remove函数了,过程基本与probe的注册过程相反:

class_device_destroy(cls,MKDEV(major, 0));//  /dev/xyz
    class_destroy(cls);
    unregister_chrdev(major, "myled");
    iounmap(gpio_con);

至于在file_operations结构体定义的open和write函数的实现就不具体说明了,跟以前的一模一样。

由此,我们就完成了平台设备和平台驱动的构建。至于测试程序,完全使用第一个驱动程序的测试程序就行。

倘若我们想要修改led,只需要在平台设备里对resource进行修改就行了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值