概述
Linux中的input子系统是为各种不同的输入设备设计的,从物理角度看,它要支持不同类型的设备,从用户的角度看,希望它能屏蔽不同的设备差异,向用户提供一个统一的接口和操作方法,这就是input子系统要解决的问题。
输入子系统原理框图:
- 驱动层
负责物理设备的管理,不同的物理设备,有具体不同的硬件操作方法。 - 核心层
对驱动层和事件处理层进行管理,将两者匹配后建立通讯通道。 - 事件处理层
为应用程序获取输入信息,将输入事件封装成标准的形式提供给应用层,同时接受应用层的控制。
用户和驱动层接口
#include <linux/input.h>
struct input_event
{
struct timeval time;
__u16 type; //事件类型
__u16 code; //事件代码
__s32 value; //状态
};
//以下是节选一部分事件类型宏定义
/*
* Event types 事件类型
*/
#define EV_SYN 0x00 //同步信号,表示一次事件完成
#define EV_KEY 0x01 //表示此次事件是按键(键盘按键,鼠标按键等)
#define EV_REL 0x02 //相对值,比如鼠标
#define EV_ABS 0x03 //绝对值,比如触摸屏
#define EV_MSC 0x04 //键盘
一、 input 核心层分析
input核心层是作为一个模块被注册到系统中的,在系统启动的时候被系统自动注册的。下面是代码分析,代码在 driver/input/input.c 中。
1、 input 模块的注册:
注册输入类设备input,在目录/sys下建立了input目录,以后所有的输入类设备都出现在这个目录中。下面是 input_init 函数的原型。
static int __init input_init(void)
{
err = class_register(&input_class); //设备类的注册,建立目录/sys/input
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册字符设备
}
函数 class_register(&input_class)
这个函数是input类的注册,下面是参数分析。
- 参数 input_class
struct class input_class =
{
.name = "input", /* 这就是创建的目录:/sys/input */
.devnode = input_devnode,
};
函数 register_chrdev(INPUT_MAJOR, “input”, &input_fops)
这个函数是字符设备的注册,输入类设备作为字符设备,且统一使用主设备号是13,不同的输入类设备使用次设备号区分,下面是参数分析。
- 参数 input_fops 字符设备操作函数
static const struct file_operations input_fops =
{
.owner = THIS_MODULE,
.open = input_open_file,
};
这里的 input_open_file 不是最终的操作硬件的函数,在这个函数中通过一个表寻找到那个具体的函数(硬件操作中实现的那个函数),然后去执行,如下所示。
static int input_open_file(struct inode *inode, struct file *file)
{
......
/* No load-on-demand here? */
handler = input_table[iminor(inode) >> 5];
if (handler)
new_fops = fops_get(handler->fops);//根据input_table表,得到具体的操作函数
......
err = new_fops->open(inode, file);//然后才打开操作硬件的函数
}
####################################################################################
2、设备驱动层接口
– 初始化函数 input_allocate_device
给具体的设备分配内存,同时初始化成员。这里的初始化是通用性的内容的设置,详细的特有的设备的数据初始化在后面的函数中进行设置。
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
if (dev)
{
dev->dev.type = &input_dev_type;
dev->dev.class = &input_class;
device_initialize(&dev->dev);
mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
__module_get(THIS_MODULE);
}
return dev;
}
- input_dev_type结构体
static struct device_type input_dev_type =
{
.groups = input_dev_attr_groups, //设备属性的初始化
.release = input_dev_release, //设备释放函数
.uevent = input_dev_uevent, //设备所支持的事件
#ifdef CONFIG_PM
.pm = &input_dev_pm_ops, //电源管理
#endif
};
- input_class 结构体
同上面使用的是同一个结构体
struct class input_class =
{
.name = "input", /* 这就是创建的目录:/sys/input */
.devnode = input_devnode,
};
– 设置函数 input_set_capability
对设备的具体功能进行设置,需要向应用层上报具体的信息设置。有多少种设备能力就调用多少次函数进行设置。
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
switch (type) {
case EV_KEY:
__set_bit(code, dev->keybit);
break;
case EV_REL:
__set_bit(code, dev->relbit);
break;
case EV_ABS:
__set_bit(code, dev->absbit);
break;
case EV_MSC:
__set_bit(code, dev->mscbit);
break;
case EV_SW:
__set_bit(code, dev->swbit);
break;
case EV_LED:
__set_bit(code, dev->ledbit);
break;
case EV_SND:
__set_bit(code, dev->sndbit);
break;
case EV_FF:
__set_bit(code, dev->ffbit);
break;
case EV_PWR:
/* do nothing */
break;
default:
printk(KERN_ERR
"input_set_capability: unknown type %u (code %u)\n",
type, code);
dump_stack();
return;
}
__set_bit(type, dev->evbit);
}
– 注册输入设备
输入设备的注册,设备添加到设备链表中,找到和当前设备匹配的那个事件响应函数handler,进行挂接。
int input_register_device(struct input_dev *dev)
{
......
dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);///sys/input目录下具体设备文件名字
error = device_add(&dev->dev);//设备添加
......
//链表管理:输入设备的主设备号13,所以所有的输入设备是通过一个链表管理的。
list_add_tail(&dev->node, &input_dev_list);//添加设备到input设备的管理链表中去
//这里注册了很多输入设备的事件处理函数。遍历链表,匹配硬件对应的具体的事件类型handler,即事件处理层。
list_for_each_entry(handler, &input_handler_list, node)//这是宏定义for
input_attach_handler(dev, handler);//匹配和连接对应的handler,对应具体的设备文件
}
设备和事件处理层handler的匹配和连接函数 input_attach_handler
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
id = input_match_device(handler, dev);//匹配对应的handler
......
error = handler->connect(handler, dev, id);//连接
......
}
二、事件驱动层分析
– 注册 handler
/driver/input/evdev.c 这个文件包括了几乎所有设备的响应函数(鼠标、键盘、触摸屏等)。它作为一个模块被注册到系统中的。
事件响应函数注册
通用事件驱动注册(系统启动时被当做模块注册),在/driver/input/ evdev.c 文件中
module_init(evdev_init); //注册模块
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
结构体 input_handler 原型
struct input_handler
{
void *private;//事件处理程序的私有数据
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); //函数指针,处理事件
bool (*match)(struct input_handler *handler, struct input_dev *dev);//进行匹配的函数
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;//事件驱动处理程序的文件操作集合(字符设备驱动函数集合)
int minor;
const char *name;
const struct input_device_id *id_table;//输入设备ID表,通过这个表匹配它的设备
struct list_head h_list;
struct list_head node;
};
下面对结构体 input_handler 原型进行分析:
- event事件响应函数。加工数据,把硬件层传递上来的数据进行封装,给应用层使用。即内核数据拷贝到应用层,等待应用读取。
- connect 当驱动和事件匹配上了,就使用本函数进行连接。
- match 函数在这里没有用,在核心层已经有这个函数了。
- *file_operations fops 应用层读写等函数对应的底层读写实际操作的函数集合。是字符设备原理。
- *input_device_id id_table 系统以及注册了的所有响应设备的集合。
- h_list node 链表,挂接device 和handler
初始化后的为 input_handler evdev.c对应的驱动响应函数定义的结构体,通用响应handler。
static struct input_handler evdev_handler =
{
.event = evdev_event,
.connect = evdev_connect,//连接函数,在设备注册阶段进行挂接。
.disconnect = evdev_disconnect,
.fops = &evdev_fops,//应用层对应的操作函数接口
.minor = EVDEV_MINOR_BASE,//后续的设备在这个设备号基础上加一
.name = "evdev",
.id_table = evdev_ids,//空,可匹配所有的设备
};
函数 evdev_connect粗略分析
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{
struct evdev *evdev;
//从这个表中找出一个空的位置
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
if (minor == EVDEV_MINORS) //如果minor已经等于32了,说明没有空间再容纳这个设备了
{
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);//申请空间,后面对handle管理项目进行初始化。
dev_set_name(&evdev->dev, "event%d", minor);//对设备文件的命名 event0 event1这样的命名。
//下面是设备的注册,它放到这里了
......
device_initialize(&evdev->dev);
......
error = device_add(&evdev->dev);
......
}
到这里,我们一直没有发现设备的注册,实际上,device的注册就再这个函数中。原型的设备注册函数是:
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
可见本文中的设备注册被拆分开来了。
初始化后的 **file_operations ** 函数实例 evdev_fops
static const struct file_operations evdev_fops =
{
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,//异步通知,应用层读写等有关
.flush = evdev_flush
};
#######################################################################
对事件处理handler的管理 input_register_handle
这个函数在handler注册的时候被调用。(上面的evdev_handler 结构体成员函数中的evdev_connect调用input_register_handle)。handle管理着所有被注册到系统的handler。
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
int error;
/*
* We take dev->mutex here to prevent race with
* input_release_device().
*/
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
/*
* Filters go to the head of the list, normal handlers
* to the tail.
*/
if (handler->filter)
list_add_rcu(&handle->d_node, &dev->h_list);
else
list_add_tail_rcu(&handle->d_node, &dev->h_list);
mutex_unlock(&dev->mutex);
/*
* Since we are supposed to be called from ->connect()
* which is mutually exclusive with ->disconnect()
* we can't be racing with input_unregister_handle()
* and so separate lock is not needed here.
*/
list_add_tail_rcu(&handle->h_node, &handler->h_list);
if (handler->start)
handler->start(handle);
return 0;
}
struct input_handle
{
void *private;
int open;
const char *name;
struct input_dev *dev; //设备,和下面的响应函数挂接
struct input_handler *handler; //响应函数
struct list_head d_node;
struct list_head h_node;
};
input_handler evdev_handler 中的evdev_event分析
static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
struct input_event event;
struct timespec ts;
//下面是对struct input_event 结构体进行数据的封装。 struct input_event 是报告给应用的数据包格式。
ktime_get_ts(&ts);
event.time.tv_sec = ts.tv_sec;
event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
event.type = type;
event.code = code;
event.value = value;
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_event(client, &event);//数据信息只通知给一个handler,因为只匹配了一个handler。
else //下面是一个设备匹配了两个handler,就通知多个。
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);//这里异步通知等方法通知应用层
rcu_read_unlock();
wake_up_interruptible(&evdev->wait);
}
struct input_event 原型:
struct input_event
{
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};