[国嵌攻略][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);
}
原文地址:https://www.cnblogs.com/d442130165/p/5262072.html