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 }