LED驱动

作者: Wood木木 | 来源:发表于2022-12-11 09:33 被阅读0次

    LED驱动

    添加节点

    /*
     * drivers/amlogic/led/led_sys.c
     *
     * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful, but WITHOUT
     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
     * more details.
     *
     */
    
    #define pr_fmt(fmt) "rc_led: " fmt
    
    #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/platform_device.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    #include <linux/leds.h>
    #include <linux/gpio.h>
    #include <linux/printk.h>
    #include <linux/string.h>
    #include <linux/slab.h>
    #include <linux/workqueue.h>
    #include <linux/mutex.h>
    #include <linux/delay.h>
    
    
    #define AML_DEV_NAME        "rc_led"
    #define AML_LED_NAME        "led-rc"
    
    enum {
        LED_GPIO_STATE_OFF = 0,
        LED_GPIO_STATE_ON,
    };
    
    struct aml_led_gpio {
        const char *name;
        unsigned int pin;
        unsigned int active_low;
        unsigned int state;
    };
    
    
    struct aml_rcled_dev {
        struct aml_led_gpio d;
        struct led_classdev cdev;
        enum led_brightness new_brightness;
    
        unsigned int status;
        struct work_struct work;
        struct mutex lock;
    };
    
    static struct class *rcled_class;
    struct aml_rcled_dev *rcled_dev;
    
    static void aml_rcled_output_setup(struct aml_rcled_dev *ldev,
                    enum led_brightness value)
    {
        unsigned int level = !!value;
    
        if (ldev->d.active_low)
            level = !level;
        gpio_direction_output(ldev->d.pin, level);
    }
    
    static void aml_rcled_work(struct work_struct *work)
    {
        char led_blink = 0,d_blink = 0;
        struct aml_rcled_dev *ldev;
    
        ldev = container_of(work, struct aml_rcled_dev, work);
        led_blink = !gpio_get_value(ldev->d.pin);
        //0 LED灯灭,1 LED灯常亮。2,LED灯慢闪。3,LED灯快闪。4,LED灯间闪
        switch(rcled_dev->status){
                case 0:
                    mutex_lock(&ldev->lock);
                    aml_rcled_output_setup(ldev, 0);
                    mutex_unlock(&ldev->lock);
                break;
                case 1:
                    mutex_lock(&ldev->lock);
                    aml_rcled_output_setup(ldev, 1);
                    mutex_unlock(&ldev->lock);
                break;
                // default:
                    // mutex_lock(&ldev->lock);
                    // aml_rcled_output_setup(ldev, ldev->new_brightness);
                    // mutex_unlock(&ldev->lock);
                // break;
        }
        while(rcled_dev->status > 1){  //快闪,慢闪,间闪需要无限循环。
            switch(rcled_dev->status){
                case 2:
                    mutex_lock(&ldev->lock);
                    aml_rcled_output_setup(ldev, led_blink);
                    led_blink = !led_blink;
                    mutex_unlock(&ldev->lock);
                    msleep(500);
                break;
                case 3:
                    mutex_lock(&ldev->lock);
                    aml_rcled_output_setup(ldev, led_blink);
                    led_blink = !led_blink;
                    mutex_unlock(&ldev->lock);
                    msleep(100);
                break;
                case 4:
                    mutex_lock(&ldev->lock);
                    aml_rcled_output_setup(ldev, led_blink);
                    led_blink = !led_blink;
                    mutex_unlock(&ldev->lock);
                    msleep(200);
                    d_blink++;
                    if(d_blink % 10 == 0)
                        msleep(2000);
                break;
            }
        }
    }
    
    
    static void aml_rcled_brightness_set(struct led_classdev *cdev,
        enum led_brightness value)
    {
        struct aml_rcled_dev *ldev;
        struct platform_device *pdev;
    
        pdev = to_platform_device(cdev->dev->parent);
        ldev = platform_get_drvdata(pdev);
        ldev->new_brightness = value;
        ldev->status = 1;
        schedule_work(&ldev->work);
    }
    
    
    static int aml_rcled_dt_parse(struct platform_device *pdev)
    {
        struct device_node *node;
        struct aml_rcled_dev *ldev;
        int led_gpio;
        enum of_gpio_flags flags;
    
        ldev = platform_get_drvdata(pdev);
        node = pdev->dev.of_node;
        if (!node) {
            pr_err("failed to find node for %s\n", AML_DEV_NAME);
            return -ENODEV;
        }
    
        led_gpio = of_get_named_gpio_flags(node, "led_gpio", 0, &flags);
        if (!gpio_is_valid(led_gpio)) {
            pr_err("gpio %d is not valid\n", led_gpio);
            return -EINVAL;
        }
    
        ldev->d.pin = led_gpio;
        ldev->d.active_low = flags & OF_GPIO_ACTIVE_LOW;
        gpio_request(ldev->d.pin, AML_DEV_NAME);
    //  gpio_direction_output(ldev->d.pin, 0);
    
        return 0;
    }
    
    
    
    static const struct of_device_id aml_rcled_dt_match[] = {
        {
            .compatible = "ruichi, rcled",
        },
        {},
    };
    
    static ssize_t led_status_show(struct class *class,
                                    struct class_attribute *attr, char *buf)
    {
        return sprintf(buf, "%d\n", rcled_dev->status);
    }
    static ssize_t led_status_store(struct class *class,
                                     struct class_attribute *attr, const char *buf, size_t count)
    {
        unsigned int val;
        int ret;
        ret = sscanf(buf, "%d", &val);
        if (ret == 1) { //success
            rcled_dev->status = val;
            rcled_dev->new_brightness = 1;
            schedule_work(&rcled_dev->work);
        } else {
            pr_info("invalid data\n");
                return -EINVAL;
        }
        return count;
    }
    
    void led_status_set(int status){
        rcled_dev->status = status;
        rcled_dev->new_brightness = 1;
        schedule_work(&rcled_dev->work);
    }
    EXPORT_SYMBOL(led_status_set);
    
    static struct class_attribute rcled_class_attrs[] =
    {
        __ATTR(status,   0644,
               led_status_show, led_status_store),
    };
    
    
    static int creat_rcled_class(void)
    {
        int i;
    
        rcled_class = class_create(THIS_MODULE, "rcled");
        if (IS_ERR(rcled_class))    {
            pr_info("create debug class failed\n");
            return -1;
        }
    
        for (i = 0; i < ARRAY_SIZE(rcled_class_attrs); i++)    {
            if (class_create_file(rcled_class,
                                  &rcled_class_attrs[i]))        {
                pr_info("create debug attribute %s failed\n",
                        rcled_class_attrs[i].attr.name);
            }
        }
    
        return 0;
    }
    
    static int remove_rcled_class(void)
    {
        int i;
    
        for (i = 0; i < ARRAY_SIZE(rcled_class_attrs); i++)
            class_remove_file(rcled_class, &rcled_class_attrs[i]);
    
        class_destroy(rcled_class);
        rcled_class = NULL;
    
        return 0;
    }
    
    static int aml_rcled_probe(struct platform_device *pdev)
    {
        struct aml_rcled_dev *ldev;
        int ret;
    
        ldev = kzalloc(sizeof(struct aml_rcled_dev), GFP_KERNEL);
    
        /* set driver data */
        platform_set_drvdata(pdev, ldev);
    
        /* parse dt param */
        ret = aml_rcled_dt_parse(pdev);
        if (ret)
            return ret;
    
        /* register led class device */
        ldev->cdev.name = AML_LED_NAME;
        ldev->cdev.brightness_set = aml_rcled_brightness_set;
        rcled_dev = ldev;
        mutex_init(&ldev->lock);
        INIT_WORK(&ldev->work, aml_rcled_work);
        ret = led_classdev_register(&pdev->dev, &ldev->cdev);
        if (ret < 0) {
            kfree(ldev);
            return ret;
        }
    
        /* set led default on */
        //aml_rcled_output_setup(ldev, 0);
        creat_rcled_class();
        led_status_set(2);
        pr_info("rcled: module probed ok\n");
        return 0;
    }
    
    
    static int __exit aml_rcled_remove(struct platform_device *pdev)
    {
        struct aml_rcled_dev *ldev = platform_get_drvdata(pdev);
        remove_rcled_class();
        led_classdev_unregister(&ldev->cdev);
        cancel_work_sync(&ldev->work);
        gpio_free(ldev->d.pin);
        platform_set_drvdata(pdev, NULL);
        kfree(ldev);
        pr_info("module removed ok\n");
        return 0;
    }
    
    
    static void aml_rcled_shutdown(struct platform_device *pdev)
    {
        struct aml_rcled_dev *ldev = platform_get_drvdata(pdev);
        /* set led off*/
        aml_rcled_output_setup(ldev, 0);
        pr_info("module shutdown ok\n");
    }
    
    
    #ifdef CONFIG_PM
    static int aml_rcled_suspend(struct platform_device *pdev, pm_message_t state)
    {
        struct aml_rcled_dev *ldev = platform_get_drvdata(pdev);
        /* set led off */
        aml_rcled_output_setup(ldev, 0);
        pr_info("module suspend ok\n");
        return 0;
    }
    
    static int aml_rcled_resume(struct platform_device *pdev)
    {
        struct aml_rcled_dev *ldev = platform_get_drvdata(pdev);
        /* set led on */
        aml_rcled_output_setup(ldev, 1);
        pr_info("module resume ok\n");
        return 0;
    }
    #endif
    
    
    static struct platform_driver aml_rcled_driver = {
        .driver = {
            .name = AML_DEV_NAME,
            .owner = THIS_MODULE,
            .of_match_table = aml_rcled_dt_match,
        },
        .probe = aml_rcled_probe,
        .remove = __exit_p(aml_rcled_remove),
        .shutdown = aml_rcled_shutdown,
    #ifdef  CONFIG_PM
        .suspend = aml_rcled_suspend,
        .resume = aml_rcled_resume,
    #endif
    };
    
    
    static int __init aml_rcled_init(void)
    {
        if (platform_driver_register(&aml_rcled_driver)) {
            pr_err("failed to register driver\n");
            return -ENODEV;
        }
    
        return 0;
    }
    
    
    static void __exit aml_rcled_exit(void)
    {
        pr_info("module exit\n");
        platform_driver_unregister(&aml_rcled_driver);
    }
    
    
    subsys_initcall(aml_rcled_init);
    module_exit(aml_rcled_exit);
    
    MODULE_DESCRIPTION("ruichi led driver");
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("rubin  zhanghb@ruichi-group.com");
    
    

    相关文章

      网友评论

          本文标题:LED驱动

          本文链接:https://www.haomeiwen.com/subject/kdkfqdtx.html