查看: 2366|回复: 1

[i.MX6ULL竞赛专区] 【我的项目666--智能插排】--2.Leds驱动分析(补充example分析)

[复制链接]
  • TA的每日心情
    开心
    昨天 10:37
  • 签到天数: 815 天

    [LV.10]以坛为家III

    71

    主题

    2438

    帖子

    24

    金牌会员

    Rank: 6Rank: 6

    积分
    5495
    最后登录
    2024-4-19
    发表于 2018-7-6 12:06:08 | 显示全部楼层 |阅读模式
    本帖最后由 leo121_3006061 于 2018-7-6 12:10 编辑

    上一篇帖子有个分析的led的example的帖子,不是十分全面,只是从devicetree的角度,当时还不是十分理解,经过一段时间的学习,终于明白,还需要有个led的driver配合才能在/sys/class/目录下出现leds,简要的说就是先在devicetree中分配好资源,然后再从为之写一个驱动程序,这时候就会出现在/sys/class/leds。当然驱动程序不是必须要写的,example中gpio_keys操作就没有写驱动,当然在/sys/class/目录下也见不到。下边我们通过三种不同的方式来理解一下gpio的驱动。

    1. 我们先打开目录,如下图所示,并且找到led_gpio.c(目录下还有pwm等其他的驱动)

    leds_118.png

    led的驱动源代码如下
    1. /*
    2. * LEDs driver for GPIOs
    3. *
    4. * Copyright (C) 2007 8D Technologies inc.
    5. * Raphael Assenat <raph@8d.com>
    6. * Copyright (C) 2008 Freescale Semiconductor, Inc.
    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. #include <linux/err.h>
    14. #include <linux/gpio.h>
    15. #include <linux/gpio/consumer.h>
    16. #include <linux/kernel.h>
    17. #include <linux/leds.h>
    18. #include <linux/module.h>
    19. #include <linux/platform_device.h>
    20. #include <linux/property.h>
    21. #include <linux/slab.h>
    22. #include <linux/workqueue.h>

    23. struct gpio_led_data {
    24.         struct led_classdev cdev;
    25.         struct gpio_desc *gpiod;
    26.         struct work_struct work;
    27.         u8 new_level;
    28.         u8 can_sleep;
    29.         u8 blinking;
    30.         int (*platform_gpio_blink_set)(struct gpio_desc *desc, int state,
    31.                         unsigned long *delay_on, unsigned long *delay_off);
    32. };

    33. static void gpio_led_work(struct work_struct *work)
    34. {
    35.         struct gpio_led_data *led_dat =
    36.                 container_of(work, struct gpio_led_data, work);

    37.         if (led_dat->blinking) {
    38.                 led_dat->platform_gpio_blink_set(led_dat->gpiod,
    39.                                         led_dat->new_level, NULL, NULL);
    40.                 led_dat->blinking = 0;
    41.         } else
    42.                 gpiod_set_value_cansleep(led_dat->gpiod, led_dat->new_level);
    43. }

    44. static void gpio_led_set(struct led_classdev *led_cdev,
    45.         enum led_brightness value)
    46. {
    47.         struct gpio_led_data *led_dat =
    48.                 container_of(led_cdev, struct gpio_led_data, cdev);
    49.         int level;

    50.         if (value == LED_OFF)
    51.                 level = 0;
    52.         else
    53.                 level = 1;

    54.         /* Setting GPIOs with I2C/etc requires a task context, and we don't
    55.          * seem to have a reliable way to know if we're already in one; so
    56.          * let's just assume the worst.
    57.          */
    58.         if (led_dat->can_sleep) {
    59.                 led_dat->new_level = level;
    60.                 schedule_work(&led_dat->work);
    61.         } else {
    62.                 if (led_dat->blinking) {
    63.                         led_dat->platform_gpio_blink_set(led_dat->gpiod, level,
    64.                                                          NULL, NULL);
    65.                         led_dat->blinking = 0;
    66.                 } else
    67.                         gpiod_set_value(led_dat->gpiod, level);
    68.         }
    69. }

    70. static int gpio_blink_set(struct led_classdev *led_cdev,
    71.         unsigned long *delay_on, unsigned long *delay_off)
    72. {
    73.         struct gpio_led_data *led_dat =
    74.                 container_of(led_cdev, struct gpio_led_data, cdev);

    75.         led_dat->blinking = 1;
    76.         return led_dat->platform_gpio_blink_set(led_dat->gpiod, GPIO_LED_BLINK,
    77.                                                 delay_on, delay_off);
    78. }

    79. static int create_gpio_led(const struct gpio_led *template,
    80.         struct gpio_led_data *led_dat, struct device *parent,
    81.         int (*blink_set)(struct gpio_desc *, int, unsigned long *,
    82.                          unsigned long *))
    83. {
    84.         int ret, state;

    85.         led_dat->gpiod = template->gpiod;
    86.         if (!led_dat->gpiod) {
    87.                 /*
    88.                  * This is the legacy code path for platform code that
    89.                  * still uses GPIO numbers. Ultimately we would like to get
    90.                  * rid of this block completely.
    91.                  */
    92.                 unsigned long flags = 0;

    93.                 /* skip leds that aren't available */
    94.                 if (!gpio_is_valid(template->gpio)) {
    95.                         dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n",
    96.                                         template->gpio, template->name);
    97.                         return 0;
    98.                 }

    99.                 if (template->active_low)
    100.                         flags |= GPIOF_ACTIVE_LOW;

    101.                 ret = devm_gpio_request_one(parent, template->gpio, flags,
    102.                                             template->name);
    103.                 if (ret < 0)
    104.                         return ret;

    105.                 led_dat->gpiod = gpio_to_desc(template->gpio);
    106.                 if (IS_ERR(led_dat->gpiod))
    107.                         return PTR_ERR(led_dat->gpiod);
    108.         }

    109.         led_dat->cdev.name = template->name;
    110.         led_dat->cdev.default_trigger = template->default_trigger;
    111.         led_dat->can_sleep = gpiod_cansleep(led_dat->gpiod);
    112.         led_dat->blinking = 0;
    113.         if (blink_set) {
    114.                 led_dat->platform_gpio_blink_set = blink_set;
    115.                 led_dat->cdev.blink_set = gpio_blink_set;
    116.         }
    117.         led_dat->cdev.brightness_set = gpio_led_set;
    118.         if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
    119.                 state = !!gpiod_get_value_cansleep(led_dat->gpiod);
    120.         else
    121.                 state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
    122.         led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
    123.         if (!template->retain_state_suspended)
    124.                 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;

    125.         ret = gpiod_direction_output(led_dat->gpiod, state);
    126.         if (ret < 0)
    127.                 return ret;

    128.         INIT_WORK(&led_dat->work, gpio_led_work);

    129.         return led_classdev_register(parent, &led_dat->cdev);
    130. }

    131. static void delete_gpio_led(struct gpio_led_data *led)
    132. {
    133.         led_classdev_unregister(&led->cdev);
    134.         cancel_work_sync(&led->work);
    135. }

    136. struct gpio_leds_priv {
    137.         int num_leds;
    138.         struct gpio_led_data leds[];
    139. };

    140. static inline int sizeof_gpio_leds_priv(int num_leds)
    141. {
    142.         return sizeof(struct gpio_leds_priv) +
    143.                 (sizeof(struct gpio_led_data) * num_leds);
    144. }

    145. static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
    146. {
    147.         struct device *dev = &pdev->dev;
    148.         struct fwnode_handle *child;
    149.         struct gpio_leds_priv *priv;
    150.         int count, ret;
    151.         struct device_node *np;

    152.         count = device_get_child_node_count(dev);
    153.         if (!count)
    154.                 return ERR_PTR(-ENODEV);

    155.         priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), GFP_KERNEL);
    156.         if (!priv)
    157.                 return ERR_PTR(-ENOMEM);

    158.         device_for_each_child_node(dev, child) {
    159.                 struct gpio_led led = {};
    160.                 const char *state = NULL;

    161.                 led.gpiod = devm_get_gpiod_from_child(dev, NULL, child);
    162.                 if (IS_ERR(led.gpiod)) {
    163.                         fwnode_handle_put(child);
    164.                         ret = PTR_ERR(led.gpiod);
    165.                         goto err;
    166.                 }

    167.                 np = of_node(child);

    168.                 if (fwnode_property_present(child, "label")) {
    169.                         fwnode_property_read_string(child, "label", &led.name);
    170.                 } else {
    171.                         if (IS_ENABLED(CONFIG_OF) && !led.name && np)
    172.                                 led.name = np->name;
    173.                         if (!led.name)
    174.                                 return ERR_PTR(-EINVAL);
    175.                 }
    176.                 fwnode_property_read_string(child, "linux,default-trigger",
    177.                                             &led.default_trigger);

    178.                 if (!fwnode_property_read_string(child, "default-state",
    179.                                                  &state)) {
    180.                         if (!strcmp(state, "keep"))
    181.                                 led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
    182.                         else if (!strcmp(state, "on"))
    183.                                 led.default_state = LEDS_GPIO_DEFSTATE_ON;
    184.                         else
    185.                                 led.default_state = LEDS_GPIO_DEFSTATE_OFF;
    186.                 }

    187.                 if (fwnode_property_present(child, "retain-state-suspended"))
    188.                         led.retain_state_suspended = 1;

    189.                 ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
    190.                                       dev, NULL);
    191.                 if (ret < 0) {
    192.                         fwnode_handle_put(child);
    193.                         goto err;
    194.                 }
    195.         }

    196.         return priv;

    197. err:
    198.         for (count = priv->num_leds - 2; count >= 0; count--)
    199.                 delete_gpio_led(&priv->leds[count]);
    200.         return ERR_PTR(ret);
    201. }

    202. static const struct of_device_id of_gpio_leds_match[] = {
    203.         { .compatible = "gpio-leds", },
    204.         {},
    205. };

    206. MODULE_DEVICE_TABLE(of, of_gpio_leds_match);

    207. static int gpio_led_probe(struct platform_device *pdev)
    208. {
    209.         struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
    210.         struct gpio_leds_priv *priv;
    211.         int i, ret = 0;

    212.         if (pdata && pdata->num_leds) {
    213.                 priv = devm_kzalloc(&pdev->dev,
    214.                                 sizeof_gpio_leds_priv(pdata->num_leds),
    215.                                         GFP_KERNEL);
    216.                 if (!priv)
    217.                         return -ENOMEM;

    218.                 priv->num_leds = pdata->num_leds;
    219.                 for (i = 0; i < priv->num_leds; i++) {
    220.                         ret = create_gpio_led(&pdata->leds[i],
    221.                                               &priv->leds[i],
    222.                                               &pdev->dev, pdata->gpio_blink_set);
    223.                         if (ret < 0) {
    224.                                 /* On failure: unwind the led creations */
    225.                                 for (i = i - 1; i >= 0; i--)
    226.                                         delete_gpio_led(&priv->leds[i]);
    227.                                 return ret;
    228.                         }
    229.                 }
    230.         } else {
    231.                 priv = gpio_leds_create(pdev);
    232.                 if (IS_ERR(priv))
    233.                         return PTR_ERR(priv);
    234.         }

    235.         platform_set_drvdata(pdev, priv);

    236.         return 0;
    237. }

    238. static int gpio_led_remove(struct platform_device *pdev)
    239. {
    240.         struct gpio_leds_priv *priv = platform_get_drvdata(pdev);
    241.         int i;

    242.         for (i = 0; i < priv->num_leds; i++)
    243.                 delete_gpio_led(&priv->leds[i]);

    244.         return 0;
    245. }

    246. static struct platform_driver gpio_led_driver = {
    247.         .probe                = gpio_led_probe,
    248.         .remove                = gpio_led_remove,
    249.         .driver                = {
    250.                 .name        = "leds-gpio",
    251.                 .of_match_table = of_gpio_leds_match,
    252.         },
    253. };

    254. module_platform_driver(gpio_led_driver);

    255. MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
    256. MODULE_DESCRIPTION("GPIO LED driver");
    257. MODULE_LICENSE("GPL");
    258. MODULE_ALIAS("platform:leds-gpio");
    复制代码
    从上边的源程序里我们找到一些相关的信息,brightness等,这就明白了,/sys/class/leds/目录下和/sys/firmware/devicetree/base/leds目录下的内容为何不同了,说的明白一点就是这两个目录是分别由drivers和devicetree生成的。从而也就解开了心中的谜团。
    2. 第一步的这种写法虽然比较规范,但有些过于复杂,适合lcd之类的驱动,其实简单的驱动例如gpio_key并不需要专门再写个驱动的,这样就比较灵活了,也可以简单的使用echo命令直接驱动gpio
    echo 9 > /sys/class/gpio/export
    echo out > /sys/class/gpio/gpio9/direction
    echo 1 > /sys/class/gpio/gpio9/value    ---> 写高电平
    echo 0 > /sys/class/gpio/gpio9/value    ---> 写低电平

    通过这种方式直接可以简单的操控gpio,非常的灵活。

    3. 也可以通过程序来直接读取和操作gpio,不过者和第2步一样,都是必须在编译内核的时候要加入gpio support才行,也就是说在/sys/class/目录下有个gpio目录才行。我从网上找了一个例程供参考,如下
    1. #include stdlib.h
    2. #include stdio.h
    3. #include string.h
    4. #include unistd.h
    5. #include fcntl.h  //define O_WRONLY and O_RDONLY

    6. //芯片复位引脚: P1_16
    7. #define SYSFS_GPIO_EXPORT          "/sys/class/gpio/export"
    8. #define SYSFS_GPIO_RST_PIN_VAL      "48"
    9. #define SYSFS_GPIO_RST_DIR          "/sys/class/gpio/gpio48/direction"
    10. #define SYSFS_GPIO_RST_DIR_VAL      "OUT"
    11. #define SYSFS_GPIO_RST_VAL          "/sys/class/gpio/gpio48/value"
    12. #define SYSFS_GPIO_RST_VAL_H        "1"
    13. #define SYSFS_GPIO_RST_VAL_L        "0"

    14. int main()
    15. {
    16.     int fd;
    17.     //打开端口/sys/class/gpio# echo 48 > export
    18.     fd = open(SYSFS_GPIO_EXPORT, O_WRONLY);
    19.     if(fd == -1)
    20.     {
    21.         printf("ERR: Radio hard reset pin open error.\n");
    22.         return EXIT_FAILURE;
    23.     }

    24.     write(fd, SYSFS_GPIO_RST_PIN_VAL ,sizeof(SYSFS_GPIO_RST_PIN_VAL));

    25.     close(fd);
    26.     //设置端口方向/sys/class/gpio/gpio48# echo out > direction
    27.     fd = open(SYSFS_GPIO_RST_DIR, O_WRONLY);

    28.     if(fd == -1)
    29.     {
    30.         printf("ERR: Radio hard reset pin direction open error.\n");
    31.         return EXIT_FAILURE;
    32.     }
    33.     write(fd, SYSFS_GPIO_RST_DIR_VAL, sizeof(SYSFS_GPIO_RST_DIR_VAL));
    34.     close(fd);

    35.     //输出复位信号: 拉高>100ns
    36.     fd = open(SYSFS_GPIO_RST_VAL, O_RDWR);
    37.     if(fd == -1)

    38.     {
    39. printf("ERR: Radio hard reset pin value open error.\n");
    40. return EXIT_FAILURE;
    41.     }     

    42.     while(1)
    43.     {

    44.         write(fd, SYSFS_GPIO_RST_VAL_H, sizeof(SYSFS_GPIO_RST_VAL_H));

    45.         usleep(1000000);

    46.         write(fd, SYSFS_GPIO_RST_VAL_L, sizeof(SYSFS_GPIO_RST_VAL_L));

    47.         usleep(1000000);
    48.     }
    49.     close(fd);
    50.     printf("INFO: Radio hard reset pin value open error.\n");

    51.     return 0;
    52. }
    复制代码
    【综述】通过上边gpio灵活多样的操作,并不局限于某种方式,在我没有理解devicetree和driver区别之前还是有些懵的,至少在我写完这篇之后,感觉又进一层了解了嵌入式开发的原理。
    该会员没有填写今日想说内容.
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2018-10-17 09:43
  • 签到天数: 47 天

    [LV.5]常住居民I

    21

    主题

    135

    帖子

    0

    中级会员

    Rank: 3Rank: 3

    积分
    487
    最后登录
    2022-10-24
    发表于 2018-7-6 13:44:22 | 显示全部楼层
    很好,学习了,谢谢楼主
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-20 03:11 , Processed in 0.112496 second(s), 20 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

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