[国嵌攻略][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!\n"); } if(key2 == 0){ keyNum = 2; printk("key2 down!\n"); } //设置数据状态 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\n", keynum); //关闭设备 close(fd); }