#include <linux/fs.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/ioport.h> #include <linux/pci.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/irq.h> #include <linux/interrupt.h> #include <asm/mach/irq.h> #include <asm/arch/regs-gpio.h> #include <linux/poll.h>
static int major = 0; static struct class *cls;
/* gpecon 0x56000040 */ /* gpfcon 0x56000050 */ /* gpgcon 0x56000060 */ static volatile unsigned long *gpecon; static volatile unsigned long *gpedat;
static volatile unsigned long *gpfcon; static volatile unsigned long *gpfdat;
static volatile unsigned long *gpgcon; static volatile unsigned long *gpgdat;
struct key_desc { int irq; int pin; char *name; char key_val; int irq_pin_val; };
struct key_desc key_desc[] = { {IRQ_EINT0, S3C2410_GPF0, "K10", 1}, /* 松开: 1, 按下: 0x81 */ {IRQ_EINT2, S3C2410_GPF2, "K7", 2}, /* 松开: 2, 按下: 0x82 */ {IRQ_EINT11, S3C2410_GPG3, "K4", 3}, /* 松开: 3, 按下: 0x83 */ {IRQ_EINT19, S3C2410_GPG11, "K1", 4}, /* 松开: 4, 按下: 0x84 */ }; volatile char key = 0;
static wait_queue_head_t button_waitq;
#define BUF_LEN 10 static char key_buf[BUF_LEN]; static volatile int r = 0, w = 0;
struct fasync_struct *buttons_async; static struct timer_list buttons_timer;
struct key_desc *cur_kd;
static int isEmpty(void) { return (r == w); }
static int isFull(void) { return (r == ((w+1)%BUF_LEN)); }
static int putData(char val) { if (isFull()) { return -1; } else { key_buf[w] = val; w = (w+1)%BUF_LEN; return 0; } }
static int getData(char *p) { if (isEmpty()) { return -1; } else { *p = key_buf[r]; r = (r+1)%BUF_LEN; return 0; } }
static irqreturn_t buttons_irq(int irq, void *dev_id) { cur_kd = (struct key_desc *)dev_id; cur_kd->irq_pin_val = s3c2410_gpio_getpin(cur_kd->pin);
mod_timer(&buttons_timer, jiffies+5);/*重置定时器*/ return IRQ_HANDLED; }
static void buttons_timer_function(unsigned long data) { /* 确定按键: 哪个按键,按下还是松开 */ // for (i = 0; i < 10000; i++); /* 浪费CPU */
/* 启动一个定时器: * 两要素: * 1. 时间 * 2. 处理函数 */ char key; int up;
if (!cur_kd) { return; } up = s3c2410_gpio_getpin(cur_kd->pin); // gpfdat, gpgdat
if (up != cur_kd->irq_pin_val) { return; } if (up) { key = cur_kd->key_val; } else { key = cur_kd->key_val | 0x80; }
// printk("key = 0x%x\n", key); putData(key);
/* 唤醒应用程序 */ wake_up_interruptible(&button_waitq); kill_fasync(&buttons_async, SIGIO, POLL_IN); //printk("buttons_irq current %s , pid = %d \n", current->comm, current->pid);
}
int buttons_open(struct inode *inode, struct file *file) { int i; /* 注册中断 */ for (i = 0; i < 4; i++) { request_irq(key_desc[i].irq, buttons_irq, IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, key_desc[i].name, &key_desc[i]); }
/* 设置GPIO为中断引脚 * 设置触发方式 * 使能中断 */
/* 设置KSCAN0(GPE11)为输出引脚,输出0 */ *gpecon &= ~(0x3 << 22); *gpecon |= (1 << 22); *gpedat &= ~(1<<11); return 0; }
ssize_t buttons_read(struct file *file, char __user *buf, size_t size, loff_t *offset) { /* 如果没有按键发生, 休眠 */ /* key 等于 0, 才会休眠 * key 非0, 不会休眠 */ // command //printk("current %s , pid = %d before sleep\n", current->comm, current->pid); char key;
if (isEmpty() && (file->f_flags & O_NONBLOCK)) return -EAGAIN; wait_event_interruptible(button_waitq, !isEmpty()); //printk("current %s , pid = %d after sleep\n", current->comm, current->pid);
/* 被唤醒后, 把按键值返回给用户程序 */ getData(&key); copy_to_user(buf, &key, 1);
/* 发生了中断, key=xxx */ //key = 0; return 1; }
int buttons_close(struct inode *inode, struct file *file) { int i; for (i = 0; i < 4; i++) { free_irq(key_desc[i].irq, &key_desc[i]); } return 0; }
static unsigned int buttons_poll(struct file *file, struct poll_table_struct *wait) { static int cnt = 0; printk("buttons_poll cnt = %d\n", cnt++); poll_wait(file, &button_waitq, wait); /* 不会休眠, 只是挂入队列 */ return isEmpty()? 0 : POLLIN | POLLRDNORM; }
static int buttons_fasync(int fd, struct file *filp, int on) { int retval;
retval = fasync_helper(fd, filp, on, &buttons_async); if (retval < 0) return retval; return 0; }
static const struct file_operations buttons_fops = { .owner = THIS_MODULE, .read = buttons_read, .open = buttons_open, /* 设置引脚,申请资源 */ .release = buttons_close, .poll = buttons_poll, .fasync = buttons_fasync, };
int buttons_init(void) { int i; major = register_chrdev(0, "buttons", &buttons_fops);
/* sysfs ==> 挂接到/sys */ cls = class_create(THIS_MODULE, "buttons_class"); class_device_create(cls, NULL, MKDEV(major, 0), NULL, "buttons");
// mdev会根据/sys下的这些内容创建/dev/buttons
gpecon = ioremap(0x56000040, 4096); gpedat = gpecon + 1;
gpfcon = gpecon + 4; gpfdat = gpfcon + 1;
gpgcon = gpecon + 8; gpgdat = gpgcon + 1;
init_waitqueue_head(&button_waitq);
init_timer(&buttons_timer); buttons_timer.function = buttons_timer_function; buttons_timer.expires = 0;
add_timer(&buttons_timer); return 0; }
void buttons_exit(void) { unregister_chrdev(major, "buttons");
class_device_destroy(cls, MKDEV(major, 0)); class_destroy(cls);
iounmap(gpecon);
del_timer(&buttons_timer); }
module_init(buttons_init); module_exit(buttons_exit);
MODULE_LICENSE("GPL");
|