• [国嵌攻略][126][平台总线驱动设计]


    平台总线概述

    平台总线(Platform bus)是Linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备与驱动进行了管理,这样提高了程序的可移植性。这条总线由系统创建,不需要用户再去创建平台总线。

    通过平台总线机制开发设备驱动的流程如下:

    1.定义platform_device

    2.注册platform_device

    3.定义platform_driver

    4.注册platform_driver

    平台总线驱动与设备匹配机制

    如果驱动由存在ID,那么匹配驱动与设备的ID;如果驱动没有ID,那么匹配驱动与设备的名称。

    平台设备

    平台设备使用struct platform_device来描述:

    struct platform_device{

    const char *name;   //设备名称,必须与驱动一样

    int id;   //设备编号,配合设备名称使用

    struct device dev;

    u32 num_resources;   //资源数目

    struct resource *resource;   //设备资源,包括寄存器基地址、中断号等

    };

    struct resource{

        resource_size_t start;   //起始地址

        resource_size_t end;     //结束地址

        const char *name;

        unsigned long flags;     //资源类型,包括中断类型、内存类型等

        struct resource *parent, *sibling, *child;

    };

    注册平台设备使用:

    int platform_device_register(struct platform_device *pdev)

    注销平台设备使用:

    int platform_device_unregister(struct platform_device *pdev)

    平台设备一般开发成内核模块直接放到内核中,在系统初始化时加载上去。

    头文件

    <linux/platform_device.h>

    keydev.c

    /********************************************************************
    *头文件
    *********************************************************************/
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/platform_device.h>
    #include <linux/interrupt.h>
    
    /********************************************************************
    *宏定义
    *********************************************************************/
    #define GPGCON 0x56000060   //按键控制寄存器地址
    #define GPGDAT 0x56000064   //按键数据寄存器地址
    
    /********************************************************************
    *模块安装
    *********************************************************************/
    //设备资源
    struct resource keyres[] = {
        [0] = {
            .start = GPGCON,          //起始地址
            .end   = GPGDAT,          //结束地址
            .flags = IORESOURCE_MEM   //地址类型
        },
        [1] = {
            .start = IRQ_EINT8,       //起始编号
            .end   = IRQ_EINT11,      //结束编号
            .flags = IORESOURCE_IRQ   //中断类型
        }
    };
    
    //平台设备
    struct platform_device keydev = {
        .name          = "ikey",   //设备名称,必须与驱动名称相同
        .id            = 0,        //设备编号
        .num_resources = 2,        //资源数量
        .resource      = keyres    //设备资源
    };
    
    //安装模块
    static int keydev_init(void){
        //注册平台设备
        int state;
        
        state = platform_device_register(&keydev);
        
        return state;
    }
    
    //卸载模块
    static void keydev_exit(void){
        //注销平台设备
        platform_device_unregister(&keydev);
    }
    
    /********************************************************************
    *模块声明
    *********************************************************************/
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("D");
    MODULE_DESCRIPTION("");
    MODULE_VERSION("v1.0");
    
    module_init(keydev_init);
    module_exit(keydev_exit);

    平台驱动

    平台驱动使用struct platform_driver描述:

    struct platform_driver{

        int (*probe)(struct platform_device *);   //设备匹配成功时调用

        int (*remove)(struct platform_device *);   //设备移除时调用

        struct device_driver driver;   //驱动信息

    };

    struct device_driver {

        const char      *name;   //驱动名称,必须与设备名称相同

        struct bus_type     *bus;   //所属总线

        struct module       *owner;   //拥有者,可填THIS_MODULE

        const char      *mod_name;  /* used for built-in modules */

        ......

    };

    平台驱动注册使用:

    int platform_driver_register(struct platform driver *)

    平台驱动注销使用:

    int platfrom_driver_unregister(struct platform driver *)

    总线驱动主要是起到找到设备的作用,在找到设备后,再根据相应的设备类型初始化设备。Linux驱动从总线的角度来看是总线模型,从功能的角度来看是设备模型。总线模型包含设备模型。

    获取设备资源使用:

    struct resource *devres;   //设备资源

    devres = platform_get_resource(设备指针, 资源类型, 同类型资源编号)

    头文件

    <linux/platform_device.h>

    解决问题:

    warning: ISO C90 forbids mixed declarations and code

    变量定义之前任何一条非变量定义的语句(注意:语句是会带分号的)都会引起这个警告!将非变量的定义移到变量定义之后即可。

    keydrv.c

    /********************************************************************
    *头文件
    *********************************************************************/
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/platform_device.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>
    
    /********************************************************************
    *全局变量
    *********************************************************************/
    struct work_struct *work;   //按键工作
    struct timer_list timer;    //按键定时
    wait_queue_head_t queue;    //等待队列
    int isData;                 //等待条件
    
    struct resource *regRes;    //按键寄存器资源
    struct resource *irqRes;    //按键中断号资源
    unsigned int *keycon;       //按键控制指针
    unsigned int *keydat;       //按键数据指针
    
    int keyNum;                 //按键编号
    
    /********************************************************************
    *定时处理
    *********************************************************************/
    //设备定时
    void key_timer(unsigned long data){
        //读取按键状态
        unsigned short datTmp;
        int key1, key2;
        
        datTmp = readw(keydat);   //获取GPGDAT值
        key1 = datTmp & (1<<0);   //获取key1电平
        key2 = datTmp & (1<<3);   //获取key2电平
        
        //判断按键状态
        if(key1 == 0){
            keyNum = 1;
            printk("key1 down!
    ");
        }
        
        if(key2 == 0){
            keyNum = 2;
            printk("key2 down!
    ");
        }
        
        //设置数据状态
        isData = 1;
        wake_up(&queue);
    }
    
    /********************************************************************
    *中断处理
    *********************************************************************/
    //设备中断下部
    void key_work(struct work_struct *work){
        //启动定时设备
        mod_timer(&timer, jiffies + HZ/100);   //定时10ms,jiffies表示系统当前嘀嗒数,1HZ = 1s = 1000jiffies
    }
    
    //设备中断上部
    irqreturn_t key_irq(int irq, void *dev_id){
        //处理硬件相关
        
        //提交按键工作
        schedule_work(work);
        
        return 0;
    }
    
    /********************************************************************
    *设备方法
    *********************************************************************/
    //设备读取
    ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){
        //判断数据状态
        wait_event(queue, isData);
        
        //读取按键编号
        copy_to_user(buf, &keyNum, sizeof(keyNum));
        
        //清零数据状态
        isData = 0;
        
        return sizeof(keyNum);
    }
    
    //设备打开
    int key_open(struct inode *node, struct file *filp){
        return 0;
    }
    
    //设备关闭
    int key_close(struct inode *node, struct file *filp){
        return 0;
    }
    
    //设备方法
    struct file_operations key_fops = {
        .read    = key_read,
        .open    = key_open,
        .release = key_close
    };
    
    /********************************************************************
    *驱动处理
    *********************************************************************/
    //混杂设备
    struct miscdevice keydev = {
        .minor = 200,        //次设备号
        .name  = "ikey",     //设备名称
        .fops  = &key_fops   //设备方法
    };
    
    //注册硬件
    void handware_init(struct platform_device *pdev){
        //初始化按键控制指针
        unsigned int conTmp;
        
        keycon = ioremap(regRes->start, 4);   //虚拟地址映射    
        conTmp = readl(keycon);               //获取GPGCON值
        conTmp &= ~((0x3<<6) | (0x3<<0));     //GPB3[7:6]:00,GPG0[1:0]:00
        conTmp |=  ((0x2<<6) | (0x2<<0));     //GPB3[7:6]:EINT[11],GPG0[1:0]:EINT[8]
        writel(conTmp, keycon);               //设置GPGCON值
        
        //初始化按键状态指针
        keydat = ioremap(regRes->end, 2);   //虚拟地址映射
        
        //初始化按键中断处理
        request_irq(irqRes->start, key_irq, IRQF_TRIGGER_FALLING, "ikey", (void *)0);   //注册EINT8
        request_irq(irqRes->end, key_irq, IRQF_TRIGGER_FALLING, "ikey", (void *)1);     //注册EINT11
    }
    
    //驱动安装
    int key_probe(struct platform_device *pdev){
        //注册混杂设备
        int state;
        
        state = misc_register(&keydev);
        
        //注册工作队列
        work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
        INIT_WORK(work, key_work);
        
        //注册定时设备
        init_timer(&timer);           //初始化定时器
        timer.function = key_timer;   //添加定时函数
        add_timer(&timer);            //添加定时设备
        
        //注册等待队列
        isData = 0;
        init_waitqueue_head(&queue);
        
        //获取设备资源
        regRes = platform_get_resource(pdev, IORESOURCE_MEM, 0);   //获取寄存器资源
        irqRes = platform_get_resource(pdev, IORESOURCE_IRQ, 0);   //获取中断号资源
        
        //注册硬件设备
        handware_init(pdev);
        
        return state;
    }
    
    //驱动卸载
    int key_remove(struct platform_device *pdev){
        //注销混杂设备
        misc_deregister(&keydev);
        
        //注销中断处理
        free_irq(irqRes->start, (void *)0);   //释放EINT8
        free_irq(irqRes->end, (void *)1);     //释放EINT11
        
        return 0;
    }
    
    /********************************************************************
    *模块安装
    *********************************************************************/
    //平台驱动
    struct platform_driver keydrv = {
        .probe  = key_probe,        //驱动安装
        .remove = key_remove,       //驱动卸载
        .driver = {
            .owner = THIS_MODULE,   //拥有信息
            .name  = "ikey"         //驱动名称,必须与设备名称相同
        }
    };
    
    //安装模块
    static int keydrv_init(void){
        //注册平台驱动
        int state;
        
        state = platform_driver_register(&keydrv);
        
        return state;
    }
    
    //卸载模块
    static void keydrv_exit(void){
        //注销平台驱动
        platform_driver_unregister(&keydrv);
    }
    
    /********************************************************************
    *模块声明
    *********************************************************************/
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("D");
    MODULE_DESCRIPTION("");
    MODULE_VERSION("v1.0");
    
    module_init(keydrv_init);
    module_exit(keydrv_exit);

    keyapp.c

    mknod /dev/keydev0 c 10 200

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    
    int main(int argc, char **argv){
        //打开设备
        int fd;
        
        fd = open("/dev/keydev0", O_RDWR);
        
        //读取设备
        int keynum;
        
        read(fd, &keynum, sizeof(keynum));
        printf("key number is %d
    ", keynum);
        
        //关闭设备
        close(fd);
    }
  • 相关阅读:
    Dump 文件生成与分析
    打造支持apk下载和html5缓存的 IIS(配合一个超简单的android APP使用)具体解释
    GridView编辑删除操作
    google域名邮箱申请 gmail域名邮箱申请(企业应用套件)指南
    nvl,空时的推断和取值
    如何将图片保存至自定义分组
    Java实现 蓝桥杯VIP 算法训练 集合运算
    Java实现 蓝桥杯VIP 算法训练 瓷砖铺放
    Java实现 蓝桥杯VIP 算法训练 瓷砖铺放
    Java实现 蓝桥杯VIP 算法训练 集合运算
  • 原文地址:https://www.cnblogs.com/d442130165/p/5262072.html
Copyright © 2020-2023  润新知