//********************************************//
// 书写规范 //
//结构体定义:一律大写字母,中间可用"_"区分 //
//全局变量 :全部用小写字母,加前缀"g_" //
//局部变量 :全部用小写字母组合,无其他前后缀 //
//指针变量 :在变量前加"p",优先级比"g_"低 //
//数组 :在变量前加"t",优先级比"g_"低 //
//自制函数 :自制函数名字都以"key_"作为前缀 //
//********************************************//
#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>
//*******************************
//some macro define
//*******************************
#define KEY_NUM 6 #define MAX_KEY_BUF 16 //for circle link
#define MINI2440_KEY_MAJOR 250 #define KEY_BUF_CLR 0x01 #define KEY_DELAY_20MS (HZ/50) //delay 20ms
#define KEY_DELAY_100MS (HZ/10) //delay 100ms
//buf循环链表:让x在mod进制下循环
//要求(mod-1)转换为二进制后必须是所有的"1"是连续在低位放置
//比如:mod=8; mod-1=7; 7 = 0x0000 0111(B)
//而不能是比如mod=9; mod-1=8; 8 = 0x0000 1000(B)
#define INC_BUF_POINTOR(x,mod) ((++(x))&((mod)-1))
//判断Key所在端口的状态
#define ISKEY_DOWN(key) (s3c2410_gpio_getpin(g_tkey_info[key].gpio_port) == 0)
//*******************************
//key status define
//*******************************
#define KEYSTATUS_UP 0 //none action
#define KEYSTATUS_DOWN 1 //press key
#define KEYSTATUS_X 2 //unsure state
static int g_key_major = MINI2440_KEY_MAJOR;
struct KEY_DEV { unsigned int tkeystatus[KEY_NUM]; //6个按键的状态
unsigned char tbuf[MAX_KEY_BUF]; //按键缓冲区
unsigned int head,tail; //按键缓冲区头和尾
wait_queue_head_t wq; //等待队列
struct cdev cdev; };
struct KEY_DEV *g_pkey_dev;
static struct timer_list g_tkey_timer[KEY_NUM]; //6个按键去抖计时器
struct KEY_INFO { int irq_no; //中断号
unsigned int gpio_port; //GPOI端口
int key_no; };
struct KEY_INFO g_tkey_info[KEY_NUM] = { //定义按键所使用的资源
{ IRQ_EINT8, S3C2410_GPG0, 0 }, //NO. 1 2 3 ... 与电路图匹配
{ IRQ_EINT11, S3C2410_GPG3, 1 }, { IRQ_EINT13, S3C2410_GPG5, 2 },
{ IRQ_EINT15, S3C2410_GPG7, 3 },
{ IRQ_EINT14, S3C2410_GPG6, 4 },
{ IRQ_EINT19, S3C2410_GPG11, 5 }, };
static irqreturn_t key_eint_handler(int irq, void *dev_id) { int cnt,key_index; key_index = 0; for(cnt=0; cnt<KEY_NUM; cnt++) { if(g_tkey_info[cnt].irq_no == irq) { key_index = g_tkey_info[cnt].key_no; break; } } // printk(KERN_NOTICE "Eint %d\n",key_index);
disable_irq(g_tkey_info[key_index].irq_no); //disable irq
g_pkey_dev->tkeystatus[key_index] = KEYSTATUS_X; //set key in unsure state
g_tkey_timer[key_index].expires = jiffies + KEY_DELAY_20MS; //set timer value
add_timer(&g_tkey_timer[key_index]); //start timer
return IRQ_HANDLED; }
static int request_irqs(void) { //申请中断
struct KEY_INFO *key_info; int i; for(i=0; i<(sizeof(g_tkey_info)/sizeof(g_tkey_info[1])); i++) { key_info = g_tkey_info + i; // set_external_irq(key_info->irq_no, EXT_LOWLEVEL, GPIO_PULLUP_DIS); //set INT low voltage level target
// if(request_irq(key_info->irq_no, , SA_INTERRUPT, "Mini2440_Key", i))
// /include/linux/irq.h
// #define RQ_TYPE_NONE 0x00000000 /* Default, unspecified type */
// #define IRQ_TYPE_EDGE_RISING 0x00000001 /* Edge rising type */
// #define IRQ_TYPE_EDGE_FALLING 0x00000002 /* Edge falling type */
// #define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)
// #define IRQ_TYPE_LEVEL_HIGH 0x00000004 /* Level high type */
// #define IRQ_TYPE_LEVEL_LOW 0x00000008 /* Level low type */
// #define IRQ_TYPE_SENSE_MASK 0x0000000f /* Mask of the above */
// #define IRQ_TYPE_PROBE 0x00000010 /* Probing in progress */
// set_external_irq(key_info->irq_no, IRQ_TYPE_LEVEL_LOW, GPIO_PULLUP_DIS); //set INT low voltage level target
// int set_irq_type (unsigned int irq, unsigned int type);
set_irq_type(g_tkey_info[i].irq_no, IRQ_TYPE_LEVEL_LOW);
/* /include/linux/interrupt.h * flags used only by the kernel as part of the irq handling routines. * IRQF_DISABLED - keep irqs disabled when calling the action handler * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator * IRQF_SHARED - allow sharing the irq among several devices * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur * IRQF_TIMER - Flag to mark this interrupt as timer interrupt * IRQF_PERCPU - Interrupt is per cpu * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is * registered first in an shared interrupt is considered for * performance reasons) * * #define IRQF_DISABLED 0x00000020 * #define IRQF_SAMPLE_RANDOM 0x00000040 * #define IRQF_SHARED 0x00000080 * #define IRQF_PROBE_SHARED 0x00000100 * #define IRQF_TIMER 0x00000200 * #define IRQF_PERCPU 0x00000400 * #define IRQF_NOBALANCING 0x00000800 * #define IRQF_IRQPOLL 0x00001000 */ if(request_irq(key_info->irq_no, key_eint_handler, IRQF_DISABLED,"Mini2440_Key", &i)) { return -1; } } return 0; }
void free_irqs(void) { struct KEY_INFO *key_info; int i; for(i=0; i<(sizeof(g_tkey_info)/sizeof(g_tkey_info[1])); i++) { key_info = g_tkey_info + i; free_irq(key_info->irq_no, &i); } }
static void keyEvent(int key_index) { g_pkey_dev->tbuf[g_pkey_dev->head] = key_index; g_pkey_dev->head = INC_BUF_POINTOR(g_pkey_dev->head,MAX_KEY_BUF); wake_up_interruptible(&g_pkey_dev->wq); }
static void key_timer_handler(unsigned long data) { int key_index = data; //printk("B:get key %d\n",s3c2410_gpio_getpin(g_tkey_info[key_index].gpio_port));
if (ISKEY_DOWN(key_index)) { // printk(KERN_NOTICE "B\n");
if(g_pkey_dev->tkeystatus[key_index] == KEYSTATUS_X) { g_pkey_dev->tkeystatus[key_index] = KEYSTATUS_DOWN; //change key state
g_tkey_timer[key_index].expires = jiffies + KEY_DELAY_100MS; //re_initial timer
keyEvent(key_index);
add_timer(&g_tkey_timer[key_index]); //restart timer
} else //wait for user release the key
{ g_tkey_timer[key_index].expires = jiffies + KEY_DELAY_100MS; add_timer(&g_tkey_timer[key_index]); } } else //user have released the key
{ g_pkey_dev->tkeystatus[key_index] = KEYSTATUS_UP; //del_timer(&g_tkey_timer[key_index]);
enable_irq(g_tkey_info[key_index].irq_no); } }
static int key_open(struct inode *inode, struct file *filp) { printk(KERN_NOTICE "key opened\n"); g_pkey_dev->head = g_pkey_dev->tail = 0; return 0; }
static int key_release(struct inode *inode, struct file *filp) { return 0; }
static ssize_t key_read(struct file *filp, char *buf, size_t count, loff_t *ppos) { unsigned int ret,temp; unsigned long flag; retry: if(g_pkey_dev->head != g_pkey_dev->tail) { local_irq_save(flag); //进入临界区,关闭中断
ret = g_pkey_dev->tbuf[g_pkey_dev->tail]; //读取尾部指针所指内容
g_pkey_dev->tail = INC_BUF_POINTOR(g_pkey_dev->tail, MAX_KEY_BUF); local_irq_restore(flag); //退出临界区
//printk(KERN_NOTICE "driver key_read,key no:%d\n",ret);
temp = copy_to_user(buf, &ret, sizeof(unsigned int)); //printk(KERN_NOTICE "copy to user return %d\n", temp);
return (sizeof(unsigned int)); } else { //printk(KERN_NOTICE "A\n");
if(filp->f_flags & O_NONBLOCK) { return -EAGAIN; }
//printk("E:test %d\n",s3c2410_gpio_getpin(g_tkey_info[0].gpio_port));
interruptible_sleep_on(&(g_pkey_dev->wq)); goto retry; } // return 0;
}
static int key_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg) { unsigned long flag; switch(cmd) { case KEY_BUF_CLR: local_irq_save(flag); g_pkey_dev->head = g_pkey_dev->tail = 0; local_irq_restore(flag); printk(KERN_NOTICE "key buf is clear\n"); break;
default: return - EINVAL; } return 0; }
static struct file_operations g_tkey_fops = { .owner = THIS_MODULE, .open = key_open, //打开设备
.release = key_release, //关闭设备
.read = key_read, //读取按键的键值
.ioctl = key_ioctl, //清除缓冲区
};
static void key_setup_cdev(struct KEY_DEV *pdev, int index) { //1. cdev init
//2. cdev bind fops
//3. cdev add
int err, devno; devno = MKDEV(g_key_major, index);
cdev_init(&(g_pkey_dev->cdev), &g_tkey_fops); pdev->cdev.owner = THIS_MODULE; pdev->cdev.ops = &g_tkey_fops;
err = cdev_add(&pdev->cdev, devno, 1); if(err) { printk(KERN_NOTICE "Error %d adding dev %d", err, index); } }
static int mini2440_key_init(void) { //**********************************
//申请设备号,添加设备
//**********************************
int ret,i; dev_t devno = MKDEV(g_key_major, 0);
if(g_key_major) { ret = register_chrdev_region(devno, 1, "Mini2440_Key"); } else { ret = alloc_chrdev_region(&devno, 0, 1,"Mini2440_Key"); g_key_major = MAJOR(devno); }
if(ret < 0) { return ret; }
g_pkey_dev = kmalloc(sizeof(struct KEY_DEV), GFP_KERNEL); if(!g_pkey_dev) { ret = -ENOMEM; goto fail_malloc; } memset(g_pkey_dev, 0, sizeof(struct KEY_DEV));
key_setup_cdev(g_pkey_dev, 0); //**********************************
//申请设备号,添加设备 完毕!
//下面初始化其他内容
//**********************************
request_irqs(); //request all the key irq
g_pkey_dev->head = g_pkey_dev->tail = 0; //initial key_dev
for(i=0; i<KEY_NUM; i++) { g_pkey_dev->tkeystatus[i] = KEYSTATUS_UP; }
init_waitqueue_head(&(g_pkey_dev->wq)); //initial wait queue
for(i=0; i<KEY_NUM; i++) { // setup_timer(&g_tkey_timer[i], key_timer_handler,i);
g_tkey_timer[i].function = key_timer_handler; g_tkey_timer[i].data = i; init_timer(&g_tkey_timer[i]); }
return 0;
fail_malloc:unregister_chrdev_region(devno, 1); return ret; }
static void key_exit(void) { free_irqs(); //free irq
cdev_del(&g_pkey_dev->cdev); //del cdev
kfree(g_pkey_dev); //free memory
g_pkey_dev = NULL; unregister_chrdev_region(MKDEV(g_key_major,0), 1); }
MODULE_AUTHOR("Benson"); MODULE_LICENSE("Dual BSD/GPL");
module_param(g_key_major, int, S_IRUGO); module_init(mini2440_key_init); module_exit(key_exit);
|