主页 > 创业  > 

Linux学习第28天:Platform设备驱动开发(二):专注与分散

Linux学习第28天:Platform设备驱动开发(二):专注与分散

Linux版本号4.1.15   芯片I.MX6ULL                                    大叔学Linux    品人间百味  思文短情长 


三、硬件原理图分析

四、驱动开发 1、platform设备与驱动程序开发 53 /* 54 * 设备资源信息,也就是 LED0 所使用的所有寄存器 55 */ 56 static struct resource led_resources[] = { 57 [0] = { 58 .start = CCM_CCGR1_BASE, 59 .end = (CCM_CCGR1_BASE + REGISTER_LENGTH - 1), 60 .flags = IORESOURCE_MEM, 61 }, 62 [1] = { 63 .start = SW_MUX_GPIO1_IO03_BASE, 64 .end = (SW_MUX_GPIO1_IO03_BASE + REGISTER_LENGTH - 1), 65 .flags = IORESOURCE_MEM, 66 }, 67 [2] = { 68 .start = SW_PAD_GPIO1_IO03_BASE, 69 .end = (SW_PAD_GPIO1_IO03_BASE + REGISTER_LENGTH - 1), 70 .flags = IORESOURCE_MEM, 71 }, 72 [3] = { 73 .start = GPIO1_DR_BASE, 74 .end = (GPIO1_DR_BASE + REGISTER_LENGTH - 1), 75 .flags = IORESOURCE_MEM, 76 }, 77 [4] = { 78 .start = GPIO1_GDIR_BASE, 79 .end = (GPIO1_GDIR_BASE + REGISTER_LENGTH - 1), 80 .flags = IORESOURCE_MEM, 81 }, 82 };

        led_resources 数组,也就是设备资源,描述了 LED 所要使用到的寄存器信息,也就是 IORESOURCE_MEM 资源。

85 /* 86 * platform 设备结构体 87 */ 88 static struct platform_device leddevice = { 89 .name = "imx6ul-led", 90 .id = -1, 91 .dev = { 92 .release = &led_release, 93 }, 94 .num_resources = ARRAY_SIZE(led_resources), 95 .resource = led_resources, 96 };

        platform 设备结构体变量 leddevice,这里要注意 name 字段为“imx6ul-led”,所 以稍后编写 platform 驱动中的 name 字段也要为“imx6ul-led”,否则设备和驱动匹配失败。

98 /* 99 * @description : 设备模块加载 100 * @param : 无 101 * @return : 无 102 */ 103 static int __init leddevice_init(void) 104 { 105 return platform_device_register(&leddevice); 106 }

        设备模块加载函数,在此函数里面通过 platform_device_register 向 Linux 内核注册 leddevice 这个 platform 设备。

108 /* 109 * @description : 设备模块注销 110 * @param : 无 111 * @return : 无 112 */ 113 static void __exit leddevice_exit(void) 114 { 115 platform_device_unregister(&leddevice); 116 }

        设备模块卸载函数,在此函数里面通过 platform_device_unregister 从 Linux内核中删除掉 leddevice 这个 platform 设备。

34 #define LEDDEV_CNT 1 /* 设备号长度 */ 35 #define LEDDEV_NAME "platled" /* 设备名字 */ 36 #define LEDOFF 0 37 #define LEDON 1 38 39 /* 寄存器名 */ 40 static void __iomem *IMX6U_CCM_CCGR1; 41 static void __iomem *SW_MUX_GPIO1_IO03; 42 static void __iomem *SW_PAD_GPIO1_IO03; 43 static void __iomem *GPIO1_DR; 44 static void __iomem *GPIO1_GDIR; 45 46 /* leddev 设备结构体 */ 47 struct leddev_dev{ 48 dev_t devid; /* 设备号 */ 49 struct cdev cdev; /* cdev */ 50 struct class *class; /* 类 */ 51 struct device *device; /* 设备 */ 52 int major; /* 主设备号 */ 53 }; 54 55 struct leddev_dev leddev; /* led 设备 */ 56 57 /* 58 * @description : LED 打开/关闭 59 * @param - sta : LEDON(0) 打开 LED, LEDOFF(1) 关闭 LED 60 * @return : 无 61 */ 62 void led0_switch(u8 sta) 63 { 64 u32 val = 0; 65 if(sta == LEDON){ 66 val = readl(GPIO1_DR); 67 val &= ~(1 << 3); 68 writel(val, GPIO1_DR); 69 }else if(sta == LEDOFF){ 70 val = readl(GPIO1_DR); 71 val|= (1 << 3); 72 writel(val, GPIO1_DR); 73 } 74 } 75 76 /* 77 * @description : 打开设备 78 * @param – inode : 传递给驱动的 inode 79 * @param - filp : 设备文件, file 结构体有个叫做 private_data 的成员变量 80 * 一般在 open 的时候将 private_data 指向设备结构体。 81 * @return : 0 成功;其他 失败 82 */ 83 static int led_open(struct inode *inode, struct file *filp) 84 { 85 filp->private_data = &leddev; /* 设置私有数据 */ 86 return 0; 87 } 88 89 /* 90 * @description : 向设备写数据 91 * @param – filp : 设备文件,表示打开的文件描述符 92 * @param - buf : 要写给设备写入的数据 93 * @param - cnt : 要写入的数据长度 94 * @param - offt : 相对于文件首地址的偏移 95 * @return : 写入的字节数,如果为负值,表示写入失败 96 */ 97 static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) 98 { 99 int retvalue; 100 unsigned char databuf[1]; 101 unsigned char ledstat; 102 103 retvalue = copy_from_user(databuf, buf, cnt); 104 if(retvalue < 0) { 105 return -EFAULT; 106 } 107 108 ledstat = databuf[0]; /* 获取状态值 */ 109 if(ledstat == LEDON) { 110 led0_switch(LEDON); /* 打开 LED 灯 */ 111 }else if(ledstat == LEDOFF) { 112 led0_switch(LEDOFF); /* 关闭 LED 灯 */ 113 } 114 return 0; 115 } 116 117 /* 设备操作函数 */ 118 static struct file_operations led_fops = { 119 .owner = THIS_MODULE, 120 .open = led_open, 121 .write = led_write, 122 };

        以上是传统的字符设备驱动。

124 /* 125 * @description : flatform 驱动的 probe 函数,当驱动与设备 126 * 匹配以后此函数就会执行 127 * @param - dev : platform 设备 128 * @return : 0,成功;其他负值,失败 129 */ 130 static int led_probe(struct platform_device *dev) 131 { 132 int i = 0; 133 int ressize[5]; 134 u32 val = 0; 135 struct resource *ledsource[5]; 136 137 printk("led driver and device has matched!\r\n"); 138 /* 1、获取资源 */ 139 for (i = 0; i < 5; i++) { 140 ledsource[i] = platform_get_resource(dev, IORESOURCE_MEM, i); 141 if (!ledsource[i]) { 142 dev_err(&dev->dev, "No MEM resource for always on\n"); 143 return -ENXIO; 144 } 145 ressize[i] = resource_size(ledsource[i]); 146 } 147 148 /* 2、初始化 LED */ 149 /* 寄存器地址映射 */ 150 IMX6U_CCM_CCGR1 = ioremap(ledsource[0]->start, ressize[0]); 151 SW_MUX_GPIO1_IO03 = ioremap(ledsource[1]->start, ressize[1]); 152 SW_PAD_GPIO1_IO03 = ioremap(ledsource[2]->start, ressize[2]); 153 GPIO1_DR = ioremap(ledsource[3]->start, ressize[3]); 154 GPIO1_GDIR = ioremap(ledsource[4]->start, ressize[4]); 155 156 val = readl(IMX6U_CCM_CCGR1); 157 val &= ~(3 << 26); /* 清除以前的设置 */ 158 val |= (3 << 26); /* 设置新值 */ 159 writel(val, IMX6U_CCM_CCGR1); 160 161 /* 设置 GPIO1_IO03 复用功能,将其复用为 GPIO1_IO03 */ 162 writel(5, SW_MUX_GPIO1_IO03); 163 writel(0x10B0, SW_PAD_GPIO1_IO03); 164 165 /* 设置 GPIO1_IO03 为输出功能 */ 166 val = readl(GPIO1_GDIR); 167 val &= ~(1 << 3); /* 清除以前的设置 */ 168 val |= (1 << 3); /* 设置为输出 */ 169 writel(val, GPIO1_GDIR); 170 171 /* 默认关闭 LED1 */ 172 val = readl(GPIO1_DR); 173 val |= (1 << 3) ; 174 writel(val, GPIO1_DR); 175 176 /* 注册字符设备驱动 */ 177 /*1、创建设备号 */ 178 if (leddev.major) { /* 定义了设备号 */ 179 leddev.devid = MKDEV(leddev.major, 0); 180 register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME); 181 } else { /* 没有定义设备号 */ 182 alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME); 183 leddev.major = MAJOR(leddev.devid); 184 } 185 186 /* 2、初始化 cdev */ 187 leddev.cdev.owner = THIS_MODULE; 188 cdev_init(&leddev.cdev, &led_fops); 189 190 /* 3、添加一个 cdev */ 191 cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT); 192 193 /* 4、创建类 */ 194 leddev.class = class_create(THIS_MODULE, LEDDEV_NAME); 195 if (IS_ERR(leddev.class)) { 196 return PTR_ERR(leddev.class); 197 } 198 199 /* 5、创建设备 */ 200 leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME); 201 if (IS_ERR(leddev.device)) { 202 return PTR_ERR(leddev.device); 203 } 204 205 return 0; 206 }

        probe 函数,当设备和驱动匹配以后此函数就会执行,当匹配成功以后会在终端上输出“led driver and device has matched!”这样语句。在 probe 函数里面初始化 LED、注册字符设备驱动。也就是将原来在驱动加载函数里面做的工作全部放到 probe 函数里面完成。

208 /* 209 * @description :移除 platform 驱动的时候此函数会执行 210 * @param - dev : platform 设备 211 * @return : 0,成功;其他负值,失败 212 */ 213 static int led_remove(struct platform_device *dev) 214 { 215 iounmap(IMX6U_CCM_CCGR1); 216 iounmap(SW_MUX_GPIO1_IO03); 217 iounmap(SW_PAD_GPIO1_IO03); 218 iounmap(GPIO1_DR); 219 iounmap(GPIO1_GDIR); 220 221 cdev_del(&leddev.cdev); /* 删除 cdev */ 222 unregister_chrdev_region(leddev.devid, LEDDEV_CNT); 223 device_destroy(leddev.class, leddev.devid); 224 class_destroy(leddev.class); 225 return 0; 226 }

        remove 函数,当卸载 platform 驱动的时候此函数就会执行。在此函数里面释放内存、注销字符设备等。也就是将原来驱动卸载函数里面的工作全部都放到 remove 函数中完成。

228 /* platform 驱动结构体 */ 229 static struct platform_driver led_driver = { 230 .driver = { 231 .name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */ 232 }, 233 .probe = led_probe, 234 .remove = led_remove, 235 };

        platform_driver 驱动结构体,注意 name 字段为"imx6ul-led",和我们在leddevice.c 文件里面设置的设备 name 字段一致。

237 /* 238 * @description : 驱动模块加载函数 239 * @param : 无 240 * @return : 无 241 */ 242 static int __init leddriver_init(void) 243 { 244 return platform_driver_register(&led_driver); 245 }

        驱动模块加载函数,在此函数里面通过 platform_driver_register 向 Linux 内核注册 led_driver 驱动。

247 /* 248 * @description : 驱动模块卸载函数 249 * @param : 无 250 * @return : 无 251 */ 252 static void __exit leddriver_exit(void) 253 { 254 platform_driver_unregister(&led_driver); 255 }

        驱动模块卸载函数,在此函数里面通过 platform_driver_unregister 从 Linux 内核卸载 led_driver 驱动。

2、测试APP开发 1 #include "stdio.h" 2 #include "unistd.h" 3 #include "sys/types.h" 4 #include "sys/stat.h" 5 #include "fcntl.h" 6 #include "stdlib.h" 7 #include "string.h" 8 /*************************************************************** 9 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 10 文件名 : ledApp.c 11 作者 : 左忠凯 12 版本 : V1.0 13 描述 : platform 驱动驱测试 APP。 14 其他 : 无 15 使用方法 : ./ledApp /dev/platled 0 关闭 LED 16 ./ledApp /dev/platled 1 打开 LED 17 论坛 : .openedv 18 日志 : 初版 V1.0 2019/8/16 左忠凯创建 19 ***************************************************************/ 20 #define LEDOFF 0 21 #define LEDON 1 22 23 /* 24 * @description : main 主程序 25 * @param - argc : argv 数组元素个数 26 * @param - argv : 具体参数 27 * @return : 0 成功;其他 失败 28 */ 29 int main(int argc, char *argv[]) 30 { 31 int fd, retvalue; 32 char *filename; 33 unsigned char databuf[2]; 34 35 if(argc != 3){ 36 printf("Error Usage!\r\n"); 37 return -1; 38 } 39 40 filename = argv[1]; 41 /* 打开 led 驱动 */ 42 fd = open(filename, O_RDWR); 43 if(fd < 0){ 44 printf("file %s open failed!\r\n", argv[1]); 45 return -1; 46 } 47 48 databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */ 49 retvalue = write(fd, databuf, sizeof(databuf)); 50 if(retvalue < 0){ 51 printf("LED Control Failed!\r\n"); 52 close(fd); 53 return -1; 54 } 55 56 retvalue = close(fd); /* 关闭文件 */ 57 if(retvalue < 0){ 58 printf("file %s close failed!\r\n", argv[1]); 59 return -1; 60 } 61 return 0; 62 } 五、运行测试 1、编译驱动程序和测试APP 4 obj-m := leddevice.o leddriver.o

        设置 obj-m 变量的值为“leddevice.o leddriver.o”。

        输入如下命令编译出驱动模块文件:                 make -j32         编译成功以后就会生成一个名为“leddevice.ko leddriver.ko”的驱动模块文件。

        输入如下命令编译测试 ledApp.c 这个测试程序:                 arm-linux-gnueabihf-gcc ledApp.c -o ledApp         编译成功以后就会生成 ledApp 这个应用程序。

2、运行测试 depmod //第一次加载驱动的时候需要运行此命令 modprobe leddevice.ko //加载设备模块 modprobe leddriver.ko //加载驱动模块

        根文件系统中/sys/bus/platform/目录下保存着当前板子 platform 总线下的设备和驱动,其中 devices 子目录为 platform 设备, drivers 子目录为 plartofm 驱动。查看/sys/bus/platform/devices/ 目录,看看我们的设备是否存在,我们在 leddevice.c 中设置 leddevice(platform_device 类型)的 name 字段为“imx6ul-led”,也就是设备名字为 imx6ul-led,因此肯定在/sys/bus/platform/devices/ 目录下存在一个名字“imx6ul-led”的文件,否则说明我们的设备模块加载失败,结果如图 所示:

        查看/sys/bus/platform/drivers/目录,看一下驱动是否存在,我们在 leddriver.c 中设置 led_driver (platform_driver 类型)的 name 字段为“imx6ul-led”,因此会在/sys/bus/platform/drivers/ 目录下存在名为“imx6ul-led”这个文件,结果如图 所示:

驱动模块和设备模块加载成功以后 platform 总线就会进行匹配,当驱动和设备匹配成功以 后就会输出如图 所示一行语句:

        驱动和设备匹配成功以后就可以测试 LED 灯驱动了,输入如下命令打开 LED 灯:

/ledApp /dev/platled 1 //打开 LED 灯

        在输入如下命令关闭 LED 灯:

./ledApp /dev/platled 0 //关闭 LED 灯

        观察一下 LED 灯能否打开和关闭,如果可以的话就说明驱动工作正常,如果要卸载驱动的 话输入如下命令即可:rmmod leddevice.kormmod leddriver.ko

六、总结:

        本篇笔记主要学习了platform设备驱动开发的相关概念。将分成两次笔记进行学习。本次笔记主要学习platform设备驱动开发相关的理论知识。主要内容包括:Linux驱动的分离与分层、platform平台驱动模型简介。其中驱动的分离与分层有包括驱动的分离、驱动的分层。platform平台驱动模型简介主要包括platform总线、platform驱动与platform设备。 ———————————————— 版权声明:本文为CSDN博主「大叔学Linux」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接: blog.csdn.net/jiage987450/article/details/134125677        


本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。

标签:

Linux学习第28天:Platform设备驱动开发(二):专注与分散由讯客互联创业栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Linux学习第28天:Platform设备驱动开发(二):专注与分散