zoukankan      html  css  js  c++  java
  • 驱动之路-platform简例按键驱动☆☆☆

    一 、重要知识点:

    ▉1.platform设备模型

        从Linux 2.6起引入了一套新的驱动管理和注册机制,platform_device和platform_driver,Linux中大部分的设备驱动都可以使用这套机制。platform是一条虚拟的总线。设备用platform_device表示,驱动用platform_driver进行注册,Linux platform driver机制和传统的device driver机制(通过driver_register进行注册)相比,一个明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动中使用这些资源时通过platform device提供的标准结构进行申请并使用。这样提高了驱动和资源的独立性,并且具有较好的可移植性和安全性(这些标准接口是安全的)。

        pltform机制本身使用并不复杂,由两部分组成:platform_device和platform_driver。通过platform机制开发底层驱动的大致流程为:定义platform_deive->注册platform_device->定义platform_driver->注册platform_driver。

        首先要确认的就是设备的资源信息,例如设备的地址,中断号等。

    1 platform_device

    在 2.6 内核中 platform 设备用结构体 platform_device 来描述,该结构体定义在 kernel/include/linux/platform_device.h 中,

    structplatform_device {

     const char * name;

     u32  id;

     struct device dev;

     u32  num_resources;

     struct resource * resource;

    };

    该结构一个重要的元素是resource ,该元素存入了最为重要的设备资源信息,定义在kernel/include/linux/ioport.h 中,

    structresource {

     const char *name;//资源的名称

     unsigned long start, end;//资源起始的和结束的物理地址

     unsigned long flags;//资源的类型,比如MEM,IO,IRQ类型

     struct resource *parent, *sibling, *child;//资源链表的指针

    };

    structplatform_device的分配使用

    structplatform_device *platform_device_alloc(const char *name, int id)

    name是设备名,id,设备id,一般为-1,如果是-1,表示同样名字的设备只有一个

    举个简单的例子,name/id是“serial/1”则它的bus_id就是serial.1  如果name/id是“serial/0”则它的bus_id就是serial.0 ,如果它的name/id是“serial/-1”则它的bus_id就是serial。

    注册平台设备,使用函数

    intplatform_device_add(struct platform_device *pdev)

    注销使用

    voidplatform_device_unregister(struct platform_device *pdev)

     2 ● platform_driver

    在平台设备驱动中获取平台设备资源使用

    structresource *platform_get_resource(struct platform_device *dev, unsigned int type,unsigned int num)

    该函数用于获取dev设备第num个类型type的资源,如果获取失败,则返回NULL。例如 platform_get_resource(pdev,IORESOURCE_IRQ, 0)。

    平台驱动描述使用

    structplatform_driver {

     int (*probe)(struct platform_device *);

     int (*remove)(struct platform_device *);

     void (*shutdown)(struct platform_device *);

     int (*suspend)(struct platform_device *, pm_message_t state);

     int (*suspend_late)(struct platform_device *, pm_message_t state);

     int (*resume_early)(struct platform_device *);

     int (*resume)(struct platform_device *);

     struct device_driver driver;

    };

    Probe()函数必须验证指定设备的硬件是否真的存在,probe()可以使用设备的资源,包括时钟,platform_data(platform_data:用户自定义的私有数据)等,Platform driver可以通过下面的函数完成对驱动的注册:

    int platform_driver_register(structplatform_driver *drv);一般来说设备是不能被热插拔的,所以可以将probe()函数放在init段里面来节省driver运行时候的内存开销:

    int platform_driver_probe(struct platform_driver *drv, int (*probe)(structplatform_device *));

    注销使用void platform_driver_unregister(struct platform_driver *drv)

    ▉2.中断处理

    在Linux驱动程序中,为设备实现一个中断包含 两个步骤1.向内核注册(申请中断)中断 2.实现中断处理函数

    request_irq用于实现中断的注册

    intrequest_irq(unsigned in irq, void(*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void*dev_id)

    向内核申请中断号为irq中断处理函数为handler指针指向的函数中断标志为flag设备名为devname的中断。成功返回0,或者返回一个错误码。

    当request_irq不用于共享中断时,dev_id可以为NULL,或者指向驱动程序自己的私有数据。但用于共享中断时dev_id必须唯一。因为free_irq时也需要dev_id做参数,这样free_irq才知道要卸载共享中断上哪个中断服务处理函数。共享中断会在后面讲到。

    在flag参数中,可以选以下参数

    IRQF_DISABLED(SA_INTERRUPT)

    如果设置该位,表示是一个“快速”中断处理程序,如果没有,那么就是一个“慢速”中断处理程序。

    IRQF_SHARED(SA_SHITQ)

    该位表示中断可以在设备间共享。

    快速/慢速中断

    这两种类型的中断处理程序的主要区别在于:快速中断保证中断处理的原子性(不被打断),而慢速中断则不保证。换句话说,也就是开启中断标志位在运行快速中断处理程序时

    关闭的,因此在服务该中断时,不会被其他类型的中断打断;而调用慢速中断处理时,其他类型中断扔可以得到服务。

        共享中断

    共享中断就是将不同的设备挂到同一个中断信号线上。linux对共享的支持主要是位PCI设备服务。

    释放中断

    voidfree_irq(unsigned int irq)

    当设备不再需要使用中断时(通常是设备关闭和驱动卸载时),应该使用该函数把他们返回给内核使用。

    禁用中断

    voiddisable_irq(int irq)

    当一些代码中不能使用中断时(如支持自旋锁的上下文中)使用该函数禁用中断。

    启用中断

    voidenable_irq(int irq)

    当禁止后可以使用该函数重新启用。

    ◐二、驱动代码

    该驱动实现能够读取按键按下的键值,比如说如果是第一个键按下读取的键值就为1。

    platform平台设备 

    #include <linux/device.h>  
    #include <linux/string.h>  
    #include <linux/platform_device.h>  
    #include <linux/module.h>  
    #include <linux/kernel.h>  
    #include <linux/fs.h>  
    #include <linux/init.h>  
    #include <linux/delay.h>  
    #include <linux/poll.h>  
    #include <linux/irq.h>  
    #include <asm/irq.h>  
    #include <linux/interrupt.h>  
    #include <asm/uaccess.h>  
    #include <mach/regs-gpio.h>  
    #include <mach/hardware.h>  
    #include <linux/cdev.h>  
    #include <linux/miscdevice.h>  
    #include <linux/sched.h>  
    #include <linux/gpio.h>  
      
    static struct resource key_resource[]=      //
    {     
        [0] = {  
            .start = IRQ_EINT8,  
            .end = IRQ_EINT8,  
            .flags = IORESOURCE_IRQ,  
        },  
        [1] = {  
            .start = IRQ_EINT11,  
            .end = IRQ_EINT11,  
            .flags = IORESOURCE_IRQ,  
        },  
        [2]= {  
            .start = IRQ_EINT13,  
            .end = IRQ_EINT13,  
            .flags = IORESOURCE_IRQ,  
        },  
        [3] = {  
            .start = IRQ_EINT14,  
            .end = IRQ_EINT14,  
            .flags = IORESOURCE_IRQ,  
        },  
        [4] = {  
            .start = IRQ_EINT15,  
            .end = IRQ_EINT15,  
            .flags = IORESOURCE_IRQ,  
        },  
        [5] = {  
            .start = IRQ_EINT19,  
            .end = IRQ_EINT19,  
            .flags = IORESOURCE_IRQ,  
        },  
    };  
      
    struct platform_device *my_buttons_dev;  
      
    static int __init platform_dev_init(void)  
    {  
        int ret;  
          
        my_buttons_dev = platform_device_alloc("my_buttons", -1);  
          
        platform_device_add_resources(my_buttons_dev,key_resource,6);//添加资源一定要用该函数,不能使用对platform_device->resource幅值  
                                                                    //否则会导致platform_device_unregister调用失败,内核异常。  
          
        ret = platform_device_add(my_buttons_dev);  
          
        if(ret)  
            platform_device_put(my_buttons_dev);  
          
        return ret;  
    }  
      
    static void __exit platform_dev_exit(void)  
    {  
        platform_device_unregister(my_buttons_dev);  
    }  
      
    module_init(platform_dev_init);  
    module_exit(platform_dev_exit);  
      
    MODULE_AUTHOR("Y-Kee");  
    MODULE_LICENSE("GPL");  

    platform平台驱动

    //platform driver  
    #include <linux/module.h>  
    #include <linux/types.h>  
    #include <linux/miscdevice.h>  
    #include <linux/fs.h>  
    #include <linux/init.h>  
    #include <linux/platform_device.h>  
    #include <linux/interrupt.h>  
    #include <linux/clk.h>  
    #include <linux/uaccess.h>  
    #include <linux/io.h>  
    #include <mach/map.h>  
    #include <mach/regs-gpio.h>  
    #include <linux/poll.h>  
    #include <linux/irq.h>  
    #include <asm/unistd.h>  
    #include <linux/device.h>  
      
      
    static int buttons_irq[6];  
      
    struct irq_des  
    {  
        int *buttons_irq;  
        char *name[6];  
    };  
      
      
    struct irq_des button_irqs = {   
        .buttons_irq = buttons_irq,  
        .name = {"KEY0", "KEY1", "KEY2", "KEY3", "KEY4", "KEY5"},  
    };  
      
    static volatile int key_values;  
      
      
    static DECLARE_WAIT_QUEUE_HEAD(button_waitq);  
      
      
    static volatile int ev_press = 0;  
      
      
    static irqreturn_t buttons_interrupt(int irq, void *dev_id)  
    {  
        int i;  
        for(i=0; i<6; i++){  
            if(irq == buttons_irq[i]){     //    
                key_values = i;  
                ev_press = 1;  
                wake_up_interruptible(&button_waitq);     
            }  
        }  
          
        return IRQ_RETVAL(IRQ_HANDLED);  
    }  
      
      
    static int s3c24xx_buttons_open(struct inode *inode, struct file *file)  
    {  
        int i;  
        int err = 0;  
          
        for (i = 0; i < 6; i++) {  
            err = request_irq(button_irqs.buttons_irq[i], buttons_interrupt, IRQ_TYPE_EDGE_BOTH,   
                              button_irqs.name[i], (void *)&button_irqs.buttons_irq[i]);  
            if (err)  
                break;  
        }  
      
        if (err) {  
            i--;  
            for (; i >= 0; i--) {  
            if (button_irqs.buttons_irq[i] < 0) {  
            continue;  
            }  
            disable_irq(button_irqs.buttons_irq[i]);  
                free_irq(button_irqs.buttons_irq[i], (void *)&button_irqs.buttons_irq[i]);  
            }  
            return -EBUSY;  
        }  
          
        return 0;  
    }  
      
      
    static int s3c24xx_buttons_close(struct inode *inode, struct file *file)  
    {  
        int i;  
          
        for (i = 0; i < 6; i++) {  
        free_irq(button_irqs.buttons_irq[i], (void *)&button_irqs.buttons_irq[i]);  
        }  
      
        return 0;  
    }  
      
      
    static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)  
    {  
        unsigned long err;  
      
        if (!ev_press) {  
        if (filp->f_flags & O_NONBLOCK)  
            return -EAGAIN;  
        else  
            wait_event_interruptible(button_waitq, ev_press);  
        }  
          
        ev_press = 0;  
      
        err = copy_to_user(buff, (const void *)&key_values, min(sizeof(key_values), count));  //return err ? -EFAULT : min(sizeof(key_values), count);  
    }  
      
    static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)  
    {  
        unsigned int mask = 0;  
        poll_wait(file, &button_waitq, wait);  
        if (ev_press)  
            mask |= POLLIN | POLLRDNORM;  
        return mask;  
    }  
        
    static struct file_operations dev_fops = {  
        .owner   =   THIS_MODULE,  
        .open    =   s3c24xx_buttons_open,  
        .release =   s3c24xx_buttons_close,   
        .read    =   s3c24xx_buttons_read,  
        .poll    =   s3c24xx_buttons_poll,  
    };  
      
    static struct miscdevice misc = {  
        .minor = MISC_DYNAMIC_MINOR,  
        .name = "my_buttons",  
        .fops = &dev_fops,  
    };  
        
    static int my_plat_probe(struct platform_device *dev)  
    {  
        int ret,i;  
        struct resource *plat_resource;  
        struct platform_device *pdev = dev;  
          
        printk("my platform dirver find my platfrom device.
    ");  
      
        for(i=0; i<6; i++){  
            plat_resource = platform_get_resource(pdev,IORESOURCE_IRQ,i);    //⑥  获取platform总线中对应资源  file_operations从platform获取资源的核心语句 ☆☆☆  
            if(plat_resource == NULL)  
                return -ENOENT;   
            buttons_irq[i] = plat_resource->start;      //
        }  
      
        ret = misc_register(&misc);  
        if(ret)  
            return ret;      
        return 0;  
    }  
      
    static int my_plat_remove(struct platform_device *dev)  
    {  
        printk("my platfrom device has removed.
    ");  
        misc_deregister(&misc);  
        return 0;  
    }  
      
    struct platform_driver my_buttons_drv = {   
        .probe = my_plat_probe,  
        .remove = my_plat_remove,  
        .driver = {   
            .owner = THIS_MODULE,  
            .name = "my_buttons",  
        },  
    };  
      
    static int __init platform_drv_init(void)  
    {  
        int ret;  
      
        ret = platform_driver_register(&my_buttons_drv);  
          
        return ret;  
    }  
      
    static void __exit platform_drv_exit(void)  
    {  
        platform_driver_unregister(&my_buttons_drv);  
    }  
      
    module_init(platform_drv_init);  
    module_exit(platform_drv_exit);  
      
    MODULE_AUTHOR("Y-Kee");  
    MODULE_LICENSE("GPL");  

    测试代码

    /*  
     *      Buttons Example for Matrix V  
     *  
     *      Copyright (C) 2004 capbily - friendly-arm  
     *  capbily@hotmail.com  
     */  
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>  
    #include <sys/ioctl.h>  
    #include <sys/types.h>  
    #include <sys/stat.h>  
    #include <fcntl.h>  
    #include <sys/select.h>  
    #include <sys/time.h>  
    #include <errno.h>  
      
    int main(void)  
    {  
        int buttons_fd;  
        int key_value;  
      
        buttons_fd = open("/dev/buttons", 0);  
        if (buttons_fd < 0) {  
            perror("open device buttons");  
            exit(1);  
        }  
      
        for (;;) {  
            fd_set rds;  
            int ret;  
      
            FD_ZERO(&rds);  
            FD_SET(buttons_fd, &rds);  
      
            ret = select(buttons_fd + 1, &rds, NULL, NULL, NULL);  
            if (ret < 0) {  
                perror("select");  
                exit(1);  
            }  
            if (ret == 0) {  
                printf("Timeout.
    ");  
            } else if (FD_ISSET(buttons_fd, &rds)) {  
                int ret = read(buttons_fd, &key_value, sizeof key_value);  //
                if (ret != sizeof key_value) {  
                    if (errno != EAGAIN)  
                        perror("read buttons
    ");  
                    continue;  
                } else {  
                    printf("buttons_value: %d
    ", key_value+1);       //
                }  
                      
            }  
        }  
      
        close(buttons_fd);  
        return 0;  
    }  

    测试结果:

    运行测试程序后按下第二个键,中断上打印了多次按键的键值,产生原因是因为按键抖动。导致按一下按键,产生多次中断。

    -----------------

  • 相关阅读:
    java 集合排序
    java传值和传址
    Dom4j操作xml
    JAXP操作xml
    乐观锁和悲观锁【转】
    java IO【转】
    java 可变参数
    Eclipse调试Java的10个技巧【转】
    编译JDK源代码【转】
    Access restriction: The method typeNameToClass(String) from the type ObjectHandler is not accessible due to restriction on required library
  • 原文地址:https://www.cnblogs.com/Ph-one/p/4831393.html
Copyright © 2011-2022 走看看