码迷,mamicode.com
首页 > 其他好文 > 详细

fl2440 platform总线led字符设备驱动

时间:2016-05-10 18:32:38      阅读:262      评论:0      收藏:0      [点我收藏+]

标签:

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

 

fl2440 platform总线led字符设备驱动

标签:

原文地址:http://www.cnblogs.com/xiaohexiansheng/p/5478636.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!