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;  
    }  

    测试结果:

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

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

  • 相关阅读:
    some tips
    ORA00847: MEMORY_TARGET/MEMORY_MAX_TARGET and LOCK_SGA cannot be set together
    Chapter 01Overview of Oracle 9i Database Perfomrmance Tuning
    Chapter 02Diagnostic and Tuning Tools
    变量与常用符号
    Chapter 18Tuning the Operating System
    标准输入输出
    Trace files
    DBADeveloped Tools
    Chapter 03Database Configuration and IO Issues
  • 原文地址:https://www.cnblogs.com/Ph-one/p/4831393.html
Copyright © 2011-2022 走看看