#Input输入子系统个人总结
Input输入子系统在驱动开发中的重要性,大家都知道, 就不多说了。突发奇想想写博客,来写写工作中常遇到的知识点,总结一下,为了更加清晰的理解。
我这里主要参考了几篇blog,如有侵权,还请谅解。
Linux input子系统分析之一:软件层次
http://blog.csdn.net/yueqian_scut/article/details/47903853
全网络对Linux input子系统最清晰、详尽的分析
http://blog.csdn.net/yueqian_scut/article/details/48026955
input子系统全面分析
http://www.cnblogs.com/lcw/p/3293302.html
一、完整的input子系统包括的层次和整个框架。
系统整体框图
input子系统框图
二、input 子系统 所涉及到的结构体和函数。
struct input_dev 设备结构体,记录了设备的信息和支持的事件
struct input_dev {
const char *name; //设备名
const char *phys; //设备节点名称
const char *uniq; // 唯一的ID号,
struct input_id id; // 输入设备ID,用于和event handler 匹配
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; //bitmap of device properties and quirks
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //设备支持的事件类型
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //按键 ex:上下左右 home
unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; //相对坐标 ex 鼠标
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //绝对坐标 ex 触摸屏
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)]; //作用力
unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; //开关
unsigned int hint_events_per_packet; /* average number of events generated by the device in a packet (between EV_SYN/SYN_REPORT events). Used by event handlers to estimate size of the buffer needed to hold events.*/
unsigned int keycodemax; //设备支持最大按键值个数
unsigned int keycodesize; //每个按键字节大小
void *keycode; //按键池,指向按键值数组首地址
int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode); //修改按键值
int (*getkeycode)(struct input_dev *dev,
struct input_keymap_entry *ke); //获取按键值
struct ff_device *ff; //强制更新输入设备的部分内容
unsigned int repeat_key; //重复按键的键值
struct timer_list timer; //连击时定时器
int rep[REP_CNT]; //重复按键参数值
struct input_mt_slot *mt; //pointer to multitouch state
int mtsize;
int slot;
int trkid;
struct input_absinfo *absinfo; /* array of struct input_absinfo elements holding information about absolute axes (current value, min, max, flat, fuzz, resolution) */
unsigned long key[BITS_TO_LONGS(KEY_CNT)]; //记录状态
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
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 __rcu *grab; //类似私有指针,可以访问到事件处理接口event
spinlock_t event_lock; //自旋锁
struct mutex mutex; //用于open close函数的连续访问互斥
unsigned int users; //设备使用计数
bool going_away; //不知
bool sync; //是否上传同步ok
struct device dev; //设备结构体
struct list_head h_list; //handle list
struct list_head node; // input dev list
};
分配和销毁input设备
struct input_dev *input_allocate_device(void);
void input_free_device(struct input_dev *dev)。
注册和注销input设备
int input_register_device(struct input_dev *dev)
void input_unregister_device(struct input_dev *dev)
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;
//动态申请内存,使用GFP_KERNEL方式,注意GFP_KERNEL可能导致睡眠,不能在中断中调用这个函数
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); //类设备初始化,添加进input类设备模型中
mutex_init(&dev->mutex); //初始化互斥锁
spin_lock_init(&dev->event_lock); //初始化自旋锁
INIT_LIST_HEAD(&dev->h_list);//初始化handle链表
INIT_LIST_HEAD(&dev->node); //初始化输入设备链表
__module_get(THIS_MODULE);
}
return dev;
}
truct input_dev *input_dev = input_allocate_device();
input_dev->name = "tiny4412 Touchscreen";
input_dev->phys = "tiny4412/input0";
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0002;
input_dev->id.version = 0x0100;
input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input_set_capability(barom->input_dev, EV_KEY, KEY_DOWN);
int input_register_device(struct input_dev *dev)
void input_unregister_device(struct input_dev *dev)
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;
/* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit);
/* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit);
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);
if (!dev->hint_events_per_packet)
dev->hint_events_per_packet =
input_estimate_events_per_packet(dev);
/*
* 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;
dev_set_name(&dev->dev, "input%ld",
(unsigned long) atomic_inc_return(&input_no) - 1);
error = device_add(&dev->dev);
if (error)
return error;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
pr_info("%s as %s\n",
dev->name ? dev->name : "Unspecified device",
path ? path : "N/A");
kfree(path);
error = mutex_lock_interruptible(&input_mutex);
if (error) {
device_del(&dev->dev);
return error;
}
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
input_register_device
进一步初始化输入设备,例如连击事件;
注册输入设备到input类中;
把输入设备挂到输入设备链表input_dev_list中;
查找并匹配输入设备对应的事件处理层,通过input_handler_list链表
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);
if (!id)
return -ENODEV;
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
pr_err("failed to attach handler %s to device %s, error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}
在此之前我们还需要了解另一个结构体struct input_handler
struct input_handler {
void *private; //driver-specific data
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); //处理input dev 产生的数据,handler区处理和上报。
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//从"filter"中分离正常事件
bool (*match)(struct input_handler *handler, struct input_dev *dev); //设备Id和handler的id_table 比较后,设备和处理器之间进行更细微的匹配
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id); //handler和dev 匹配之后会被调用
void (*disconnect)(struct input_handle *handle);//关闭handler从input dev
void (*start)(struct input_handle *handle);//根据handle 启动handler
const struct file_operations *fops;
int minor;
const char *name; //handler name,可以在/proc/bus/input/handlers 中看到
const struct input_device_id *id_table;//a table for input_dev_ids this driver can handle
struct list_head h_list;
struct list_head node;
};
数据包 和 上传数据包
struct input_event {
struct timeval time; //时间
__u16 type; // 数据类型: 按键数据/绝对数据/相对数据
__u16 code; // 类型中某个特定数据: 比如 上键,下键, 回车键,...
__s32 value; // 状态: 按下会抬起
};
上传数据event ,可以看出input_report_xxx() 是对input_event()进一步封装。
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
static inline void input_report_key(struct input_dev *dev,
unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_ABS, code, value);
}
了解了上门的结构体和函数,现在我们开始整个input子系统,分三个层次,input device,input core和input handle。
input device
dev = input_allocate_device()
dev->name = "xxx"
...//填充dev。
//设置input_dev 能够产生的数据类型
dev->evbit[0] = BIT_MASK(EV_KEY);
set_bit(EV_ABS,dev->evbit);
input_register_device(dev)
将当前input dev 加入到input dev list
//遍历input handler list ,取出每个handler,和input dev比对,匹配,所有的input dev 都可以和 event handler匹配。然后执行handler 的connect 方法。
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
id = input_match_device(handler, dev);
handler->connect(handler, dev, id);
input core
input_init()
class_register()//创建类。 /sys/class/input/dev->name/
register_chrdev() //创建设备。
input handler
evdev.c
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,
};
input_register_handler(&evdev_handler)
//将当前的handler加入到全局的数组
//input_table[handler->minor >> 5] = handler;
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int error;
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
if (minor == EVDEV_MINORS) {
pr_err("no more free evdev devices\n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = true;
evdev->minor = minor;
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;
error = evdev_install_chrdev(evdev);
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev);
if (error)
goto err_cleanup_evdev;
return 0;
err_cleanup_evdev:
evdev_cleanup(evdev);
err_unregister_handle:
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
return error;
}