linux驱动开发之key
creator
sz111@126.com
今天做了key的驱动,程序测试ok。
key的驱动牵涉的内核知识很多,有中断,内核定时器,阻塞。
后续有时间我会再写一个详细的分析。
测试如下:
先通过cat /proc/devices 插看utukey的主号为252.
mknod /dev/utukey c 252 0
测试程序如下:
/*
* Buttons Example for utulinux 2440
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(void)
{
int buttons_fd;
int key_value;
buttons_fd = open("/dev/utukey", 0);
if (buttons_fd < 0) {
perror("cann't open device /dev/buttons");
exit(1);
}
for (;;) {
int ret = read(buttons_fd, &key_value, sizeof key_value);
printf("You pressed buttons %d\n", key_value);
}
close(buttons_fd);
return 0;
}
驱动程序如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
按键共6个。
EINT0 EINT1 EINT2 EINT3 EINT11 EINT19
GPF0 GPF1 GPF2 GPF3 GPG3 GPG11
K1 K2 K3 K4 K5 K6
*/
#define DEVICE_NAME "utukey"
#define MAX_KEY_BUF 16 //KEY BUFFER SIZE
#define KEY_NUM 6 //key num
#define BUF_HEAD (utukey_dev.buf[utukey_dev.head])
#define BUF_TAIL (utukey_dev.buf[utukey_dev.tail])
#define INCBUF(x,mod) ((++(x))&((mod)-1))
#define KEY_TIME_DELAY (HZ/5)
#define KEY_TIME_DELAY1 (HZ/100)
#define UTU_KEY_UP 0x1000
#define UTU_KEY_HOLD 0x2000
#define UTUKEY_MAJOR 252
static dev_t utukey_major = UTUKEY_MAJOR;
#define KEYSTATUS_DOWNX 0
#define KEYSTATUS_DOWN 1
#define KEYSTATUS_UP 2
typedef unsigned int KEY_RET;
struct utukey_dev_t
{
struct cdev cdev;
unsigned int keyStatus[KEY_NUM];
KEY_RET buf[MAX_KEY_BUF];
unsigned int head,tail;
wait_queue_head_t wq;
unsigned int key_status;
};
struct utukey_dev_t utukey_dev;
struct timer_list key_timer[KEY_NUM];
static struct key_info
{
int irq;//中断号
unsigned int pin;//gpio port
unsigned int pin_setting;
int key_code;//key value
char *name;
}key_info_tab[] =
{
{
IRQ_EINT0,S3C2410_GPF0,S3C2410_GPF0_EINT0,KEY_UP,"Key Up"
},
{
IRQ_EINT1,S3C2410_GPF1,S3C2410_GPF1_EINT1,KEY_DOWN,"Key Down"
},
{
IRQ_EINT2,S3C2410_GPF2,S3C2410_GPF2_EINT2,KEY_LEFT,"Key Left"
},
{
IRQ_EINT3,S3C2410_GPF3,S3C2410_GPF3_EINT3,KEY_RIGHT,"Key Right"
},
{
IRQ_EINT11,S3C2410_GPG3,S3C2410_GPG3_EINT11,KEY_ENTER,"Key Enter"
},
{
IRQ_EINT19,S3C2410_GPG11,S3C2410_GPG11_EINT19,KEY_EXIT,"Key Exit"
},
};
#define ISKEY_DOWN(key) (s3c2410_gpio_getpin(key_info_tab[key].pin) == 0)
static void utu2440button_timer_callback(unsigned long data);
static irqreturn_t utu2440_isr_kbd(int irq, void *dev_id, struct pt_regs *reg);
static int utukey_open(struct inode *inode, struct file *filp);
static int utukey_release(struct inode *inode, struct file *filp);
static ssize_t utukey_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos);
static ssize_t utukey_read(struct file *file,char __user *buffer, size_t count, loff_t *ppos);
static const struct file_operations utukey_fops =
{
.owner = THIS_MODULE,
.read = utukey_read,
.write = utukey_write,
.open = utukey_open,
.release = utukey_release,
};
static void utukey_setup_cdev(void)
{
int i;
int err,devno = MKDEV(utukey_major,0);
cdev_init(&utukey_dev.cdev,&utukey_fops);
utukey_dev.cdev.owner = THIS_MODULE;
utukey_dev.cdev.ops = &utukey_fops;
utukey_dev.key_status = 0;
err = cdev_add(&utukey_dev.cdev, devno, 1);
if (err)
{
printk(KERN_NOTICE "Error %d adding utukey",err);
}
request_irqs();
utukey_dev.head = utukey_dev.tail = 0;
for(i=0; i {
utukey_dev.keyStatus[i] = KEYSTATUS_UP;
}
init_waitqueue_head(&(utukey_dev.wq));
for(i=0; i {
key_timer[i].function = utu2440button_timer_callback;
key_timer[i].data = i;
init_timer(&key_timer[i]);
}
}
static int utukey_open(struct inode *inode, struct file *filp)
{
filp->private_data = &utukey_dev;
printk(KERN_NOTICE "utukey opened\n");
return 0;
}
static int utukey_release(struct inode *inode, struct file *filp)
{
printk(KERN_NOTICE "utukey released\n");
return 0;
}
static ssize_t utukey_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos)
{
return 0;
}
static ssize_t utukey_read(struct file *filp,char __user *buffer, size_t count, loff_t *ppos)
{
KEY_RET key_ret;
struct utukey_dev_t *dev;
dev = (struct utukey_dev_t*)filp->private_data;
retry:
if (utukey_dev.head != utukey_dev.tail)
{
key_ret = BUF_TAIL;
dev->tail = INCBUF(dev->tail,MAX_KEY_BUF);
copy_to_user(buffer,(char*)&key_ret,sizeof(KEY_RET));
return sizeof(KEY_RET);
}else
{
if (filp->f_flags & O_NONBLOCK)
{
return -EAGAIN;
}
interruptible_sleep_on(&(dev->wq));
if (signal_pending(current))
{
return -ERESTARTSYS;
}
goto retry;
}
return sizeof(KEY_RET);
}
static void keyEvent(KEY_RET key)
{
if (utukey_dev.keyStatus[key] == KEYSTATUS_DOWNX)//刚按下
{
BUF_HEAD = key_info_tab[key].key_code;
}else if (utukey_dev.keyStatus[key] == KEYSTATUS_DOWN)//一直按下
{
BUF_HEAD = key_info_tab[key].key_code | UTU_KEY_HOLD;
}else if (utukey_dev.keyStatus[key] == KEYSTATUS_UP)//抬起
{
BUF_HEAD = key_info_tab[key].key_code | UTU_KEY_UP;
}
utukey_dev.head = INCBUF(utukey_dev.head,MAX_KEY_BUF);
wake_up_interruptible(&(utukey_dev.wq));
}
static irqreturn_t utu2440_isr_kbd(int irq, void *dev_id, struct pt_regs *reg)
{
int key = dev_id;
int i;
int found = 0;
for (i=0; i {
if (key_info_tab[i].irq == irq) {
found = 1;
break;
}
}
if (!found)
{
printk(KERN_NOTICE"bad irq %d in button\n", irq);
return IRQ_NONE;
}
//printk(KERN_NOTICE "utus2440_isr_kbd:key:%d\n",key);
disable_irq(key_info_tab[key].irq);
utukey_dev.keyStatus[key] = KEYSTATUS_DOWNX;
key_timer[key].expires = jiffies + KEY_TIME_DELAY1;
add_timer(&key_timer[key]);
return IRQ_HANDLED;
}
static void utu2440button_timer_callback(unsigned long data)
{
int key = data;
if (ISKEY_DOWN(key))
{
//printk(KERN_NOTICE"utukey_dev.keyStatus[key]:%d\n",key);
if (utukey_dev.keyStatus[key] == KEYSTATUS_DOWNX)//从中断进入
{
keyEvent(key);
utukey_dev.keyStatus[key] = KEYSTATUS_DOWN;
key_timer[key].expires = jiffies + KEY_TIME_DELAY;
add_timer(&key_timer[key]);
}else
{
keyEvent(key);
key_timer[key].expires = jiffies + KEY_TIME_DELAY;//HOLD key,每隔200MS发送一次
add_timer(&key_timer[key]);
}
}else
{
utukey_dev.keyStatus[key] = KEYSTATUS_UP;
//del_timer(&key_info_tab[key]);
keyEvent(key);
enable_irq(key_info_tab[key].irq);
}
}
//申请irq中断
static int request_irqs(void)
{
int i;
for (i=0; i {
s3c2410_gpio_cfgpin(key_info_tab[i].pin, key_info_tab[i].pin_setting);
set_irq_type(key_info_tab[i].irq, IRQT_FALLING);//下降沿触发
if (request_irq(key_info_tab[i].irq, utu2440_isr_kbd, SA_INTERRUPT, DEVICE_NAME, i))
{
return -1;
}
}
return 0;
}
//释放irq中断
static void free_irqs(void)
{
int i;
for (i=0; i {
free_irq(key_info_tab[i].irq, i);
}
}
static int __init utukey_init(void)
{
int result;
dev_t devno = MKDEV(utukey_major,0);
if (utukey_major)
{
result = register_chrdev_region(devno, 1, DEVICE_NAME);
}else
{
result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
utukey_major = MAJOR(devno);
}
if (result < 0)
{
return result;
}
utukey_setup_cdev();
//key init
//key_init();
return 0;
}
static void __exit utukey_exit(void)
{
int i;
cdev_del(&utukey_dev.cdev);
unregister_chrdev_region(MKDEV(utukey_major,0),1);
for(i=0; i {
del_timer(&key_timer[i]);
}
free_irqs();
}
MODULE_AUTHOR("Creator");
MODULE_LICENSE("Dual BSD/GPL");
module_init(utukey_init);
module_exit(utukey_exit);
阅读(716) | 评论(0) | 转发(0) |