按键驱动(浏览按键)
来源:互联网 发布:游戏公司防止源码外泄 编辑:程序博客网 时间:2024/05/20 12:47
//#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/spinlock.h>
#include <linux/irq.h>
#include <asm/hardware.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <asm-arm/arch-s3c2410/regs-gpio.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm-arm/arch-s3c2410/irqs.h>
#include <asm-arm/irq.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include <asm/hardware.h>
#include <asm/arch/regs-gpio.h>
#define DEVICE_NAME "button"
#define MAX_KEY_COUNT 32
#define EXTINT0 *(volatile unsigned int *)S3C2410_EXTINT0
#define EXTINT1 *(volatile unsigned int *)S3C2410_EXTINT1
#define EXTINT2 *(volatile unsigned int *)S3C2410_EXTINT2
MODULE_LICENSE("GPL");//模块应该指定代码所使用的许可证
typedef struct
{
unsigned long jiffy[MAX_KEY_COUNT]; //按键时间, 如果读键时, 5秒钟以前的铵键作废
unsigned char buf[MAX_KEY_COUNT]; //按键缓冲区
unsigned int head,tail; //按键缓冲区头和尾
}KEY_BUFFER;
static KEY_BUFFER g_keyBuffer; //键盘缓冲区
static spinlock_t buffer_lock; //缓冲区锁
static int button_major = 255; //Define device major add by yoyo
static void *gpecon;
static void *gpedat;
static void *gpfcon;
static void *gpfdat;
static void *gpgcon;
static void *gpgdat;
/*
*功能: 获取当前的毫秒数(从系统启动开始)
*入口:
*/
static unsigned long GetTickCount(void)
{
struct timeval currTick;
unsigned long ulRet;
do_gettimeofday(&currTick);
ulRet = currTick.tv_sec;
ulRet *= 1000;
ulRet += (currTick.tv_usec + 500) / 1000;
return ulRet;
}
/*
*功能: 初始化键盘缓冲区
*入口:
*/
static void init_keybuffer(void)
{
int i;
spin_lock_irq(&buffer_lock); //获得一个自旋锁具有不会受中断的干扰
g_keyBuffer.head = 0;
g_keyBuffer.tail = 0;
for(i = 0; i < MAX_KEY_COUNT; i++)
{
g_keyBuffer.buf[i] = 0;
g_keyBuffer.jiffy[i] = 0;
}
spin_unlock_irq(&buffer_lock);//释放自旋锁
}
/*
*功能: 删除过时(5秒前的按键值)
*入口:
*/
static void remove_timeoutkey(void)
{
unsigned long ulTick;
spin_lock_irq(&buffer_lock); //获得一个自旋锁具有不会受中断的干扰
while(g_keyBuffer.head != g_keyBuffer.tail)
{
ulTick = GetTickCount() - g_keyBuffer.jiffy[g_keyBuffer.head];
if (ulTick < 5000) //5秒
break;
g_keyBuffer.buf[g_keyBuffer.head] = 0;
g_keyBuffer.jiffy[g_keyBuffer.head] = 0;
g_keyBuffer.head ++;
g_keyBuffer.head &= (MAX_KEY_COUNT -1);
}
spin_unlock_irq(&buffer_lock);//释放自旋锁
}
/*
*功能: 初始化GPIO, 设置中断0, 2, 11, 19为下降沿中断
*入口:
*/
static void init_gpio(void)
{
//将GPE13 11 设置低位
writel((readl(gpecon) | ((3<<26)|(3<<22))) & (~((1<<27)|(1<<23))), gpecon); //GPE13,11 设置为输出
writel(readl(gpedat) & 0xffffd7ff, gpedat); //GPE13,11 输出为0
//将GPG6, 2 设置低位
writel((readl(gpgcon) | 0x3030) & 0xffffdfdf, gpgcon); //GPG6,2 设置为输出
writel(readl(gpgdat) & 0xffffffbb, gpgdat); //GPG6,2 输出为0
writel((readl(gpfcon) | 0x33) & 0xffffffee, gpfcon); //GPF2, 0 设置为中断
writel((readl(gpgcon) | (3<<22) | (3<<6)) & (~((1<<22) | (1<<6))), gpgcon); //GPG11,3 设置为中断
set_irq_type(IRQ_EINT0, IRQT_FALLING); //下降沿触发(设置中断类型)
EXTINT0=(EXTINT0&(~0x07))+0x02;
set_irq_type(IRQ_EINT2, IRQT_FALLING);
EXTINT0=(EXTINT0&(~(0x07<<8)))+(0x02<<8);
set_irq_type(IRQ_EINT11, IRQT_FALLING);
EXTINT1=(EXTINT1&(~(0x07<<12)))+(0x02<<12);
set_irq_type(IRQ_EINT19, IRQT_FALLING);
EXTINT2=(EXTINT2&(~(0x07<<12)))+(0x02<<12);
}
/*
*功能: 激活中断
*入口:
*/
static __inline void enable_irqs(void)
{
enable_irq(IRQ_EINT0);
enable_irq(IRQ_EINT2);
enable_irq(IRQ_EINT11);
enable_irq(IRQ_EINT19);
}
/*
*功能: 屏蔽中断
*入口:
*/
static __inline void disable_irqs(void)
{
disable_irq(IRQ_EINT0);
disable_irq(IRQ_EINT2);
disable_irq(IRQ_EINT11);
disable_irq(IRQ_EINT19);
}
/*
*功能: 进入中断后, 扫描铵键码
*入口:
*返回: 按键码(1-16), 0xff表示错误
*/
static __inline unsigned char button_scan(int irq)
{
long lGPF, lGPG;
writel((readl(gpfcon) | 0x33) & 0xffffffcc, gpfcon); //GPF2,0 input
writel((readl(gpgcon) | (3<<22) | (3<<6)) & (~((3<<22) | (3<<6))), gpgcon); //GPG11,3 input
//不利用irq号, 直接扫描键盘
//设置G2低位, G6, E11, E13高位
writel((readl(gpgdat) | (1<<6)) & (~(1<<2)), gpgdat);
writel(readl(gpedat) | (1<<11) | (1<<13), gpedat);
//取GPF0, GPF2, GPG3, GPG11的值
lGPF = readl(gpfdat);
lGPG = readl(gpgdat);
//判断按键
if ((lGPF & (1<<0)) == 0) return 16;
else if((lGPF & (1<<2)) == 0) return 15;
else if((lGPG & (1<<3)) == 0) return 14;
else if((lGPG & (1<<11)) == 0) return 13;
//设置G6低位, G2, E11, E13高位
writel((readl(gpgdat) | (1<<2)) & (~(1<<6)), gpgdat);
lGPF = readl(gpfdat);
lGPG = readl(gpgdat);
if ((lGPF & (1<<0)) == 0) return 11;
else if((lGPF & (1<<2)) == 0) return 8;
else if((lGPG & (1<<3)) == 0) return 5;
else if((lGPG & (1<<11)) == 0) return 2;
//设置E11低位, G2, G6, E13高位
writel(readl(gpgdat) | (1<<6) | (1<<2), gpgdat);
writel((readl(gpedat) | (1<<13)) & (~(1<<11)), gpedat);
lGPF = readl(gpfdat);
lGPG = readl(gpgdat);
if ((lGPF & (1<<0)) == 0) return 10;
else if((lGPF & (1<<2)) == 0) return 7;
else if((lGPG & (1<<3)) == 0) return 4;
else if((lGPG & (1<<11)) == 0) return 1;
//设置E13低位, G2, G6, E11高位
//writel(readl(gpgdat) | (1<<6) | (1<<2), gpgdat);
writel((readl(gpedat) | (1<<11)) & (~(1<<13)), gpedat);
lGPF = readl(gpfdat);
lGPG = readl(gpgdat);
if ((lGPF & (1<<0)) == 0) return 12;
else if((lGPF & (1<<2)) == 0) return 9;
else if((lGPG & (1<<3)) == 0) return 6;
else if((lGPG & (1<<11)) == 0) return 3;
return 0xff ;
}
/*
*功能: 中断函数,
*入口: irq 中断号
*
*/
//struct pt_regs:This struct defines the way the registers are stored on the
//stack during a system call
static irqreturn_t button_irq(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned char ucKey;
disable_irqs();
printk("in irq\n");
//延迟50毫秒, 屏蔽按键毛刺
__udelay(50000);
ucKey = button_scan(irq);
if ((ucKey >= 1) && (ucKey <= 16))
{
//如果缓冲区已满, 则不添加
if (((g_keyBuffer.head + 1) & (MAX_KEY_COUNT - 1)) != g_keyBuffer.tail)
{
spin_lock_irq(&buffer_lock);
g_keyBuffer.buf[g_keyBuffer.tail] = ucKey;
g_keyBuffer.jiffy[g_keyBuffer.tail] = GetTickCount();
g_keyBuffer.tail ++;
g_keyBuffer.tail &= (MAX_KEY_COUNT -1);
spin_unlock_irq(&buffer_lock);
}
}
init_gpio();
enable_irqs();
//printk("in irq! %x\n",EXTINT0);
return IRQ_HANDLED;//2.6内核返回值一般是这个宏。
}
/*
*功能: 申请中断
*入口:
*
*/
static int request_irqs()
{
int ret;
ret = request_irq(IRQ_EINT0, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
if (ret < 0)
return ret;
ret = request_irq(IRQ_EINT2, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
if (ret >= 0)
{
ret = request_irq(IRQ_EINT11, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
if (ret >= 0)
{
ret = request_irq(IRQ_EINT19, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
if (ret >= 0)
return ret;
free_irq(IRQ_EINT11, button_irq);
}
free_irq(IRQ_EINT2, button_irq);
}
free_irq(IRQ_EINT0, button_irq);
return ret;
}
/*
*功能: 释放中断
*入口:
*
*/
static __inline void free_irqs()
{
free_irq(IRQ_EINT0, NULL);//button_irq);
free_irq(IRQ_EINT2, NULL);//button_irq);
free_irq(IRQ_EINT11, NULL);//button_irq);
free_irq(IRQ_EINT19, NULL);//button_irq);
}
/*
*功能: 打开文件, 开始中断
*入口:
*
*/
static int button_open(struct inode *inode,struct file *filp)
{
int ret = nonseekable_open(inode, filp);
if (ret >= 0)
{
init_keybuffer();
enable_irqs();
}
return ret;
}
/*
*功能: 关闭文件, 屏蔽中断
*入口:
*
*/
static int button_release(struct inode *inode,struct file *filp)
{
disable_irqs();
return 0;
}
/*
*功能: 读键盘
*入口:
*
*/
static ssize_t button_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
ssize_t ret = 0;
remove_timeoutkey();
spin_lock_irq(&buffer_lock);
while((g_keyBuffer.head != g_keyBuffer.tail) && (((size_t)ret) < count) )
{
buffer[ret] = (char)(g_keyBuffer.buf[g_keyBuffer.head]);
g_keyBuffer.buf[g_keyBuffer.head] = 0;
g_keyBuffer.jiffy[g_keyBuffer.head] = 0;
g_keyBuffer.head ++;
g_keyBuffer.head &= (MAX_KEY_COUNT -1);
ret ++;
}
spin_unlock_irq(&buffer_lock);
return ret;
}
/*
*功能: 清空键盘缓冲区
*入口:
*
*/
static int button_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
init_keybuffer();
return 1;
}
//add by yoyo
/*
*初始化并添加结构提struct cdev到系统之中
*/
static void led_setup_cdev(struct cdev *dev,int minor,struct file_operations *fops)
{
int err;
int devno=MKDEV(button_major,minor);
cdev_init(dev,fops);//初始化结构体struct cdev
dev->owner=THIS_MODULE;
dev->ops=fops;//给结构体里的ops成员赋初值,这里是对设备操作的具体的实现函数
err=cdev_add(dev,devno,1);//将结构提struct cdev添加到系统之中
if(err)
printk(KERN_INFO"Error %d adding button %d\n",err,minor);
}
/*
*定义一个file_operations结构体,来实现对设备的具体操作的功能
*/
static struct file_operations button_fops =
{
.owner = THIS_MODULE,
.ioctl = button_ioctl,
.open = button_open,
.read = button_read,
.release = button_release,
};
static struct cdev SimpleDevs; //add by yoyo
/*
*功能: 驱动初始化
*入口:
*
*/
static int button_init(void)
{
int ret;
int result; //add by yoyo
gpecon = ioremap(0x56000040, 0x04);//得到相应IO口的虚拟地址,下同
gpedat = ioremap(0x56000044, 0x04);
gpfcon = ioremap(0x56000050, 0x04);
gpfdat = ioremap(0x56000054, 0x04);
gpgcon = ioremap(0x56000060, 0x04);
gpgdat = ioremap(0x56000064, 0x04);
init_gpio();
ret = request_irqs();
if (ret < 0) return ret;
disable_irqs();
//add by yoyo
dev_t dev=MKDEV(button_major,0);//将主设备号和次设备号定义到一个dev_t数据类型的结构体之中
if(button_major)
result=register_chrdev_region(dev,1,"button");//静态注册一个设备,设备号先前指定好,并得到一个设备名,cat /proc/device来查看信息
else
{
result=alloc_chrdev_region(&dev,0,1,"button");//如果主设备号被占用,则由系统提供一个主设备号给设备驱动程序
button_major=MAJOR(dev);//得到主设备号
}
if(result<0)
{
printk(KERN_WARNING"button:unable to get major %d\n",button_major);
return result;
}
if(button_major==0)
button_major=result;//如果静态分配失败。把动态非配的设备号给设备驱动程序
printk(KERN_INFO"button register ok!!!!!!!!!!\n");
led_setup_cdev(&SimpleDevs,0,&button_fops);//初始化和添加结构体struct cdev到系统之中
//return 0;
printk("button initialized.\n");
return 0;
}
/*
*功能: 驱动释放
*入口:
*
*/
static void __exit button_exit(void)
{
disable_irqs();
free_irqs();
iounmap(gpecon);
iounmap( gpedat);
iounmap(gpfcon);
iounmap(gpfdat);
iounmap(gpgcon);
iounmap(gpgdat);
cdev_del(&SimpleDevs);//删除结构体struct cdev
printk("button_major=%d\n",button_major);
unregister_chrdev_region(MKDEV(button_major,0),1);//卸载设备驱动所占有的资源
printk("button device uninstalled\n");
}
module_init(button_init);//初始化设备驱动程序的入口
module_exit(button_exit);//卸载设备驱动程序的入口
应用编程:
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
main()
{
int fd;
char key=0;
fd = open("/dev/button_scan", O_RDWR);//打开设备
if (fd == -1)
{
printf("open device button errr!\n");
return 0;
}
ioctl(fd,0,0); //清空键盘缓冲区, 后面两个参数没有意义,
while(key != 16)
{
if (read(fd, &key, 1) > 0)//读键盘设备,得到相应的键值
{
printf("*********************Key Value = %d*****************************\n", key);
}
}
close(fd);// //关闭设备
return 0;
}
网上资料:
/*
1、阻塞读取,队列概念
2、睡眠等待中断产生
3、定时器产生及相应函数
疑问:去除按键缓冲
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-irq.h>
#define KEYSTATUS_DOWNX 2 // 定义按键不确定状态
#define KEYSTATUS_DOWN 0 // 定义按键按下后电平
#define KEYSTATUS_UP 1 // 定义按键抬起后电平
#define BUF_CLEAR _IO(0xFF, 0) // 清除键盘缓冲区命令
#define DEVICE_NAME "utukey"
#define MAX_KEY_BUF 16 // 按键缓冲区大小
#define KEY_NUM 6 // 按键个数
#define BUF_HEAD (utukey_dev.buf[utukey_dev.head]) //缓冲头
#define BUF_TAIL (utukey_dev.buf[utukey_dev.tail]) //缓冲尾
#define ISKEY_DOWN(key) (s3c2410_gpio_getpin(key_info_tab[key].gpio_port) == KEYSTATUS_DOWN)
#define INCBUF(x,mod) ((++(x))&((mod)-1))
#define KEY_TIME_DELAY (HZ/10) // 100ms
#define KEY_TIME_DELAY1 (HZ/100) // 10ms
#define UTUKEY_MAJOR 0 // 定义0使用自动分配设备号
unsigned int utukey_major = UTUKEY_MAJOR;
/* 定义按键设备结构体 */
struct utukey_dev
{
struct cdev cdev; // cdev结构体
unsigned int key_status[KEY_NUM]; // 记录按键状态
unsigned int buf[MAX_KEY_BUF]; // 按键环形缓冲区
unsigned int head, tail; // 按键缓冲区头和尾
wait_queue_head_t wq; // 等待队列
};
struct utukey_dev utukey_dev; // 定义设备结构体
struct utukey_dev *utukey_devp; // 定义设备结构体指针
struct timer_list key_timer[KEY_NUM]; // 定义6个按键去抖动定时器
static struct key_info // 定义按键所用资源结构体
{
int irq_no; // 占用的中断号
int irq_type; // 中断类型
unsigned int gpio_port; // 占用的引脚
unsigned int gpio_setting; // 引脚设置值
int key_code; // 按键值
char *name; // 按键的对应字符串
}key_info_tab[] =
{
{
IRQ_EINT11, IRQT_FALLING, S3C2410_GPG3, S3C2410_GPG3_INP, 1, "Key Up" //下降沿触发
},
{
IRQ_EINT0, IRQT_FALLING, S3C2410_GPF0, S3C2410_GPF0_INP, 2, "Key Down"
},
{
IRQ_EINT19, IRQT_FALLING, S3C2410_GPG11,S3C2410_GPG11_INP, 3, "Key Left"
},
{
IRQ_EINT2, IRQT_FALLING, S3C2410_GPF2,S3C2410_GPF2_INP, 4, "Key Right"
},
{
IRQ_EINT6, IRQT_FALLING, S3C2410_GPF6,S3C2410_GPF6_INP, 5, "Key Enter"
},
{
IRQ_EINT5, IRQT_FALLING, S3C2410_GPF5,S3C2410_GPF5_INP, 6, "Key Exit"
},
};
/* 按键中断服务程序 */
static irqreturn_t utukey_irq(int irq, void *dev_id)
{
int key = (int)dev_id; // 传递key_info_tab[]索引
int i;
int found = 0;
for (i = 0; i < ARRAY_SIZE(key_info_tab); i++) // 查找产生中断的按键
{
if (key_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(key_info_tab[key].irq_no); // 找到,关闭对应中断
utukey_dev.key_status[key] = KEYSTATUS_DOWNX; // 按键处于不确定状态
key_timer[key].expires = jiffies + KEY_TIME_DELAY1; // 去抖动延时
add_timer(&key_timer[key]); //add_timer
return IRQ_HANDLED; //正确的中断
}
/* 申请irq中断 */
static int request_irqs(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(key_info_tab); i++)
{
s3c2410_gpio_cfgpin(key_info_tab[i].gpio_port, key_info_tab[i].gpio_setting); // 设置按键引脚的模式(输入)
set_irq_type(key_info_tab[i].irq_no, key_info_tab[i].irq_type); // 设置中断类型
if (request_irq(key_info_tab[i].irq_no, utukey_irq, SA_INTERRUPT, DEVICE_NAME, (void *)i)) // 向内核注册中断
{
printk(KERN_WARNING "buttons:can't get irq no.%d\n", key_info_tab[i].irq_no);
return 1;
}
}
return 0;
}
/* 释放irq中断 */
static void free_irqs(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(key_info_tab); i++)
{
free_irq(key_info_tab[i].irq_no, (void *)i);
}
}
/* 记录键值并唤醒等待队列 */
static void keyEvent(unsigned key)
{
BUF_HEAD = key_info_tab[key].key_code; // 记录键值
utukey_dev.head = INCBUF(utukey_dev.head, MAX_KEY_BUF); // 调整缓冲区头指针
wake_up_interruptible(&(utukey_dev.wq)); // 唤醒等待队列
}
/* 驱动读函数 */
static ssize_t utukey_read(struct file *filp,char __user *buffer, size_t count, loff_t *ppos)
{
unsigned int key_ret;
unsigned long flags;
retry:
if (utukey_dev.head != utukey_dev.tail) // 缓冲区有数据?
{
local_irq_save(flags); // 进入临界区 ,关中断
key_ret = BUF_TAIL; // 读出键值
utukey_dev.tail = INCBUF(utukey_dev.tail, MAX_KEY_BUF); // 调整缓冲区尾指针
local_irq_restore(flags); // 退出临界区,开中断
copy_to_user(buffer, &key_ret, sizeof(unsigned int)); // 拷贝到用户空间
return sizeof(unsigned int);
}else // 缓冲区没数据
{
if (filp->f_flags & O_NONBLOCK) // 若采用非阻塞方式读取则返回错误
{
return -EAGAIN;
}
interruptible_sleep_on(&(utukey_dev.wq)); // 使进程睡眠
if (signal_pending(current)) //在这里等中断
{ // 如果是信号中断
return -ERESTARTSYS;
}
goto retry;
}
return sizeof(unsigned int);
}
/* ioctl设备控制函数 */
static int utukey_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);
utukey_dev.head = utukey_dev.tail = 0;
local_irq_restore(flags);
printk(KERN_INFO "key buffer is cleared\n");
break;
default:
return -EINVAL;
}
return 0;
}
/* 定时器中断回调函数 */
static void utukey_timer_callback(unsigned long data) //定时器时间到了调用 参数代表哪个定时器
{
int key = data;
if (ISKEY_DOWN(key)) // 按键处于按下状态?
{
if (utukey_dev.key_status[key] == KEYSTATUS_DOWNX) // 已经延时10ms,完成去抖动?
{
utukey_dev.key_status[key] = KEYSTATUS_DOWN;
key_timer[key].expires = jiffies + KEY_TIME_DELAY;
keyEvent(key); // 记录键值,唤醒等待队列
add_timer(&key_timer[key]); //抬起按键去抖动延时
}else
{ // 已经完成去抖动延时
key_timer[key].expires = jiffies + KEY_TIME_DELAY;
add_timer(&key_timer[key]);
}
}else
{ // 按键已经抬起
utukey_dev.key_status[key] = KEYSTATUS_UP;
enable_irq(key_info_tab[key].irq_no);
}
}
static int utukey_open(struct inode *inode, struct file *filp)
{
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 const struct file_operations utukey_fops =
{
.owner = THIS_MODULE,
.read = utukey_read,
.ioctl = utukey_ioctl,
.open = utukey_open,
.release = utukey_release,
};
/* 初始化并注册cdev */
static void utukey_setup_cdev(void)
{
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;
err = cdev_add(&utukey_dev.cdev, devno, 1);
if (err)
printk(KERN_NOTICE "Error %d adding utukey", err);
}
/* 初始化函数 */
static int __init utukey_init(void)
{
int result, i;
dev_t devno = MKDEV(utukey_major,0); // 用主次设备号生成设备号
/* 申请中断 */
result = request_irqs();
if (result) {
unregister_chrdev_region(devno,1);
return result;
}
/* 申请设备号 */
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);
printk(KERN_INFO "Todo: mknod /dev/%s c %d 0\n", DEVICE_NAME, utukey_major);
}
if (result < 0)
return result;
/* 动态申请设备结构体的内存 */
utukey_devp = kmalloc(sizeof(struct utukey_dev), GFP_KERNEL);
if (!utukey_devp) /* 申请失败 */
{
result = -ENOMEM;
goto fail_malloc;
}
memset(utukey_devp, 0, sizeof(struct utukey_dev)); /* 清零分配的设备结构体内存 */
utukey_setup_cdev();
init_waitqueue_head(&(utukey_dev.wq)); /* 初始化等待队列头 */
utukey_dev.head = utukey_dev.tail = 0;
/* 初始化按键状态 */
for(i = 0; i < KEY_NUM; i++)
{
utukey_dev.key_status[i] = KEYSTATUS_UP;
}
/* 初始化定时器 */
for(i = 0; i < KEY_NUM; i++)
{
key_timer[i].function = utukey_timer_callback;
key_timer[i].data = i;
init_timer(&key_timer[i]);
}
return 0;
fail_malloc: unregister_chrdev_region(devno, 1);
return result;
}
/* 退出函数 */
static void __exit utukey_exit(void)
{
int i;
cdev_del(&utukey_dev.cdev); // 注销cdev
kfree(utukey_devp); // 释放结构体内存
unregister_chrdev_region(MKDEV(utukey_major, 0), 1); // 释放设备号
free_irqs(); // 释放中断
for(i = 0; i < KEY_NUM; i++) // 注销定时器
{
del_timer(&key_timer[i]);
}
}
MODULE_AUTHOR("lxm<lxm650@163.com>");
MODULE_LICENSE("Dual BSD/GPL");
module_init(utukey_init);
module_exit(utukey_exit);
//测试程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#define BUF_CLEAR _IO(0xFF, 0)
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/utukey");
exit(1);
}
else
{
if (ioctl(buttons_fd, BUF_CLEAR, 0) < 0)
printf(" ioctl command failed\n");
}
while(1)
{
int ret = read(buttons_fd, &key_value, sizeof key_value);
printf("You pressed buttons %d\n", key_value);
}
close(buttons_fd);
return 0;
}
- 按键驱动(浏览按键)
- 按键驱动
- 按键驱动
- 按键驱动
- 按键驱动
- 按键驱动
- 按键驱动
- 中断处理(按键驱动)
- 按键驱动(一)-中断
- android按键驱动开发实例1(修改一个按键)
- S3C6410 按键驱动(二) ---按键中断的基本流程
- GPIO按键驱动分析(包括矩阵按键)
- S3C2440驱动之按键驱动(一)
- Linux驱动---按键驱动
- 4412按键驱动移植(按键对应Android系统的三个虚拟按键)
- Mstar虚拟按键触摸屏驱动(芯片msg2133,按键使用android虚拟按键实现,特定报点,识别为按键)
- 按键
- 按键
- java中import用法
- 在一个js文件中包含另一个js文件的方法
- 系统架构师-基础到企业应用架构系列之--开卷有益
- sqlplus命令大全总集
- Oracle OEM 重建
- 按键驱动(浏览按键)
- How to config spring objects in app.config
- string 分割字符串 2011.07.13
- 50 个新酷的 jQuery 插件
- Oracle删除后,重新安装的方法
- 解决X5中一个保存按钮不能同时保存数据问题(多个数据集中其中某个数据集的store-type:simple 时,不能同时保存)
- Java中的import
- XML的简单读取与写入
- git 冲突解决