zoukankan      html  css  js  c++  java
  • 驱动程序实例(一):LED设备驱动程序( platform + cdev)

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

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

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

    硬件接口:

      CPU:s5pv210;

      LED的GPIO:GPIO_J0_3 ~ GPIO_J0_5;

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

    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");

    2. led_driver.c

    led_driver_init():模块加载函数
      platform_driver_register()将驱动对象模块注册到平台总线
      led_probe()探测函数,提取相应的信息
        platform_get_resource()获取设备资源
        request_mem_region()、ioremap()虚拟内存映射
        readl()、write()初始化硬件设备
        cdev_alloc()申请cdev内存
        cdev_init()初始化cdev对象,将cdev与fops结构体绑定
        alloc_chrdev_region()申请设备号
        cdev_add()注册LED设备对象,将cdev添加至系统字符设备链表中
        class_create()创建设备类
        device_create()创建设备文件

    #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>
    #include <asm/uaccess.h>
    #include <linux/cdev.h>
    #include <linux/kdev_t.h>
    #include <linux/ioctl.h>
    
    #define LED_IOC_MAGIC  'l'                  //ioctl幻数  
    #define LED_IOC_MAXNR    2                  //ioctl最大命令序数
    #define    LED_ON    _IO(LED_IOC_MAGIC, 0)   //ioctl自定义命令
    #define    LED_OFF    _IO(LED_IOC_MAGIC, 1)
    
    #define DEVNAME "led"        //设备名称
    
    static int led_major = 0;    //主设备号
    static int led_minor = 0;    //次设备号
    const  int led_count = 1;    //次设备数量
    
    //GPIO寄存器变量定义
    typedef struct GPJ0REG
    {
        volatile unsigned int gpj0con;
        volatile unsigned int gpj0dat;
    }gpj0_reg_t;
    
    static gpj0_reg_t *pGPIOREG = NULL;
    
    static dev_t led_devnum;                 //设备号
    static struct cdev *led_cdev = NULL;     //设备对象
    
    static struct class *led_cls = NULL;     //设备类
    static struct device *led_dev = NULL;    //设备
    
    
    //LED设备的ioctl函数实现
    static int led_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
    {
        int reg_value = 0;
        
        
        //检测命令的有效性
        if (_IOC_TYPE(cmd) != LED_IOC_MAGIC) 
            return -EINVAL;
        if (_IOC_NR(cmd) > LED_IOC_MAXNR) 
            return -EINVAL;
        
        
        //根据命令,执行相应的硬件操作
        switch(cmd) 
        {
            case LED_ON:
                    reg_value = readl(&pGPIOREG->gpj0dat); 
                    reg_value &= ~((1 << 3) | (1 << 4) | (1 << 5));
                    writel(&pGPIOREG->gpj0dat, reg_value);
            break;
            
            case LED_OFF:
                    reg_value = readl(&pGPIOREG->gpj0dat); 
                    reg_value |= (1 << 3) | (1 << 4) | (1 << 5);
                    writel(&pGPIOREG->gpj0dat, reg_value);
            break;
            
            default:  
                return -EINVAL;
        }
        
        return 0;
    }
    
    static int led_open(struct inode *inode, struct file *filp)
    {
        return 0;
    }
    
    static int led_release(struct inode *inode, struct file *filp)
    {
        return 0;
    }
    
    //LED设备的write函数实现
    static ssize_t led_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
    {
        char kbuf[20] = {0};
        int reg_value = 0;
        
        memset(kbuf, 0, sizeof(kbuf));
        if (copy_from_user(kbuf, user_buf, count))
        {
            return -EFAULT;
        }
        
        if (kbuf[0] == '0')
        {
            reg_value = readl(&(pGPIOREG->gpj0dat)); 
            reg_value |= (1 << 3) | (1 << 4) | (1 << 5);
            writel(reg_value, &(pGPIOREG->gpj0dat));
        }
        else
        {
            reg_value = readl(&(pGPIOREG->gpj0dat));  
            reg_value &= ~((1 << 3) | (1 << 4) | (1 << 5));
            writel(reg_value, &(pGPIOREG->gpj0dat));
        }
        
        return 1;
    }
    
    //定义并初始化LED设备的操作集
    static const struct file_operations led_ops = 
    {
        .owner      = THIS_MODULE,
        .open       = led_open,
        .write      = led_write,
        .ioctl      = led_ioctl,
        .release    = led_release,
    };
    
    
    //LED设备的probe函数实现
    static int led_probe(struct platform_device *pdev)
    {
        struct resource *res_led = NULL;
        int ret = -1;
        int reg_value = 0;
        int i = 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->gpj0con)); 
        reg_value |= (1 << (3*4)) | (1 << (4*4)) | (1 << (5*4));
        writel(reg_value, &(pGPIOREG->gpj0con));
        
        /***************************创建接口(cdev)***************************/
        //申请cdev内存
        led_cdev = cdev_alloc();
        if (led_cdev == NULL)
        {
            ret = -ENOMEM;
            goto ERR_STEP1;
        }
        
        //初始化led_cdev(将led_cdev于led_ops关联)
        cdev_init(led_cdev, &led_ops);
        
        //申请设备号
        ret = alloc_chrdev_region(&led_devnum, led_minor, led_count, DEVNAME);
        if (ret < 0)
        {
            goto ERR_STEP1;
        }
        
        //注册LED设备对象(将cdev添加至系统字符设备链表中)
        ret = cdev_add(led_cdev, led_devnum, led_count);
        if (ret < 0)
        {
            goto ERR_STEP2;
        }
        
        //创建设备类
        led_cls = class_create(THIS_MODULE, DEVNAME);
        if (IS_ERR(led_cls)) 
        {
            ret = PTR_ERR(led_cls);
            goto ERR_STEP3;
        }
        
        //在设备类下创建设备文件
        led_major = MAJOR(led_devnum);
        for(i = led_minor; i < (led_count + led_minor); i++)
        {
            led_dev = device_create(led_cls, NULL, MKDEV(led_major, i), NULL, "%s%d", DEVNAME, i);
            if(IS_ERR(led_dev))
            {
                ret = PTR_ERR(led_dev);
                goto ERR_STEP4;
            }
        }
        
        return 0;
    
        /*******************************倒映式错误处理*******************************/
    ERR_STEP4:
        for(--i; i >= led_minor; i--)
        {
            device_destroy(led_cls, MKDEV(led_major, i));
        }
        class_destroy(led_cls);
        
    ERR_STEP3:
        cdev_del(led_cdev);
        
    ERR_STEP2:
        unregister_chrdev_region(led_devnum, led_count);
        
    ERR_STEP1:
        iounmap(pGPIOREG);    
        
    ERR_STEP:
        release_mem_region(res_led->start, resource_size(res_led));
                
        return ret;     
    }
        
    int led_remove(struct platform_device *pdev)
    {
        int i = 0;
        
        //删除设备文件
        for(i = led_minor; i < (led_count + led_minor); i++)
        {
            device_destroy(led_cls, MKDEV(led_major, i));
        }
        
        //删除设备类
        class_destroy(led_cls);
        //删除设备对象
        cdev_del(led_cdev);
        //注销设备号
        unregister_chrdev_region(led_devnum, led_count);
        //释放内存
        iounmap(pGPIOREG);
        return 0;
    }
    
    //定义并初始化LED驱动信息
    static struct platform_driver led_drv = 
    {
        .driver = 
        {
            .name  = "led",
            .owner = THIS_MODULE,
        },
        .probe = led_probe,
        .remove = led_remove,
    };
    
    //注册LED驱动
    static int __init led_driver_init(void)
    {
        return platform_driver_register(&led_drv);
    }
    
    //注销LED驱动
    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");

    3. 测试所用应用程序

    运行应用程序之前,需确保上述两个模块(device、driver)被装载。运行结果表明LED设备能被应用程序操作。

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/ioctl.h>
    
    #define    FILE_NAME    "/dev/led0"
    
    #define    LED_ON    _IO(LED_IOC_MAGIC, 0)
    #define    LED_OFF    _IO(LED_IOC_MAGIC, 1)
    
    char WriteBuf[30];
    char ReadBuf[30];
    char ScanBuf[30];
    
    int main(void)
    {
        int fd = -1;
        int i = 0;
        
        //打开设备文件
        if ((fd = open(FILE_NAME, O_RDWR)) < 0)
        {
            printf("%s open error
    ", FILE_NAME);
            return -1;
        }
        
        while (1)
        {
            memset(ScanBuf, 0, sizeof(ScanBuf));
            
            printf("please input data for LED
    ");
            if (scanf("%s", ScanBuf))
            {
                //打开LED设备
                if (!strcmp(ScanBuf, "on"))
                {
                    write(fd, "1", 1);
                }
                
                //关闭LED设备
                else if (!strcmp(ScanBuf, "off"))
                {
                    write(fd, "0", 1);
                }
                
                //闪烁LED设备
                else if (!strcmp(ScanBuf, "flash"))
                {
                    for (i=5; i>0; i--)
                    {
                        ioctl(fd, LED_ON);
                        sleep(1);
                        
                        ioctl(fd, LED_OFF);
                        sleep(1);
                    }
                }
    else { break; } } } close(fd); return 0; }
  • 相关阅读:
    Spring Boot 配置文件和命令行配置
    Spring Boot RestApi 测试教程 Mock 的使用
    Spring Boot 集成 Swagger2 教程
    Spring Boot Web 开发@Controller @RestController 使用教程
    SpringBoot RESTful API 架构风格实践
    Spring Boot 使用 Aop 实现日志全局拦截
    Spring Boot 全局异常处理
    Spring Boot Thymeleaf 模板引擎的使用
    Spring Boot Log 日志使用教程
    IDEA 下的 github 创建提交与修改
  • 原文地址:https://www.cnblogs.com/linfeng-learning/p/9376804.html
Copyright © 2011-2022 走看看