目录
输入子系统概念介绍
1.1 input子系统的作用
1.2 框架分析
编写程序与测试
没办法用在别人写的现成的应用程序上,因为别人的应用程序不会open"/dev/buttons",打开可能是其他现成的设备如/dev/tty,甚至不打开什么设备,例如直接scanf就可以获取按键的输入
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler = input_table[iminor(inode) >> 5];
const struct file_operations *old_fops, *new_fops = NULL;
int err;
/* No load-on-demand here? */
if (!handler || !(new_fops = fops_get(handler->fops)))
return -ENODEV;
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops->open) {
fops_put(new_fops);
return -ENODEV;
}
old_fops = file->f_op;
file->f_op = new_fops;
err = new_fops->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
return err;
}
在 input_open_file函数中,struct input_handler *handler = input_table[iminor(inode) >> 5];根据打开的文件的次设备号得到input_handler
new_fops = fops_get(handler->fops) ,定义了一个构造file_operations结构体从input_handler中得到
file->f_op = new_fops;err = new_fops->open(inode, file);应用程序的open最终会调用到file->f_op->open,因此应用程序的read最终会调用到file->f_op->read
在input.c中对于input_register_handler,input_table[handler->minor >> 5] = handler例如把evdev.c的handler放入数组,list_add_tail(&handler->node, &input_handler_list)放入链表
list_for_each_entry(dev, &input_dev_list, node) 对于每个input_dev,调用input_attach_handler
input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5])
return -EBUSY;
input_table[handler->minor >> 5] = handler;
}
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
return 0;
}
在input.c中对于input_register_device,list_add_tail(&dev->node, &input_dev_list)放入链表,也会对于每个input_dev,调用input_attach_handler
1.分配一个input_handle结构体
2.input_handle.dev = input_dev; // 指向左边的input_dev
input_handle.handler = input_handler; // 指向右边的input_handler
3. 注册:
input_handler->h_list = &input_handle;
inpu_dev->h_list = &input_handle;
app调用read,例如调用到input_handler层的evdev.c的evdev_read
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
...
// 无数据并且是非阻塞方式打开,则立刻返回
if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
// 否则休眠
retval = wait_event_interruptible(evdev->wait,
client->head != client->tail || !evdev->exist);
...
return retval;
}
client->head == client->tail,环型缓冲冲区,若为空则返回错误,有读写两个位置,空的时候R==W,满的时候(W+1)%LEN == R, 写:buf[w] = val,w = (w + 1) % LEN,读:val = buf[R], R = (R + 1) % LEN
若休眠了,谁来唤醒,在evdev.c中的evdev_event函数调用了wake_up_interruptible(&evdev->wait)
evdev_event被谁调用?硬件相关的代码,input_dev层调用,在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数
例如在input_dev层gpio_keys_isr.c中发现利用以下两个函数实现唤醒
input_event(input, type, button->code, !!state);
input_sync(input);
在input_event中,调用加到到链表中每一项handle,判断哪项被open从而调用event来唤醒
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);
1. 分配一个input_dev结构体 2.设置input_dev结构体 3.注册input_dev结构体 4.硬件相关的代码,如在中断服务程序里上报事件
struct input_dev {
void *private;
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[NBITS(MSC_MAX)];
unsigned long ledbit[NBITS(LED_MAX)];
unsigned long sndbit[NBITS(SND_MAX)];
unsigned long ffbit[NBITS(FF_MAX)];
unsigned long swbit[NBITS(SW_MAX)];...
};--------------------------------------------------------
#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
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
set_bit(EV_KEY, buttons_dev->evbit);
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);
DEMO:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct pin_desc{
int irq;
char *name;
unsigned int pin;
unsigned int key_val;
};
struct pin_desc pins_desc[4] = {
{IRQ_EINT0, "S2", S3C2410_GPF0 , KEY_L},
{IRQ_EINT2, "S3", S3C2410_GPF2 , KEY_S},
{IRQ_EINT11,"S4", S3C2410_GPG3 , KEY_ENTER},
{IRQ_EINT19,"S5", S3C2410_GPG11 , KEY_LEFTSHIFT}
};
static struct input_dev *buttons_dev;
static struct pin_desc *irq_pd;
static struct timer_list buttons_timer;
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
/* 10ms后启动定时器 */
irq_pd = (struct pin_desc *)dev_id;
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;
if(!pindesc)
return;
/* 读取PIN值 */
pinval = s3c2410_gpio_getpin(pindesc->pin);
/* 确定按键值 */
if(pinval){
/* 松开最后一个参数:1-按下 0-松开 */
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_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 能产生这类操作里的哪些事件 L,S,ENTER,LEFTSHIFT */
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.硬件相关的操作 */
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
add_timer(&buttons_timer);
for(i = 0; i < 4; i++)
{
request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
}
return 0;
}
static void buttons_exit(void)
{
int i;
for(i = 0; i < 4; 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_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");
# ls -l /dev/event*
crw-rw---- 1 0 0 13, 64 Jan 1 00:00 /dev/event0
# insmod buttons.ko
input: Unspecified device as /class/input/input1
# ls -l /dev/event*
crw-rw---- 1 0 0 13, 64 Jan 1 00:00 /dev/event0
crw-rw---- 1 0 0 13, 65 Jan 1 00:00 /dev/event1
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
...
sprintf(evdev->name, "event%d", minor);
evdev_table[minor] = evdev;
devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
cdev = class_device_create(&input_class, &dev->cdev, devt,
dev->cdev.dev, evdev->name);
...
return error;
}
第一种方法:
秒 微妙 type code value
0000000 038d 0000 1053 000b 0001 001c 0001 0000
0000010 038d 0000 105f 000b 0000 0000 0000 0000
0000020 038d 0000 310f 000e 0001 001c 0000 0000
0000030 038d 0000 3118 000e 0000 0000 0000 0000
static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
{
if (copy_to_user(buffer, event, sizeof(struct input_event)))
return -EFAULT;return 0;
}struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
第二种方法:
个人单板:S3C2440
static const struct input_device_id kbd_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
.evbit = { BIT(EV_KEY) },
},{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
.evbit = { BIT(EV_SND) },
},{ }, /* Terminating entry */
};
# cat /dev/tty1
ls
set_bit(EV_REP, buttons_dev->evbit);