编写LED驱动-设备树

写LED驱动 

首先,我们要清楚总线设备驱动相比于传统驱动模型而言,在操作上做了精简。传统驱动模型针对不同的board或者change gpio,都需要对相应drv.c进行修改,这样操作相对比较复杂,也不容易后期的维护,总线驱动将所有的设备全部注册在platform_dev结构体内,每个设备不同的在于他们相应的resource不同,如果change gpio,只需修改platform_dev 结构体即可。

 

linux kernel不能直接访问register的物理地址,需要采用ioremap的映射映射到对应的物理地址:  led_cnt = ioremap(addr, size)

后面在通过分配/设置/注册来设计相应的硬件操作。

 

首先为了方便维护与扩展,首先我们定义了两个文件:platform_devices-----存储相应的寄存器信息;platform_driver----寄存器的相应操作

怎么去理解上面两个文件呢?

platform_devices就相当于一个仓库,里面存放了很多的物品(硬件资源),他把物品的地址信息告诉你。

platform_driver 就相当于一个操作员,你从仓库中拿到这个物品,接下来你需要如何操作他,该文件进行了定义,也同传统方式一样,分配file_operations/设置/注册。

当然,一个仓库里面这么多物品,你如何去管理他呢?

接下来引入了总线,总线分为两部分:platform_devices,platform_driver,在内核内定义了两个结构体,相比于传统驱动模式,只需要修改platform_devices内的GPIO配置即可,不需要在修改drv的部分,相对来说扩展性更强一点。但是也存在一个问题,每个硬件资源修改后,都需要重新编译,并且随着驱动设备的增多,重复性的platform-devices随之增加,浪费了内核资源,这也就是后面为什么会引入设备树的原因,只需要修改dts文件即可,内核只负责解析这些配置不需要在重新编译。

 

下面是韦东山老师的笔记,从中可以看出BUS将总线划分了两个部分:platform_devices, platform_driver.

resource: 用于指定硬件资源,flags用于指定硬件资源类型:GPIO,IRQ,IIC等。start指定硬件资源位置。

那么如何匹配platform_devices, platform_driver, 换句话怎么知道platform_devices对应的是那个platform_driver?  name/idtable-----platform_devices, platform_driver两者拥有相同的name

在dev与driver定义了两个链表,首先先找到对应的dev,然后在driver链表中查找name/idtable匹配的对应的driver

probe: 定义了主要的driver驱动函数。write/read/open等。

两个结构体都被注册到了内核?即注册了两个字符设备驱动?    其实是我们将之前的字符驱动拆分为了:硬件资源与驱动资源两部分,所以需要同时将两个都注册到内核内。

 

 

 

下方是注册到匹配drv/dev的流程,这里不需要太过深入理解,通过下面流程可以了解在注册platform_device/platform_driver时,会自动匹配对应的drv

复制代码
platform_device_register 
  platform_device_add 
     device_add 
       bus_add_device // 放入链表 
       bus_probe_device // probe 枚举设备,即找到匹配的(dev, drv) 
         device_initial_probe 
           __device_attach 
             bus_for_each_drv(...,__device_attach_driver,...) 
               __device_attach_driver 
                 driver_match_device(drv, dev) // 是否匹配 
                 driver_probe_device // 调用 drv 的 probe 
 
platform_driver_register 
  __platform_driver_register 
     driver_register 
       bus_add_driver // 放入链表 
         driver_attach(drv) 
           bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); 
             __driver_attach 
               driver_match_device(drv, dev) // 是否匹配 
               driver_probe_device // 调用 drv 的 probe 
复制代码

 

常用的函数:

① 注册/反注册
platform_device_register/ platform_device_unregister
platform_driver_register/ platform_driver_unregister
platform_add_devices // 注册多个 device
② 获得资源
返回该 dev 中某类型(type)资源中的第几个(num):

 

返回该 dev 所用的第几个(num)中断:

 

通过名字(name)返回该 dev 的某类型(type)资源:

 

通过名字(name)返回该 dev 的中断号:
   

 

① 分配/设置/注册 platform_device 结构体 在里面定义所用资源,指定设备名字。

② 分配/设置/注册 platform_driver 结构体 在其中的 probe 函数里,分配/设置/注册 file_operations 结构体, 并从 platform_device 中确实所用硬件资源。 指定 platform_driver 的名字。

 

 

 

 

 

首先,我们要建立一个file_operator结构体:

复制代码
/* 定义自己的file_operations结构体                                              */
static struct file_operations led_drv = {
    .owner     = THIS_MODULE,       //主设备号
    .open    = led_drv_open,     // driver function
    .read    = l


ed_drv_read, .write = led_drv_write, .release = led_drv_close, };
复制代码

 

编写相应的四个driver function:

复制代码
/* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

/* write(fd, &val, 1); */
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 */
    gpiod_set_value(led_gpio, status);
    
    return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
    //int minor = iminor(node);
    
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    /* 根据次设备号初始化LED */
    gpiod_direction_output(led_gpio, 0);
    
    return 0;
}

static int led_drv_close (struct inode *node, struct file *file)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}
复制代码

 

注册filer_operator: 告诉CPU:这里采用设备树的方式:

 

 

 

 

 b

posted @   嵌入式小白—  阅读(93)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示