i.MX6ULL终结者Platform设备驱动实验程序

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 }

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值