九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 7678|回复: 3
打印 上一主题 下一主题

x4412&ibox项目实战25-使用sysfs创建LED驱动

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-2 10:20:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
有了前面两个实验,我们就可以依葫芦画瓢,实现自己想要的驱动了。本实验通过sysfs,即前面的kobject实现LED灯的控制。默认x4412开发板已经将LED驱动集成进去了,但是它使用的linux内核自带的驱动,尽管它本质上也是使用的sysfs文件系统,但是由于里面结构复杂,不便于新手理解,因此我们特别将它提取出来,编写了非常简单明了的驱动。用户真正消化本驱动后,再去研究自带的驱动,就显得得心应手了。
在kernel/drivers/char/x4412目录下创建x4412-led-sysfs.c文件,编辑源码如下:
  1. #include <linux/module.h>
  2. #include <linux/init.h>
  3. #include <linux/platform_device.h>
  4. #include <mach/hardware.h>
  5. #include <asm/mach-types.h>
  6. #include <linux/gpio.h>
  7. #include <plat/gpio-cfg.h>
  8. /*
  9. * X4412:
  10. *
  11. * LED1 -> D22 -> XEINT14
  12. * LED2 -> D23 -> XEINT15
  13. * LED3 -> D24 -> XEINT22
  14. * LED4 -> D25 -> XEINT23
  15. */
  16. static int __x4412_led_status[4] = { 0 };
  17. static void __x4412_led_probe(void)       //初始化LED对应GPIO口
  18. {
  19.          int ret;
  20.          ret = gpio_request(EXYNOS4_GPX1(6), "GPX1");
  21.          if(ret)
  22.                    printk("x4412-led: request gpio GPX1(6) fail\n");
  23.          s3c_gpio_setpull(EXYNOS4_GPX1(6), S3C_GPIO_PULL_UP);
  24.          gpio_direction_output(EXYNOS4_GPX1(6), 1);
  25.          ret = gpio_request(EXYNOS4_GPX1(7), "GPX1");
  26.          if(ret)
  27.                    printk("x4412-led: request gpio GPX1(7) fail\n");
  28.          s3c_gpio_setpull(EXYNOS4_GPX1(7), S3C_GPIO_PULL_UP);
  29.          gpio_direction_output(EXYNOS4_GPX1(7), 1);
  30.          ret = gpio_request(EXYNOS4_GPX2(6), "GPX2");
  31.          if(ret)
  32.                    printk("x4412-led: request gpio GPX2(6) fail\n");
  33.          s3c_gpio_setpull(EXYNOS4_GPX2(6), S3C_GPIO_PULL_UP);
  34.          gpio_direction_output(EXYNOS4_GPX2(6), 1);
  35.          ret = gpio_request(EXYNOS4_GPX2(7), "GPX2");
  36.          if(ret)
  37.                    printk("x4412-led: request gpio GPX2(7) fail\n");
  38.          s3c_gpio_setpull(EXYNOS4_GPX2(7), S3C_GPIO_PULL_UP);
  39.          gpio_direction_output(EXYNOS4_GPX2(7), 1);
  40.          __x4412_led_status[0] = 0;   //定义四盏LED灯的默认状态
  41.          __x4412_led_status[1] = 0;
  42.          __x4412_led_status[2] = 0;
  43.          __x4412_led_status[3] = 0;
  44. }
  45. static void __x4412_led_remove(void)    //释放GPIO口
  46. {
  47.          gpio_free(EXYNOS4_GPX1(6));
  48.          gpio_free(EXYNOS4_GPX1(7));
  49.          gpio_free(EXYNOS4_GPX2(6));
  50.          gpio_free(EXYNOS4_GPX2(7));
  51. }
  52. static ssize_t x4412_led_read(struct device *dev, struct device_attribute *attr, char *buf)
  53. {
  54.          if(!strcmp(attr->attr.name, "led1"))           //根据从应用读取的文件名称确定操作指定LED灯
  55.          {
  56.                    if(__x4412_led_status[0] != 0)       //返回给应用LED灯当前的状态
  57.                             return strlcpy(buf, "1\n", 3);
  58.                    else
  59.                             return strlcpy(buf, "0\n", 3);
  60.          }
  61.          else if(!strcmp(attr->attr.name, "led2"))
  62.          {
  63.                    if(__x4412_led_status[1] != 0)
  64.                             return strlcpy(buf, "1\n", 3);
  65.                    else
  66.                             return strlcpy(buf, "0\n", 3);
  67.          }
  68.          else if(!strcmp(attr->attr.name, "led3"))
  69.          {
  70.                    if(__x4412_led_status[2] != 0)
  71.                             return strlcpy(buf, "1\n", 3);
  72.                    else
  73.                             return strlcpy(buf, "0\n", 3);
  74.          }
  75.          else if(!strcmp(attr->attr.name, "led4"))
  76.          {
  77.                    if(__x4412_led_status[3] != 0)
  78.                             return strlcpy(buf, "1\n", 3);
  79.                    else
  80.                             return strlcpy(buf, "0\n", 3);
  81.          }
  82.          return strlcpy(buf, "\n", 3);
  83. }
  84. static ssize_t x4412_led_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
  85. {
  86.          unsigned long on = simple_strtoul(buf, NULL, 10);//从应用读取需要写入的数据
  87.          if(!strcmp(attr->attr.name, "led1"))
  88.          {
  89.                    if(on)                   //如果收到1,则将对应标志位置1,同时将对应GPIO置0,对应LED灯亮
  90.                    {
  91.                             gpio_direction_output(EXYNOS4_GPX1(6), 0);
  92.                             __x4412_led_status[0] = 1;
  93.                    }
  94.                    else             //如果收到0,则将对应标志位清0,同时将对应GPIO置1,对应LED灯灭
  95.                    {
  96.                             gpio_direction_output(EXYNOS4_GPX1(6), 1);
  97.                             __x4412_led_status[0] = 0;
  98.                    }
  99.          }
  100.          else if(!strcmp(attr->attr.name, "led2"))
  101.          {
  102.                    if(on)
  103.                    {
  104.                             gpio_direction_output(EXYNOS4_GPX1(7), 0);
  105.                             __x4412_led_status[1] = 1;
  106.                    }
  107.                    else
  108.                    {
  109.                             gpio_direction_output(EXYNOS4_GPX1(7), 1);
  110.                             __x4412_led_status[1] = 0;
  111.                    }
  112.          }
  113.          else if(!strcmp(attr->attr.name, "led3"))
  114.          {
  115.                    if(on)
  116.                    {
  117.                             gpio_direction_output(EXYNOS4_GPX2(6), 0);
  118.                             __x4412_led_status[2] = 1;
  119.                    }
  120.                    else
  121.                    {
  122.                             gpio_direction_output(EXYNOS4_GPX2(6), 1);
  123.                             __x4412_led_status[2] = 0;
  124.                    }
  125.          }
  126.          else if(!strcmp(attr->attr.name, "led4"))
  127.          {
  128.                    if(on)
  129.                    {
  130.                             gpio_direction_output(EXYNOS4_GPX2(7), 0);
  131.                             __x4412_led_status[3] = 1;
  132.                    }
  133.                    else
  134.                    {
  135.                             gpio_direction_output(EXYNOS4_GPX2(7), 1);
  136.                             __x4412_led_status[3] = 0;
  137.                    }
  138.          }

  139.          return count;
  140. }
  141. //kobject目录下的四个文件对应的属性,读写函数
  142. static DEVICE_ATTR(led1, 0666, x4412_led_read, x4412_led_write);
  143. static DEVICE_ATTR(led2, 0666, x4412_led_read, x4412_led_write);
  144. static DEVICE_ATTR(led3, 0666, x4412_led_read, x4412_led_write);
  145. static DEVICE_ATTR(led4, 0666, x4412_led_read, x4412_led_write);
  146. static struct attribute * x4412_led_sysfs_entries[] = {          //对应kobject目录下的四个文件
  147.          &dev_attr_led1.attr,
  148.          &dev_attr_led2.attr,
  149.          &dev_attr_led3.attr,
  150.          &dev_attr_led4.attr,
  151.          NULL,
  152. };
  153. static struct attribute_group x4412_led_attr_group = {
  154.          .name         = NULL,
  155.          .attrs  = x4412_led_sysfs_entries,   //指定注册的kobject对应的文件属性接入点
  156. };
  157. static int x4412_led_probe(struct platform_device *pdev)
  158. {
  159.          __x4412_led_probe();                                       //初始化LED对应GPIO口
  160.          return sysfs_create_group(&pdev->dev.kobj, &x4412_led_attr_group);//注册kobject
  161. }
  162. static int x4412_led_remove(struct platform_device *pdev)
  163. {
  164.          __x4412_led_remove();                                     //释放GPIO
  165.          sysfs_remove_group(&pdev->dev.kobj, &x4412_led_attr_group);//注销kobject
  166.          return 0;
  167. }
  168. static struct platform_driver x4412_led_driver = {
  169.          .probe                  = x4412_led_probe,               //平台驱动探测函数
  170.          .remove               = x4412_led_remove,
  171.          .driver                  = {
  172.                    .name         = "x4412-led",
  173.          },
  174. };
  175. static struct platform_device x4412_led_device = {
  176.          .name      = "x4412-led",
  177.          .id        = -1,
  178. };
  179. static int __devinit x4412_led_init(void)
  180. {
  181.          int ret;
  182.          printk("x4412 led driver\r\n");
  183.          ret = platform_device_register(&x4412_led_device); //注册平台设备
  184.          if(ret)
  185.                   printk("failed to register x4412 led device\n");
  186.          ret = platform_driver_register(&x4412_led_driver);            //注册平台驱动
  187.          if(ret)
  188.                    printk("failed to register x4412 led driver\n");
  189.          return ret;
  190. }
  191. static void x4412_led_exit(void)
  192. {
  193.          platform_driver_unregister(&x4412_led_driver);
  194. }
  195. module_init(x4412_led_init);
  196. module_exit(x4412_led_exit);
  197. MODULE_LICENSE("GPL");
  198. MODULE_AUTHOR("www.9tripod.com");
  199. MODULE_DESCRIPTION("x4412 led driver");
复制代码
       本驱动并不像前面两个驱动那样,只是单一的驱动模块,而是引入了平台设备和平台驱动,然后再借助于平台驱动的探测函数,直接调用sysfs_create_group函数将kobject注册进平台设备,后面生成的相关文件将会出现在/sys/devices/platform目录,而不是/sys/sys/kernel目录。而且,在平台设备下注册kobject,无需再调用kobject_init_and_add()函数或kobject_create_and_add()函数。
       将上一章节的x4412-kobject驱动通过本章节创建LED驱动的方式注册到平台设备,参考源码如下:
  1. #include <linux/module.h>
  2. #include <linux/init.h>
  3. #include <linux/string.h>
  4. #include <linux/sysfs.h>
  5. #include <linux/platform_device.h>
  6. char str[] = "www.9tripod.com";
  7. static ssize_t x4412_kobj_show(struct device *dev, struct device_attribute *attr, char *buf)
  8. {
  9.          return sprintf(buf, "%s\n", str);
  10. }
  11. static ssize_t x4412_kobj_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
  12. {
  13.          sscanf(buf, "%s", str);
  14.          return count;
  15. }

  16. static DEVICE_ATTR(xbox, S_IRWXUGO, x4412_kobj_show, x4412_kobj_store);
  17. static struct attribute * x4412_kobj_sysfs_entries[] = {
  18.          &dev_attr_xbox.attr,
  19.          NULL,
  20. };

  21. static struct attribute_group x4412_kobj_attr_group = {
  22.          .name         = NULL,
  23.          .attrs  = x4412_kobj_sysfs_entries,
  24. };

  25. static int x4412_kobj_probe(struct platform_device *pdev)
  26. {
  27.          return sysfs_create_group(&pdev->dev.kobj, &x4412_kobj_attr_group);
  28. }

  29. static int x4412_kobj_remove(struct platform_device *pdev)
  30. {
  31.          sysfs_remove_group(&pdev->dev.kobj, &x4412_kobj_attr_group);
  32.          return 0;
  33. }
  34. static struct platform_driver x4412_kobj_driver = {
  35.          .probe                  = x4412_kobj_probe,
  36.          .remove               = x4412_kobj_remove,
  37.          .driver                  = {
  38.                    .name         = "x4412-kobj",
  39.          },
  40. };

  41. static struct platform_device x4412_kobj_device = {
  42.          .name      = "x4412-kobj",
  43.          .id        = -1,
  44. };
  45. static int x4412_kobj_init(void)
  46. {
  47.          int ret;
  48.          printk("x4412 kobj driver\r\n");
  49.          ret = platform_device_register(&x4412_kobj_device);
  50.          if(ret)
  51.                    printk("failed to register x4412 kobj device\n");
  52.          ret = platform_driver_register(&x4412_kobj_driver);
  53.          if(ret)
  54.                    printk("failed to register x4412 kobj driver\n");
  55.          return ret;
  56. }
  57. static void x4412_kobj_exit(void)
  58. {
  59.          platform_driver_unregister(&x4412_kobj_driver);
  60. }
  61. module_init(x4412_kobj_init);
  62. module_exit(x4412_kobj_exit);
  63. MODULE_AUTHOR("www.9tripod.com");
  64. MODULE_LICENSE("GPL");
复制代码
       重新编译成模块后,加载驱动模块,测试结果如下:
  1. [root@x4412 mnt]# insmod x4412-kobject.ko
  2. [   44.455498] x4412 led driver
  3. [root@x4412 mnt]# cd /sys/devices/platform/x4412-kobj/
  4. [root@x4412 x4412-kobj]#
  5. [root@x4412 x4412-kobj]# ls
  6. driver@    modalias   power/     subsystem@ uevent     xbox*
  7. [root@x4412 x4412-kobj]# cat xbox
  8. www.9tripod.com
  9. [root@x4412 x4412-kobj]# echo www.bbs.9tripod.com > xbox
  10. [root@x4412 x4412-kobj]# cat xbox
  11. www.bbs.9tripod.com
  12. [root@x4412 x4412-kobj]#
复制代码
       可见,驱动创建的文件已经移步到/sys/devices/platform目录了。通常移植驱动,都会采用这种方式,而很少采用前面的方式。
       再回到LED驱动,在kernel/drivers/char/x4412/Kconfig中添加如下语句:
  1. config X4412_LED_SYSFS_DRIVER
  2.          tristate "x4412 led sysfs driver"
  3.          default y
  4.          help
  5.          compile for x4412 sysfs driver,y for kernel,m for module.
复制代码
       kernel/drivers/char/x4412/Makefile中添加如下语句:
  1. obj-$(CONFIG_X4412_LED_SYSFS_DRIVER) += x4412-led-sysfs.o
复制代码
       编译源码,更新内核后,执行如下指令,即通过sysfs文件调用驱动中的读写函数,进而控制LED对应GPIO,实现LED灯的亮灭控制。
  1. echo  0  >  sys/devices/platform/x4412-led/led1
  2. echo  1  >  sys/devices/platform/x4412-led/led1
  3. echo  0  >  sys/devices/platform/x4412-led/led2
  4. echo  1  >  sys/devices/platform/x4412-led/led2
  5. echo  0  >  sys/devices/platform/x4412-led/led3
  6. echo  1  >  sys/devices/platform/x4412-led/led3
  7. echo  0  >  sys/devices/platform/x4412-led/led4
  8. echo  1  >  sys/devices/platform/x4412-led/led4
复制代码
回复

使用道具 举报

沙发
发表于 2014-11-28 18:31:44 | 只看该作者
很好,学习了
回复 支持 反对

使用道具 举报

板凳
发表于 2014-12-12 12:15:31 | 只看该作者
非常感谢您的分享~~~~~~~~
回复 支持 反对

使用道具 举报

地板
发表于 2014-12-22 16:46:43 | 只看该作者
不错不错不错不错
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-5-3 09:10 , Processed in 0.019535 second(s), 16 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表