之前的部落格有涉及到linux的input子系統,這裡學習記錄一下input子產品.
input子系統,作為管理輸入裝置與系統進行互動的中樞,任何的輸入裝置驅動都要通過input向核心注冊其裝置,
常用的輸入裝置也就是滑鼠,鍵盤,觸摸屏。
稍微細分一點整個輸入體系,就是 硬體驅動層,input核心中轉層,事件處理層.層次之間傳遞都以event事件的形式,這其中input連接配接上下層,分别提供接口.
之前有分析usbtouchscreen的驅動,也就是硬體驅動部分,這裡簡單記錄一下input核心中轉處理 input.c .
撰寫不易,轉載需注明出處:http://blog.csdn.net/jscese/article/details/42123673
input_init:
源碼位于/kernel/drivers/input/input.c ,子產品初始調用口subsys_initcall(input_init),
由kernel啟動的時候由kernel_init——>do_basic_setup();——>do_initcalls調用到,這個啟動邏輯,後續有機會去學習一下,
這裡首先調用到初始函數:
static int __init input_init(void)
{
int err;
err = class_register(&input_class); //注冊input class,可在/sys/class下看到對應節點檔案
if (err) {
pr_err("unable to register input_dev class\n");
return err;
}
err = input_proc_init(); //proc fs的下的一些初始操作,函數原型在input.c,可檢視/proc/bus/input
if (err)
goto fail1;
err = register_chrdev(INPUT_MAJOR, "input", &input_fops); // 注冊input字元裝置,主節點為INPUT_MAJOR==13,可以去input_fops裡看注冊函數,注冊到/dev/input
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
這就是最開始的初始化過程了.
可以看下注冊方法函數:
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};
這裡面關注open file方法即可,後面分析。
input.c中還有很多其它的接口以及全局資料,後面陸續聯通,先從裝置驅動最先調用到的注冊 input_register_device
input_register_device:
/**
* input_register_device - register device with input core
* @dev: device to be registered
*
* This function registers device with input core. The device must be
* allocated with input_allocate_device() and all it's capabilities
* set up before registering.
* If function fails the device must be freed with input_free_device().
* Once device has been successfully registered it can be unregistered
* with input_unregister_device(); input_free_device() should not be
* called in this case.
*/
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
//這個原子變量,代表總共注冊的input裝置,每注冊一個加1,因為是靜态變量,是以每次調用都不會清零的
struct input_handler *handler;
const char *path;
int error;
__set_bit(EV_SYN, dev->evbit); //EN_SYN 這個是裝置都要支援的事件類型,是以要設定
/*
* 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;
//以上設定的預設函數由input核心提供
dev_set_name(&dev->dev, "input%ld",
(unsigned long) atomic_inc_return(&input_no) - 1);
//設定input_dev中device的名字,這個名字會在/class/input中出現
error = device_add(&dev->dev);
//将device加入到linux裝置模型中去
if (error)
return error;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %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);
// 将新配置設定的input裝置連接配接到input_dev_list連結清單上
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
//周遊input_handler_list連結清單,配對 input_dev 和 input_handler
//input_attach_handler 這個函數是配對的關鍵,下面将詳細分析
input_wakeup_procfs_readers();
// 和proc檔案系統有關,暫時不考慮
mutex_unlock(&input_mutex);
return 0;
}
可以看到前面都是一些初始設定,加入到input.c 的全局input_dev 連結清單裡面,同時下面就行比對對應handler的時候需要周遊 handler 連結清單:
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
可以看到用到了一個 list_for_each_entry, 剛開始看到還沒看懂,這是一個宏定義,原型是在/kernel/include/linux/list.h:
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \ //就是個for循環,跳出條件周遊了一遍,又回到連結清單頭
pos = list_entry(pos->member.next, typeof(*pos), member))
input_attach_handler(dev, handler)則是比對這個要注冊dev的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); //傳回比對的id,類型是struct input_device_id
if (!id)
return -ENODEV;
error = handler->connect(handler, dev, id); //<span><span class="comment">//配對成功調用handler的connect函數,這個函數在事件處理器中定義,主要生成一個input_handle結構,并初始化,還生成一個事件處理器相關的裝置結構</span></span>
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;
}
可以看下比對 id 的結構:
struct input_device_id {
kernel_ulong_t flags;
__u16 bustype;
__u16 vendor;
__u16 product;
__u16 version;
kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
kernel_ulong_t driver_info;
};
有兩個函數input_match_device 以及 下面的 connect需要了解:
input_match_device:
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
int i;
for (id = handler->id_table; id->flags || id->driver_info; id++) {
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) //比對總線id
if (id->bustype != dev->id.bustype)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) //比對生産商id
if (id->vendor != dev->id.vendor)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) //比對産品id
if (id->product != dev->id.product)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) //比對版本
if (id->version != dev->id.version)
continue;
MATCH_BIT(evbit, EV_MAX); //比對id的evbit和input_dev中evbit的各個位,如果不比對則continue,數組中下一個裝置
MATCH_BIT(keybit, KEY_MAX);
MATCH_BIT(relbit, REL_MAX);
MATCH_BIT(absbit, ABS_MAX);
MATCH_BIT(mscbit, MSC_MAX);
MATCH_BIT(ledbit, LED_MAX);
MATCH_BIT(sndbit, SND_MAX);
MATCH_BIT(ffbit, FF_MAX);
MATCH_BIT(swbit, SW_MAX);
if (!handler->match || handler->match(handler, dev))
return id;
}
return NULL;
}
MATCH_bit 原型:
#define MATCH_BIT(bit, max) \
for (i = 0; i < BITS_TO_LONGS(max); i++) \
if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
break; \
if (i != BITS_TO_LONGS(max)) \
continue;
可以看到這麼多步的目的除了初始以及添加input_dev到連結清單,就是為了去比對 input_handler_list 中對應的handler ,
比對的最終是需要比對handler以及input_dev中的 id,其中input_dev 中的id類型為 input_id :
struct input_id {
__u16 bustype;
__u16 vendor;
__u16 product;
__u16 version;
};
這跟上面 input_handler 結構裡面的 input_device_id 比對id 變量,來确認 handler!
在最開始的時候就有提到,整個input輸入體系,分三個層次,現在的input核心層做的事就是:
在硬體驅動層調用 input_register_device時 ,往核心注冊驅動的同時,根據硬體的相關id去比對 适用的事件處理層(input_handler)!
這裡比對上之後就會調用對應 input_handler 的connect 函數。
input_handler:
input_dev 變量代表的是硬體裝置,前文Linux/Android——輸入子系統input_event傳遞 (二)中有介紹
input_handler 變量代表的是事件處理器
同樣在input.h 中定義:
/**
* struct input_handler - implements one of interfaces for input devices
* @private: driver-specific data
* @event: event handler. This method is being called by input core with
* interrupts disabled and dev->event_lock spinlock held and so
* it may not sleep
* @filter: similar to @event; separates normal event handlers from
* "filters".
* @match: called after comparing device's id with handler's id_table
* to perform fine-grained matching between device and handler
* @connect: called when attaching a handler to an input device
* @disconnect: disconnects a handler from input device
* @start: starts handler for given handle. This function is called by
* input core right after connect() method and also when a process
* that "grabbed" a device releases it
* @fops: file operations this driver implements
* @minor: beginning of range of 32 minors for devices this driver
* can provide
* @name: name of the handler, to be shown in /proc/bus/input/handlers
* @id_table: pointer to a table of input_device_ids this driver can
* handle
* @h_list: list of input handles associated with the handler
* @node: for placing the driver onto input_handler_list
*
* Input handlers attach to input devices and create input handles. There
* are likely several handlers attached to any given input device at the
* same time. All of them will get their copy of input event generated by
* the device.
*
* The very same structure is used to implement input filters. Input core
* allows filters to run first and will not pass event to regular handlers
* if any of the filters indicate that the event should be filtered (by
* returning %true from their filter() method).
*
* Note that input core serializes calls to connect() and disconnect()
* methods.
*/
struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*filter)(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; //這個就是上面說到的 會跟input_dev中的input_id 比對 id項的
struct list_head h_list;
struct list_head node;
};
這個結構詳細的含義,注釋有。
這個結構裡面暫時隻需要了解的:
注冊input_dev ,在事件處理資料連結清單裡面比對上 input_handler ,就會調用其 *connect 函數指針 進行連接配接,
将input_dev 跟 input_handler 進行綁定, 後續的運作事件的handler處理将會走這個input_handler的 *event !
在上篇input_event 傳遞中最後調用到event階段.
這裡簡單記錄到這裡,下篇介紹input_handler 的處理機制~