输入子系统由驱动层、输入子系统核心、事件处理层三部分组成。一个输入事件,如鼠标移动、键盘按下等通过Driver->Inputcore->Event handler->userspace的顺序到达用户控件的应用程序。
系统框图
假设打开一个字符设备驱动程序/dev/event0,event代表的是输入子系统的设备文件,当应用程序调用C库的open函数后,open函数会进入系统调用,最后定位到driversinputinput.c文件下(这个文件就是核心层)的。这个函数的功能主要是根据设备的次设备号找到新的fops结构,然后切换到新的fops结构,然后调用它的打开函数。输入子系统的主设备号恒为#define INPUT_MAJOR 13,定义在includelinuxmajor.h中。
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler = input_table[iminor(inode) >> 5];//根据次设备号找到在input_table表中找到handler结构体
const struct file_operations *old_fops, *new_fops = NULL;
int err;
/* No load-on-demand here? */
if (!handler || !(new_fops = fops_get(handler->fops)))//判断handler结构体是否存在,存在的话将里面的fops变量赋给new_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;//切换f_op变量,以后调用诸如read、write等系统调用时会进入到new_fops的read、write函数
err = new_fops->open(inode, file);//调用new_fops的open函数
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);//释放掉老的fops结构
return err;
}
接着先来看到input_table表的建立,可以看到它是一个静态变量,在本文件(driversinputinput.c)中搜索它,可以看到它位于input_register_handler函数,这是一个全局的函数,可以供外部的文件调用,这个函数的主要功能是注册一个handler结构体,这个结构体中存在minor这个设备的次设备号,这个结构所在的函数对应的其实就是上述的事件层。
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
INIT_LIST_HEAD(&handler->h_list);//初始化handler的h_list结构体,这是一个双向链表
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5])//检查是否已经存在这个次设备号的handler结构
return -EBUSY;
input_table[handler->minor >> 5] = handler;//将handler结构次设备号放入input_table表
}
list_add_tail(&handler->node, &input_handler_list);//将handler结构根据node成员放入input_handler_list链表
list_for_each_entry(dev, &input_dev_list, node)//根据node这个成员在input_dev_list链表中循环查找dev结构
input_attach_handler(dev, handler);//对于每一个dev结构调用input_attach_handler函数
input_wakeup_procfs_readers();//将这个设备信息写入proc文件系统
return 0;
}
接着搜索input_register_handler,抽取driversinputevdev.c这个文件,可以看到在这个模块的入口函数调用了注册函数
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
接着看到evdev_handler这个结构体,在这个结构体里面找到了evdev_fops这个结构
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_fops结构体,可以看到应用层调用的read、write等函数在这里被定义
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
};
知道了事件层对应的位置,那么设备驱动层在哪里呢?接着往下看,回到input_register_handler函数,在里面看到如下语句,这句语句的作用是将事件层与驱动层联系起来。
list_for_each_entry(dev, &input_dev_list, node)//根据node这个成员在input_dev_list链表中循环查找dev结构
input_attach_handler(dev, handler);//对于每一个dev结构调用input_attach_handler函数
这里可以看到一个新的结构体dev,先看一下dev结构体,它的原型为input_dev,跟抽取driversinputevdev.c这个文件一样,搜索input_dev这个结构体,先列出input_dev这个结构体
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)];
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;
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;
};
接着看到driversinputtabletkbtab.c这个文件,这个文件代表的就是设备驱动层,简单分析一下,可以看到它也是一个内核的模块,可以动态加载,一旦加载后,它会调用kbtab_init函数,最终会调用到kbtab_probe这个函数,可以看到最终又定位到了input_register_device这个注册设备的函数,它位于核心层,即driversinputinput.c文件下。
static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
...
...
input_dev = input_allocate_device();//分配一个input_dev 结构体
if (!kbtab || !input_dev)
goto fail1;
...
...
input_dev->name = 'KB Gear Tablet';//初始化input_dev 结构体
input_dev->phys = kbtab->phys;
usb_to_input_id(dev, &input_dev->id);
input_dev->dev.parent = &intf->dev;
input_set_drvdata(input_dev, kbtab);
input_dev->open = kbtab_open;
input_dev->close = kbtab_close;
input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC);
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH);
input_dev->mscbit[0] |= BIT(MSC_SERIAL);
input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0);
input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0);
...
...
error = input_register_device(kbtab->dev);//注册input_dev结构体
...
...
}
接着看到input_register_device这个函数,它根input_register_handler相对应,前一个注册设备驱动层,后一个注册事件层。列出input_register_device函数,它同样位于driversinputinput.c文件中。
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
const char *path;
int error;
set_bit(EV_SYN, dev->evbit);//设置同步事件
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
init_timer(&dev->timer);//初始化一个定时器
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {//按键是否需要重复,如果需要设置重复函数与重复时间
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;//获得按键值默认函数
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;//设置按键值默认函数
list_add_tail(&dev->node, &input_dev_list);//将dev->node放入input_dev_list链表
snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
'input%ld', (unsigned long) atomic_inc_return(&input_no) - 1);
if (!dev->cdev.dev)
dev->cdev.dev = dev->dev.parent;
error = class_device_add(&dev->cdev);
if (error)
return error;
path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
上一篇:Linux驱动之一个简单的输入子系统程序编写
下一篇:Linux驱动之定时器在按键去抖中的应用
推荐阅读最新更新时间:2024-11-11 10:41
设计资源 培训 开发板 精华推荐
- 使用 Analog Devices 的 LT8608IMSE 的参考设计
- LTC6990HDCB#TRMPBF 过压检测器/警报的典型应用。压电报警器直接驱动
- AD9446、16 位、80-MSPS ADC 用作测试平台,仅更改了背对背二极管的源
- 使用 Analog Devices 的 LTC2908ITS8-C1 的参考设计
- L7812C 稳压器用于光控制器的典型应用 (Vo(min) = Vxx + VBE)
- L6566A 多模控制器的典型应用,用于带 PFC 前端的 SMPS
- AM3G-2415DH30Z ±15V 3 瓦 DC-DC 转换器的典型应用
- DER-733 - 基于 HiperPFS-4 和 LYTSwitch-6 的 65W 双级 PFC 升压和隔离反激式可调光 LED 镇流器
- 功放板-单通道100W MAX
- 使用 Analog Devices 的 LTC1419CSW 的参考设计
- 开学季!EEworld下载中心 Cortex主题资源上传
- 有奖直播:ams投影照明(MLA)增强汽车与道路的沟通
- 浏览Intel物联网时代下的工厂&建筑,下载赢奖品
- 一天一个Webench设计,看谁能撑30天?
- 免费获赠LPC800迷你板 抢先体验MO+优异性能!
- NXP微控制器电路板,针对USB Type C接口设计,评测创意有奖征集,晒心得更有多重好礼!
- 下载有礼:看“智””造“热”侦探 FLIR ETS320 红外热像仪如何纠错!
- MPS有奖活动|绿色AI“芯” 动力,探索节能低碳的AI供电方案!
- 秀宝宝照片,赢EEWORLD神秘“六一”礼品
- 直播主题: 助力“双碳”目标 — ADI 智能工厂方案