一. Input子系統(tǒng)結(jié)構(gòu)與功能實(shí)現(xiàn)
1. Input子系統(tǒng)是分層結(jié)構(gòu)的,總共分為三層: 硬件驅(qū)動(dòng)層,子系統(tǒng)核心層,事件處理層。
(1)其中硬件驅(qū)動(dòng)層負(fù)責(zé)操作具體的硬件設(shè)備,這層的代碼是針對具體的驅(qū)動(dòng)程序的,需要驅(qū)動(dòng)程序的作者來編寫。
(2)子系統(tǒng)核心層是鏈接其他兩個(gè)層之間的紐帶與橋梁,向下提供驅(qū)動(dòng)層的接口,向上提供事件處理層的接口。
(3)事件處理層負(fù)責(zé)與用戶程序打交道,將硬件驅(qū)動(dòng)層傳來的事件報(bào)告給用戶程序。
2. 各層之間通信的基本單位就是事件,任何一個(gè)輸入設(shè)備的動(dòng)作都可以抽象成一種事件,如鍵盤的按下,觸摸屏的按下,鼠標(biāo)的移動(dòng)等。事件有三種屬性:類型(type),編碼(code),值(value),Input子系統(tǒng)支持的所有事件都定義在input.h中,包括所有支持的類型,所屬類型支持的編碼等。事件傳送的方向是 硬件驅(qū)動(dòng)層-->子系統(tǒng)核心-->事件處理層-->用戶空間
3. 以觸摸屏為例說明輸入子系統(tǒng)的工作流程:
注:mini2440的觸摸屏驅(qū)動(dòng)所用驅(qū)動(dòng)層對應(yīng)的模塊文件為:s3c2410_ts.c,事件處理層對應(yīng)的模塊文件為 evdev.c
(1)s3c2410_ts模塊初始化函數(shù)中將觸摸屏注冊到了輸入子系統(tǒng)中,于此同時(shí),注冊函數(shù)在事件處理層鏈表中尋找事件處理器,這里找到的是evdev,并且將驅(qū)動(dòng)與事件處理器掛載。并且在/dev/input中生成設(shè)備文件event0,以后我們訪問這個(gè)文件就會(huì)找的我們的觸摸屏驅(qū)動(dòng)程序。
(2)應(yīng)用程序打開設(shè)備文件/dev/input/event0,讀取設(shè)備文件,調(diào)用evdev模塊中read,如果沒有事件進(jìn)程就會(huì)睡眠。
(3)當(dāng)觸摸屏按下,驅(qū)動(dòng)層通過子系統(tǒng)核心將事件(就是X,Y坐標(biāo)),傳給事件處理層也就是evdev,evdev喚醒睡眠的進(jìn)程,將事件傳給進(jìn)程處理。
二.主要input通用數(shù)據(jù)結(jié)構(gòu)
1.input_dev 這是input設(shè)備基本的設(shè)備結(jié)構(gòu),每個(gè)input驅(qū)動(dòng)程序中都必須分配初始化這樣一個(gè)結(jié)構(gòu),成員比較多
(1)有以下幾個(gè)數(shù)組:
- unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //事件支持的類型
- // 下面是每種類型支持的編碼
- unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //按鍵
- unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
- unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //絕對坐標(biāo),其中觸摸屏驅(qū)動(dòng)使用的就是這個(gè)
- 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)];
dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS)
absbit[BITS_TO_LONGS(ABS_CNT)]; 這個(gè)數(shù)組也是以位掩碼的形式,代表這個(gè)類型的事件支持的編碼
觸摸屏驅(qū)動(dòng)支持EV_ABS,所以要設(shè)置這個(gè)數(shù)組, 有一個(gè)專門設(shè)置這個(gè)數(shù)組的函數(shù)input_set_abs_params,代碼如下:
- static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat)
- {
- dev->absmin[axis] = min;
- dev->absmax[axis] = max;
- dev->absfuzz[axis] = fuzz;
- dev->absflat[axis] = flat;
- dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis); //填充了absbit這個(gè)數(shù)組
- }
input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0); //這個(gè)是設(shè)置ad轉(zhuǎn)換的x坐標(biāo)
input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0); //這個(gè)是設(shè)置ad轉(zhuǎn)換的y坐標(biāo)
input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0); //這個(gè)是設(shè)置觸摸屏是否按下的標(biāo)志
設(shè)置ABS_X編碼值范圍為0-0x3ff,因?yàn)閙ini2440的AD轉(zhuǎn)換出的數(shù)據(jù)最大為10位,所以不會(huì)超過0x3ff。
(2) struct input_id id 成員
這個(gè)是標(biāo)識設(shè)備驅(qū)動(dòng)特征的
- struct input_id {
- __u16 bustype; //總線類型
- __u16 vendor; //生產(chǎn)廠商
- __u16 product; //產(chǎn)品類型
- __u16 version; //版本
- };
也無關(guān)緊要。
(3) 還有其他一些成員,也比較重要,但是驅(qū)動(dòng)程序可以不用管,都是由子系統(tǒng)核心來處理的。
(4) 可以看出input_dev 結(jié)構(gòu)所屬層為硬件驅(qū)動(dòng)層,以后就用input_dev來表示輸入設(shè)備。
2. input_handler 這是事件處理器的數(shù)據(jù)結(jié)構(gòu),代表一個(gè)事件處理器
(1)幾個(gè)操作函數(shù)
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
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);
event 函數(shù)是當(dāng)事件處理器接收到了來自input設(shè)備傳來的事件時(shí)調(diào)用的處理函數(shù),負(fù)責(zé)處理事件,非常重要,在事件傳遞過程中會(huì)詳細(xì)分析。
connect 函數(shù)是當(dāng)一個(gè)input設(shè)備模塊注冊到內(nèi)核的時(shí)候調(diào)用的,將事件處理器與輸入設(shè)備聯(lián)系起來的函數(shù),也就是將input_dev和input_handler配對的函數(shù)。
disconnect 函數(shù)實(shí)現(xiàn)connect相反的功能。
start 暫時(shí)沒有發(fā)現(xiàn)有什么作用。
(2) 兩個(gè)id
const struct input_device_id *id_table; //這個(gè)是事件處理器所支持的input設(shè)備
const struct input_device_id *blacklist; //這個(gè)是事件處理器應(yīng)該忽略的input設(shè)備
這兩個(gè)數(shù)組都會(huì)用在connect函數(shù)中,input_device_id結(jié)構(gòu)與input_id結(jié)構(gòu)類似,但是input_device_id有一個(gè)flag,用來讓程序選擇比較哪項(xiàng),如:busytype,vendor還是其他。
(3) 兩個(gè)鏈表
struct list_headh_list; //這個(gè)鏈表用來鏈接他所支持的input_handle結(jié)構(gòu),input_dev與input_handler配對之后就會(huì)生成一個(gè)input_handle結(jié)構(gòu)
struct list_headnode; //鏈接到input_handler_list,這個(gè)鏈表鏈接了所有注冊到內(nèi)核的事件處理器
(4) 其他的成員一看代碼就知道是什么意思,這里就不說明了。
3. input_handle 結(jié)構(gòu)體代表一個(gè)成功配對的input_dev和input_handler
- struct input_handle {
- void *private; //每個(gè)配對的事件處理器都會(huì)分配一個(gè)對應(yīng)的設(shè)備結(jié)構(gòu),如evdev事件處理器的evdev結(jié)構(gòu),注意這個(gè)結(jié)構(gòu)與設(shè)備驅(qū)動(dòng)層的input_dev不同,初始化handle時(shí),保存到這里。
- int open; //打開標(biāo)志,每個(gè)input_handle 打開后才能操作,這個(gè)一般通過事件處理器的open方法間接設(shè)置
- const char *name;
- struct input_dev *dev; //關(guān)聯(lián)的input_dev結(jié)構(gòu)
- struct input_handler *handler; //關(guān)聯(lián)的input_handler結(jié)構(gòu)
- struct list_head d_node; //input_handle通過d_node連接到了input_dev上的h_list鏈表上
- struct list_head h_node; //input_handle通過h_node連接到了input_handler的h_list鏈表上
- };
input_dev 是硬件驅(qū)動(dòng)層,代表一個(gè)input設(shè)備
input_handler 是事件處理層,代表一個(gè)事件處理器
input_handle 個(gè)人認(rèn)為屬于核心層,代表一個(gè)配對的input設(shè)備與input事件處理器
input_dev 通過全局的input_dev_list鏈接在一起。設(shè)備注冊的時(shí)候?qū)崿F(xiàn)這個(gè)操作。
input_handler 通過全局的input_handler_list鏈接在一起。事件處理器注冊的時(shí)候?qū)崿F(xiàn)這個(gè)操作(事件處理器一般內(nèi)核自帶,一般不需要我們來寫)
input_hande 沒有一個(gè)全局的鏈表,它注冊的時(shí)候?qū)⒆约悍謩e掛在了input_dev 和 input_handler 的h_list上了。通過input_dev 和input_handler就可以找到input_handle 在設(shè)備注冊和事件處理器, 注冊的時(shí)候都要進(jìn)行配對工作,配對后就會(huì)實(shí)現(xiàn)鏈接。通過input_handle也可以找到input_dev和input_handler。
主要函數(shù)
因?yàn)榉治鲆凰v的每種數(shù)據(jù)結(jié)構(gòu)都代表一類對象,所以每種數(shù)據(jù)結(jié)構(gòu)都會(huì)對應(yīng)一個(gè)注冊函數(shù),他們都定義在子系統(tǒng)核心的input.c文件中。主要有三個(gè)注冊函數(shù)
input_register_device 向內(nèi)核注冊一個(gè)input設(shè)備
input_register_handle 向內(nèi)核注冊一個(gè)handle結(jié)構(gòu)
input_register_handler 注冊一個(gè)事件處理器
1. input_register_device 注冊一個(gè)input輸入設(shè)備,這個(gè)注冊函數(shù)在三個(gè)注冊函數(shù)中是驅(qū)動(dòng)程序唯一調(diào)用的。下面分析這個(gè)函數(shù):
- int input_register_device(struct input_dev *dev)
- {
- static atomic_t input_no = ATOMIC_INIT(0);
- //這個(gè)原子變量,代表總共注冊的input設(shè)備,每注冊一個(gè)加1,因?yàn)槭庆o態(tài)變量,所以每次調(diào)用都不會(huì)清零的
- struct input_handler *handler;
- const char *path;
- int error;
- __set_bit(EV_SYN, dev->evbit); //EN_SYN 這個(gè)是設(shè)備都要支持的事件類型,所以要設(shè)置
- /*
- * 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.
- */
- // 這個(gè)內(nèi)核定時(shí)器是為了重復(fù)按鍵而設(shè)置的
- 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;
- //如果沒有定義有關(guān)重復(fù)按鍵的相關(guān)值,就用內(nèi)核默認(rèn)的
- }
- if (!dev->getkeycode)
- dev->getkeycode = input_default_getkeycode;
- if (!dev->setkeycode)
- dev->setkeycode = input_default_setkeycode;
- //以上設(shè)置的默認(rèn)函數(shù)由input核心提供
- dev_set_name(&dev->dev, "input%ld",
- (unsigned long) atomic_inc_return(&input_no) - 1);
- //設(shè)置input_dev中device的名字,這個(gè)名字會(huì)在/class/input中出現(xiàn)
- error = device_add(&dev->dev);
- //將device加入到linux設(shè)備模型中去
- 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);
- //這個(gè)得到路徑名稱,并打印出來
- error = mutex_lock_interruptible(&input_mutex);
- if (error) {
- device_del(&dev->dev);
- return error;
- }
- list_add_tail(&dev->node, &input_dev_list);
- // 將新分配的input設(shè)備連接到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 這個(gè)函數(shù)是配對的關(guān)鍵,下面將詳細(xì)分析
- input_wakeup_procfs_readers();
- // 和proc文件系統(tǒng)有關(guān),暫時(shí)不考慮
- mutex_unlock(&input_mutex);
- return 0;
- }
- static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
- {
- const struct input_device_id *id;
- int error;
- if (handler->blacklist && input_match_device(handler->blacklist, dev))
- return -ENODEV;
- //blacklist是handler因該忽略的input設(shè)備類型,如果應(yīng)該忽略的input設(shè)備也配對上了,那就出錯(cuò)了
- id = input_match_device(handler->id_table, dev);
- //這個(gè)是主要的配對函數(shù),主要比較id中的各項(xiàng),下面詳細(xì)分析
- if (!id)
- return -ENODEV;
- error = handler->connect(handler, dev, id);
- //配對成功調(diào)用handler的connect函數(shù),這個(gè)函數(shù)在事件處理器中定義,主要生成一個(gè)input_handle結(jié)構(gòu),并初始化,還生成一個(gè)事件處理器相關(guān)的設(shè)備結(jié)構(gòu),后面詳細(xì)分析
- if (error && error != -ENODEV)
- printk(KERN_ERR
- "input: failed to attach handler %s to device %s, "
- "error: %d\n",
- handler->name, kobject_name(&dev->dev.kobj), error);
- //出錯(cuò)處理
- return error;
- }
下面分析input_match_device函數(shù):
- static const struct input_device_id *input_match_device(const struct input_device_id *id,
- struct input_dev *dev)
- {
- int i;
- //函數(shù)傳入的參數(shù)是所要配對handler的id_table,下面遍歷這個(gè)id_table尋找合適的id進(jìn)行配對
- for (; id->flags || id->driver_info; id++) {
- if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
- if (id->bustype != dev->id.bustype)
- continue;
- ......
- //針對handler->id->flag,比較不同的類型
- //如果比較成功進(jìn)入下面的宏,否則進(jìn)入下一個(gè)id
- MATCH_BIT(evbit, EV_MAX);
- ......
- MATCH_BIT(swbit, SW_MAX);
- return id;
- }
- }
然后依據(jù)id->flag來比較內(nèi)容,如果都比較成功進(jìn)入MATCH_BIT,這個(gè)宏是用來按位進(jìn)行比較的,功能是比較所支持事件的類型,只有所有的位都匹配才成功返回,否則進(jìn)行下一個(gè)id的比較。
- #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;
對于connect函數(shù),每種事件處理器的實(shí)現(xiàn)都有差異,但原理都相同,因?yàn)橛|摸屏用的事件處理器為evdev,下面分析evdev的connect函數(shù)evdev_connect
- static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
- const struct input_device_id *id)
- {
- //此函數(shù)傳入三個(gè)參數(shù),分別是:handler,dev,id
- struct evdev *evdev;
- int minor;
- int error;
- for (minor = 0; minor < EVDEV_MINORS; minor++)
- if (!evdev_table[minor])
- break;
- //EVDEV_MINORS為32,說明evdev這個(gè)handler可以同時(shí)有32個(gè)輸入設(shè)備和他配對,evdev_table中以minor(非次設(shè)備號,但是有一個(gè)換算關(guān)系)存放evdev結(jié)構(gòu)體,后面要詳細(xì)分析這個(gè)結(jié)構(gòu)體
- if (minor == EVDEV_MINORS) {
- printk(KERN_ERR "evdev: no more free evdev devices\n");
- return -ENFILE;
- }
- //這個(gè)說明32個(gè)位置全都被占用了,連接失敗
- evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
- //分配一個(gè)evdev結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體是evdev事件處理器特有的,后面會(huì)詳細(xì)分析
- 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);
- //初始化結(jié)構(gòu)體的一些成員
- dev_set_name(&evdev->dev, "event%d", minor);
- //這個(gè)是設(shè)置evdev中device的名字,他將出現(xiàn)在/class/input中。
- //前面也有一個(gè)device是input_dev的,名字是input(n),注意與他的不同
- //這個(gè)結(jié)構(gòu)是配對后的虛擬設(shè)備結(jié)構(gòu),沒有對應(yīng)的硬件,但是通過它可以找到相關(guān)的硬件
- evdev->exist = 1;
- 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;
- //因?yàn)閑vdev中包含handle了,所以初始化它就可以了,這樣就連接了input_handler與input_dev
- evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); //注意:這個(gè)minor不是真正的次設(shè)備號,還要加上EVDEV_MINOR_BASE
- evdev->dev.class = &input_class;
- evdev->dev.parent = &dev->dev;
- //配對生成的device,父設(shè)備是與他相關(guān)連的input_dev
- evdev->dev.release = evdev_free;
- device_initialize(&evdev->dev);
- error = input_register_handle(&evdev->handle);
- //注冊handle結(jié)構(gòu)體,這個(gè)函數(shù)后面詳細(xì)分析
- if (error)
- goto err_free_evdev;
- error = evdev_install_chrdev(evdev);
- //這個(gè)函數(shù)只做了一件事,就是把evdev結(jié)構(gòu)保存到evdev_table中,這個(gè)數(shù)組也minor為索引
- if (error)
- goto err_unregister_handle;
- error = device_add(&evdev->dev);
- //注冊到linux設(shè)備模型中
- 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;
- }
2. input_register_handle 注冊一個(gè)input_handle結(jié)構(gòu)體,比較簡單
- int input_register_handle(struct input_handle *handle)
- {
- struct input_handler *handler = handle->handler;
- struct input_dev *dev = handle->dev;
- int error;
- /*
- * We take dev->mutex here to prevent race with
- * input_release_device().
- */
- error = mutex_lock_interruptible(&dev->mutex);
- if (error)
- return error;
- list_add_tail_rcu(&handle->d_node, &dev->h_list);
- //將handle的d_node,鏈接到其相關(guān)的input_dev的h_list鏈表中
- mutex_unlock(&dev->mutex);
- list_add_tail(&handle->h_node, &handler->h_list);
- //將handle的h_node,鏈接到其相關(guān)的input_handler的h_list鏈表中
- if (handler->start)
- handler->start(handle);
- return 0;
- }
3. input_register_handler 注冊一個(gè)input_handler結(jié)構(gòu)體
- int input_register_handler(struct input_handler *handler)
- {
- struct input_dev *dev;
- int retval;
- retval = mutex_lock_interruptible(&input_mutex);
- if (retval)
- return retval;
- INIT_LIST_HEAD(&handler->h_list);
- if (handler->fops != NULL) {
- if (input_table[handler->minor >> 5]) {
- retval = -EBUSY;
- goto out;
- }
- input_table[handler->minor >> 5] = handler;
- }
- //input_table,每個(gè)注冊的handler都會(huì)將自己保存到這里,索引值為handler->minor右移5為,也就是除以32
- //為什么會(huì)這樣呢,因?yàn)槊總€(gè)handler都會(huì)處理最大32個(gè)input_dev,所以要以minor的32為倍數(shù)對齊,這個(gè)minor是傳進(jìn)來的handler的MINOR_BASE
- //每一個(gè)handler都有一個(gè)這一個(gè)MINOR_BASE,以evdev為例,EVDEV_MINOR_BASE = 64,可以看出系統(tǒng)總共可以注冊8個(gè)handler
- list_add_tail(&handler->node, &input_handler_list);
- //連接到input_handler_list鏈表中
- list_for_each_entry(dev, &input_dev_list, node)
- input_attach_handler(dev, handler);
- //又是配對,不過這次遍歷input_dev,和注冊input_dev過程一樣的
- input_wakeup_procfs_readers();
- out:
- mutex_unlock(&input_mutex);
- return retval;
- }
輸入子系統(tǒng)核心分析
一. 輸入子系統(tǒng)核心分析。
1.輸入子系統(tǒng)核心對應(yīng)與/drivers/input/input.c文件,這個(gè)也是作為一個(gè)模塊注冊到內(nèi)核的。所以首先分析模塊初始化函數(shù)。- static int __init input_init(void)
- {
- int err;
- input_init_abs_bypass();
- //這個(gè)暫時(shí)沒有發(fā)現(xiàn)是做什么的
- err = class_register(&input_class);
- //向內(nèi)核注冊一個(gè)類,用于linux設(shè)備模型。注冊后會(huì)在/sys/class下面出現(xiàn)input目錄
- if (err) {
- printk(KERN_ERR "input: unable to register input_dev class\n");
- return err;
- }
- err = input_proc_init();
- //和proc文件系統(tǒng)有關(guān),暫時(shí)不管
- if (err)
- goto fail1;
- err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
- //注冊字符設(shè)備,接口是2.4內(nèi)核的。以主設(shè)備號INPUT_MAJOR,次設(shè)備號0-255,注冊266個(gè)設(shè)備,說明input設(shè)備最大只能有255個(gè)
- if (err) {
- printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
- goto fail2;
- }
- return 0;
- fail2: input_proc_exit();
- fail1: class_unregister(&input_class);
- return err;
- }
2. 輸入子系統(tǒng)的核心其他部分都是提供的接口,向上連接事件處理層,向下連接驅(qū)動(dòng)層。
向下對驅(qū)動(dòng)層的接口主要有:
input_allocate_device 這個(gè)函數(shù)主要是分配一個(gè)input_dev接口,并初始化一些基本的成員,這就是我們不能簡單用kmalloc分配input_dev結(jié)構(gòu)的原因,因?yàn)槿鄙倭艘恍┏跏蓟?br> input_unregister_device 注冊一個(gè)input設(shè)備
input_event 這個(gè)函數(shù)很重要,是驅(qū)動(dòng)層向input子系統(tǒng)核心報(bào)告事件的函數(shù),在事件傳遞過程中再分析。
input_allocate_device 分配并初始化一個(gè)input_dev結(jié)構(gòu)
向上對事件處理層接口主要有:
input_register_handler 注冊一個(gè)事件處理器
input_register_handle 注冊一個(gè)input_handle結(jié)構(gòu)
事件處理層分析
1.事件處理層與用戶程序和輸入子系統(tǒng)核心打交道,是他們兩層的橋梁。一般內(nèi)核有好幾個(gè)事件處理器,像evdev mousedev jotdev。evdev事件處理器可以處理所有的事件,觸摸屏驅(qū)動(dòng)就是用的這個(gè),所以下面分析這個(gè)事件處理器的實(shí)現(xiàn)。它也是作為模塊注冊到內(nèi)核中的,首先分析它的模塊初始化函數(shù)。
- static int __init evdev_init(void)
- {
- return input_register_handler(&evdev_handler);
- }
2.主要數(shù)據(jù)結(jié)構(gòu)
(1) evdev設(shè)備結(jié)構(gòu)
- struct evdev {
- int exist;
- int open; //打開標(biāo)志
- int minor; //次設(shè)備號
- struct input_handle handle; //關(guān)聯(lián)的input_handle
- wait_queue_head_t wait; //等待隊(duì)列,當(dāng)進(jìn)程讀取設(shè)備,而沒有事件產(chǎn)生的時(shí)候,進(jìn)程就會(huì)睡在其上面
- struct evdev_client *grab; //強(qiáng)制綁定的evdev_client結(jié)構(gòu),這個(gè)結(jié)構(gòu)后面再分析
- struct list_head client_list; //evdev_client 鏈表,這說明一個(gè)evdev設(shè)備可以處理多個(gè)evdev_client,可以有多個(gè)進(jìn)程訪問evdev設(shè)備
- spinlock_t client_lock; /* protects client_list */
- struct mutex mutex;
- struct device dev; //device結(jié)構(gòu),說明這是一個(gè)設(shè)備結(jié)構(gòu)
- };
索引值是minor
(2) evdev用戶端結(jié)構(gòu)
- struct evdev_client {
- struct input_event buffer[EVDEV_BUFFER_SIZE];
- //這個(gè)是一個(gè)input_event數(shù)據(jù)結(jié)構(gòu)的數(shù)組,input_event代表一個(gè)事件,基本成員:類型(type),編碼(code),值(value)
- int head; //針對buffer數(shù)組的索引
- int tail; //針對buffer數(shù)組的索引,當(dāng)head與tail相等的時(shí)候,說明沒有事件
- spinlock_t buffer_lock; /* protects access to buffer, head and tail */
- struct fasync_struct *fasync; //異步通知函數(shù)
- struct evdev *evdev; //evdev設(shè)備
- struct list_head node; // evdev_client 鏈表項(xiàng)
- };
3.主要函數(shù)
(1)evdev設(shè)備打開函數(shù)
- static int evdev_open(struct inode *inode, struct file *file)
- {
- struct evdev *evdev;
- struct evdev_client *client;
- int i = iminor(inode) - EVDEV_MINOR_BASE;
- int error;
- if (i >= EVDEV_MINORS)
- return -ENODEV;
- error = mutex_lock_interruptible(&evdev_table_mutex);
- if (error)
- return error;
- evdev = evdev_table[i];
- //得到evdev設(shè)備結(jié)構(gòu),每次調(diào)用evdev_connect配對成功后都會(huì)把分配的evdev結(jié)構(gòu)以minor為索引,保存在evdev_table數(shù)組中
- if (evdev)
- get_device(&evdev->dev); //增加device引用計(jì)數(shù)
- mutex_unlock(&evdev_table_mutex);
- if (!evdev)
- return -ENODEV;
- client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); //分配用戶端結(jié)構(gòu)
- if (!client) {
- error = -ENOMEM;
- goto err_put_evdev;
- }
- spin_lock_init(&client->buffer_lock);
- client->evdev = evdev; //使用戶端與evdev設(shè)備結(jié)構(gòu)聯(lián)系起來
- evdev_attach_client(evdev, client);
- //這個(gè)函數(shù)所做的就是把client連接到evdev的client鏈表中
- error = evdev_open_device(evdev);
- //這個(gè)函數(shù)打開設(shè)備,有很多層調(diào)用,后面詳細(xì)分析
- if (error)
- goto err_free_client;
- file->private_data = client;
- return 0;
- err_free_client:
- evdev_detach_client(evdev, client);
- kfree(client);
- err_put_evdev:
- put_device(&evdev->dev);
- return error;
- }
- static int evdev_open_device(struct evdev *evdev)
- {
- int retval;
- retval = mutex_lock_interruptible(&evdev->mutex);
- if (retval)
- return retval;
- if (!evdev->exist)
- retval = -ENODEV;
- //判斷設(shè)備結(jié)構(gòu)是否存在,在evdev_connect中初始話此成員為1
- else if (!evdev->open++) {
- retval = input_open_device(&evdev->handle);
- if (retval)
- evdev->open--;
- }
- //evdev->open分配結(jié)構(gòu)的時(shí)候沒有初始化,默認(rèn)為0,也就是沒有打開,每次打開都會(huì)加1
- mutex_unlock(&evdev->mutex);
- return retval;
- }
- int input_open_device(struct input_handle *handle)
- {
- struct input_dev *dev = handle->dev;
- int retval;
- retval = mutex_lock_interruptible(&dev->mutex);
- if (retval)
- return retval;
- if (dev->going_away) {
- retval = -ENODEV;
- goto out;
- }
- handle->open++;
- //將handle的打開計(jì)數(shù)加1,注意和evdev的open的區(qū)別
- if (!dev->users++ && dev->open)
- retval = dev->open(dev);
- //如果此input_dev沒有進(jìn)程在引用,并且定義了open方法,就調(diào)用open方法
- if (retval) { //retval = 1 說明沒有打開成功
- dev->users--;
- if (!--handle->open) { //說明有其他的進(jìn)程已經(jīng)打開了這個(gè)handle
- /*
- * Make sure we are not delivering any more events
- * through this handle
- */
- synchronize_rcu();
- }
- }
- out:
- mutex_unlock(&dev->mutex);
- return retval;
- }
- static ssize_t evdev_read(struct file *file, char __user *buffer,
- size_t count, loff_t *ppos)
- {
- struct evdev_client *client = file->private_data; //這個(gè)客戶端結(jié)構(gòu)在打開的時(shí)候分配并保存在file->private_data中
- struct evdev *evdev = client->evdev;
- struct input_event event;
- int retval;
- if (count < input_event_size())
- return -EINVAL;
- //這條語句提示,用戶進(jìn)程每次讀取設(shè)備的字節(jié)數(shù),不要少于input_event結(jié)構(gòu)的大小
- if (client->head == client->tail && evdev->exist &&
- (file->f_flags & O_NONBLOCK))
- return -EAGAIN;
- //head等于tail說明目前還沒有事件傳回來,如果設(shè)置了非阻塞操作,則會(huì)立刻返回
- retval = wait_event_interruptible(evdev->wait,
- client->head != client->tail || !evdev->exist);
- //沒有事件就會(huì)睡在evdev的等待隊(duì)列上了,等待條件是有事件到來或者設(shè)備不存在了(設(shè)備關(guān)閉的時(shí)候,清這個(gè)標(biāo)志)
- if (retval)
- return retval;
- //如果能執(zhí)行上面這條語句說明有事件傳來或者,設(shè)備被關(guān)閉了,或者內(nèi)核發(fā)過來終止信號
- if (!evdev->exist)
- return -ENODEV;
- while (retval + input_event_size() <= count &&
- evdev_fetch_next_event(client, &event)) {
- // evdev_fetch_next_event這個(gè)函數(shù)遍歷client里面的input_event buffer數(shù)組
- if (input_event_to_user(buffer + retval, &event))
- //將事件復(fù)制到用戶空間
- return -EFAULT;
- retval += input_event_size();
- }
- return retval; //返回復(fù)制的數(shù)據(jù)字節(jié)數(shù)
- }
事件傳遞過程
三. 事件傳遞過程(以s3c2410_ts為例)
1. 事件產(chǎn)生
當(dāng)按下觸摸屏?xí)r,進(jìn)入觸摸屏按下中斷,開始ad轉(zhuǎn)換,ad轉(zhuǎn)換完成進(jìn)入ad完成中斷,在這個(gè)終端中將事件發(fā)送出去,調(diào)用
input_report_abs(dev, ABS_X, xp);
input_report_abs(dev, ABS_Y, yp); 這兩個(gè)函數(shù)調(diào)用了 input_event(dev, EV_ABS, code, value)
所有的事件報(bào)告函數(shù)都調(diào)用這個(gè)函數(shù)。
2. 事件報(bào)告
(1) input_event 函數(shù)分析,這個(gè)函數(shù)定義在input.c中(2) input_handle_event 函數(shù)分析,這個(gè)函數(shù)定義在input.c中- 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);
- //對系統(tǒng)隨機(jī)熵池有貢獻(xiàn),因?yàn)檫@個(gè)也是一個(gè)隨機(jī)過程
- input_handle_event(dev, type, code, value);
- //這個(gè)函數(shù)是事件處理的關(guān)鍵函數(shù),下面詳細(xì)分析
- spin_unlock_irqrestore(&dev->event_lock, flags);
- }
- }
這個(gè)函數(shù)主要是根據(jù)事件類型的不同,做相應(yīng)的處理。這里之關(guān)心EV_KEY類型,其他函數(shù)和事件傳遞關(guān)系不大,只要關(guān)心,disposition這個(gè)是事件處理的方式,默認(rèn)的是INPUT_IGNORE_EVENT,忽略這個(gè)事件,如果是INPUT_PASS_TO_HANDLERS則是傳遞給事件處理器,如果是INPUT_PASS_TO_DEVICE,則是傳遞給設(shè)備處理,觸摸屏驅(qū)動(dòng)沒有定義這個(gè)。下面分析input_pass_event函數(shù)。- static void input_handle_event(struct input_dev *dev,
- unsigned int type, unsigned int code, int value)
- {
- int disposition = INPUT_IGNORE_EVENT;
- switch (type) {
- ......
- case EV_KEY:
- if (is_event_supported(code, dev->keybit, KEY_MAX) &&
- !!test_bit(code, dev->key) != value) {
- if (value != 2) {
- __change_bit(code, dev->key);
- if (value)
- input_start_autorepeat(dev, code);
- else
- input_stop_autorepeat(dev);
- }
- disposition = INPUT_PASS_TO_HANDLERS;
- }
- break;
- ......
- if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- dev->sync = 0;
- if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
- dev->event(dev, type, code, value);
- if (disposition & INPUT_PASS_TO_HANDLERS)
- input_pass_event(dev, type, code, value);
- }
下面分析 evdev事件處理器的event函數(shù)- static void input_pass_event(struct input_dev *dev,
- unsigned int type, unsigned int code, int value)
- {
- struct input_handle *handle;
- rcu_read_lock();
- handle = rcu_dereference(dev->grab); //如果是綁定的handle,則調(diào)用綁定的handler->event函數(shù)
- if (handle)
- handle->handler->event(handle, type, code, value);
- else
- //如果沒有綁定,則遍歷dev的h_list鏈表,尋找handle,如果handle已經(jīng)打開,說明有進(jìn)程讀取設(shè)備關(guān)聯(lián)的evdev。
- list_for_each_entry_rcu(handle, &dev->h_list, d_node)
- if (handle->open)
- handle->handler->event(handle,
- type, code, value);
- // 調(diào)用相關(guān)的事件處理器的event函數(shù),進(jìn)行事件的處理
- rcu_read_unlock();
- }
下面分析 evdev_pass_event 函數(shù)- static void evdev_event(struct input_handle *handle,
- unsigned int type, unsigned int code, int value)
- {
- struct evdev *evdev = handle->private;
- struct evdev_client *client;
- struct input_event event;
- do_gettimeofday(&event.time);
- event.type = type;
- event.code = code;
- event.value = value;
- //將傳過來的事件,賦值給input_event結(jié)構(gòu)
- rcu_read_lock();
- client = rcu_dereference(evdev->grab);
- //如果evdev綁定了client那么,處理這個(gè)客戶端,觸摸屏驅(qū)動(dòng)沒有綁定
- if (client)
- evdev_pass_event(client, &event);
- else
- //遍歷client鏈表,調(diào)用evdev_pass_event函數(shù)
- list_for_each_entry_rcu(client, &evdev->client_list, node)
- evdev_pass_event(client, &event);
- rcu_read_unlock();
- wake_up_interruptible(&evdev->wait); //喚醒等待的進(jìn)程
- }
可以看出, evdev_pass_event函數(shù)最終將事件傳遞給了用戶端的client結(jié)構(gòu)中的input_event數(shù)組中,只需將這個(gè)input_event數(shù)組復(fù)制給用戶空間,進(jìn)程就能收到觸摸屏按下的信息了。具體處理由具體的應(yīng)用程序來完成。- static void evdev_pass_event(struct evdev_client *client,
- struct input_event *event)
- {
- /*
- * Interrupts are disabled, just acquire the lock
- */
- spin_lock(&client->buffer_lock);
- client->buffer[client->head++] = *event; //將事件賦值給客戶端的input_event 數(shù)組
- client->head &= EVDEV_BUFFER_SIZE - 1;
- spin_unlock(&client->buffer_lock);
- kill_fasync(&client->fasync, SIGIO, POLL_IN);
- }
linux內(nèi)核input子系統(tǒng)解析
作者:劉洪濤
Android、X windows、qt等眾多應(yīng)用對于linux系統(tǒng)中鍵盤、鼠標(biāo)、觸摸屏等輸入設(shè)備的支持都通過、或越來越傾向于標(biāo)準(zhǔn)的input輸入子系統(tǒng)。
因?yàn)?span lang="EN-US">input子系統(tǒng)已經(jīng)完成了字符驅(qū)動(dòng)的文件操作接口,所以編寫驅(qū)動(dòng)的核心工作是完成input系統(tǒng)留出的接口,工作量不大。但如果你想更靈活的應(yīng)用它,就需要好好的分析下input子系統(tǒng)了。
一、input輸入子系統(tǒng)框架
下圖是input輸入子系統(tǒng)框架,輸入子系統(tǒng)由輸入子系統(tǒng)核心層( Input Core ),驅(qū)動(dòng)層和事件處理層(Event Handler)三部份組成。一個(gè)輸入事件,如鼠標(biāo)移動(dòng),鍵盤按鍵按下,joystick的移動(dòng)等等通過 input driver -> Input core -> Event handler -> userspace 到達(dá)用戶空間傳給應(yīng)用程序。

注意:keyboard.c不會(huì)在/dev/input下產(chǎn)生節(jié)點(diǎn),而是作為ttyn終端(不包括串口終端)的輸入。
二、Input driver編寫要點(diǎn)
1、分配、注冊、注銷input設(shè)備
struct input_dev *input_allocate_device(void)
int input_register_device(struct input_dev *dev)
void input_unregister_device(struct input_dev *dev)
2、設(shè)置input設(shè)備支持的事件類型、事件碼、事件值的范圍、input_id等信息
參見usb鍵盤驅(qū)動(dòng):usbkbd.c
usb_to_input_id(dev, &input_dev->id);//設(shè)置bustype、vendo、product等
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);//支持的事件類型
input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);// EV_LED事件支持的事件碼
for (i = 0; i < 255; i++)
set_bit(usb_kbd_keycode[i], input_dev->keybit); //EV_KEY事件支持的事件碼
include/linux/input.h中定義了支持的類型(下面列出的是2.6.22內(nèi)核的情況)
#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02
#define EV_ABS 0x03
#define EV_MSC 0x04
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
一個(gè)設(shè)備可以支持一個(gè)或多個(gè)事件類型。每個(gè)事件類型下面還需要設(shè)置具體的觸發(fā)事件碼。比如:EV_KEY事件,需要定義其支持哪些按鍵事件碼。
3、如果需要,設(shè)置input設(shè)備的打開、關(guān)閉、寫入數(shù)據(jù)時(shí)的處理方法
參見usb鍵盤驅(qū)動(dòng):usbkbd.c
input_dev->open = usb_kbd_open;
input_dev->close = usb_kbd_close;
input_dev->event = usb_kbd_event;
4、在發(fā)生輸入事件時(shí),向子系統(tǒng)報(bào)告事件
用于報(bào)告EV_KEY、EV_REL、EV_ABS等事件的函數(shù)有:
void input_report_key(struct input_dev *dev, unsigned int code, int value)
void input_report_rel(struct input_dev *dev, unsigned int code, int value)
void input_report_abs(struct input_dev *dev, unsigned int code, int value)
如果你覺得麻煩,你也可以只記住1個(gè)函數(shù)(因?yàn)樯鲜龊瘮?shù)都是通過它實(shí)現(xiàn)的)
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
三、Event Handler層解析
1、Input輸入子系統(tǒng)數(shù)據(jù)結(jié)構(gòu)關(guān)系圖
2、input_handler結(jié)構(gòu)體
以evdev.c中的evdev_handler為例:
static struct input_handler evdev_handler = {
.event = evdev_event, //向系統(tǒng)報(bào)告input事件,系統(tǒng)通過read方法讀取
.connect = evdev_connect, //和input_dev匹配后調(diào)用connect構(gòu)建
.disconnect = evdev_disconnect,
.fops = &evdev_fops, //event設(shè)備文件的操作方法
.minor = EVDEV_MINOR_BASE, //次設(shè)備號基準(zhǔn)值
.name = "evdev",
.id_table = evdev_ids, //匹配規(guī)則
};
3、input字符設(shè)備注冊過程
drivers/input/input.c中:
static int __init input_init(void)
{
int err;
err = class_register(&input_class);
……
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
……
}
input_fops定義:
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
Input_dev和input_handler匹配后調(diào)用input_handler的connect。以evdev_handler為例:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
struct class_device *cdev;
dev_t devt;
int minor;
int error;
for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices/n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);//為每個(gè)匹配evdev_handler的設(shè)備創(chuàng)建一個(gè)evdev。
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list);
init_waitqueue_head(&evdev->wait);
evdev->exist = 1;
evdev->minor = minor;
evdev->handle.dev = dev;
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;
evdev->handle.private = evdev;
sprintf(evdev->name, "event%d", minor);
evdev_table[minor] = evdev;//記錄evdev的位置,字符設(shè)備/dev/input/evnetx訪問時(shí)根據(jù)次設(shè)備號及EVDEV_MINOR_BASE最終在evdev_open中找到對應(yīng)的evdev
devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
cdev = class_device_create(&input_class, &dev->cdev, devt,
dev->cdev.dev, evdev->name);//創(chuàng)建了event字符設(shè)備節(jié)點(diǎn)
……
}
4、input字符設(shè)備的打開過程
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler = input_table[iminor(inode) >> 5];
//得到對應(yīng)的input_handler
const struct file_operations *old_fops, *new_fops = NULL;
int err;
if (!handler || !(new_fops = fops_get(handler->fops)))
//取出對應(yīng)input_handler的file_operations
return -ENODEV;
if (!new_fops->open) {
fops_put(new_fops);
return -ENODEV;
}
old_fops = file->f_op;
file->f_op = new_fops;//重定位打開的設(shè)備文件的操作方法
err = new_fops->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
return err;
}
5、input字符設(shè)備的其它操作
由于在open階段已經(jīng)把設(shè)備文件的操作操作方法重定位了到了具體的input_handler,所以其它接口操作(read、write、ioctl等),由各個(gè)input_handler的fops方法決定。如evdev.c中的:evdev_fops