概述
一、为什么引入输入子系统
之前我们学习了一些简单的字符设备驱动,我们在写按键驱动时,都采用字符设备、混杂设备处理的,使我们的驱动程序只能我们自己使用,别人很难使用。如果所有的输入设备都这样处理的话,那么大量的输入设备驱动将会非常的混乱分散。大神为了将不同类别的,分散的输入设备进行统一的驱动,出现了输入子系统。
二、输入子系统的
输入子系统分为三层:核心层(Input Core),驱动层和事件处理层(Event Handler)。内核通过核心层将驱动层和时间处理层连接在一起,向上为应用层提供接口,向下为输入设备提供接口,这样在原本分散的,不同类别的输入设备的驱动在应用层就有了统一的接口。
从图中可以看出当我们写输入设备驱动时,只需要关注驱动层的事情,然后让核心层将驱动层和事件处理层连接起来。
现在我们根据源代码分析整个输入子系统的框架,输入子系统的核心层在driver/input/input.c中,首先看它的初始化代码(为了减少文章长度,下面会将一些不重要的代码省略掉):
static int __init input_init(void)
{
int err;
err = class_register(&input_class);//注册一个类
......
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册一个字符设备
......
}
可见在核心层的初始化函数中,注册了一个类,并没有注册节点。还注册了一个字符设备,主设备号是INPUT_MAJOR。再来看看它的file_operations结构:
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
在它的file_operations结构中只提供了一个结构体,当我们打开这个设备是,就回调用这个open函数:
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler = input_table[iminor(inode) >> 5];//以次设备号从input_table中找到一个input_handler结构
const struct file_operations *old_fops, *new_fops = NULL;
int err;
if (!handler || !(new_fops = fops_get(handler->fops))) //从这个input_handler结构得到一个新的file_operations结构
return -ENODEV;
old_fops = file->f_op;
file->f_op = new_fops;//使新的fops代替旧的fops
err = new_fops->open(inode, file);//调用新的fops
......
}
从上面知道open函数会从input_table数组中以次设备号得到一个结构,用它的fops替代打开文件的fops。那么这个数组由谁来设置呢?通过查找会发现,它是在input_register_handler函数中被设置的。
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);//把handler添加到input_handler_list量表中
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);//遍历input_dev_list中的每一项,让dev和handler建立联系。
input_wakeup_procfs_readers();
return 0;
}
从函数中可以看到,它将一个input_handler结构的地址以次设备号放入数组中,并遍历input_dev_list中的每一项,让dev和handler建立联系。从这个函数的名字我们可以猜测它是注册一个handler,它是被事件处理层调用的,并在input_dev_list链表中查找匹配的dev和它建立联系。那这个链表谁来设置的呢?它是由input_register_device函数设置的。
int input_register_device(struct input_dev *dev)
{
......
list_add_tail(&dev->node, &input_dev_list);//将一个input_dev结构加到input_dev_list链表
......
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);//遍历input_handler_list中的每一项,让dev和handler建立联系。
input_wakeup_procfs_readers();
return 0;
}
这个函数是被驱动层调用的,他注册一个input_dev结构,并遍历input_handler_list链表使这个input_dev结构和匹配的handler建立联系。从中可以看出,不管是事件处理层先注册还是驱动层想注册,都将使它们之间建立联系。我们赢在驱动层注册一个input_dev结构,这样就能和事件处理层建立相应的联系了。
当建立联系后,我们在应用层调用read,write等函数时,将调用相应的事件处理层的read,write等函数。那这些read,write等函数函数是怎么做的呢?我们以evdev.c中的为例看一下:
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);//当client环形缓冲区为空,并且是非阻塞方式打开时,休眠
......
}
在read中,当client环形缓冲区为空,并且是非阻塞方式打开时,程序休眠。那么谁来唤醒它呢?是在input_handler结构中的event函数中。当驱动程序有数据时上报一个事件,并发送一个事件信号。一般在中断服务函数中完成。
下面我们来总结一下输入子系统驱动的框架:
1、分配一个input_dev结构
2、设置input_dev结构
3、注册input_dev结构
4、硬件相关的操作,初始化硬件,注册中断……
5、在中断服务函数中上报事件,并发送事件信号
三、输入子系统的重要数据结构
1、input_handler结构,由时间处理层注册,提供相应的read,write等函数,并提供连接函数。
struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//事件处理函数
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);//连接函数
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
const struct file_operations *fops;//提供file_operations结构
int minor;//次设备号
const char *name;
const struct input_device_id *id_table;//用于匹配input_dev结构
const struct input_device_id *blacklist;
struct list_head h_list;
struct list_head node;
};
2、input_dev结构,由驱动层分配,描述一些设备信息,设备类型等
struct input_dev {
void *private;
const char *name;//设备名
const char *phys;
const char *uniq;
struct input_id id;//用于匹配事件处理层的input_handler结构
unsigned long evbit[NBITS(EV_MAX)];//用于记录支持的事件类型
unsigned long keybit[NBITS(KEY_MAX)];//记录支持的按键值
unsigned long relbit[NBITS(REL_MAX)];//记录支持的相对坐标
unsigned long absbit[NBITS(ABS_MAX)];//记录支持的绝对坐标
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)];
unsigned int keycodemax;//支持的按键值的个数
unsigned int keycodesize;//每个键值的字节数
void *keycode;//存储按键值的数组首地址
int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);// 修改键值的函数,可选
int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);//获取扫描码的键值,可选
struct ff_device *ff;
unsigned int repeat_key;//最近一次按键值,用于连击
struct timer_list timer;//自动连击计时器
int state;
int sync;//最后一次同步后没有新的事件置1
int abs[ABS_MAX + 1];//当前各个坐标的值
int rep[REP_MAX + 1];//自动连击的参数
unsigned long key[NBITS(KEY_MAX)];
unsigned long led[NBITS(LED_MAX)];
unsigned long snd[NBITS(SND_MAX)];
unsigned long sw[NBITS(SW_MAX)];
int absmax[ABS_MAX + 1];
int absmin[ABS_MAX + 1];
int absfuzz[ABS_MAX + 1];
int absflat[ABS_MAX + 1];
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
struct input_handle *grab;
struct mutex mutex; /* serializes open and close operations */
unsigned int users;
struct class_device cdev;
union { /* temporarily so while we switching to struct device */
struct device *parent;
} dev;
struct list_head h_list;
struct list_head node;
};
四、输入子系统驱动层的编写
以下是一个简单的驱动的例子:
/* 参考driversinputkeyboardgpio_keys.c */
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
struct buttons_desc{
int irq;
char *name;
unsigned int pin;
unsigned int key_val;
};
static struct buttons_desc pins_desc[] = {
{IRQ_EINT0, "S1", S3C2410_GPF0, KEY_L},
{IRQ_EINT2, "S2", S3C2410_GPF2, KEY_S},
{IRQ_EINT11, "S3", S3C2410_GPG3, KEY_ENTER},
{IRQ_EINT19, "S4", S3C2410_GPG11, KEY_LEFTSHIFT},
};
static struct input_dev *buttons_dev = NULL;
static struct buttons_desc *irq_pd = NULL;
static struct timer_list buttons_timer;
static void buttons_timer_function(unsigned long data)
{
unsigned int pin_val;
if(irq_pd == NULL){
return;
}
pin_val = s3c2410_gpio_getpin(irq_pd->pin);
if(pin_val){
/*松开:输出参数:0*/
input_event(buttons_dev,EV_KEY,irq_pd->key_val,0);
input_sync(buttons_dev);
}
else{
/*按下:输出参数:0*/
input_event(buttons_dev,EV_KEY,irq_pd->key_val,1);
input_sync(buttons_dev);
}
}
static irqreturn_t buttons_irq(int irq,void *dev_id)
{
irq_pd = (struct buttons_desc *)dev_id;
mod_timer(&buttons_timer, jiffies + HZ/100);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int __init buttons_init(void)
{
int i,err;
/*1、分配一个input_dev结构体*/
buttons_dev = input_allocate_device();
if(!buttons_dev){
return -ENOMEM;
}
/*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、注册*/
err = input_register_device(buttons_dev);
if(err){
printk(KERN_ERR "buttons: unable to register buttons input devicen");
goto fail1;
}
/*4、硬件相关的操作*/
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
add_timer(&buttons_timer);
for(i = 0;i < 4;i += 1){
err = request_irq(pins_desc[i].irq,buttons_irq,IRQT_BOTHEDGE,pins_desc[i].name,&pins_desc[i]);
if(err){
printk(KERN_ERR "buttons: unable to request irqn");
goto fail2;
}
}
return 0;
fail2:
for(i -= 1; i >= 0; i -= 1){
free_irq(pins_desc[i].irq, &pins_desc[i]);
}
del_timer(&buttons_timer);
fail1:
input_free_device(buttons_dev);
return err;
}
static void __exit buttons_exit(void)
{
int i;
for(i = 0;i < 4; i += 1){
free_irq(pins_desc[i].irq, &pins_desc[i]);
}
del_timer(&buttons_timer);
input_free_device(buttons_dev);
}
module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");
最后
以上就是贪玩宝贝为你收集整理的输入子系统(input subsystem)的全部内容,希望文章能够帮你解决输入子系统(input subsystem)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复