fl2440 platform总线led字符设备驱动

首先需要知道的是,设备跟驱动是分开的。设备通过struct device来定义,也可以自己将结构体封装到自己定义的device结构体中:

例如:struct platform_device:

1 在include/linux/platform_device.h文件中:          //这个位置还是个盲点,我一直没找到这个位置在哪里
2 struct platform_device {
3     const char    * name
4     u32        id
5     struct device    dev
6     u32        num_resources
7     struct resource    * resource
8 }

platform_device通过向内核注册struct device dev这个结构体来告诉内核加载这个设备,方法就是  device_register(&platform_device->dev)。

驱动通过struct driver这个结构体来定义,与struct device一致,你也可以用自己的结构体去封装:例如,struct platform_driver。

 1 include/linux/platform_device.h文件中:
 2 struct platform_driver {
 3     int (*probe)(struct platform_device *)
 4     int (*remove)(struct platform_device *)
 5     void (*shutdown)(struct platform_device *)
 6     int (*suspend)(struct platform_device *, pm_message_t state)
 7     int (*suspend_late)(struct platform_device *, pm_message_t state)
 8     int (*resume_early)(struct platform_device *)
 9     int (*resume)(struct platform_device *)
10     struct device_driver driver
11 }

Platform设备驱动匹配基本流程:Platform 设备先被注册然后platfrom驱动加载时会调用驱动程序中的probe()入口函数,扫描系统中已注册的设备,通过。Name域找到匹配设备后将驱动和设备绑定。一个驱动可以对应多个设备,但是一个设备只对一个驱动。

驱动和设备的结构体成员有差别,但是用来匹配识别的name域必须是相同的。只有这样才能实现互相匹配,实现驱动找设备,设备找驱动。(系统为platform总线定义了一个bus_type(总线类型)的实例platform_bus_type,在此结构体中有一个成员函数:.match,系统通过这个函数完成相关匹配)。

在接下来的代码中我们定义的platform_device与plateform_driver分别为:

 1 static struct platform_device s3c_led_device = {
 2    .name    = "s3c_led",
 3     .id      = 1,
 4     .dev     = 
 5     {
 6         .platform_data = &s3c_led_data, 
 7         .release = platform_led_release,
 8     },
 9 };
10 
11 static struct platform_driver s3c_led_driver = { 
12     .probe      = s3c_led_probe, 
13     .remove     = s3c_led_remove, 
14     .driver     = { 
15         .name       = "s3c_led", 
16         .owner      = THIS_MODULE, 
17     },
18 };

platform_device由platform_device_register注册函数调用:

platform_device结构体中定义了name域id以及设备dev信息的函数指针;dev指针指向包含platform_data的指针与release的函数,platform_data指向设备存放的数据,数据存放在结构体类型为s3c_led_platform_data的结构体变量s3c_led_data中;s3c_led_data包含leds的指针与nleds的整型变量,nleds中保存的是s3c_led_info的结构体数组。

platform_driver由platform_driver_register注册函数调用:

platform_driver结构体中定义了指向s3c_led_probe的probe指针与remove指针以及driver指针,通过s3c_led_probe驱动找到platform_device设备,然后初始化led设备,更改led灯gpio口的状态以及通过判断主设备号静态或动态获取设备号,初始化cdev结构体并且将fops结构体与之绑定,最后字符设备驱动注册。

其次在led生成设备节点的时候可以利用mknod手动创建,也可以利用Linux内核为我们提供的一组函数来在内核加载的时候自动在/dev目录下创建相应的设备节点。

device_creat这个函数可以为我们自动创建设备节点。这是利用在驱动中加入了对mdev的支持所完成的。而要支持mdev:在驱动初始化的代码里调用class_create()为该设备创建一个class,再为每个设备调用device_create()创建对应的设备( 在2.6较早的内核中用class_device_create)。内核中定义的struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的mdev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。

接下来看代码:

  1 #include "s3c_driver.h"
  2 
  3 #define DRV_DESC                  "S3C24XX LED driver"
  4 
  5 /* Driver version*/
  6 #define DRV_MAJOR_VER             1
  7 #define DRV_MINOR_VER             0
  8 #define DRV_REVER_VER             0
  9 
 10 #define DEV_NAME                  DEV_LED_NAME
 11 
 12 //#define DEV_MAJOR                 DEV_LED_MAJOR
 13 #ifndef DEV_MAJOR
 14 #define DEV_MAJOR                 0 /*  dynamic major by default */ 
 15 #endif
 16 
 17 #define TIMER_TIMEOUT             40
 18 
 19 static int debug = DISABLE;
 20 static int dev_major = DEV_MAJOR;
 21 static int dev_minor = 0;
 22 
 23 
 24 /* ============================ Platform Device part ===============================*/
 25 /*  LED hardware informtation structure*/
 26 struct s3c_led_info                           //定义led结构体信息
 27 {
 28     unsigned char           num;              /* The LED number  */
 29     unsigned int            gpio;             /* Which GPIO the LED used */  
 30     unsigned char           active_level;     /* The GPIO pin level(HIGHLEVEL or LOWLEVEL) to turn on or off  */
 31     unsigned char           status;           /* Current LED status: OFF/ON */
 32     unsigned char           blink;            /* Blink or not */           
 33 };
 34 
 35 /*  The LED platform device private data structure */
 36 struct s3c_led_platform_data
 37 {
 38     struct s3c_led_info    *leds;
 39     int                     nleds;
 40 };
 41 
 42 
 43 /*  LED hardware informtation data*/ 
 44 static struct s3c_led_info  s3c_leds[] = {                //定义各个灯的信息
 45     [0] = {
 46         .num = 1,
 47         .gpio = S3C2410_GPB(5),
 48         .active_level = LOWLEVEL,
 49         .status = OFF,
 50         .blink = ENABLE,
 51     },
 52     [1] = {
 53         .num = 2,
 54         .gpio = S3C2410_GPB(6),
 55         .active_level = LOWLEVEL,
 56         .status = OFF,
 57         .blink = DISABLE,
 58     },
 59     [2] = {
 60         .num = 3,
 61         .gpio = S3C2410_GPB(8),
 62         .active_level = LOWLEVEL,
 63         .status = OFF,
 64         .blink = DISABLE,
 65     },
 66     [3] = { 
 67         .num = 4,
 68         .gpio = S3C2410_GPB(10),
 69         .active_level = LOWLEVEL,
 70         .status = OFF,
 71         .blink = DISABLE,
 72     }, 
 73 };
 74 
 75 /*  The LED platform device private data */
 76 static struct s3c_led_platform_data s3c_led_data = {        //定义led灯的结构体信息
 77     .leds = s3c_leds,                                        //每个灯的信息
 78     .nleds = ARRAY_SIZE(s3c_leds),                            //灯的数量
 79 };
 80 
 81 struct led_device                                            //定义一个led_device的结构体
 82 {
 83     struct s3c_led_platform_data    *data;                    
 84     struct cdev                     cdev;
 85     struct class                    *dev_class;
 86     struct timer_list               blink_timer;
 87 } led_device;
 88 
 89 static void platform_led_release(struct device * dev)                //将所有灯关掉
 90 {
 91     int i;
 92     struct s3c_led_platform_data *pdata = dev->platform_data;         //pdata指向led灯的结构体
 93 
 94     dbg_print("%s():%d\n", __FUNCTION__,__LINE__);                    //调试打印
 95 
 96     /* Turn all LED off */
 97     for(i=0; i<pdata->nleds; i++)
 98     {
 99          s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level); 
100     }
101 }
102 
103 static struct platform_device s3c_led_device = {                    //设备节点结构体
104     .name    = "s3c_led",
105     .id      = 1,
106     .dev     = 
107     {
108         .platform_data = &s3c_led_data, 
109         .release = platform_led_release,
110     },
111 };
112 
113 
114 
115 /* ===================== led device driver part ===========================*/
116 
117 void led_timer_handler(unsigned long data)                    //实现led的亮灭
118 { 
119     int  i; 
120     struct s3c_led_platform_data *pdata = (struct s3c_led_platform_data *)data;
121 
122     for(i=0; i<pdata->nleds; i++) 
123     { 
124         if(ON == pdata->leds[i].status)                        //如果要求是ON则转换成ON
125         {
126               s3c2410_gpio_setpin(pdata->leds[i].gpio, pdata->leds[i].active_level); 
127         }
128         else                                                //如果要求是OFF则转换成OFF
129         {
130               s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level); 
131         }
132 
133         if(ENABLE == pdata->leds[i].blink )  /* LED should blink */           //如果要求是闪,则让它闪
134         {
135             /* Switch status between 0 and 1 to turn LED ON or off */
136             pdata->leds[i].status = pdata->leds[i].status ^ 0x01;  
137         }
138 //更新计时器,jiffies是个全局变量,cpu每来一个时钟脉冲就自加,一个jiffes=10ms,利用这种方法就不需要单独的一个进程
139         mod_timer(&(led_device.blink_timer), jiffies + TIMER_TIMEOUT);        
140     }
141 }
142 
143 
144 static int led_open(struct inode *inode, struct file *file)            //连接自static struct file_operations led_fops的结构体
145 { 
146     struct led_device *pdev ;
147     struct s3c_led_platform_data *pdata;
148 
149     pdev = container_of(inode->i_cdev,struct led_device, cdev);
150 //container_of是通过一个结构变体量中一个成员的地址找到这个结构体变量的首地址。
151     pdata = pdev->data;
152 
153     file->private_data = pdata;
154     //这里为什么不直接用file->private_data = pdev->data;
155 
156     return 0;
157 }
158 
159 
160 static int led_release(struct inode *inode, struct file *file)
161 { 
162     return 0;
163 }
164 
165 static void print_led_help(void)                    //定义help结构体
166 {
167     printk("Follow is the ioctl() command for LED driver:\n");
168     printk("Enable Driver debug command: %u\n", SET_DRV_DEBUG);
169     printk("Get Driver verion  command : %u\n", GET_DRV_VER);
170     printk("Turn LED on command        : %u\n", LED_ON);
171     printk("Turn LED off command       : %u\n", LED_OFF);
172     printk("Turn LED blink command     : %u\n", LED_BLINK);
173 }
174 
175 /* compatible with kernel version >=2.6.38*/
176 static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
177 { 
178     struct s3c_led_platform_data *pdata = file->private_data;
179 
180     switch (cmd)
181     {
182         case SET_DRV_DEBUG:
183             dbg_print("%s driver debug now.\n", DISABLE == arg ? "Disable" : "Enable");
184             debug = (0==arg) ? DISABLE : ENABLE;
185             break;
186         case GET_DRV_VER:
187             print_version(DRV_VERSION);
188             return DRV_VERSION;
189 
190         case LED_OFF:
191             if(pdata->nleds <= arg)
192             {
193                printk("LED%ld doesn't exist\n", arg);  
194                return -ENOTTY;
195             }
196             pdata->leds[arg].status = OFF;
197             pdata->leds[arg].blink = DISABLE;
198             break;
199 
200         case LED_ON:
201             if(pdata->nleds <= arg)
202             {
203                printk("LED%ld doesn't exist\n", arg);  
204                return -ENOTTY;
205             }
206             pdata->leds[arg].status = ON;
207             pdata->leds[arg].blink = DISABLE;
208             break;
209 
210         case LED_BLINK:
211             if(pdata->nleds <= arg)
212             {
213                printk("LED%ld doesn't exist\n", arg);  
214                return -ENOTTY;
215             }
216             pdata->leds[arg].blink = ENABLE;
217             pdata->leds[arg].status = ON;
218             break;
219 
220         default: 
221             dbg_print("%s driver don't support ioctl command=%d\n", DEV_NAME, cmd); 
222             print_led_help();
223             return -EINVAL;
224 
225     }
226     return 0;
227 }
228 
229 
230 static struct file_operations led_fops = { 
231     .owner = THIS_MODULE, 
232     .open = led_open, 
233     .release = led_release, 
234     .unlocked_ioctl = led_ioctl, /* compatible with kernel version >=2.6.38*/
235 };
236 
237 
238 static int s3c_led_probe(struct platform_device *dev)                
239 /*由内核将platform_device的结构体传给probe函数,当设备找到驱动或者驱动找到设备的时候会调probe函数。
240 一旦找到,内核就会通过platform_device_register找到s3c_led_device,然后传给probe函数,这里的*dev就指向
241 了platform_device定义的s3c_led_device*/
242 {
243     struct s3c_led_platform_data *pdata = dev->dev.platform_data; 
244 //*pdata就获取到了static struct s3c_led_platform_data定义的s3c_led_data的地址
245     int result = 0;
246     int i;
247     dev_t devno;
248 
249     /* Initialize the LED status */
250     for(i=0; i<pdata->nleds; i++)
251     {
252          s3c2410_gpio_cfgpin(pdata->leds[i].gpio, S3C2410_GPIO_OUTPUT);        //s3c2410_gpio_cfgpin配置端口的GPIO的功能
253 /*void s3c2410_gpio_cfgpin(unsigned int pin,unsigned int function)
254 第一个参数pin 是对应的io引脚(这里用宏S3C2410_GPB(5),5不是固定的,看你需要引用的引脚而定)
255 第二个引脚是设置该引脚的功能的,(由S3C2410_GPIO_INPUT,S3C2410_GPIO_OUTPUT,S3C2410_GPIO_SFN2,S3C2410_GPIO_SFN3这4个宏进行定义)
256 例如:s3c2410_gpio_cfgpin(S3C2410_GPB(5),S3C2410_GPIO_INPUT)
257 设置GPB5引脚为输入。*/
258          if(ON == pdata->leds[i].status)
259          {
260             s3c2410_gpio_setpin(pdata->leds[i].gpio, pdata->leds[i].active_level);     //设置led管脚的亮
261          }
262          else
263          {
264             s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level);     //设置led管脚灭
265          }
266     }
267 
268     /*  Alloc the device for driver */
269     if (0 != dev_major) 
270     { 
271         devno = MKDEV(dev_major, dev_minor); 
272         result = register_chrdev_region(devno, 1, DEV_NAME);                        //分配主次设备号
273     } 
274     else 
275     { 
276         result = alloc_chrdev_region(&devno, dev_minor, 1, DEV_NAME);                 //动态分配主次设备号
277         dev_major = MAJOR(devno); 
278     }
279 
280     /* Alloc for device major failure */ 
281     if (result < 0) 
282     { 
283         printk("%s driver can't get major %d\n", DEV_NAME, dev_major); 
284         return result; 
285     }
286 
287     /* Initialize button structure and register cdev*/
288     memset(&led_device, 0, sizeof(led_device));                        //
289     led_device.data = dev->dev.platform_data;
290     cdev_init (&(led_device.cdev), &led_fops);
291     led_device.cdev.owner  = THIS_MODULE;
292 
293     result = cdev_add (&(led_device.cdev), devno , 1); 
294     if (result) 
295     { 
296         printk (KERN_NOTICE "error %d add %s device", result, DEV_NAME); 
297         goto ERROR; 
298     } 
299     
300     led_device.dev_class = class_create(THIS_MODULE, DEV_NAME); 
301     if(IS_ERR(led_device.dev_class)) 
302     { 
303         printk("%s driver create class failture\n",DEV_NAME); 
304         result =  -ENOMEM; 
305         goto ERROR; 
306     }
307 
308 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)     
309     device_create(led_device.dev_class, NULL, devno, NULL, DEV_NAME);
310 #else
311     device_create (led_device.dev_class, NULL, devno, DEV_NAME);
312 #endif
313 
314     /*  Initial the LED blink timer */                            //异步不会阻塞?
315     init_timer(&(led_device.blink_timer));                        //初始化时钟
316     led_device.blink_timer.function = led_timer_handler;        //时间到了之后执行的函数
317     led_device.blink_timer.data = (unsigned long)pdata;            //传给函数的参数
318     led_device.blink_timer.expires  = jiffies + TIMER_TIMEOUT;    //定义闹钟间隔
319     add_timer(&(led_device.blink_timer)); 
320 
321     printk("S3C %s driver version %d.%d.%d initiliazed.\n", DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER, DRV_REVER_VER); 
322 
323     return 0;
324                
325 
326 ERROR: 
327     printk("S3C %s driver version %d.%d.%d install failure.\n", DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER, DRV_REVER_VER); 
328     cdev_del(&(led_device.cdev)); 
329 
330     unregister_chrdev_region(devno, 1); 
331     return result;
332 
333 }
334 
335 static int s3c_led_remove(struct platform_device *dev)
336 {
337     dev_t devno = MKDEV(dev_major, dev_minor);
338 
339     del_timer(&(led_device.blink_timer));
340 
341     cdev_del(&(led_device.cdev)); 
342     device_destroy(led_device.dev_class, devno); 
343     class_destroy(led_device.dev_class); 
344     
345     unregister_chrdev_region(devno, 1); 
346     printk("S3C %s driver removed\n", DEV_NAME);
347 
348     return 0;
349 }
350 
351 
352 static struct platform_driver s3c_led_driver = { 
353     .probe      = s3c_led_probe, 
354     .remove     = s3c_led_remove, 
355     .driver     = { 
356         .name       = "s3c_led", 
357         .owner      = THIS_MODULE, 
358     },
359 };
360 
361 
362 static int __init s3c_led_init(void)
363 {
364    int       ret = 0;
365 
366    ret = platform_device_register(&s3c_led_device);
367    if(ret)
368    {
369         printk(KERN_ERR "%s:%d: Can't register platform device %d\n", __FUNCTION__,__LINE__, ret); 
370         goto fail_reg_plat_dev;
371    }
372    dbg_print("Regist S3C LED Platform Device successfully.\n");
373 
374    ret = platform_driver_register(&s3c_led_driver);
375    if(ret)
376    {
377         printk(KERN_ERR "%s:%d: Can't register platform driver %d\n", __FUNCTION__,__LINE__, ret); 
378         goto fail_reg_plat_drv;
379    }
380    dbg_print("Regist S3C LED Platform Driver successfully.\n");
381 
382    return 0;
383 
384 fail_reg_plat_drv:
385    platform_driver_unregister(&s3c_led_driver);
386 fail_reg_plat_dev:
387    return ret;
388 }
389 
390 
391 static void s3c_led_exit(void)
392 {
393     dbg_print("%s():%d remove LED platform drvier\n", __FUNCTION__,__LINE__);
394     platform_driver_unregister(&s3c_led_driver);
395     dbg_print("%s():%d remove LED platform device\n", __FUNCTION__,__LINE__);
396     platform_device_unregister(&s3c_led_device);
397 }
398 
399 module_init(s3c_led_init);
400 module_exit(s3c_led_exit);
401 
402 module_param(debug, int, S_IRUGO);
403 module_param(dev_major, int, S_IRUGO);
404 module_param(dev_minor, int, S_IRUGO);
405 
406 MODULE_AUTHOR(DRV_AUTHOR);
407 MODULE_DESCRIPTION(DRV_DESC);
408 MODULE_LICENSE("GPL");
409 MODULE_ALIAS("platform:S3C24XX_led");
下面是一个小小的应用程序:
 1 /*********************************************************************************
 2  *      Copyright:  (C) 2016 2013dianxin_3
 3  *                  All rights reserved.
 4  *
 5  *       Filename:  ledapp.c
 6  *    Description:  This file 
 7  *                 
 8  *        Version:  1.0.0(06/06/2016)
 9  *         Author:  xiaohexiansheng <1392195453@qq.com>
10  *      ChangeLog:  1, Release initial version on "06/06/2016 05:21:50 PM"
11  *                 
12  ********************************************************************************/
13 #include "plat_ioctl.h"
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <fcntl.h>
18 
19 int main(int argc, char **argv)
20 {
21     int fd;
22 
23     fd = open("/dev/led", O_RDWR);
24     ioctl(fd,LED_ON,0);
25     ioctl(fd,LED_ON,1);
26     ioctl(fd,LED_ON,2);
27     ioctl(fd,LED_BLINK,3);
28     close(fd);
29     return 0;
30 }
posted @ 2016-05-10 17:33  小禾先生  阅读(442)  评论(0编辑  收藏  举报