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; }
  • 相关阅读:
    14.4.9 Configuring Spin Lock Polling 配置Spin lock 轮询:
    14.4.8 Configuring the InnoDB Master Thread IO Rate 配置InnoDB Master Thread I/O Rate
    14.4.7 Configuring the Number of Background InnoDB IO Threads 配置 后台InnoDB IO Threads的数量
    14.4.7 Configuring the Number of Background InnoDB IO Threads 配置 后台InnoDB IO Threads的数量
    14.4.6 Configuring Thread Concurrency for InnoDB 配置Thread 并发
    14.4.6 Configuring Thread Concurrency for InnoDB 配置Thread 并发
    14.4.5 Configuring InnoDB Change Buffering 配置InnoDB Change Buffering
    14.4.5 Configuring InnoDB Change Buffering 配置InnoDB Change Buffering
    14.4.4 Configuring the Memory Allocator for InnoDB InnoDB 配置内存分配器
    14.4.4 Configuring the Memory Allocator for InnoDB InnoDB 配置内存分配器
  • 原文地址:https://www.cnblogs.com/linfeng-learning/p/9376804.html
Copyright © 2011-2022 走看看