学完了普通中断的按键驱动和输入子系统的按键驱动,还是有点迷糊,写这篇文章梳理下
普通按键驱动参考了这篇文章:http://blog.csdn.net/lwj103862095/article/details/17511867
一、普通中断按键驱动实现
1、内核中断体系
ARM架构linux内核中,有5种常见的异常,其中中断异常是其一,Linux内核将所有中断统一编号,使用一个irq_desc结构体来描述这些中断,里面记录了中断名称、中断状态、中断标记、并提供了中断的底层硬件访问函数(如:清除、屏蔽、使能中断),提供了这个中断的处理函数入口,通过它还可以调用用户注册的的中断处理函数。
-
struct irq_desc {
-
unsigned int irq;
-
......
-
irq_flow_handler_t handle_irq;
-
struct irq_chip *chip;
-
......
-
struct irqaction *action; /* IRQ action list */
-
unsigned int status; /* IRQ status */
-
......
-
unsigned int irq_count; /* For detecting broken IRQs */
-
......
-
const char *name;
-
} ____cacheline_internodealigned_in_smp;
2、使用request_irq 向内核注册中断,其原型:
-
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
-
const char *name, void *dev)
-
{
-
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
-
}
具体使用方式:
-
request_irq(IRQ_EINT4, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
-
request_irq(IRQ_EINT5, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
(1)第一个参数是硬件中断号
(2)第二个参数是中断服务函数,当发生中断时调用
(3)第三个参数是中断标准,规定中断出发的方式,上升沿/下降沿。。
(4)第四个是名字,以通过cat proc/interrupts查看
(5)第五个是传递参数,可传递给中断服务函数 buttons_irq() ,如传递给buttons_irq里的dev_id.
这个是中断服务函数原型static irqreturn_t buttons_irq(int irq, void *dev_id)
3、request_irq()的作用
(1)irq_desc[irq]结构体中的action链表中已经链入了用户注册的中断处理函数。
(2)中断触发方式已经被设置好。
(3)中断已经被使能。
4、linux内核怎么样休眠
使用wait_event函数,其扩展型常用的函数为wait_event_interruptible(wq, condition),即可被中断打断的休眠。
wq是一个等待队列,condition是条件,如果condition = 0,则将会进行休眠,直到condition = 1,并且有唤醒函数唤醒它。
5、linux如何进行唤醒
使用wait_up函数,其扩展型常用的函数为wake_up_interruptible(wq),wq与wait_event_interruptible的wq是一致的。
6、注意不能直接使用wait_event_interruptible和wake_up_interruptible函数,必须先事先使用static DECLARE_WAIT_QUEUE_HEAD(wq)定义并初始化一个等待队列头,并设置condition条件变量。
7、注意驱动代码,主要报告如下函数
(1)驱动入口函 static int third_drv_init(void) ,用于主设备号的获取,累的创建、设备的创建
(2)驱动出口函数 static int third_drv_exit(void),用于卸载类和设备
(3)module_init(third_drv_init); //用于修饰入口函数 module_exit(third_drv_exit); //用于修饰出口函数
(4)static int third_drv_open(struct inode * inode, struct file * filp) 设备打开函数,内涵request_irq(),用于进行中断的注册和初始化。
(5)static ssize_t third_drv_read(struct file *file, char __user *user, size_t size,loff_t *ppos) 设备的读取函数,copy_to_user()
(6)最后是中断处理函数,通过request_irq()传入的参数判断是哪个引脚发生了中断,并将不同的值返回给 设备的读函数, 最后copy_to_user。
-
/* 定义并初始化等待队列头 */
-
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
-
-
-
static struct class *thirddrv_class;
-
static struct device *thirddrv_device;
-
-
static struct pin_desc{
-
unsigned int pin;
-
unsigned int key_val;
-
};
-
-
static struct pin_desc pins_desc[4] = {
-
{S3C2410_GPF1,0x01},
-
{S3C2410_GPF4,0x02},
-
{S3C2410_GPF2,0x03},
-
{S3C2410_GPF0,0x04},
-
};
-
-
static int ev_press = 0;
-
-
/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
-
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
-
static unsigned char key_val;
-
int major;
-
-
/* 用户中断处理函数 */
-
static irqreturn_t buttons_irq(int irq, void *dev_id)
-
{
-
struct pin_desc *pindesc = (struct pin_desc *)dev_id;
-
unsigned int pinval;
-
pinval = s3c2410_gpio_getpin(pindesc->pin);
-
-
if(pinval)
-
{
-
/* 松开 */
-
key_val = 0x80 | (pindesc->key_val);
-
}
-
else
-
{
-
/* 按下 */
-
key_val = pindesc->key_val;
-
}
-
-
ev_press = 1; /* 表示中断已经发生 */
-
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
-
return IRQ_HANDLED;
-
}
-
static int third_drv_open(struct inode * inode, struct file * filp)
-
{
-
/* K1 ---- EINT1,K2 ---- EINT4,K3 ---- EINT2,K4 ---- EINT0
-
* 配置GPF1、GPF4、GPF2、GPF0为相应的外部中断引脚
-
* IRQT_BOTHEDGE应该改为IRQ_TYPE_EDGE_BOTH
-
*/
-
request_irq(IRQ_EINT1, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K1",&pins_desc[0]);
-
request_irq(IRQ_EINT4, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K2",&pins_desc[1]);
-
request_irq(IRQ_EINT2, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K3",&pins_desc[2]);
-
request_irq(IRQ_EINT0, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K4",&pins_desc[3]);
-
return 0;
-
}
-
-
static ssize_t third_drv_read(struct file *file, char __user *user, size_t size,loff_t *ppos)
-
{
-
if (size != 1)
-
return -EINVAL;
-
-
/* 当没有按键按下时,休眠。
-
* 即ev_press = 0;
-
* 当有按键按下时,发生中断,在中断处理函数会唤醒
-
* 即ev_press = 1;
-
* 唤醒后,接着继续将数据通过copy_to_user函数传递给应用程序
-
*/
-
wait_event_interruptible(button_waitq, ev_press);
-
copy_to_user(user, &key_val, 1);
-
-
/* 将ev_press清零 */
-
ev_press = 0;
-
return 1;
-
}
-
-
static int third_drv_close(struct inode *inode, struct file *file)
-
{
-
free_irq(IRQ_EINT1,&pins_desc[0]);
-
free_irq(IRQ_EINT4,&pins_desc[1]);
-
free_irq(IRQ_EINT2,&pins_desc[2]);
-
free_irq(IRQ_EINT0,&pins_desc[3]);
-
return 0;
-
}
-
-
/* File operations struct for character device */
-
static const struct file_operations third_drv_fops = {
-
.owner = THIS_MODULE,
-
.open = third_drv_open,
-
.read = third_drv_read,
-
.release = third_drv_close,
-
};
-
-
-
/* 驱动入口函数 */
-
static int third_drv_init(void)
-
{
-
/* 主设备号设置为0表示由系统自动分配主设备号 */
-
major = register_chrdev(0, "third_drv", &third_drv_fops);
-
-
/* 创建thirddrv类 */
-
thirddrv_class = class_create(THIS_MODULE, "thirddrv");
-
-
/* 在thirddrv类下创建buttons设备,供应用程序打开设备*/
-
thirddrv_device = device_create(thirddrv_class, NULL, MKDEV(major, 0), NULL, "buttons");
-
-
return 0;
-
}
-
-
/* 驱动出口函数 */
-
static void third_drv_exit(void)
-
{
-
unregister_chrdev(major, "third_drv");
-
device_unregister(thirddrv_device); //卸载类下的设备
-
class_destroy(thirddrv_class); //卸载类
-
}
-
-
module_init(third_drv_init); //用于修饰入口函数
-
module_exit(third_drv_exit); //用于修饰出口函数
-
-
MODULE_AUTHOR("LWJ");
-
MODULE_DESCRIPTION("Just for Demon");
-
MODULE_LICENSE("GPL"); //遵循GPL协议
二、输入子系统 input_dev
参考这两篇文章和视频:http://blog.csdn.net/lwj103862095/article/details/17734625
http://blog.chinaunix.net/uid-20746260-id-3065868.html
1、Input子系统框架
(1)分配一个输入子系统
(2)设置能产生哪类事件
(3)设置能产生这类事件的哪些事件
(4)注册
(5)硬件相关的操作,定时器相关操作,申请中断等
2、使用input_allocate_device函数进行分配
input_dev结构体的重要成员
-
struct input_dev {
-
const char *name;
-
const char *phys;
-
const char *uniq;
-
struct input_id id;
-
-
unsigned long evbit[NBITS(EV_MAX)]; // 表示能产生哪类事件
-
unsigned long keybit[NBITS(KEY_MAX)]; // 表示能产生哪些按键
-
unsigned long relbit[NBITS(REL_MAX)]; // 表示能产生哪些相对位移事件, x,y,滚轮
-
unsigned long absbit[NBITS(ABS_MAX)]; // 表示能产生哪些绝对位移事件, x,y
-
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
-
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
-
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
-
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
-
...
-
}
3、设置产生哪类事件和这类事件的哪些操作,在下面的代码中有的
4、有如下这些个类
-
#define EV_SYN 0x00 //同步类
-
#define EV_KEY 0x01 //按键类
-
#define EV_REL 0x02 //相对位移类
-
#define EV_ABS 0x03 //绝对位移类
-
#define EV_MSC 0x04
-
#define EV_SW 0x05
-
#define EV_LED 0x11
-
#define EV_SND 0x12 //声音类
-
#define EV_REP 0x14 //重复类
-
#define EV_FF 0x15
-
#define EV_PWR 0x16
5、最后使用input_register_device(struct input_dev *dev)函数来注册
6、驱动源码:
包括如下主要的函数
(1)最主要的是驱动入口函数
static int buttons_input_init(void),功能包括以上四步,申请Input子系统、设置、注册、硬件等,还包括定时器的初始化和初始化中断request_irq()
(2)驱动出口函数包括:释放中断、删除定时器、删除设备等
(3)定时器处理函数:static void buttons_timer_function(unsigned long data) ,可根据传入的参数对功能进行判断。
(4)用户中断处理函数static irqreturn_t buttons_irq(int irq, void *dev_id) ,仅仅是增加定时器的时间。具体的处理在void_timer_function()函数中实现。
-
static struct pin_desc{
-
int irq;
-
unsigned char *name;
-
unsigned int pin;
-
unsigned int key_val;
-
};
-
-
static struct pin_desc pins_desc[4] = {
-
{IRQ_EINT1,"K1",S3C2410_GPF1,KEY_L},
-
{IRQ_EINT4,"K2",S3C2410_GPF4,KEY_S},
-
{IRQ_EINT2,"K3",S3C2410_GPF2,KEY_ENTER},
-
{IRQ_EINT0,"K4",S3C2410_GPF0,KEY_LEFTSHIFT},
-
};
-
-
static struct pin_desc *irq_pd;
-
static struct input_dev *buttons_dev;
-
static struct timer_list buttons_timer;
-
-
/* 用户中断处理函数 */
-
static irqreturn_t buttons_irq(int irq, void *dev_id)
-
{
-
irq_pd = (struct pin_desc *)dev_id;
-
-
/* 修改定时器定时时间,定时10ms,即10秒后启动定时器
-
* HZ 表示100个jiffies,jiffies的单位为10ms,即HZ = 100*10ms = 1s
-
* 这里HZ/100即定时10ms
-
*/
-
mod_timer(&buttons_timer, jiffies + (HZ /100));
-
return IRQ_HANDLED;
-
}
-
-
-
/* 定时器处理函数 */
-
static void buttons_timer_function(unsigned long data)
-
{
-
struct pin_desc *pindesc = irq_pd;
-
unsigned int pinval;
-
pinval = s3c2410_gpio_getpin(pindesc->pin);
-
-
if(pinval)
-
{
-
/* 松开 最后一个参数: 0-松开, 1-按下 */
-
input_event(buttons_dev,EV_KEY,pindesc->key_val,0);
-
input_sync(buttons_dev);
-
}
-
else
-
{
-
/* 按下 */
-
input_event(buttons_dev,EV_KEY,pindesc->key_val,1);
-
input_sync(buttons_dev);
-
}
-
}
-
-
/* 驱动入口函数 */
-
static int buttons_input_init(void)
-
{
-
int i;
-
-
/* 1.分配一个input_dev结构体 */
-
buttons_dev = input_allocate_device();
-
-
/* 2.设置 */
-
/* 2.1 设置按键能产生哪类事件 */
-
set_bit(EV_KEY,buttons_dev->evbit);
-
set_bit(EV_REP,buttons_dev->evbit);
-
-
/* 2.2 设置能产生这类操作的哪些事件 */
-
set_bit(KEY_L,buttons_dev->keybit);
-
set_bit(KEY_S,buttons_dev->keybit);
-
set_bit(KEY_ENTER,buttons_dev->keybit);
-
set_bit(KEY_LEFTSHIFT,buttons_dev->keybit);
-
-
/* 3.注册 */
-
input_register_device(buttons_dev);
-
-
-
/* 4.硬件相关的设置 */
-
/* 4.1 定时器相关的操作 */
-
init_timer(&buttons_timer);
-
buttons_timer.function = buttons_timer_function;
-
add_timer(&buttons_timer);
-
-
/* 4.2 申请中断 */
-
for(i = 0;i < sizeof(pins_desc)/sizeof(pins_desc[0]);i++)
-
{
-
request_irq(pins_desc[i].irq, buttons_irq, IRQ_TYPE_EDGE_BOTH, pins_desc[i].name, &pins_desc[i]);
-
}
-
-
return 0;
-
}
-
-
/* 驱动出口函数 */
-
static void buttons_input_exit(void)
-
{
-
int i;
-
for(i = 0;i < sizeof(pins_desc)/sizeof(pins_desc[0]);i++)
-
{
-
free_irq(pins_desc[i].irq, &pins_desc[i]);
-
}
-
del_timer(&buttons_timer);
-
input_unregister_device(buttons_dev);
-
input_free_device(buttons_dev);
-
}
-
-
module_init(buttons_input_init); //用于修饰入口函数
-
module_exit(buttons_input_exit); //用于修饰出口函数
-
-
MODULE_AUTHOR("LWJ");
-
MODULE_DESCRIPTION("Just for Demon");
-
MODULE_LICENSE("GPL"); //遵循GPL协议
阅读(1779) | 评论(0) | 转发(0) |