zoukankan      html  css  js  c++  java
  • 驱动程序实例(二):LED设备驱动程序( platform + /sys接口)

    结合之前对Linux内核的platform总线 ,以及对Linux内核的LED设备的驱动框架的分析,本文将编写基于platform总线与/sys接口的LED设备的实例代码并对其进行分析。

    platform总线分析,详见Linux platform驱动模型

    字符设备的cdev接口分析,详见Linux字符设备驱动框架(一):Linux内核的LED设备驱动框架

    硬件接口:

      CPU:s5pv210;

      LED的GPIO:GPIO_J0_3 ~ GPIO_J0_5;

      LED的工作方式:低电平亮,高电平灭。

    使用Linux内核的LED驱动框架之前,需确保Linux内核支持LED驱动框架。进入Linux内核的配置界面menuconfig进行设置,具体配置如下:

      Device Drivers  --->

        [*] LED Support  --->    

    本文将以两种形式编写LED驱动代码:

    (1)将led_device和led_driver分别写成两个单独的文件模块;(这种做法适合学习使用)

    (2)将led_device集成至Linux内核的/arch/arm/mach-s5pv210/mach-x210.c中,再将led_driver编写为独立模块。(这种做法更接近于实战的驱动程序,更好的使用设备树)

    1.第一种写法

    1.1 led_device.c

    本文将设备信息写成一个模块的形式,需要时加载该模块即可。

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/ioport.h>
    #include <linux/platform_device.h>
    
    //定义并初始化LED设备的相关资源
    static struct resource led_resource = 
    {
        .start = 0xE0200240,
        .end = 0xE0200240 + 8 - 1,
        .flags = IORESOURCE_MEM,
    };
    
    //定义并初始化LED设备信息
    static struct platform_device led_dev = 
    {
        .name = "led",             //设备名称
        .id = -1,                  //设备数量,-1表示只有一个设备
        .num_resources = 1,        //资源数量
        .resource = &led_resource, //资源指针
        .dev = 
        {
            .release = led_release,
        },
    };
    
    //注册LED设备
    static int __init led_device_init(void)
    {
        return platform_device_register(&led_dev);
    }
    
    //注销LED设备
    static void __exit led_device_exit(void)
    {
        platform_device_unregister(&led_dev);
    }
    
    module_init(led_device_init);
    module_exit(led_device_exit);
    
    MODULE_AUTHOR("Lin");
    MODULE_DESCRIPTION("led device for x210");
    MODULE_LICENSE("GPL");

    1.2 led_driver.c

    led_driver_init():模块加载函数
      platform_driver_register()将驱动对象模块注册到平台总线
      led_probe()探测函数,提取相应的信息
        platform_get_resource()获取设备资源
        request_mem_region()、ioremap()虚拟内存映射
        readl()、write()初始化硬件设备
        kzalloc()申请led_classdev内存
        led_classdev_register()注册LED设备

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/ioport.h>
    #include <linux/platform_device.h>
    #include <linux/slab.h>
    #include <asm/io.h>
    #include <linux/leds.h>
    
    //定义GPIO_J0寄存器变量
    typedef struct GPJ0REG
    {
        volatile unsigned int gpj0con;
        volatile unsigned int gpj0dat;
    }gpj0_reg_t;
    
    static gpj0_reg_t *pGPIOREG = NULL;
    static struct led_classdev *led_cdev = NULL;
    
    //LED设备实际的硬件操作函数
    void led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness)
    {
        int reg_value = 0;
        
        //向LED设备属性文件brightness写入0,LED设备灭
        if (brightness == LED_OFF)
        {
            reg_value = readl(&(pGPIOREG->gpj0dat)); 
            reg_value |= (1 << 3) | (1 << 4) | (1 << 5);
            writel(reg_value, &(pGPIOREG->gpj0dat));
        }
        //否则,LED设备亮
        else
        {
            reg_value = readl(&(pGPIOREG->gpj0dat)); 
            reg_value &= ~((1 << 3) | (1 << 4) | (1 << 5));
            writel(reg_value, &(pGPIOREG->gpj0dat));
        }    
    }
    
    static int led_probe(struct platform_device *pdev)
    {
        struct resource *res_led = NULL;
        int ret = -1;
        int reg_value = 0;
        
        /********************************申请资源*********************************/
        //获取资源
        res_led = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res_led) 
        {
            return -ENOMEM;
        }
        
        //动态内存映射
        if (!request_mem_region(res_led->start, resource_size(res_led), "GPIOJ0"))
        {
            return -EBUSY;
        }
        
        pGPIOREG = ioremap(res_led->start, resource_size(res_led));
        if (pGPIOREG ==  NULL) 
        {
            ret = -ENOENT;
            goto ERR_STEP;
        }
        
        /********************************设备初始化********************************/
        //初始化资源,设置GPIO为输出模式
        reg_value = readl(&(pGPIOREG->gpj0dat)); 
        reg_value |= (1 << (3*4)) | (1 << (4*4)) | (1 << (5*4));
        writel(reg_value, &(pGPIOREG->gpj0dat));
        
        /********************************创建接口**********************************/
        //申请led_classdev内存
        led_cdev = kzalloc(sizeof(struct led_classdev), GFP_KERNEL);
        if (led_cdev == NULL)
        {
            ret = -ENOMEM;
            goto ERR_STEP1;
        }
        
        led_cdev->name = pdev->name;
        led_cdev->brightness_set = led_brightness_set;
        
        //注册LED设备
        ret = led_classdev_register(NULL, led_cdev);
        if (ret) 
        {
            goto ERR_STEP2;
        }
        
        return 0;
        
        /******************************倒映式错误处理********************************/
    ERR_STEP2:
        kfree(led_cdev);
        
    ERR_STEP1:
        iounmap(pGPIOREG);    
        
    ERR_STEP:
        release_mem_region(res_led->start, resource_size(res_led));
                
        return ret;
            
    }
        
    static int led_remove(struct platform_device *pdev)
    {
        led_classdev_unregister(led_cdev); //注销LED设备
        iounmap(pGPIOREG);                   //释放内存
        kfree(led_cdev);                   //释放内存
        
        return 0;
    }
    
    //定义并初始化LED驱动信息
    static struct platform_driver led_drv = 
    {
        .driver = 
        {
            .name  = "led",
            .owner = THIS_MODULE,
        },
        .probe = led_probe,
        .remove = led_remove,
    };
    
    static int __init led_driver_init(void)
    {
        return platform_driver_register(&led_drv);
    }
    
    static void __exit led_driver_exit(void)
    {
        platform_driver_unregister(&led_drv);
    }
    
    module_init(led_driver_init);
    module_exit(led_driver_exit);
    
    MODULE_AUTHOR("Lin");
    MODULE_DESCRIPTION("led driver for x210");
    MODULE_LICENSE("GPL");

    2.第二种写法

    2.1 device

    在/kernel/arch/arm/mach-s5pv210/include/mach目录下,建立一个leds_gpio.h文件,并填充如下内容。

    #ifndef __ASM_ARCH_LEDSGPIO_H
    #define __ASM_ARCH_LEDSGPIO_H "leds-gpio.h"
    
    #define S3C24XX_LEDF_ACTLOW    (1<<0)        /* LED is on when GPIO low */
    #define S3C24XX_LEDF_TRISTATE    (1<<1)      /* tristate to turn off */
    
    //定义一个LED设备的数据结构
    struct s3c24xx_led_platdata 
    {
        unsigned int     gpio;
        unsigned int     flags;
        char            *name;
        char            *def_trigger;
    };
    #endif

    在/kernel/arch/arm/mach-s5pv210/mach-x210.c下,添加如下内容,并添加对leds_gpio.h的包含。

    /* LEDS */
    
    static struct s5pv210_led_platdata s5pv210_led1_pdata = {
        .name        = "xled1",
        .gpio        = S5PV210_GPJ0(3),
        .flags       = S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,
        .def_trigger = "heartbeat",
    };
    
    static struct s5pv210_led_platdata s5pv210_led2_pdata = {
        .name        = "xled2",
        .gpio        = S5PV210_GPJ0(4),
        .flags       = S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,
        .def_trigger = "heartbeat",
    };
    
    static struct s5pv210_led_platdata s5pv210_led3_pdata = {
        .name        = "xled3",
        .gpio        = S5PV210_GPJ0(5),
        .flags       = S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,
        .def_trigger = "heartbeat",
    };
    
    static struct platform_device s5pv210_led1 = {
        .name      = "s5pv210_led",
        .id        = 1,
        .dev       = {
            .platform_data    = &s5pv210_led1_pdata,
        },
    };
    
    static struct platform_device s5pv210_led2 = {
        .name      = "s5pv210_led",
        .id        = 2,
        .dev       = {
            .platform_data    = &s5pv210_led2_pdata,
        },
    };
    
    static struct platform_device s5pv210_led3 = {
        .name      = "s5pv210_led",
        .id        = 3,
        .dev       = {
            .platform_data   = &s5pv210_led3_pdata,
        },
    };

    将LED设备信息集成至smdkc110_devices,内核初始化时smdkc110_devices中的设备将被注册进内核。

    static struct platform_device *smdkc110_devices[] __initdata = 
    {
        ......
        &s5pv210_led1,
        &s5pv210_led2,
        &s5pv210_led3,
    }; 

    2.2 driver

    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/platform_device.h>
    #include <linux/leds.h>
    #include <linux/gpio.h>
    #include <linux/slab.h>
    
    #include <mach/hardware.h>
    #include <mach/regs-gpio.h>
    #include <mach/leds-gpio.h>
    
    #define X210_LED_OFF    1            // X210中LED是正极接电源,负极节GPIO
    #define X210_LED_ON     0            // 所以1是灭,0是亮
    
    struct s5pv210_gpio_led 
    {
        struct led_classdev          cdev;
        struct s5pv210_led_platdata  *pdata; 
    };
    
    static inline struct s5pv210_gpio_led *pdev_to_gpio(struct platform_device *dev)
    {
        return platform_get_drvdata(dev);
    }
    
    static inline struct s5pv210_gpio_led *to_gpio(struct led_classdev *led_cdev)
    {
        return container_of(led_cdev, struct s5pv210_gpio_led, cdev);
    }
    
    //LED设备硬件操作函数
    static void s5pv210_led_set(struct led_classdev *led_cdev, enum led_brightness value)
    {
        struct s5pv210_gpio_led *led = to_gpio(led_cdev);
        struct s5pv210_led_platdata *pd = led->pdata;
    
        if (value == LED_OFF)
        {
            gpio_set_value(pd->gpio, X210_LED_OFF);
        }
        else
        {
            gpio_set_value(pd->gpio, X210_LED_ON);
        }
    
    }
    
    
    static int s5pv210_led_remove(struct platform_device *dev)
    {
        struct s5pv210_gpio_led *led = pdev_to_gpio(dev);
    
        led_classdev_unregister(&led->cdev);//注销LED设备
        kfree(led);                         //释放内存
        gpio_free(led->pdata->gpio);        //释放GPIO
    
        return 0;
    }
    
    static int s5pv210_led_probe(struct platform_device *dev)
    {
        struct s5pv210_led_platdata *pdata = dev->dev.platform_data;
        struct s5pv210_gpio_led *led;
        int ret;
        
        /*****************************申请GPIO资源******************************/
        ret = gpio_request(pdata->gpio, pdata->name);
        if (ret) 
        {
            printk(KERN_ERR "gpio_request failed, ret = %d.
    ", ret);
            return -EIO;
        } 
        
        /************************申请,并初始化GPIO资源*************************/
        // 设置为输出模式,并且默认输出1让LED灯灭
        gpio_direction_output(pdata->gpio, 1);
    
        
        /********************************创建接口********************************/
        led = kzalloc(sizeof(struct s5pv210_gpio_led), GFP_KERNEL);
        if (led == NULL) {
            dev_err(&dev->dev, "No memory for device
    ");
            return -ENOMEM;
        }
    
        platform_set_drvdata(dev, led);//将led存入dev->p->driver_data
    
        led->cdev.brightness_set = s5pv210_led_set;
        led->cdev.name = pdata->name;
        led->pdata = pdata;
    
        //注册LED设备
        ret = led_classdev_register(&dev->dev, &led->cdev);
        if (ret < 0) {
            dev_err(&dev->dev, "led_classdev_register failed
    ");
            kfree(led);
            return ret;
        }
    
        return 0;
    }
    
    //定义并初始化驱动信息
    static struct platform_driver s5pv210_led_driver = {
        .probe        = s5pv210_led_probe,
        .remove       = s5pv210_led_remove,
        .driver       = 
      { .name
    = "s5pv210_led", .owner = THIS_MODULE, }, }; //注册驱动 static int __init s5pv210_led_init(void) { return platform_driver_register(&s5pv210_led_driver); } //注销驱动 static void __exit s5pv210_led_exit(void) { platform_driver_unregister(&s5pv210_led_driver); } module_init(s5pv210_led_init); module_exit(s5pv210_led_exit); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_DESCRIPTION("S5PV210 LED driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:s5pv210_led");

    3. 测试

    装载上述两个模块(device、driver)之后,进入/sys/class/leds目录下,会发现多了一个led文件夹。进入led文件夹,会看到LED设备的属性文件。

    (1)向属性文件brightness写入0,"  echo  0  >  brightness ",LED设备灭;

    (2)向属性文件brightness写入1,"  echo  1  >  brightness ",LED设备亮;

    (3)读取属性文件max_brightness," cat  max_brightness ",获取LED设备的最大亮度值。

  • 相关阅读:
    JTAG的SWD接线方式
    Qt のEXecl
    人脸识别
    Qt实现基本QMainWindow主窗口程序
    Qt学习之路MainWindow学习过程中的知识点
    QT_FORWARD_DECLARE_CLASS
    标准的并发控制实现
    C++ DFS
    C# 互操作(一) 编写一个C++ COM组件
    Socket使用SOAP调用WCF
  • 原文地址:https://www.cnblogs.com/linfeng-learning/p/9322515.html
Copyright © 2011-2022 走看看