• arm-linux 字符设备带设备树io操作


    设备树内容如下

     

    除了需要/* 设备操作函数 */

    还需要 /* 设备结构体 */

    eg:

    struct dtsled_dev{

        dev_t devid;            /* 设备号   */

        struct cdev cdev;       /* cdev     */

        struct class *class;        /* 类        */

        struct device *device;  /* 设备    */

        int major;              /* 主设备号   */

        int minor;              /* 次设备号   */

        struct device_node  *nd; /* 设备节点 */

    };

    初始化注册函数也需要变化

    初始化函数需要使用 of_find_node_by_path函数,找到设备树节点:

        /* 1、获取设备节点:alphaled */

        dtsled.nd = of_find_node_by_path("/alphaled");

    初始化函数需要使用 of_find_property函数,找到设备树的兼容性属性

        /* 2、获取compatible属性内容 */

        proper = of_find_property(dtsled.nd, "compatible", NULL);

    /* 3、获取status属性内容 */

        ret = of_property_read_string(dtsled.nd, "status", &str);

    获取寄存器的内容,很重要!!!

    /* 4、获取reg属性内容 */

        ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);

    需要配置IO映射

    IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);

        SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);

        SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);

        GPIO1_DR = of_iomap(dtsled.nd, 3);

        GPIO1_GDIR = of_iomap(dtsled.nd, 4);

    of_iomap
    void __iomem *of_iomap(struct device_node *node, int index);

    通过设备树的设备结点直接进行设备内存区间的 ioremap(),index是内存段的索引。若设备结点的reg属性有多段,可通过index标示要ioremap的是哪一段,只有1段的情况, index为0。

    采用Device Tree后,大量的设备驱动通过of_iomap()进行映射,而不再通过传统的ioremap

    引用:https://blog.csdn.net/alimingh/article/details/111666429

    在注册驱动使用MKDEV获取主设备号和次设备号

    版本:linux-2.6.24.4

    宏:

    MKDEV(MAJOR, MINOR);  

    说明: 获取设备在设备表中的位置。

    MAJOR   主设备号

    MINOR   次设备号

       

    内核使用的版本号说明文件:

        在内核 /Documentation 目录下的 devices.txt 有说明。

        一般本地保留的

            MAJOR

    234-239 UNASSIGNED

    240-254 char LOCAL/EXPERIMENTAL USE

    240-254 block LOCAL/EXPERIMENTAL USE  

            MINOR

    1 ~ 250 (次设备号的 0 不能使用)

         

    静态的设备文件建立:

        mknod /dev/gpio_led c 240 

    利用register_chrdev_region函数注册

    register_chrdev_region() 函数用于分配指定的设备编号范围。如果申请的设备编号范围跨越了主设备号,它会把分配范围内的编号按主设备号分割成较小的子范围,并在每个子范围上调用 __register_chrdev_region() 。如果其中有一次分配失败的话,那会把之前成功分配的都全部退回。

    在2.4版本后,内核里就加入了以下几个函数也可以来实现注册字符设备:

    分为了静态注册(指定设备编号来注册)、动态分配(不指定设备编号来注册),以及有连续注册的次设备编号范围区间,避免了register_chrdev()浪费资源的缺点   

    2.1: 

    /*指定设备编号来静态注册一个字符设备*/
    int register_chrdev_region(dev_t from, unsigned count, const char *name);   

    from: 注册的指定起始设备编号,比如:MKDEV(100, 0),表示起始主设备号100, 起始次设备号为0

    count:需要连续注册的次设备编号个数,比如: 起始次设备号为0,count=100,表示0~99的次设备号都要绑定在同一个file_operations操作方法结构体上

    *name:字符设备名称

    当返回值小于0,表示注册失败

    2.2:

    /*动态分配一个字符设备,注册成功并将分配到的主次设备号放入*dev里*/
    int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);

    *dev: 存放起始设备编号的指针,当注册成功, *dev就会等于分配到的起始设备编号,可以通过MAJOR()和MINNOR()函数来提取主次设备号

    baseminor:次设备号基地址,也就是起始次设备号

    count:需要连续注册的次设备编号个数,比如: 起始次设备号(baseminor)为0,baseminor=2,表示0~1的此设备号都要绑定在同一个file_operations操作方法结构体上

    *name:字符设备名称

    当返回值小于0,表示注册失败

    eg:

        /* 注册字符设备驱动 */

        /* 1、创建设备号 */

        if (dtsled.major) {     /*  定义了设备号 */

            dtsled.devid = MKDEV(dtsled.major, 0);

            register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);

        } else {                        /* 没有定义设备号 */

            alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME); /* 申请设备号 */

            dtsled.major = MAJOR(dtsled.devid); /* 获取分配号的主设备号 */

            dtsled.minor = MINOR(dtsled.devid); /* 获取分配号的次设备号 */

        }

        /* 2、初始化cdev */

    函数cdev_init()用于初始化一个静态分配的cdev结构体变量,函数cdev_init会自动初始化cdev->ops对象,将函数的第二个输入参数赋值给cdev->ops对象,不会初始化cdev->owner对象,因此在经过函数cdev_alloc()和函数cdev_init()处理之后的cdev结构体变量,在应用程序中只需要给cdev->owner对象赋值,此结构变量就可以被插入Linux内核系统了,作为一个可用的字符设备使用。

        dtsled.cdev.owner = THIS_MODULE;

        cdev_init(&dtsled.cdev, &dtsled_fops);

    #include <linux/cdev.h>

    初始化 cdev 后,需要把它添加到系统中去。为此可以调用 cdev_add()函数。传入cdev 结构的指针,起始设备编号,以及设备编号范围。

    函数首先将分配的设备号与设备数目保存进cdev结构体中。然后再讲cdev结构体记录在一个 kobj_map 结构的 cdev_map 变量中。

    /* 3、添加一个cdev */

        cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);

        /* 4、创建类 */

        dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);

        if (IS_ERR(dtsled.class)) {

            return PTR_ERR(dtsled.class);

        }

        /* 5、创建设备 */

        dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);

        if (IS_ERR(dtsled.device)) {

            return PTR_ERR(dtsled.device);

        }

    注销设备驱动

    /*

     * @description : 驱动出口函数

     * @param       : 无

     * @return      : 无

     */

    static void __exit led_exit(void)

    {

        /* 取消映射 */

        iounmap(IMX6U_CCM_CCGR1);

        iounmap(SW_MUX_GPIO1_IO03);

        iounmap(SW_PAD_GPIO1_IO03);

        iounmap(GPIO1_DR);

        iounmap(GPIO1_GDIR);

        /* 注销字符设备驱动 */

        cdev_del(&dtsled.cdev);/*  删除cdev */

        unregister_chrdev_region(dtsled.devid, DTSLED_CNT); /* 注销设备号 */

        device_destroy(dtsled.class, dtsled.devid);

        class_destroy(dtsled.class);

    }

  • 相关阅读:
    hive基于新浪微博的日志数据分析——项目及源码
    物理服务器和云服务器的区别
    Android ImageView 四个角自定义角度,以及角度的变换
    Opencv_14_多边形填充与绘制
    一篇搞懂C++(万字总结)
    感知SOTA模型
    学习笔记二十三:Deployment入门到企业实战应用
    Redis系列:Redis主从、哨兵、集群介绍
    第七篇 基于JSP 技术的网上购书系统——新品上架、推荐产品、在线留言、搜索功能实现(网上商城、仿淘宝、当当、亚马逊)
    微服务 | Springboot整合Seata+Nacos实现分布式事务
  • 原文地址:https://blog.csdn.net/L1153413073/article/details/125486527