input是一种典型的驱动,目标是支持所有的linux输入设备,当前仅支持USB(2.4), 2。5/2.6以后将会支持大多数现有输入系统。
这部分模块直接与硬件交互,传递事件给输入模块(input module).
他同时也能处理分辨输入事件。
关键字:
HID:人机交互设备(界面),此类设备固件必须支持HID报表格式。例如touchscreen中所使用到的
input_report_abs
input_report_key
input_mt_sync
./Documnetation/input/ 中有如下文件
amijoy.txt
appletouch.txt
atarikbd.txt
bcm5974.txt
cd32.txt
cma3000_d0x.txt
cs461x.txt
elantech.txt
event-codes.txt
ff.txt
gameport-programming.txt
iforce-protocol.txt
input-programming.txt
input.txt
interactive.fig
joystick-api.txt
joystick-parport.txt
joystick.txt
multi-touch-protocol.txt
notifier.txt
ntrig.txt
rotary-encoder.txt
sentelic.txt
shape.fig
walkera0701.txt
xpad.txt
yealink.txt
将其归类为3类
游戏杆类 *joy* , cd32.txt, gameport-programming.txt, rotary-encoder.txt(编码),walkera0701.txt,xpad.txt
触摸屏/板类 *touch*,bcm5974.txt,elantech.txt,ntrig.txt,
键盘类 atarikbd.txt,notifier.txt, sentelic.txt(鼠标),yealink.txt(电话面板)
动机加速探测 cma3000_d0x.txt,cs461x.txt,
震动(力反馈)探测 ff.txt, iforce-protocol.txt,(技术新颖 ff iforce 2套协议,后期跟进)
input_dev相关阐述类 event-codes.txt(通报事件编码),input-programming.txt(例子),input.txt(简单介绍,过于陈旧)
很多,量也很大,我决定从通用到特殊这样的顺序来阅读这些文档,先阅读input_dev通用相关阐述,然后针对个人所需阅读相关类型的通用文档,例如,键盘编码,然后再参考的阅读下某个特定的说明文档。
input.txt 这个是我们应该第一个看的文档,他简单的介绍了input_dev的用途
event-code.txt中讲述了一些详细的宏编码及其意义,注意内部介绍的发送顺序注意点。(注意并非所用宏关键字都已经完善eg EV_PWR)
翻译几句,将会用到的TS相关描述:
ABS_{X, Y}用于上报触摸点(注意区别与鼠标的REL_{X,Y}),触屏发生触摸时,必须使用BTN_TOUCH进行上报,BTN_TOOL_<name>也可用。
阅读input-programming.txt 了解input_dev的使用。
input_dev的使用
1.input_allocate_device 申请input_dev内存空间(NOTE:其使用位置)
2.设置input_dev的属性(接受发送事件类型)
3.注册input_register_device;
下图是我对input_dev的理解, 他可以看作一种通用型接口层(类似HAL),也可以独立作为一个驱动来进行对物理硬件的操作。
|------------------------------------------|
| |
|
input_dev |
| |
|------------------------------------------|
| any other driver |
| or direct access |
| the phyiscal hardware |
|------------------------------------------|
| |
| hardware |
|____________________________|
*************************multi-touch-protocol.txt**************
这个协议是为了上报多个触点而规定的,按照设备的兼容性分为A类与B类
触点的信息按照顺序,以独立分隔的报文包发送ABS_MT事件。只识别ABS_MT事件。因为ABS_MT是被ST(single touch)所忽略的,所以MT可以在其基础上进行实现。
A类设备的驱动以input_mt_sync()函数在每个包的最后发送来区别不同的包,他会产生一个SYN_MT_REPORT事件,告诉接收者接收数据并准备下一次接收。
B类设备的驱动以input_mt_slot()函数在每个包发送前调用,这个会产生一个ABS_MT_SLOT事件。
所有的驱动以调用input_sync()函数作为多触点传输结束标志。
无状态A类协议与状态B类协议主要区别就是是否使用标记联系量来减少传输到用户层的数据数量。
这点表示没看懂,我看了下正在使用的源码,感觉2类就是分割不一样。有知道的请告知
qingluo
个人猜测是不用发送完整的XY坐标,如果只是单个坐标变换,仅需要发送ID与变换的那个坐标就行了。
多点触控,触摸压力的探测:
首先有2个变量
ABS_MT_TOUCH_MAJOR 手指尖实际触摸到屏幕的面积的直径
ABS_MT_WIDTH_MAJOR 整个手指的周长的等效面积的直径
ABS_MT_WIDTH_MAJOR => ABS_MT_TOUCH_MAJOR 这个等式恒成立。
所以在刚开始触摸(轻触)时,TOUCH/WIDTH 比值较小,
当触摸压力增大,TOUCH值增加,TOUCH/WIDTH的比值增大;
当触摸压力减小,TOUCH值减小,TOUCH/WIDTH的比值减小;
这样就能智能的探测压力的变化了。在设备初始化期,我们可以设定并校正一个基础值。
其他的一些特性比较抽象,在以后用的到时候再提(实际上也比较新颖,但是在较为低端的触控IC上无法完全支持)。
***********************************接下来看源码*****************************************
通用型的源码文件有以下几个
input.c input-compat.c input-compat.h input-mt.c input-polldev.c
这几个文件在链接的时候会组合成input-core.o,这个就是input device的核心文件了。
首先看input.c:
来到最下面,额为什么是subsys_initcall而不是module_init呢?以前没有弄清楚这2个东西,现在来弄清吧!
参考文章:
这2个都会被#ifndef MODULE/#else/#endif区分成2种实现
非MODULE形式
首先来跟踪module_init
include/linux/init.h中
258 /**
259 * module_init() - driver initialization entry point
260 * @x: function to be run at kernel boot time or module insertion
261 *
262 * module_init() will either be called during do_initcalls() (if
263 * builtin) or at module insertion time (if a module). There can only
264 * be one per module.
265 */
266 #define module_init(x) __initcall(x);
继续深入跟踪__initcall(x)
include/linux/init.h中
212 #define __initcall(fn) device_initcall(fn)
好了接下来的跟踪等会儿进行,我们来跟踪subsys_init
在include/linux/init.h中
258 /**
259 * module_init() - driver initialization entry point
260 * @x: function to be run at kernel boot time or module insertion
261 *
262 * module_init() will either be called during do_initcalls() (if
263 * builtin) or at module insertion time (if a module). There can only
264 * be one per module.
265 */
266 #define module_init(x) __initcall(x);
说明在MODULE形式中2者的最终实现是完全相同的,
接下来MODULE形式:
287 #define subsys_initcall(fn) module_init(fn) 注意这个是不被建议的使用方式,具体参考282行
*****************************************************************************************
295 #define module_init(initfn) \
296 static inline initcall_t __inittest(void) \
297 { return initfn; } \
298 int init_module(void) __attribute__((alias(#initfn)));
这里就是一个简单的回调
接下来我们开非MODULE模式的__initcall(x)的具体实现。
他们都在
188 /*
189 * A "pure" initcall has no dependencies on anything else, and purely
190 * initializes variables that couldn't be statically initialized.
191 *
192 * This only exists for built-in code, not for modules.
193 */
194 #define pure_initcall(fn) __define_initcall("0",fn,0)
195
196 #define core_initcall(fn) __define_initcall("1",fn,1)
197 #define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
198 #define postcore_initcall(fn) __define_initcall("2",fn,2)
199 #define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
200 #define arch_initcall(fn) __define_initcall("3",fn,3)
201 #define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
202 #define subsys_initcall(fn) __define_initcall("4",fn,4)
203 #define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
204 #define fs_initcall(fn) __define_initcall("5",fn,5)
205 #define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
206 #define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
207 #define device_initcall(fn) __define_initcall("6",fn,6)
208 #define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
209 #define late_initcall(fn) __define_initcall("7",fn,7)
210 #define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
这个中,深入__define_initcall
167 /* initcalls are now grouped by functionality into separate
168 * subsections. Ordering inside the subsections is determined
169 * by link order.
170 * For backwards compatibility, initcall() puts the call in
171 * the device init subsection.
172 *
173 * The `id' arg to __define_initcall() is needed so that multiple initcalls
174 * can point at the same handler without causing duplicate-symbol build errors.
175 */
176
177 #define __define_initcall(level,fn,id) \
178 static initcall_t __initcall_##fn##id __used \
179 __attribute__((__section__(".initcall" level ".init"))) = fn
so subsys_initcall == __initcall_fn4 它将被链接器放于section .initcall4.init. 中
在启动过程中,do_basic_setup--->do_initcalls里有以下代码:
699 static void __init do_initcalls(void)
700 {
701 initcall_t *fn;
702
703 for (fn = __early_initcall_end; fn < __initcall_end; fn++)
704 do_one_initcall(*fn);
705 }
do_one_initcall中
666 int __init_or_module do_one_initcall(initcall_t fn)
......
671 if (initcall_debug)
672 ret = do_one_initcall_debug(fn);
673 else
674 ret = fn();
......
694 }
完成初始化回调。
接下来看初始化函数
2139 static int __init input_init(void)
2140 {
2141 int err;
2142
2143 err = class_register(&input_class); //1.注册类型 input类
2144 if (err) {
2145 pr_err("unable to register input_dev class\n");
2146 return err;
2147 }
2148
2149 err = input_proc_init(); //2.做一些proc文件的初始化操作
2150 if (err)
2151 goto fail1;
2152
2153 err = register_chrdev(INPUT_MAJOR, "input", &input_fops);//3.注册字符设备
2154 if (err) {
2155 pr_err("unable to register char major %d", INPUT_MAJOR);
2156 goto fail2;
2157 }
2158
2159 return 0;
2160
2161 fail2: input_proc_exit();
2162 fail1: class_unregister(&input_class);
2163 return err;
2164 }
接下来一步一步分析。
第一步:注册input类
1632 struct class input_class = {
1633 .name = "input",
1634 .devnode = input_devnode,
1635 };
1636 EXPORT_SYMBOL_GPL(input_class);
由于class是会被其他所使用的,所以我们应该将他符号外抛,name不解释了,devnode在注释中是Callback to provide the devtmpfs.
这个暂时不知,放一边先!
这里就注册了一个class。
第二步:proc文件初始化操作
1165 static int __init input_proc_init(void)
1166 {
1167 struct proc_dir_entry *entry;
1168
1169 proc_bus_input_dir = proc_mkdir("bus/input", NULL);
1170 if (!proc_bus_input_dir)
1171 return -ENOMEM;
1172
1173 entry = proc_create("devices", 0, proc_bus_input_dir,
1174 &input_devices_fileops);
1175 if (!entry)
1176 goto fail1;
1177
1178 entry = proc_create("handlers", 0, proc_bus_input_dir,
1179 &input_handlers_fileops);
1180 if (!entry)
1181 goto fail2;
1182
1183 return 0;
1184
1185 fail2: remove_proc_entry("devices", proc_bus_input_dir);
1186 fail1: remove_proc_entry("bus/input", NULL);
1187 return -ENOMEM;
1188 }
这里创建了一个proc文件夹路径 bus/input 2个文件 devices, handlers,这里注意与这2个文件相关联的文件操作函数指针。
第三步:注册字符设备
看似完毕,实际上刚刚开始。具体的文件操作函数我们涉及到的时候再来看。
在某款multitouch产品的驱动初始化函数中,第一个首先调用的是input_alloc_device()
1638 /**
1639 * input_allocate_device - allocate memory for new input device
1640 *
1641 * Returns prepared struct input_dev or NULL.
1642 *
1643 * NOTE: Use input_free_device() to free devices that have not been
1644 * registered; input_unregister_device() should be used for already
1645 * registered devices.
1646 */
1647 struct input_dev *input_allocate_device(void)
1648 {
1649 struct input_dev *dev;
1650
1651 dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); //申请动态存储空间
1652 if (dev) {
1653 dev->dev.type = &input_dev_type; //设置struct device中的type属性
1654 dev->dev.class = &input_class; //设置struct device中的class属性
1655 device_initialize(&dev->dev); //稍后分析
1656 mutex_init(&dev->mutex); //初始化互斥量
1657 spin_lock_init(&dev->event_lock); //初始化自旋锁
1658 INIT_LIST_HEAD(&dev->h_list); //初始化input handlers链表,这个链表存放了所有与该设备相关联的input handlers,访问该链表时
//注意使用mutex
1659 INIT_LIST_HEAD(&dev->node); //初始化node,node是用于将设备放到input_dev_list的
1660
1661 __module_get(THIS_MODULE); //这个不大理解,猜测是标记设备属于当前调用模块
1662 }
1663
1664 return dev;
1665 }
1666 EXPORT_SYMBOL(input_allocate_device);
**********************************************************************************************************
接下来我们来深入分析下 device_initialize(),这个也是devcie_register函数的前半部分。
587 void device_initialize(struct device *dev)
588 {
589 dev->kobj.kset = devices_kset;
590 kobject_init(&dev->kobj, &device_ktype);
591 INIT_LIST_HEAD(&dev->dma_pools);
592 mutex_init(&dev->mutex);
593 lockdep_set_novalidate_class(&dev->mutex);
594 spin_lock_init(&dev->devres_lock);
595 INIT_LIST_HEAD(&dev->devres_head);
596 device_pm_init(dev);
597 set_dev_node(dev, -1);
598 }
这也是最复杂的部分,我看着kobject,kset就头大,因为还没有理清他们。
文章2相对清晰很多。我的理解就是kobject,类似于JAVA中的OBJECT或QT中的QObject之类的,配置list_head与container_of,明确的体现了面向对象思想中的封装(内嵌),继承并小小的体现了“多态“。
589 dev->kobj.kset = devices_kset;
590 kobject_init(&dev->kobj, &device_ktype);
那么我们这里就是,dev中有自己的kobject,我们将他设置到devices_kset的链中去,我们就能发现在/sys/devices/目录下有出现相应的设备(内部包含了object)了。建议反复阅读文章2,深入理解linux如何管理模块的。
lockdep_set_novalidate_class(&dev->mutex);这个大概就是将这个互斥量映射到系统的管理模块中,让其他需要使用这个量的模块可以使用,恩,大概就这么猜,跳过。
596 device_pm_init(dev);这个才是比较重要的,PC级开发都可以忽略,但是在嵌入式设备中,能耗管理是十分重要的一个指标与技术。
这里主要干了2件事
68 void device_pm_init(struct device *dev)
69 {
70 dev->power.is_prepared = false;
71 dev->power.is_suspended = false;
72 init_completion(&dev->power.completion);
73 complete_all(&dev->power.completion);
74 dev->power.wakeup = NULL;
75 spin_lock_init(&dev->power.lock);
76 pm_runtime_init(dev);
77 INIT_LIST_HEAD(&dev->power.entry);
78 }
1.初始化power相关的属性量
2.设置相关默认值与挂载相关的能源管理完成队列
**************************************哇,input_alloc_device都做了这么多东西**************************************************
接下来可以按照各自的需求设置一下input_device的支持的动作类型,与相应的初始化值。
注册input_device input_register_device()
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");
printk("<qingluo>position mark\n");
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实际工作过程:
例如某multitouch中,A类设备
首先调用
input_mt_slot()
static inline void input_mt_slot(struct input_dev *dev, int slot)
{
input_event(dev, EV_ABS, ABS_MT_SLOT, slot);
}
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;
if (is_event_supported(type, dev->evbit, EV_MAX)) { //检查是否支持该类型的事件
spin_lock_irqsave(&dev->event_lock, flags);
add_input_randomness(type, code, value);
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
EXPORT_SYMBOL(input_event);
void add_input_randomness(unsigned int type, unsigned int code,
unsigned int value)
{
static unsigned char last_value;
/* ignore autorepeat and the like */
if (value == last_value)
return;
DEBUG_ENT("input event\n");
last_value = value;
add_timer_randomness(&input_timer_state,
(type << 4) ^ code ^ (code >> 4) ^ value);
}
EXPORT_SYMBOL_GPL(add_input_randomness);