1 Platform设备和驱动程序
本实验例程路径:i.MX6UL终结者光盘资料/06_Linux驱动例程/14_gpioled_platform
我们先来看一下platform设备和驱动程序,创建led_device.c和led_driver.c两个文件,分别表示LED灯的platform设备和platform驱动文件。
led_device.c文件具体内容如下:
1 #include <linux/types.h>
2 #include <linux/kernel.h>
3 #include <linux/delay.h>
4 #include <linux/ide.h>
5 #include <linux/init.h>
6 #include <linux/module.h>
7 #include <linux/errno.h>
8 #include <linux/gpio.h>
9 #include <linux/cdev.h>
10 #include <linux/device.h>
11 #include <linux/of_gpio.h>
12 #include <linux/semaphore.h>
13 #include <linux/timer.h>
14 #include <linux/irq.h>
15 #include <linux/wait.h>
16 #include <linux/poll.h>
17 #include <linux/fs.h>
18 #include <linux/fcntl.h>
19 #include <linux/platform_device.h>
20 #include <asm/mach/map.h>
21 #include <asm/uaccess.h>
22 #include <asm/io.h>
23
24 /*
25 * 寄存器地址定义
26 */
27 #define CCM_CCGR1_BASE (0X020C406C)
28 #define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
29 #define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
30 #define GPIO1_DR_BASE (0X0209C000)
31 #define GPIO1_GDIR_BASE (0X0209C004)
32 #define REGISTER_LENGTH 4
33
34 /* @description : 释放flatform设备模块的时候此函数会执行
35 * @param - dev : 要释放的设备
36 * @return : 无
37 */
38 static void led_release(struct device *dev)
39 {
40 printk("led device released!\r\n");
41 }
42
43 /*
44 * 设备资源信息,也就是LED0所使用的所有寄存器
45 */
46 static struct resource led_resources[] = {
47 [0] = {
48 .start = CCM_CCGR1_BASE,
49 .end = (CCM_CCGR1_BASE + REGISTER_LENGTH - 1),
50 .flags = IORESOURCE_MEM,
51 },
52 [1] = {
53 .start = SW_MUX_GPIO1_IO03_BASE,
54 .end = (SW_MUX_GPIO1_IO03_BASE + REGISTER_LENGTH - 1),
55 .flags = IORESOURCE_MEM,
56 },
57 [2] = {
58 .start = SW_PAD_GPIO1_IO03_BASE,
59 .end = (SW_PAD_GPIO1_IO03_BASE + REGISTER_LENGTH - 1),
60 .flags = IORESOURCE_MEM,
61 },
62 [3] = {
63 .start = GPIO1_DR_BASE,
64 .end = (GPIO1_DR_BASE + REGISTER_LENGTH - 1),
65 .flags = IORESOURCE_MEM,
66 },
67 [4] = {
68 .start = GPIO1_GDIR_BASE,
69 .end = (GPIO1_GDIR_BASE + REGISTER_LENGTH - 1),
70 .flags = IORESOURCE_MEM,
71 },
72 };
73
74
75 /*
76 * platform设备结构体
77 */
78 static struct platform_device leddevice = {
79 .name = "imx6ul-led",
80 .id = -1,
81 .dev = {
82 .release = &led_release,
83 },
84 .num_resources = ARRAY_SIZE(led_resources),
85 .resource = led_resources,
86 };
87
88 /*
89 * @description : 设备模块加载
90 * @param : 无
91 * @return : 无
92 */
93 static int __init leddevice_init(void)
94 {
95 return platform_device_register(&leddevice);
96 }
97
98 /*
99 * @description : 设备模块注销
100 * @param : 无
101 * @return : 无
102 */
103 static void __exit leddevice_exit(void)
104 {
105 platform_device_unregister(&leddevice);
106 }
107
108 module_init(leddevice_init);
109 module_exit(leddevice_exit);
110 MODULE_LICENSE("GPL");
111 MODULE_AUTHOR("topeet");
第 46~72 行,led_resources 数组,描述了 LED 所要使用到的寄存器信息,也就是 IORESOURCE_MEM 资源。
第 78~86,platform 设备结构体变量 leddevice,这里要注意 name 字段为“imx6ul-led”,所以稍后编写 platform 驱动中的 name 字段也要为“imx6ul-led”,否则设备和驱动匹配失败。
第 95 行,通过 platform_device_register 向 Linux 内核注册 leddevice 这个 platform 设备。
第 105 行,通过 platform_device_unregister 从 Linux内核中删除掉 leddevice 这个platform 设备。
led_driver.c文件具体内容如下:
1 #include <linux/types.h>
2 #include <linux/kernel.h>
3 #include <linux/delay.h>
4 #include <linux/ide.h>
5 #include <linux/init.h>
6 #include <linux/module.h>
7 #include <linux/errno.h>
8 #include <linux/gpio.h>
9 #include <linux/cdev.h>
10 #include <linux/device.h>
11 #include <linux/of_gpio.h>
12 #include <linux/semaphore.h>
13 #include <linux/timer.h>
14 #include <linux/irq.h>
15 #include <linux/wait.h>
16 #include <linux/poll.h>
17 #include <linux/fs.h>
18 #include <linux/fcntl.h>
19 #include <linux/platform_device.h>
20 #include <asm/mach/map.h>
21 #include <asm/uaccess.h>
22 #include <asm/io.h>
23
24 #define LEDDEV_CNT 1 /* 设备号长度 */
25 #define LEDDEV_NAME "platled" /* 设备名字 */
26 #define LEDOFF 0
27 #define LEDON 1
28
29 /* 寄存器名 */
30 static void __iomem *IMX6U_CCM_CCGR1;
31 static void __iomem *SW_MUX_GPIO1_IO03;
32 static void __iomem *SW_PAD_GPIO1_IO03;
33 static void __iomem *GPIO1_DR;
34 static void __iomem *GPIO1_GDIR;
35
36 /* leddev设备结构体 */
37 struct leddev_dev{
38 dev_t devid; /* 设备号 */
39 struct cdev cdev; /* cdev */
40 struct class *class; /* 类 */
41 struct device *device; /* 设备 */
42 int major; /* 主设备号 */
43 };
44
45 struct leddev_dev leddev; /* led设备 */
46
47 /*
48 * @description : LED打开/关闭
49 * @param - sta : LEDON(0) 打开LED,LEDOFF(1) 关闭LED
50 * @return : 无
51 */
52 void led0_switch(u8 sta)
53 {
54 u32 val = 0;
55 if(sta == LEDON){
56 val = readl(GPIO1_DR);
57 val &= ~(1 << 3);
58 writel(val, GPIO1_DR);
59 }else if(sta == LEDOFF){
60 val = readl(GPIO1_DR);
61 val|= (1 << 3);
62 writel(val, GPIO1_DR);
63 }
64 }
65
66 /*
67 * @description : 打开设备
68 * @param - inode : 传递给驱动的inode
69 * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
70 * 一般在open的时候将private_data指向设备结构体。
71 * @return : 0 成功;其他 失败
72 */
73 static int led_open(struct inode *inode, struct file *filp)
74 {
75 filp->private_data = &leddev; /* 设置私有数据 */
76 return 0;
77 }
78
79 /*
80 * @description : 向设备写数据
81 * @param - filp : 设备文件,表示打开的文件描述符
82 * @param - buf : 要写给设备写入的数据
83 * @param - cnt : 要写入的数据长度
84 * @param - offt : 相对于文件首地址的偏移
85 * @return : 写入的字节数,如果为负值,表示写入失败
86 */
87 static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
88 {
89 int retvalue;
90 unsigned char databuf[1];
91 unsigned char ledstat;
92
93 retvalue = copy_from_user(databuf, buf, cnt);
94 if(retvalue < 0) {
95 return -EFAULT;
96 }
97
98 ledstat = databuf[0]; /* 获取状态值 */
99 if(ledstat == LEDON) {
100 led0_switch(LEDON); /* 打开LED灯 */
101 }else if(ledstat == LEDOFF) {
102 led0_switch(LEDOFF); /* 关闭LED灯 */
103 }
104 return 0;
105 }
106
107 /* 设备操作函数 */
108 static struct file_operations led_fops = {
109 .owner = THIS_MODULE,
110 .open = led_open,
111 .write = led_write,
112 };
113
114 /*
115 * @description : flatform驱动的probe函数,当驱动与设备匹配后此函数就会执行
116 * @param - dev : platform设备
117 * @return : 0,成功;其他负值,失败
118 */
119 static int led_probe(struct platform_device *dev)
120 {
121 int i = 0;
122 int ressize[5];
123 u32 val = 0;
124 struct resource *ledsource[5];
125
126 printk("led driver and device has matched!\r\n");
127 /* 1、获取资源 */
128 for (i = 0; i < 5; i++) {
129 ledsource[i] = platform_get_resource(dev, IORESOURCE_MEM, i);
/* 依次MEM类型资源 */
130 if (!ledsource[i]) {
131 dev_err(&dev->dev, "No MEM resource for always on\n");
132 return -ENXIO;
133 }
134 ressize[i] = resource_size(ledsource[i]);
135 }
136
137 /* 2、初始化LED */
138 /* 寄存器地址映射 */
139 IMX6U_CCM_CCGR1 = ioremap(ledsource[0]->start, ressize[0]);
140 SW_MUX_GPIO1_IO03 = ioremap(ledsource[1]->start, ressize[1]);
141 SW_PAD_GPIO1_IO03 = ioremap(ledsource[2]->start, ressize[2]);
142 GPIO1_DR = ioremap(ledsource[3]->start, ressize[3]);
143 GPIO1_GDIR = ioremap(ledsource[4]->start, ressize[4]);
144
145 val = readl(IMX6U_CCM_CCGR1);
146 val &= ~(3 << 26); /* 清除以前的设置 */
147 val |= (3 << 26); /* 设置新值 */
148 writel(val, IMX6U_CCM_CCGR1);
149
150 /* 设置GPIO1_IO03复用功能,将其复用为GPIO1_IO03 */
151 writel(5, SW_MUX_GPIO1_IO03);
152 writel(0x10B0, SW_PAD_GPIO1_IO03);
153
154 /* 设置GPIO1_IO03为输出功能 */
155 val = readl(GPIO1_GDIR);
156 val &= ~(1 << 3); /* 清除以前的设置 */
157 val |= (1 << 3); /* 设置为输出 */
158 writel(val, GPIO1_GDIR);
159
160 /* 默认关闭LED1 */
161 val = readl(GPIO1_DR);
162 val |= (1 << 3) ;
163 writel(val, GPIO1_DR);
164
165 /* 注册字符设备驱动 */
166 /*1、创建设备号 */
167 if (leddev.major) { /* 定义了设备号 */
168 leddev.devid = MKDEV(leddev.major, 0);
169 register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);
170 } else { /* 没有定义设备号 */
171 alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT,
LEDDEV_NAME); /* 申请设备号 */
172 leddev.major = MAJOR(leddev.devid); /* 获取分配号的主设备号 */
173 }
174
175 /* 2、初始化cdev */
176 leddev.cdev.owner = THIS_MODULE;
177 cdev_init(&leddev.cdev, &led_fops);
178
179 /* 3、添加一个cdev */
180 cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);
181
182 /* 4、创建类 */
183 leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
184 if (IS_ERR(leddev.class)) {
185 return PTR_ERR(leddev.class);
186 }
187
188 /* 5、创建设备 */
189 leddev.device = device_create(leddev.class, NULL,
leddev.devid, NULL, LEDDEV_NAME);
190 if (IS_ERR(leddev.device)) {
191 return PTR_ERR(leddev.device);
192 }
193
194 return 0;
195 }
196
197 /*
198 * @description : platform驱动的remove函数,移除platform驱动的
时候此函数会执行
199 * @param - dev : platform设备
200 * @return : 0,成功;其他负值,失败
201 */
202 static int led_remove(struct platform_device *dev)
203 {
204 iounmap(IMX6U_CCM_CCGR1);
205 iounmap(SW_MUX_GPIO1_IO03);
206 iounmap(SW_PAD_GPIO1_IO03);
207 iounmap(GPIO1_DR);
208 iounmap(GPIO1_GDIR);
209
210 cdev_del(&leddev.cdev);/* 删除cdev */
211 unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备号 */
212 device_destroy(leddev.class, leddev.devid);
213 class_destroy(leddev.class);
214 return 0;
215 }
216
217 /* platform驱动结构体 */
218 static struct platform_driver led_driver = {
219 .driver = {
220 .name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */
221 },
222 .probe = led_probe,
223 .remove = led_remove,
224 };
225
226 /*
227 * @description : 驱动模块加载函数
228 * @param : 无
229 * @return : 无
230 */
231 static int __init leddriver_init(void)
232 {
233 return platform_driver_register(&led_driver);
234 }
235
236 /*
237 * @description : 驱动模块卸载函数
238 * @param : 无
239 * @return : 无
240 */
241 static void __exit leddriver_exit(void)
242 {
243 platform_driver_unregister(&led_driver);
244 }
245
246 module_init(leddriver_init);
247 module_exit(leddriver_exit);
248 MODULE_LICENSE("GPL");
249 MODULE_AUTHOR("topeet");
第 119~195 行,probe 函数,当设备和驱动匹配以后此函数就会执行,加了一条打印语句,当匹配成功以后会在终端上输出“led driver and device has matched!”。在 probe 函数里面初始化 LED、注册字符设备驱动。也就是将原来在驱动加载函数里面做的工作全部放到 probe 函数里面完成。
第 202~215 行,remobe 函数,当卸载 platform 驱动的时候此函数就会执行。在此函数里面释放内存、注销字符设备等。也就是将原来驱动卸载函数里面的工作全部都放到 remove 函数中完成。
第 218~224 行,platform_driver 驱动结构体,注意 name 字段为"imx6ul-led",和我们在led_device.c 文件里面设置的设备 name 字段一致。
第 233 行,通过 platform_driver_register 向 Linux 内核注册 led_driver 驱动。
第 243 行,通过 platform_driver_unregister 从 Linux内核卸载 led_driver 驱动。
2 应用测试程序
创建led_test.c文件,具体内容如下:
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 #define LEDOFF 0
10 #define LEDON 1
11
12 /*
13 * @description : main主程序
14 * @param - argc : argv数组元素个数
15 * @param - argv : 具体参数
16 * @return : 0 成功;其他 失败
17 */
18 int main(int argc, char *argv[])
19 {
20 int fd, retvalue;
21 char *filename;
22 unsigned char databuf[2];
23
24 if(argc != 3){
25 printf("Error Usage!\r\n");
26 return -1;
27 }
28
29 filename = argv[1];
30
31 /* 打开led驱动 */
32 fd = open(filename, O_RDWR);
33 if(fd < 0){
34 printf("file %s open failed!\r\n", argv[1]);
35 return -1;
36 }
37
38 databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */
39 retvalue = write(fd, databuf, sizeof(databuf));
40 if(retvalue < 0){
41 printf("LED Control Failed!\r\n");
42 close(fd);
43 return -1;
44 }
45
46 retvalue = close(fd); /* 关闭文件 */
47 if(retvalue < 0){
48 printf("file %s close failed!\r\n", argv[1]);
49 return -1;
50 }
51 return 0;
52 }