#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/poll.h>#include <linux/irq.h>#include <asm/irq.h>#include <linux/interrupt.h>#include <asm/uaccess.h>#include <mach/regs-gpio.h>#include <mach/hardware.h>#include <linux/platform_device.h>#include <linux/cdev.h>#include <linux/miscdevice.h>
#define DEVICE_NAME "buttons"
struct button_irq_desc { //定义按键中断的结构体 int irq; //中断号 int pin; //按键引脚 int pin_setting; // 按键引脚设置(设置为中断) int number; //按键编号 char *name; //按键名称};
//利用结构体struct button_irq_desc 定义6个按键的信息,把按键的资源进行了组织
#if !defined (CONFIG_QQ2440_BUTTONS)static struct button_irq_desc button_irqs [] = { {IRQ_EINT8 , S3C2410_GPG0 , S3C2410_GPG0_EINT8 , 0, "KEY0"}, {IRQ_EINT11, S3C2410_GPG3 , S3C2410_GPG3_EINT11 , 1, "KEY1"}, {IRQ_EINT13, S3C2410_GPG5 , S3C2410_GPG5_EINT13 , 2, "KEY2"}, {IRQ_EINT14, S3C2410_GPG6 , S3C2410_GPG6_EINT14 , 3, "KEY3"}, {IRQ_EINT15, S3C2410_GPG7 , S3C2410_GPG7_EINT15 , 4, "KEY4"}, {IRQ_EINT19, S3C2410_GPG11, S3C2410_GPG11_EINT19, 5, "KEY5"},};#else /* means QQ */static struct button_irq_desc button_irqs [] = { {IRQ_EINT19, S3C2410_GPG11, S3C2410_GPG11_EINT19, 0, "KEY0"}, {IRQ_EINT11, S3C2410_GPG3, S3C2410_GPG3_EINT11, 1, "KEY1"}, {IRQ_EINT2, S3C2410_GPF2, S3C2410_GPF2_EINT2, 2, "KEY2"}, {IRQ_EINT0, S3C2410_GPF0, S3C2410_GPF0_EINT0, 3, "KEY3"}, { -1, -1, -1, 4, "KEY4"}, { -1, -1, -1, 5, "KEY5"},};#endifstatic volatile char key_values [] = {'0', '0', '0', '0', '0', '0'}; //按键的状态(按下还是弹起)存储在这里
static DECLARE_WAIT_QUEUE_HEAD(button_waitq); //定义等待队列
static volatile int ev_press = 0;
//static irqreturn_t buttons_interrupt(int irq, void *dev_id) { struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id; //定义了按键中断结构体指针 int down;
// udelay(0); down = !s3c2410_gpio_getpin(button_irqs->pin); //读取按键引脚值取反赋给down
if (down != (key_values[button_irqs->number] & 1)) { // Changed //如果引脚值发生改变
key_values[button_irqs->number] = '0' + down; ev_press = 1; wake_up_interruptible(&button_waitq); } return IRQ_RETVAL(IRQ_HANDLED);}
static int s3c24xx_buttons_open(struct inode *inode, struct file *file){ int i; int err = 0; for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) { //逐个扫描按键 if (button_irqs[i].irq < 0) { //中断号小于0 continue; //跳出,直接执行下一个循环,扫描下一个按键 } err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, button_irqs[i].name, (void *)&button_irqs[i]); if (err) break; }
if (err) { i--; for (; i >= 0; i--) { if (button_irqs[i].irq < 0) { continue; } disable_irq(button_irqs[i].irq); free_irq(button_irqs[i].irq, (void *)&button_irqs[i]); } return -EBUSY; }
ev_press = 1; return 0;}
static int s3c24xx_buttons_close(struct inode *inode, struct file *file){ int i; for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) { if (button_irqs[i].irq < 0) { continue; } free_irq(button_irqs[i].irq, (void *)&button_irqs[i]); }
return 0;}
static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp){ unsigned long err;
if (!ev_press) { if (filp->f_flags & O_NONBLOCK) return -EAGAIN; else wait_event_interruptible(button_waitq, ev_press); } ev_press = 0;
err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));
return err ? -EFAULT : min(sizeof(key_values), count);}
static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait){ unsigned int mask = 0; poll_wait(file, &button_waitq, wait); if (ev_press) mask |= POLLIN | POLLRDNORM; return mask;}
static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = s3c24xx_buttons_open, .release = s3c24xx_buttons_close, .read = s3c24xx_buttons_read, .poll = s3c24xx_buttons_poll,};
static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops,};
static int __init dev_init(void){ int ret;
ret = misc_register(&misc);
printk (DEVICE_NAME"/tinitialized/n");
return ret;}
static void __exit dev_exit(void){ misc_deregister(&misc);}
module_init(dev_init);module_exit(dev_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("FriendlyARM Inc.");
1 中断处理
http://blog.chinaunix.net/u1/34474/showart_432711.html
内核维护了一个中断信号线的注册表,类似于 I/O 端口的注册表。模块在使用中断前要先请求一个中断通道(或者 IRQ中断请求),并在使用后释放它。所用的函数声明在 <linux/interrupt.h> (在此文件中并未真正包含,是通过它include的文件间接包含的,函数在/kernel/irq/Manage.h中),中断注册和释放的函数接口如下:
int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long flags,
const char *dev_name, void *dev_id);void free_irq(unsigned int irq, void *dev_id);
request_irq 的返回值: 0 指示成功,或返回一个负的错误码,如 -EBUSY 表示另一个驱动已经占用了你所请求的中断线。函数的参数如下:
unsigned int irq :请求的中断号 irqreturn_t (*handler) :安装的处理函数指针。 unsigned long flags :一个与中断管理相关的位掩码选项。 const char *dev_name :传递给 request_irq 的字符串,用来在 /proc/interrupts 来显示中断的拥有者。 void *dev_id :用于共享中断信号线的指针。它是唯一的标识,在中断线空闲时可以使用它,驱动程序也可以用它来指向自己的私有数据区(来标识哪个设备产生中断)。若中断没有被共享,dev_id 可以设置为 NULL,但推荐用它指向设备的数据结构。 flags 中可以设置的位如下: SA_INTERRUPT :快速中断标志。快速中断处理例程运行在当前处理器禁止中断的状态下。 SA_SHIRQ : 在设备间共享中断标志。 SA_SAMPLE_RANDOM :该位表示产生的中断能对 /dev/random 和 /dev/urandom 使用的熵池(entropy pool)有贡献。 读取这些设备会返回真正的随机数,从而有助于应用程序软件选择用于加密的安全密钥。 若设备以真正随机的周期产生中断,就应当设置这个标志。若设备中断是可预测的,这个标志不值得设置。可能被攻击者影响的设备不应当设置这个标志。更多信息看 drivers/char/random.c 的注释。中断处理例程可在驱动初始化时或在设备第一次打开时安装。推荐在设备第一次打开、硬件被告知产生中断前时申请中断,因为可以共享有限的中断资源。这样调用 free_irq 的位置是设备最后一次被关闭、硬件被告知不用再中断处理器之后。但这种方式的缺点是必须为每个设备维护一个打开计数。
http://blog.chinaunix.net/u2/88438/showart_2011915.html
