s3c2440按键驱动 -- 混杂设备
2011-08-18 11:30
417 查看
本驱动的开发环境参见:http://blog.csdn.net/gzliu_hit/article/details/6694199
(1)将按键设备实现为混杂设备驱动,会动态生成次设备号,并自动创建设备节点/dev/misc/gzliu_2440_key,完整驱动gzliu_2440_key.c如下:
![](http://hi.csdn.net/attachment/201108/18/0_1313638262zPEw.gif)
(1)将按键设备实现为混杂设备驱动,会动态生成次设备号,并自动创建设备节点/dev/misc/gzliu_2440_key,完整驱动gzliu_2440_key.c如下:
#include <linux/errno.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/input.h> #include <linux/init.h> #include <linux/interrupt.h> #include <asm/io.h> #include <linux/miscdevice.h> #include <asm/uaccess.h> #include <asm/arch/regs-gpio.h> #define DEV_NAME "gzliu_2440_key" #define MAX_KEY_BUF 16 // 按键缓冲区大小 #define KEY_NUM 4 #define GZLIU_KEY_DOWN_X 0 // 按键按下,不确定是否是干扰 #define GZLIU_KEY_DOWN 1 // 按键按下 #define GZLIU_KEY_UP 2 // 按键抬起 #define KEY_TIMER_DELAY_1 (HZ/50) // 按键按下延迟20ms #define KEY_TIMER_DELAY_2 (HZ/10) // 按键抬起之前延迟100ms typedef unsigned char KEY_RET; // 设备结构体 static struct key_dev { unsigned int key_status[KEY_NUM]; // 4个按键的状态 KEY_RET buf[MAX_KEY_BUF]; // 按键缓冲区 unsigned int head, tail; // 按键缓冲区头,尾 wait_queue_head_t wq; // 等待队列 }dev; static struct timer_list key_timer[KEY_NUM]; // 4个按键的去抖定时器 // 按键硬件资源、键值信息结构体 struct key_info { int irq; // 中断号 unsigned int port; // GPIO端口 int port_type; // GPIO端口配置 char *key; // 键名 }; // 中断、GPIO宏定义与s3c2410一样,来自头文件: // include/asm-arm/arch-s3c2410/regs-gpio.h // include/asm-arm/arch-s3c2410/irqs.h static struct key_info key_info_tab[KEY_NUM] = { { IRQ_EINT0, S3C2410_GPF0, S3C2410_GPF0_EINT0, "KEY_1" }, { IRQ_EINT2, S3C2410_GPF2, S3C2410_GPF2_EINT2, "KEY_2" }, { IRQ_EINT3, S3C2410_GPF3, S3C2410_GPF3_EINT3, "KEY_3" }, { IRQ_EINT4, S3C2410_GPF4, S3C2410_GPF4_EINT4, "KEY_4" }, }; // 中断处理程序 static irqreturn_t s3c2440_key_irq(int irq, void *dev_id) { int key = (int)dev_id; // 关中断,转入查询模式 // 每次按键只产生一次中断 disable_irq(key_info_tab[key].irq); dev.key_status[key] = GZLIU_KEY_DOWN_X; // 状态为按下 key_timer[key].expires = jiffies + KEY_TIMER_DELAY_1; // 延迟 add_timer(&key_timer[key]); // 启动定时器 return IRQ_HANDLED; }/* s3c2440_key_irq() */ // 申请系统中断,中断方式为下降沿触发 static int request_irqs(void) { int i, ret; for (i=0; i<KEY_NUM; i++) { // 设置4个GPIO口为中断触发方式 s3c2410_gpio_cfgpin(key_info_tab[i].port, key_info_tab[i].port_type); // 申请中断,快速中断,设置为下降沿触发 // 将按键序号作为参数传入中断服务程序 ret = request_irq(key_info_tab[i].irq, (void *)s3c2440_key_irq, SA_INTERRUPT | IRQT_FALLING, key_info_tab[i].key, (void *)i); if (ret) { return i; } } return 0; }/* request_irqs() */ // 释放中断 static void free_irqs(void) { int i; for (i=0; i<KEY_NUM; i++) { disable_irq(key_info_tab[i].irq); free_irq(key_info_tab[i].irq, (void *)i); } } // 定时器处理函数 static void s3c2440_key_timer(unsigned long data) { int key = data; int status = s3c2410_gpio_getpin(key_info_tab[key].port); if (!status) // 按键为按下状态 { if (dev.key_status[key] == GZLIU_KEY_DOWN_X) // 从中断进入 { dev.key_status[key] = GZLIU_KEY_DOWN; dev.buf[dev.tail] = (KEY_RET)key; dev.tail = (dev.tail + 1) % MAX_KEY_BUF; wake_up_interruptible(&dev.wq); // 唤醒等待队列 } // 延迟更长的时间,等待按键抬起 key_timer[key].expires = jiffies + KEY_TIMER_DELAY_2; add_timer(&key_timer[key]); } else // 按键已经抬起 { dev.key_status[key] = GZLIU_KEY_UP; enable_irq(key_info_tab[key].irq); // 按键抬起,使能中断 } }/* s3c2440_key_timer */ static int s3c2440_key_open(struct inode *inode, struct file *filp) { int i, ret; dev.head = dev.tail = 0; for (i=0; i<KEY_NUM; i++) { // 初始化按键状态为抬起 dev.key_status[i] = GZLIU_KEY_UP; // 初始化定时器 init_timer(&key_timer[i]); key_timer[i].data = i; // 把按键序号作为参数传入定时器处理函数 key_timer[i].function = s3c2440_key_timer; // 定时器相应函数 } init_waitqueue_head(&(dev.wq)); // 初始化等待队列 // 申请中断 ret = request_irqs(); if (ret > 0) // 如果申请失败,释放已经申请的中断 { printk("request_irqs() failed, line: %d\n", __LINE__); for (i=ret; i>=0; i--) { disable_irq(key_info_tab[i].irq); free_irq(key_info_tab[i].irq, (void *)i); } return -EBUSY; } return 0; }/* s3c2440_key_open() */ static ssize_t s3c2440_key_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { int ret; unsigned int size; if (dev.head == dev.tail) // 没有按键被按下 { if (filp->f_flags & O_NONBLOCK) // 如果应用程序采用非阻塞方式,则返回错误 { return -EAGAIN; } else // 进入阻塞模式,使应用程序休眠 { wait_event_interruptible(dev.wq, dev.head != dev.tail); } } size = (dev.tail - dev.head) % MAX_KEY_BUF; // 数据分为两个部分,在buf的两端 if (dev.head > dev.tail) { ret = copy_to_user(buf, (dev.buf+dev.head), MAX_KEY_BUF - dev.head); if (ret) { return ret; } ret = copy_to_user(buf+MAX_KEY_BUF - dev.head, dev.buf, dev.tail); if (ret) { return ret; } dev.head = dev.tail; } else { ret = copy_to_user(buf, (dev.buf+dev.head), size); dev.head = dev.tail; if (ret) { return ret; } } return size; }/* s3c2440_key_read() */ static int s3c2440_key_release(struct inode *inode, struct file *filp) { int i; for (i=0; i<KEY_NUM; i++) { del_timer(&key_timer[i]); } free_irqs(); return 0; } static struct file_operations s3c2440_key_fops = { .owner =THIS_MODULE, .open =s3c2440_key_open, .read =s3c2440_key_read, .release =s3c2440_key_release, }; static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEV_NAME, .fops = &s3c2440_key_fops, }; // s3c2440-key驱动模块加载函数 static int __init s3c2440_key_init(void) { int ret; // 注册混杂设备 // 会自动创建设备节点,/dev/misc/<DEV_NAME>,主设备号10,次设备号自动获取 ret = misc_register(&misc); if (ret < 0) { printk(DEV_NAME " register failed, line: %d\n", __LINE__); printk("ret: %d\n", ret); return ret; } return 0; } // s3c2440-key驱动模块卸载函数 static void __exit s3c2440_key_exit(void) { misc_deregister(&misc); } module_init(s3c2440_key_init); module_exit(s3c2440_key_exit); MODULE_AUTHOR("gzliu <gzliu@qq.com>"); MODULE_DESCRIPTION("s3c2440 key driver"); MODULE_LICENSE("GPL");(2)对应的测试应用程序:
#include <stdio.h> #include <fcntl.h> #define MAX_KEY_BUF 16 // 按键缓冲区大小 int main() { unsigned char buf[MAX_KEY_BUF]; int fd_key; int ret, i; fd_key = open("/dev/misc/gzliu_2440_key", O_RDONLY); if (fd_key == -1) { printf("open(fd_key) failed\n"); return -1; } while (1) { // 阻塞模式, 没有按键按下时, 进程会被阻塞 ret = read(fd_key, buf, MAX_KEY_BUF); for (i=0; i<ret; i++) { printf("Key %d is down\n", buf[i]); } } return 0; }(3)使用insmod gzliu_2440_key.ko加载驱动后,查看/dev/misc目录,发现自动生成了名为gzliu_2440_key的设备文件,主设备号10,次设备号63,之后直接运行测试程序key_test即可。而当执行rmmod gzliu_2440_key.ko将驱动卸载时,又会自动删除设备文件/dev/misc/gzliu_2440_key。
![](http://hi.csdn.net/attachment/201108/18/0_1313638262zPEw.gif)
相关文章推荐
- s3c2440 按键驱动 -- 字符设备
- Linux混杂设备驱动 - 按键设备驱动
- Linux按键驱动程序设计(1)-混杂设备驱动模型
- 12.按键驱动程序设计(1)-混杂设备驱动模型
- 按键驱动程序设计---混杂设备、中断分层处理、工作队列、阻塞型驱动
- 嵌入式学习-驱动开发-lesson4-按键混杂设备驱动
- OK6410中断按键 混杂设备驱动
- 写一个具体的按键驱动(WinCE5.0 S3C2440)
- 字符设备驱动之按键中断(阻塞机制)——FS2410
- miscdevice混杂设备驱动 2
- linux 混杂设备驱动之adc驱动
- Linux驱动修炼之道-混杂设备
- 蜕变成蝶~Linux设备驱动之按键设备驱动
- 按键中断的platform bus设备和驱动 收藏
- linux字符设备驱动(按键)
- 基于RFID无线射频的设备管理系统---矩阵键盘驱动(按键码)
- 关于混杂设备驱动
- Linux 输入设备驱动上报按键值方法
- Linux 设备驱动--- 内核等待队列 --- wait_queue_head --- wait_event_interruptible --- 按键驱动程序优化
- Linux设备驱动之s3c2440添加LCD驱动