轉載自:點擊打開鏈接
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個東西,現在來弄清吧!
參考文章:http://blog.csdn.net/heyunqi/article/details/1897108
這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);
- 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就頭大,因爲還沒有理清他們。
參考文章1 http://www.cnblogs.com/leaven/archive/2010/04/24/1719191.html
參考文章2 http://blog.chinaunix.net/uid-11319766-id-3253414.html
文章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);