标签:
首先需要知道的是,设备跟驱动是分开的。设备通过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");
标签:
原文地址:http://www.cnblogs.com/xiaohexiansheng/p/5478636.html