按键驱动,写成标准字符驱动。下面这个是这个是杨创提供的代码。
/* * * A button driver for UTU2410 a board based on s3c2440 * * The source code in this file can be freely used, adapted, * and redistributed in source or binary form, so long as an * acknowledgment appears in derived source files.No warranty * is attached;we cannot take responsibility for errors or * fitness for use. * * */ #include <linux/module.h> #include <linux/errno.h> #include <linux/input.h> #include <linux/kernel.h> /* printk() */ #include <linux/fs.h> /* everything... */ #include <linux/cdev.h> #include <linux/interrupt.h> /* request_irq() */ #include <asm/arch/regs-gpio.h> #include <asm/arch/regs-irq.h> #include <asm/io.h> #include <asm/uaccess.h> /* copy_to_user() */ #include <linux/delay.h> /* mdelay() */ #include <linux/irq.h> #define BUTTONSSTATUS_DOWNX 2 #define BUTTONSSTATUS_DOWN 0 #define BUTTONSSTATUS_UP 1 #define BUF_CLEAR _IO(0xFF, 0) #define DEVICE_NAME "utu2440-buttons" #define MAX_BUTTONS_BUF 16 #define BUTTONS_NUM 6 #define BUF_HEAD (utubuttons_dev.buf[utubuttons_dev.head]) #define BUF_TAIL (utubuttons_dev.buf[utubuttons_dev.tail]) #define ISBUTTONS_DOWN(buttons) (s3c2410_gpio_getpin(buttons_info_tab[buttons].gpio_port) == BUTTONSSTATUS_DOWN) #define INCBUF(x,mod) ((++(x))&((mod)-1)) #define BUTTONS_TIME_DELAY (HZ/10) #define BUTTONS_TIME_DELAY1 (HZ/100) #define UTUBUTTONS_MAJOR 0 unsigned int utubuttons_major = UTUBUTTONS_MAJOR; struct utubuttons_dev { struct cdev cdev; unsigned int buttons_status[BUTTONS_NUM]; unsigned int buf[MAX_BUTTONS_BUF]; unsigned int head, tail; wait_queue_head_t wq; }; struct utubuttons_dev utubuttons_dev; struct utubuttons_dev *utubuttons_devp; struct timer_list buttons_timer[BUTTONS_NUM]; static struct buttons_info { int irq_no; int irq_type; unsigned int gpio_port; unsigned int gpio_setting; int buttons_code; char *name; }buttons_info_tab[] = { { IRQ_EINT0, IRQT_FALLING, S3C2410_GPF0, S3C2410_GPF0_INP, 1, "Key Up" }, { IRQ_EINT1, IRQT_FALLING, S3C2410_GPF1, S3C2410_GPF1_INP, 2, "Key Down" }, { IRQ_EINT2, IRQT_FALLING, S3C2410_GPF2,S3C2410_GPF2_INP, 3, "Key Left" }, { IRQ_EINT3, IRQT_FALLING, S3C2410_GPF3,S3C2410_GPF3_INP, 4, "Key Right" }, { IRQ_EINT11, IRQT_FALLING, S3C2410_GPG3,S3C2410_GPG3_INP, 5, "Key Enter" }, { IRQ_EINT19, IRQT_FALLING, S3C2410_GPG11,S3C2410_GPG11_INP, 6, "Key Exit" }, }; static irqreturn_t utubuttons_irq(int irq, void *dev_id) { int buttons = (int)dev_id; int i; int found = 0; for (i = 0; i < ARRAY_SIZE(buttons_info_tab); i++) { if (buttons_info_tab[i].irq_no == irq) { found = 1; break; } } if (!found) { printk(KERN_NOTICE"bad irq %d in button\n", irq); return IRQ_NONE; } disable_irq(buttons_info_tab[buttons].irq_no); utubuttons_dev.buttons_status[buttons] = BUTTONSSTATUS_DOWNX; buttons_timer[buttons].expires = jiffies + BUTTONS_TIME_DELAY1; add_timer(&buttons_timer[buttons]); return IRQ_HANDLED; } static int request_irqs(void) { int i; for (i = 0; i < ARRAY_SIZE(buttons_info_tab); i++) { s3c2410_gpio_cfgpin(buttons_info_tab[i].gpio_port, buttons_info_tab[i].gpio_setting); set_irq_type(buttons_info_tab[i].irq_no, buttons_info_tab[i].irq_type); if (request_irq(buttons_info_tab[i].irq_no, utubuttons_irq, SA_INTERRUPT, DEVICE_NAME, (void *)i)) { printk(KERN_WARNING "buttons:can't get irq no.%d\n", buttons_info_tab[i].irq_no); return -1; } } return 0; } static void free_irqs(void) { int i; for (i = 0; i < ARRAY_SIZE(buttons_info_tab); i++) { free_irq(buttons_info_tab[i].irq_no, (void *)i); } } static void buttonsEvent(unsigned buttons) { BUF_HEAD = buttons_info_tab[buttons].buttons_code; utubuttons_dev.head = INCBUF(utubuttons_dev.head, MAX_BUTTONS_BUF); wake_up_interruptible(&(utubuttons_dev.wq)); } static ssize_t utubuttons_read(struct file *filp,char __user *buffer, size_t count, loff_t *ppos) { unsigned int buttons_ret; unsigned long flags; retry: if (utubuttons_dev.head != utubuttons_dev.tail) { local_irq_save(flags); buttons_ret = BUF_TAIL; utubuttons_dev.tail = INCBUF(utubuttons_dev.tail, MAX_BUTTONS_BUF); local_irq_restore(flags); copy_to_user(buffer, &buttons_ret, sizeof(unsigned int)); return sizeof(unsigned int); }else { if (filp->f_flags & O_NONBLOCK) { return -EAGAIN; } interruptible_sleep_on(&(utubuttons_dev.wq)); if (signal_pending(current)) { return -ERESTARTSYS; } goto retry; } return sizeof(unsigned int); } static int utubuttons_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg) { unsigned long flags; switch (cmd) { case BUF_CLEAR: local_irq_save(flags); utubuttons_dev.head = utubuttons_dev.tail = 0; local_irq_restore(flags); printk(KERN_INFO "buttons buffer is cleared\n"); break; default: return -EINVAL; } return 0; } static void utubuttons_timer_callback(unsigned long data) { int buttons = data; if (ISBUTTONS_DOWN(buttons)) { if (utubuttons_dev.buttons_status[buttons] == BUTTONSSTATUS_DOWNX) { utubuttons_dev.buttons_status[buttons] = BUTTONSSTATUS_DOWN; buttons_timer[buttons].expires = jiffies + BUTTONS_TIME_DELAY; buttonsEvent(buttons); add_timer(&buttons_timer[buttons]); }else { buttons_timer[buttons].expires = jiffies + BUTTONS_TIME_DELAY; add_timer(&buttons_timer[buttons]); } }else { utubuttons_dev.buttons_status[buttons] = BUTTONSSTATUS_UP; enable_irq(buttons_info_tab[buttons].irq_no); } } static int utubuttons_open(struct inode *inode, struct file *filp) { printk(KERN_NOTICE "utubuttons opened\n"); return 0; } static int utubuttons_release(struct inode *inode, struct file *filp) { printk(KERN_NOTICE "utubuttons released\n"); return 0; } static const struct file_operations utubuttons_fops = { .owner = THIS_MODULE, .read = utubuttons_read, .ioctl = utubuttons_ioctl, .open = utubuttons_open, .release = utubuttons_release, }; static void utubuttons_setup_cdev(void) { int err,devno = MKDEV(utubuttons_major,0); cdev_init(&utubuttons_dev.cdev,&utubuttons_fops); utubuttons_dev.cdev.owner = THIS_MODULE; utubuttons_dev.cdev.ops = &utubuttons_fops; err = cdev_add(&utubuttons_dev.cdev, devno, 1); if (err) printk(KERN_NOTICE "Error %d adding utubuttons", err); } static int __init utubuttons_init(void) { int result, i; dev_t devno = MKDEV(utubuttons_major,0); printk(KERN_INFO "Initial utulinux 2440 Buttons driver!\n"); result = request_irqs(); if (result) { unregister_chrdev_region(devno,1); return result; } if (utubuttons_major) result = register_chrdev_region(devno, 1, DEVICE_NAME); else { result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME); utubuttons_major = MAJOR(devno); printk(KERN_INFO "Todo: mknod /dev/%s c %d 0\n", DEVICE_NAME, utubuttons_major); } if (result < 0) return result; utubuttons_devp = kmalloc(sizeof(struct utubuttons_dev), GFP_KERNEL); if (!utubuttons_devp) { result = -ENOMEM; goto fail_malloc; } memset(utubuttons_devp, 0, sizeof(struct utubuttons_dev)); utubuttons_setup_cdev(); init_waitqueue_head(&(utubuttons_dev.wq)); utubuttons_dev.head = utubuttons_dev.tail = 0; for(i = 0; i < BUTTONS_NUM; i++) { utubuttons_dev.buttons_status[i] = BUTTONSSTATUS_UP; } for(i = 0; i < BUTTONS_NUM; i++) { buttons_timer[i].function = utubuttons_timer_callback; buttons_timer[i].data = i; init_timer(&buttons_timer[i]); } return 0; fail_malloc: unregister_chrdev_region(devno, 1); return result; } static void __exit utubuttons_exit(void) { int i; cdev_del(&utubuttons_dev.cdev); kfree(utubuttons_devp); unregister_chrdev_region(MKDEV(utubuttons_major, 0), 1); free_irqs(); for(i = 0; i < BUTTONS_NUM; i++) { del_timer(&buttons_timer[i]); } } MODULE_AUTHOR(""); MODULE_LICENSE("Dual BSD/GPL"); module_init(utubuttons_init); module_exit(utubuttons_exit);
分析一下这个驱动。
1.数据结构分析。
a.按键的状态,个人感觉最好用一个union。按键有三种情况,按下,按起,长按
b.按键设备。1,继承字符设备;2,按键状态;3,按键的缓冲区;4,等待队列及其头尾
c.定时器。
d.按键信息,个人理解是按键在特定的开发板上的资源信息,个人感觉最好放在与特定的平台相关的代码下,有利于做到移植时的与平台无关。
1.中断号;2,中断类型;3,哪个GPIO;4,GPIO设置;5,按键编码;6,按键名称
e.其他。1.设备名称;2.主设备号。
2.驱动分析
a.入口,出口。module_init(utubuttons_init);module_exit(utubuttons_exit);
utubuttons_init主要做了以下的事情:
1.申请设备号,申请中断
2.注册设备号,分配设备内存并初始化
3.安装字符设备
4.初始化按键队列及其头尾
5.初始化按键状态和定时器
utubuttons_exit主要做了一下事情:
1.卸载字符设备
2.释放设备内存
3.注销设备号
4.释放中断
5. 释放定时器
其中,安装字符设备为其中心枢纽,联系了设备和驱动的关系
主要做了如下事情:
1.初始化字符设备,将驱动和设备关联
2.填充按键设备
3.将按键设备添加到内核中
b.标准文件驱动编写
1.文件操作指针结构赋值
2.完成相应操作
c.中断回调和时间回调,以及按键事件相应