led子系统驱动,以及此类驱动(sysfs访问方式)应用程序编写

看了这么多内核代码,终于要自己开始做驱动了.按照由易到难,由浅入深的顺序,就从LED开始.

        LED驱动可以说是hello world之后最简单的驱动模块了.如果自己写一个LED驱动那是很简单的,其实用linux内核中的leds子系统来做也是比较简单的,内核中的leds子系统是将led抽象成platform_device,并有leds_class.这样,在/sys/class/leds/目录下面就可以利用sysfs文件系统来实现LED的操作.其实,也可以在此基础上添加/dev访问的接口,甚至用不着包含mknod的脚本和udev等工具,在模块注册后就可以生成/dev下的设备文件.

        一步一步的来,首先利用platform虚拟总线来实现sysfs文件系统下的LED操作.

        在mach-smdk2440.c中新增platform_device如下:

  1. static struct platform_device smdk_led5 = {  
  2.     .name       = "s3c24xx_led",  
  3.     .id     = 0,  
  4.     .dev        = {  
  5.         .platform_data = &smdk_pdata_led5,  
  6.         .release = &platform_led_release,  
  7.     },  
  8. };  
        对于具体的板子,是GPB5-8对应四个LED,所以这里smdk_pdata_led5的定义如下:

  1. static struct s3c24xx_led_platdata smdk_pdata_led5 = {  
  2.     .gpio       = S3C2410_GPB(5),  
  3.     .flags      = S3C24XX_LEDF_ACTLOW ,//| S3C24XX_LEDF_TRISTATE,  
  4.     .name       = "led5",  
  5.     //.def_trigger  = "nand-disk",  
  6. };  
        接着在static struct platform_device *smdk2440_devices[] __initdata中新增&smdk_led5,这样系统在启动时就会由smdk2440_machine_init调用platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));来添加刚才加上的platform_device.

        看完了platform_device再看下platform_driver是如何注册的:

        在drivers/leds目录下的Makefile中有obj-$(CONFIG_LEDS_S3C24XX)+= leds-s3c24xx.o

        所以在make menuconfig的时候记得将其选为M.

        这样,注册leds-s3c24xx.ko就会在/sys/class/leds中有

        led5  led6  led7  led8四个目录,进入led5目录,有

        brightness      device          power           trigger
        dev             max_brightness  subsystem       uevent

        执行echo 0 > brightness就可以关闭led,而echo 1 > brightness就可以打开led.

        因为有leds_class,而在led-class.c中leds-init函数中有leds_class->dev_attrs = led_class_attrs;
        其中,led_class_attrs定义如下

  1. static struct device_attribute led_class_attrs[] = {  
  2.     __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),  
  3.     __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),  
  4. #ifdef CONFIG_LEDS_TRIGGERS  
  5.     __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),  
  6. #endif  
  7.     __ATTR_NULL,  
  8. };  
        根据sysfs的属性文件,读写属性文件最终调用的就是show和store函数,在这里就是led_brightness_show和led_brightness_store函数.
        顺着调用其实设置寄存器的动作是在leds-s3c24xx.c中的s3c24xx_led_set实现的.从这里可以看出,LEDS这个子系统也做了抽象,与平台相关的放在一个文件中,而与平台无关的抽象出来放在led-class中.

        到此,就可以通过sysfs文件系统来访问led设备了.

        那么,开头说的在此基础如何做/dev访问的接口呢?

        在leds目录下新建一个文件led-dev.c

        在Makefile中添加obj-$(CONFIG_LEDS_INTF_DEV)+= led-dev.o

        记得在Kconfig文件中也要添加对应的信息.

        led-dev.c文件内容如下:

  1. /* 
  2.  * LEDS subsystem, dev interface 
  3.  * 
  4.  * Copyright (C) 2012  Baikal 
  5.  * Author: Baikal <dndxhej@gmail.com> 
  6.  * 
  7.  * 
  8.  * This program is free software; you can redistribute it and/or modify 
  9.  * it under the terms of the GNU General Public License version 2 as 
  10.  * published by the Free Software Foundation. 
  11. */  
  12.   
  13.   
  14. #include <linux/module.h>  
  15. #include <linux/leds.h>  
  16. #include <linux/sched.h>  
  17.   
  18. #include <linux/fs.h>  
  19. #include <linux/device.h>  
  20. #include <asm/uaccess.h>  
  21. #include "leds.h"  
  22.   
  23. static dev_t led_devt;  
  24.   
  25. #define LED_DEV_MAX 8   /*actually,there is 4 leds on the 2440 board*/  
  26.   
  27.   
  28. static int led_dev_open(struct inode *inode, struct file *file)  
  29. {  
  30.   
  31.     printk(KERN_INFO "debug by baikal: led dev open\n");  
  32.     printk(KERN_INFO "i_rdev:0x%x\n",inode->i_rdev);  
  33.     printk(KERN_INFO "i_rdev:%d\n",inode->i_rdev);  
  34.     printk(KERN_INFO "i_rdev:major:%d minor:%d\n",MAJOR(inode->i_rdev),MINOR(inode->i_rdev));  
  35.     struct led_classdev *led = container_of(inode->i_cdev,  
  36.                     struct led_classdev, char_dev);  
  37.   
  38.   
  39.     file->private_data = led;  
  40.   
  41.   
  42.     return 0;  
  43. }  
  44.   
  45. static ssize_t  
  46. led_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)  
  47. {  
  48.     printk(KERN_INFO "debug by baikal: led dev read\n");  
  49. }  
  50.   
  51.   
  52. static ssize_t  
  53. led_dev_write(struct file *file, char __user *buf, size_t count, loff_t *ppos)  
  54. {  
  55.     char data;  
  56.     printk(KERN_INFO "debug by baikal: led dev write\n");  
  57.   
  58.     copy_from_user(&data,buf,count);  
  59.     if(data == '0')  
  60.         led_set_brightness(file->private_data,0);  
  61.     else  
  62.         led_set_brightness(file->private_data,1);          
  63. }  
  64.   
  65. static const struct file_operations led_dev_fops = {  
  66.     .owner      = THIS_MODULE,  
  67.     .read       = led_dev_read,  
  68.     .write      = led_dev_write,  
  69.     .open       = led_dev_open,  
  70. //  .release    = led_dev_release,  
  71.   
  72. };  
  73.   
  74.   
  75.   
  76. void led_dev_prepare(struct led_classdev *led)  
  77. {  
  78.     static int minor = 0;  
  79.     printk(KERN_INFO "debug by baikal: led dev pre\n");  
  80.   
  81.     led_devt = MKDEV(254,minor); //add by baikal  
  82.     minor++;  
  83.     led->dev->devt = MKDEV(MAJOR(led_devt), MINOR(led_devt));  
  84.   
  85.   
  86.     cdev_init(&led->char_dev, &led_dev_fops);  
  87.     //led->char_dev.owner = led->owner;  
  88.   
  89. }  
  90.   
  91. void led_dev_add_device(struct led_classdev *led)  
  92. {  
  93.     printk(KERN_INFO "debug by baikal: led dev add \n");  
  94.   
  95.     if (cdev_add(&led->char_dev, led->dev->devt, 1))  
  96.         printk(KERN_WARNING " failed to add char device ");  
  97.     else  
  98.         pr_debug("%s: dev (%d:)\n", led->name,  
  99.             MAJOR(led_devt));  
  100. }  
  101.   
  102. void led_dev_del_device(struct led_classdev *led)  
  103. {  
  104.     if (led->dev->devt)  
  105.         cdev_del(&led->char_dev);  
  106. }  
  107.   
  108.   
  109. void __init led_dev_init(void)  
  110. {  
  111. /* 
  112.     int err; 
  113.  
  114.     err = alloc_chrdev_region(&led_devt, 0, LED_DEV_MAX, "led"); 
  115.     if(err < 0) 
  116.         printk(KERN_ERR "%s: failed to allocate char dev region\n",__FILE__); 
  117. */  
  118. }  
  119.   
  120. void __exit led_dev_exit(void)  
  121. {  
  122. /* 
  123.     if(led_devt) 
  124.         unregister_chrdev_region(led_devt, LED_DEV_MAX); 
  125. */  
  126. }  
        并在led-class.c中的led_classdev_register函数的开头处添加
  1. static int minor = 0;  
  2. dev_t led = MKDEV(254,minor); //add by baikal  
  3. printk(KERN_INFO "debug by baikal: led class reg 1\n");  
  4.   
  5.   
  6. led_cdev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);  
  7. if (led_cdev->dev == NULL) {  
  8.     return -ENOMEM;  
  9. }  
  10. led_dev_prepare(led_cdev);              //add by baikal  
  11. printk(KERN_INFO "debug by baikal: led class reg 2\n");  
  12.   
  13. minor++;  
        并将led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
     "%s", led_cdev->name);

        改为led_cdev->dev = device_create(leds_class, parent, led, led_cdev,
     "%s", led_cdev->name);
        在函数的结尾处再调用 led_dev_add_device(led_cdev);

        这样,在模块注册后在/dev目录下就有

[root@BaikalHu led5]# ls /dev/l*
/dev/led5   /dev/led7   /dev/loop0  /dev/loop2  /dev/loop4  /dev/loop6
/dev/led6   /dev/led8   /dev/loop1  /dev/loop3  /dev/loop5  /dev/loop7

        这样就在/dev下生成了设备文件.

        可以照下面的方式来测试/dev下的led设备文件的功能:

  1. #include <sys/types.h>  
  2. #include <sys/stat.h>  
  3. #include <fcntl.h>  
  4. #include <stdio.h>  
  5. #include <errno.h>  
  6. #include <string.h>  
  7.   
  8. int main(int argc, char **argv)  
  9. {  
  10.     int fd;  
  11.     if(argc != 3)  
  12.     {  
  13.         printf("usage : ./led led5-8 on/off\n");  
  14.         return -1;  
  15.     }  
  16.   
  17.     char file[20] = "/dev/";  
  18.   
  19.     strcat(file, argv[1]);  
  20.   
  21.     fd = open(file, O_RDWR);  
  22.   
  23.     if(fd < 0)  
  24.     {     
  25.         perror("open led");  
  26.         return -1;  
  27.     }  
  28.   
  29.     if(!strcmp(argv[2],"on"))  
  30.     {  
  31.         write(fd,"1",1);  
  32.     }  
  33.     else if(!strcmp(argv[2],"off"))  
  34.     {  
  35.         write(fd,"0",1);  
  36.     }  
  37.   
  38.    
  39.     close(fd);  
  40.   
  41.     return 0;  
  42.   
  43.   
  44. }  
        测试一切正常.

        回头解释下为什么这样就可以自己生成/dev下的设备文件,原因在于在新内核中的device_add函数中有这么一段:

  1. if (MAJOR(dev->devt)) {  
  2.     error = device_create_file(dev, &devt_attr);  
  3.     if (error)  
  4.         goto ueventattrError;  
  5.   
  6.     error = device_create_sys_dev_entry(dev);  
  7.     if (error)  
  8.         goto devtattrError;  
  9.   
  10.     devtmpfs_create_node(dev);  
  11. }  
        看到devtmpfs_create_node了没,这就是答案.如果对devtmpfs有兴趣的话,以前写的《 linux设备模型之字符设备》大概提到了devtmpfs.

        通过自己的添加,可以用sysfs和dev两种接口方式来访问LED.但是实际设置寄存器的函数都是同一处地方.

        linux下的驱动,最终还是要操作寄存器的,但是与裸机不同的是linux将硬件设备抽象为文件,那我们就必须按照这种机制来书写linux下的驱动,机制的根本是文件系统和设备模型,但是可以通过不同的策略来达到目的,比如sysfs和devtmpfs.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值