• 浅谈平台总线驱动设计


    平台总线是linux2.6内核加入的一种虚拟总线,使用流程:

      1、定义设备

      2、注册设备

      3、定义驱动

      4、注册驱动

    总线上的设备和驱动相互匹配由总线来完成。

    一、定义设备

      平台设备描述结构:struct platform_device

    struct platform_device {
        const char    * name;
        int        id;
        struct device    dev;
        u32        num_resources;
        struct resource    * resource;
    
        const struct platform_device_id    *id_entry;
    
        /* MFD cell pointer */
        struct mfd_cell *mfd_cell;
    
        /* arch specific additions */
        struct pdev_archdata    archdata;
    };

      重要成员:

        name:设备名

        struct resource:设备资源(包括中断号、寄存器等)

        id:设备id

        num_resources:硬件占有资源的数量

      

    struct resource {
        resource_size_t start;
        resource_size_t end;
        const char *name;
        unsigned long flags;
        struct resource *parent, *sibling, *child;
    };

      start和end的意义由flags来定,flags代表硬件资源的类型,linux规定了如下类型

    /*
     * IO resources have these defined flags.
     */
    #define IORESOURCE_BITS        0x000000ff    /* Bus-specific bits */
    
    #define IORESOURCE_TYPE_BITS    0x00001f00    /* Resource type */
    #define IORESOURCE_IO        0x00000100
    #define IORESOURCE_MEM        0x00000200
    #define IORESOURCE_IRQ        0x00000400
    #define IORESOURCE_DMA        0x00000800
    #define IORESOURCE_BUS        0x00001000
    
    #define IORESOURCE_PREFETCH    0x00002000    /* No side effects */
    #define IORESOURCE_READONLY    0x00004000
    #define IORESOURCE_CACHEABLE    0x00008000
    #define IORESOURCE_RANGELENGTH    0x00010000
    #define IORESOURCE_SHADOWABLE    0x00020000
    
    #define IORESOURCE_SIZEALIGN    0x00040000    /* size indicates alignment */
    #define IORESOURCE_STARTALIGN    0x00080000    /* start field is alignment */
    
    #define IORESOURCE_MEM_64    0x00100000
    #define IORESOURCE_WINDOW    0x00200000    /* forwarded by bridge */
    #define IORESOURCE_MUXED    0x00400000    /* Resource is software muxed */
    
    #define IORESOURCE_EXCLUSIVE    0x08000000    /* Userland may not map this resource */
    #define IORESOURCE_DISABLED    0x10000000
    #define IORESOURCE_UNSET    0x20000000
    #define IORESOURCE_AUTO        0x40000000
    #define IORESOURCE_BUSY        0x80000000    /* Driver has marked this resource busy */

      例如当flags取IORESOURCE_MEM时,start、end代表硬件占有的首末内存地址;当flags取IORESOURCE_IRQ时,start、end则代表硬件占有的首末中断号。

    二、注册平台设备

      函数原型:int platform_device_register(struct platform_device *pdev)

    以mini2440键盘为例,定义并注册设备代码如下:

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/platform_device.h>
    #include <linux/interrupt.h>
    
    #define GPGCON 0x56000060
    #define GPGDAT 0x56000064
    
    struct resource key_resource[] = 
    {
        [0] = {
            .start = GPGCON,
            .end = GPGCON + 8,
            .flags = IORESOURCE_MEM,
        },
    
        [1] = {
            .start = IRQ_EINT8,
            .end = IRQ_EINT19,
            .flags = IORESOURCE_IRQ,
        },
    };
    
    struct platform_device key_device = 
    {
        .name = "my key",
        .id = 0,
        .num_resources = 2,        //硬件占有寄存器和中断号两个资源
        .resource = key_resource,
    };
    
    static int key_dev_init()
    {
        /*注册平台设备*/
        platform_device_register(&key_device);
        return 0;
    }
    
    void key_dev_exit()
    {
        /*注销平台设备*/
        platform_device_unregister(&key_device);
    }
    
    MODULE_LICENSE("GPL");
    module_init(key_dev_init);
    module_exit(key_dev_exit);

    三、定义驱动

      平台驱动描述结构:struct platform_driver

    struct platform_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 (*resume)(struct platform_device *);
        struct device_driver driver;
        const struct platform_device_id *id_table;
    };

      重要成员:

        name:驱动名称  (仍采取设备名称和驱动名称来匹配,所以二者一定要一致)

        probe函数: 匹配成功后调用此函数

        remove函数:设备移除后执行此函数

    四、注册驱动平台驱动

      函数原型:int platform_driver_register(struct platform_driver *drv)

    将按键驱动程序由混杂设备驱动优化为平台总线驱动。之前的驱动程序都包含了硬件资源的信息,如GPGCON、GPGDAT寄存器的地址,各个按键的中断号等,移植性不高。优化过后,这些硬件信息都从匹配到的设备的platform_device结构中去取,从而将驱动和设备彻底分开了。从匹配到的设备中取硬件信息使用platform_get_resource函数

    struct resource *platform_get_resource(struct platform_device *dev,
                           unsigned int type, unsigned int num)
    {
        int i;
    
        for (i = 0; i < dev->num_resources; i++) {
            struct resource *r = &dev->resource[i];
    
            if (type == resource_type(r) && num-- == 0)
                return r;
        }
        return NULL;
    }

    该函数遍历设备资源后返回与type指定类型相同的资源。

    优化后代码如下

    key.h:

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/miscdevice.h>
    #include <linux/interrupt.h>
    #include <linux/io.h>
    #include <linux/fs.h>
    #include <asm/uaccess.h>
    #include <linux/sched.h>
    #include <linux/platform_device.h>
    
    /*存储键盘控制寄存器基地址*/
    unsigned int *key_base;
    
    /*定义工作项结构体*/
    struct work_struct *work1;
    
    /*定义定时器变量结构体*/
    struct timer_list key_timer;
    
    unsigned int key_num = 0;
    
    /*定义等待队列*/
    wait_queue_head_t key_queue;
    
    struct resource *res_irq;
    struct resource *res_mem;
    
    /*open函数接口*/
    int key_open(struct inode *node, struct file *filp)
    {
        return 0; 
    }
    
    /*read函数接口*/
    ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
    {
        wait_event(key_queue,key_num);
    
        copy_to_user(buf,&key_num,4);
    
        /*清空按键键值*/
        key_num = 0;
    
        return 4;
    }
    
    /* 函数映射关系表*/
    struct file_operations key_fops = 
    {
        .open = key_open,
        .read = key_read,
        //.unlocked_ioctl = key_ioctl,
    };
    
    /*字符设备描述结构*/
    struct miscdevice key_miscdev = 
    {
        .minor = 200,
        .name = "key",
        .fops = &key_fops,
    };

    驱动程序:

    #include "key.h"
    
    /********************
    函数名:work1_func
    参数:无
    返回值:无
    函数功能:实现工作项
    结构体中的func成员
    ********************/
    void work1_func()
    {
        /*启动定时器*/
        mod_timer(&key_timer,jiffies + (HZ/10));
    }
    
    /************************
    函数名:que_init
    参数:无
    返回值:无
    函数功能:创建一个工作项
    *************************/
    int que_init()
    {
        /*创建工作*/
        work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    
        INIT_WORK(work1, work1_func);
    }
    
    /************************
    函数名:key_int
    参数:无
    返回值:0
    函数功能:按键中断处理函数
    *************************/
    irqreturn_t key_int(int irq, void *dev_id)
    {
        /*1、检测设备是否产生中断*/
    
        /*2、清除中断产生标志*/
    
        /*3、提交下半部分工作*/
        schedule_work(work1);
    
        return 0;
    }
    
    /************************
    函数名:key_hw_init
    参数:无
    返回值:无
    函数功能:初始化与按键相关
    的寄存器
    *************************/
    void key_hw_init()
    {
        unsigned int data; 
        data = readw(key_base);
        data &= ((3)|(3<<6)|(3<<10)|(3<<12)|(3<<14)|(3<<22));//~(0b11);
        data |= (2|(2<<6)|(2<<10)|(2<<12)|(2<<14)|(2<<22));//0b10;
        writew(data,key_base);
    }
    
    /************************
    函数名:key_timer_func
    参数:无
    返回值:无
    函数功能:定时器超时处理函
    数,达到规定时间执行此函数
    *************************/
    void key_timer_func()
    {
        unsigned int key_val,i;
    
        for(i = 0;i < 15;i++)
        {
            if((i == 0)||(i == 3)||(i == 5)||(i == 6)||(i == 7)||(i == 11))
            {
                key_val = readw(key_base + 1) & (1 << i);
                if(key_val == 0)
                    key_num = i+1;
            }
        }
        wake_up(&key_queue);
    }
    
    static int __devinit key_probe(struct platform_device *pdev)
    {
        int size,ret;
    
        /*注册设备*/
        ret = misc_register(&key_miscdev);
        
      /*从匹配到的设备中取出设备硬件信息*/
    res_irq = platform_get_resource(pdev,IORESOURCE_IRQ,0); res_mem = platform_get_resource(pdev,IORESOURCE_MEM,0);
      /*注册中断*/ request_irq(res_irq
    ->start,key_int,IRQF_TRIGGER_FALLING ,"key",(void *) 0); request_irq(res_irq->start + 3,key_int,IRQF_TRIGGER_FALLING ,"key",(void *) 0); request_irq(res_irq->start+5,key_int,IRQF_TRIGGER_FALLING ,"key",(void *) 0); request_irq(res_irq->start+6,key_int,IRQF_TRIGGER_FALLING ,"key",(void *) 0); request_irq(res_irq->start+7,key_int,IRQF_TRIGGER_FALLING ,"key",(void *) 0); request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING ,"key",(void *) 0); size = res_mem->end - res_mem->start + 1; key_base = ioremap(res_mem->start,size); /*硬件初始化*/ key_hw_init(); /*工作队列初始化*/ que_init(); /*初始化定时器*/ init_timer(&key_timer); key_timer.function = key_timer_func; /*注册定时器*/ add_timer(&key_timer); /*初始化等待队列*/ init_waitqueue_head(&key_queue); return ret; } static int key_remove(struct platform_device *pdev) { /*注销设备*/ misc_deregister(&key_miscdev); /*注销中断*/ free_irq(IRQ_EINT8, 0); } /*定义平台总线驱动*/ struct platform_driver key_driver = { .driver = { .name = "my key", }, .probe = key_probe, .remove = key_remove, }; static int button_init() { /*注册平台驱动*/ platform_driver_register(&key_driver); printk("key.ko is ready "); return 0; } static void button_exit() { /*注销平台驱动*/ platform_driver_unregister(&key_driver); } MODULE_LICENSE("GPL"); module_init(button_init); module_exit(button_exit);

    这次的随笔内容大致这么多,这里我有一个小问题想不明白,就是platform_get_resource这个函数的第三个参数num意义何在?我查阅linux内核代码发现大多数用到这个函数的地方将num赋为0,表示不知道为什么。如果有知道的麻烦告诉我,感激不尽~

    此代码适用mini2440开发板,不同型号开发板IO口和中断号不同。如果有疑问或建议,欢迎指出。

       

      

  • 相关阅读:
    ubuntu下安装配置apache2(含虚拟主机配置)
    ubuntu安装软件包apt-get和dpkg方法
    python日期,时间函数
    python多线程
    截取utf8中文字符串
    python解析json
    sqlite读写
    lambda,map,filter,reduce
    pyinstaller生成exe可执行程序
    对象练习
  • 原文地址:https://www.cnblogs.com/51qianrushi/p/4295397.html
Copyright © 2020-2023  润新知