--以觸摸屏驅(qū)動為例
第一章、了解linux input子系統(tǒng)
Linux輸入設(shè)備總類繁雜,常見的包括有按鍵、鍵盤、觸摸屏、鼠標(biāo)、搖桿等等,他們本身就是字符設(shè)備,而linux內(nèi)核將這些設(shè)備的共同性抽象出來,簡化驅(qū)動開發(fā)建立了一個input子系統(tǒng)。子系統(tǒng)共分為三層,如圖1所示。
 |
圖1 input輸入子系統(tǒng)
|
驅(qū)動層和硬件相關(guān),直接捕捉和獲取硬件設(shè)備的數(shù)據(jù)信息等(包括觸摸屏被按下、按下位置、鼠標(biāo)移動、鍵盤按下等等),然后將數(shù)據(jù)信息報(bào)告到核心層。核心層負(fù)責(zé)連接驅(qū)動層和事件處理層,設(shè)備驅(qū)動(device driver)和處理程序(handler)的注冊需要通過核心層來完成,核心層接收來自驅(qū)動層的數(shù)據(jù)信息,并將數(shù)據(jù)信息選擇對應(yīng)的handler去處理,最終handler將數(shù)據(jù)復(fù)制到用戶空間。
先了解三個定義在/linux/input.h下重要的結(jié)構(gòu)體input_dev、input_handler、input_handle。
struct input_dev {
void *private;
const char *name;
const char *phys;
const char *uniq;
struct input_id id; //與input_handler匹配用的id
unsigned long evbit[NBITS(EV_MAX)]; //設(shè)備支持的事件類型
unsigned long keybit[NBITS(KEY_MAX)]; //按鍵事件支持的子事件類型
unsigned long relbit[NBITS(REL_MAX)];
unsigned long absbit[NBITS(ABS_MAX)]; //絕對坐標(biāo)事件支持的子事件類型
unsigned long mscbit[NBITS(MSC_MAX)];
unsigned long ledbit[NBITS(LED_MAX)];
unsigned long sndbit[NBITS(SND_MAX)];
unsigned long ffbit[NBITS(FF_MAX)];
unsigned long swbit[NBITS(SW_MAX)];
int ff_effects_max;
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
unsigned int repeat_key;
struct timer_list timer;
struct pt_regs *regs;
int state;
int sync;
int abs[ABS_MAX + 1];
int rep[REP_MAX + 1];
unsigned long key[NBITS(KEY_MAX)];
unsigned long led[NBITS(LED_MAX)];
unsigned long snd[NBITS(SND_MAX)];
unsigned long sw[NBITS(SW_MAX)];
int absmax[ABS_MAX + 1]; //絕對坐標(biāo)事件的最大鍵值
int absmin[ABS_MAX + 1]; //絕對坐標(biāo)事件的最小鍵值
int absfuzz[ABS_MAX + 1];
int absflat[ABS_MAX + 1];
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*accept)(struct input_dev *dev, struct file *file);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);
int (*erase_effect)(struct input_dev *dev, int effect_id);
struct input_handle *grab; //當(dāng)前占有該設(shè)備的handle
struct mutex mutex; /* serializes open and close operations */
unsigned int users; //打開該設(shè)備的用戶量
struct class_device cdev;
struct device *dev; /* will be removed soon */
int dynalloc; /* temporarily */
struct list_head h_list; //該鏈表頭用于鏈接該設(shè)備所關(guān)聯(lián)的input_handle
struct list_head node; //該鏈表頭用于將設(shè)備鏈接到input_dev_list
};
Input_dev是一個很強(qiáng)大的結(jié)構(gòu)體,它把所有的input設(shè)備(觸摸屏、鍵盤、鼠標(biāo)等)的信息都考慮到了,對于觸摸屏來說只用到它里面的一部分而已,尤其是加粗的部分,注意該結(jié)構(gòu)體中最后兩行定義的兩個list_head結(jié)構(gòu)體,list_head在/linux/list.h中有定義,深入跟蹤
struct list_head {
struct list_head *next, *prev;
};
該結(jié)構(gòu)體內(nèi)部并沒有定義數(shù)據(jù)而只定義了兩個指向本身結(jié)構(gòu)體的指針,預(yù)先說明一下,所有的input device在注冊后會加入一個input_dev_list(輸入設(shè)備鏈表),所有的eventhandler在注冊后會加入一個input_handler_list(輸入處理程序鏈表),這里的list_head主要的作用是作為input_dev_list和input_handler_list的一個節(jié)點(diǎn)來保存地址。Input_dev_list和input_handler_list之間的對應(yīng)關(guān)系由input_handle結(jié)構(gòu)體橋接,具體后面說明。
struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
const struct file_operations *fops; //提供給用戶對設(shè)備操作的函數(shù)指針
int minor;
char *name;
struct input_device_id *id_table; //與input_dev匹配用的id
struct input_device_id *blacklist; //標(biāo)記的黑名單
struct list_head h_list; //用于鏈接和該handler相關(guān)的handle
struct list_head node; //用于將該handler鏈入input_handler_list
};
input_handler顧名思義,它是用來處理input_dev的一個結(jié)構(gòu)體,相關(guān)的處理函數(shù)在結(jié)構(gòu)里內(nèi)部都有定義,最后兩行定義的list_head結(jié)構(gòu)體作用同input_dev所定義的一樣,這里不再說明。
注:input_device_id結(jié)構(gòu)體在/linux/mod_devicetable.h中有定義
struct input_handle {
void *private;
int open; //記錄設(shè)備打開次數(shù)
char *name;
struct input_dev *dev; //指向所屬的input_dev
struct input_handler *handler; //指向所屬的input_handler
struct list_head d_node; //用于鏈入所指向的input_dev的handle鏈表
struct list_head h_node; //用于鏈入所指向的input_handler的handle鏈表
};
可以看到input_handle中擁有指向input_dev和input_handler的指針,即input_handle是用來關(guān)聯(lián)input_dev和input_handler。為什么用input_handle來關(guān)聯(lián)input_dev和input_handler而不將input_dev和input_handler直接對應(yīng)呢?因?yàn)?/span>一個device可以對應(yīng)多個handler,而一個handler也可處理多個device。就如一個觸摸屏設(shè)備可以對應(yīng)event handler也可以對應(yīng)tseve handler。
input_dev、input_handler、input_handle的關(guān)系如下圖2所示。
 |
圖2 input_dev,input_handler,input_handle關(guān)系圖 |
第二章、input device的注冊
Input device的注冊實(shí)際上僅僅只有幾行代碼,因?yàn)樵趇nput.c中已經(jīng)將大量的代碼封裝好了,主需要調(diào)用幾個關(guān)鍵的函數(shù)就能完成對input device的注冊。
在xxx_ts.c中預(yù)先定義全局變量struct input_dev tsdev;然后進(jìn)入到初始化函數(shù)
static int __init xxx_probe(struct platform_device *pdev)
{
…
if (!(tsdev = input_allocate_device()))
{
printk(KERN_ERR "tsdev: not enough memory\n");
err = -ENOMEM;
goto fail;
}
…
tsdev->name = "xxx TouchScreen"; //xxx為芯片型號
tsdev ->phys = "xxx/event0";
tsdev ->id.bustype = BUS_HOST; //設(shè)備id,用于匹配handler的id
tsdev ->id.vendor = 0x0005;
tsdev ->id.product = 0x0001;
tsdev ->id.version = 0x0100;
tsdev ->open = xxx_open;
tsdev ->close =xxx_close;
tsdev ->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_SYN); //設(shè)置支持的事件類型
tsdev ->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
input_set_abs_params(tsdev, ABS_X, 0, 0x400, 0, 0); //限定絕對坐標(biāo)X的取值范圍
input_set_abs_params(tsdev, ABS_Y, 0, 0x400, 0, 0); //同上
input_set_abs_params(tsdev, ABS_PRESSURE, 0, 1000, 0, 0); //觸摸屏壓力值范圍
…
If(input_register_device(tsdev) == error) //注冊設(shè)備
goto fail;
…
fail:
input_free_device(tsdev);
printk(“ts probe failed\n”);
return err;
}
先看該函數(shù)中的tsdev = input_allocate_device(),深入最終,該函數(shù)在input.c中有定義
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
if (dev) {
dev->dynalloc = 1;
dev->cdev.class = &input_class;
class_device_initialize(&dev->cdev);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
}
return dev;
}
學(xué)過C語言應(yīng)該都知道m(xù)alloc函數(shù)(開辟內(nèi)存空間),而這里的kzalloc也類似于C語言中的malloc一樣,是linux內(nèi)核空間分配內(nèi)存函數(shù),后面的GFP_KERNEL標(biāo)志意為常規(guī)的內(nèi)存分配,更多的分配標(biāo)志可參閱先關(guān)資料(我也不懂)。再回到前面的函數(shù)中來,接著后面的代碼為對tsdev結(jié)構(gòu)體中的成員進(jìn)行賦值初始化,賦值完成后就要開始進(jìn)入主題:注冊設(shè)備了,進(jìn)入到函數(shù)input_register_device(tsdev),該函數(shù)在input.c中有定義。
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0); //定義原子變量,禁止線程并發(fā)訪問
struct input_handle *handle; //定義一些變量備后文使用
struct input_handler *handler;
struct input_device_id *id;
const char *path;
int error;
if (!dev->dynalloc) {
printk(KERN_WARNING "input: device %s is statically allocated, will not register\n"
dev->name ? dev->name : "<Unknown>");
return -EINVAL;
}
mutex_init(&dev->mutex); //互斥鎖初始化,防止臨界區(qū)代碼被并發(fā)訪問
set_bit(EV_SYN, dev->evbit); //設(shè)置支持同步事件,input設(shè)備全部默認(rèn)支持同步事件
/*
* 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;
}
INIT_LIST_HEAD(&dev->h_list); //初始化需要關(guān)聯(lián)的handle鏈表頭
list_add_tail(&dev->node, &input_dev_list); //將設(shè)備添加到input_dev_list中
dev->cdev.class = &input_class;
snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
error = class_device_add(&dev->cdev);
if (error)
return error;
error = sysfs_create_group(&dev->cdev.kobj, &input_dev_attr_group);
if (error)
goto fail1;
error = sysfs_create_group(&dev->cdev.kobj, &input_dev_id_attr_group);
if (error)
goto fail2;
error = sysfs_create_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
if (error)
goto fail3;
__module_get(THIS_MODULE);
path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path);
/*** 遍歷input_handler_list上全部的handler,尋找與該設(shè)備匹配的handler ***/
list_for_each_entry(handler, &input_handler_list, node)
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev)))
if ((handle = handler->connect(handler, dev, id)))
input_link_handle(handle);
input_wakeup_procfs_readers();
return 0;
fail3: sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group);
fail2: sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group);
fail1: class_device_del(&dev->cdev);
return error;
}
先看函數(shù)中前面代碼加粗的部分mutex_init(&dev->mutex),與互斥鎖相關(guān)的東西(我也不太懂),set_bit(EV_SYN, dev->evbit)設(shè)置支持同步事件,linux的input子系統(tǒng)默認(rèn)要支持同步事件。
接著看中間代碼加粗的部分,有關(guān)list操作的在/linux/list.h中有定義
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
可以看得出INIT_LIST_HEAD(struct list_head *list)就是讓list指向結(jié)構(gòu)體的成員再指向其本身完成初始化操作,list_add_tail(struct list_head *new, struct list_head *head)是將new所指向的結(jié)構(gòu)體作為一節(jié)點(diǎn)插入到head所指向鏈表節(jié)點(diǎn)之前。因此函數(shù)中的list_add_tail(&dev->node, &input_dev_list)就是將該設(shè)備添加到input_dev_list中。而input_dev_list這個雙向鏈表在什么時候被定義了呢,且看input.c源代碼開始部分全局部分有定義:
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
而LIST_HEAD的宏定義:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
顯然這在最開始就已經(jīng)定義了input_dev_list和input_handler_list這兩個鏈表,且這兩個鏈表都只有一個節(jié)點(diǎn),而在device和handler注冊的時候會在這兩條鏈表中加入節(jié)點(diǎn),list_add_tail(&dev->node, &input_dev_list)就是將該設(shè)備添加到input_dev_list中。
注意最后代碼加粗的部分,該部分完成了input_dev和input_handler的橋接。先看list_for_each_entry(handler, &input_handler_list, node),list_for_each_entry在list.h中有定義,其作用相當(dāng)于一個for循環(huán)。其約等價于:(個人理解)
for(handler = input_handler_list表頭所屬的input_handler結(jié)構(gòu)體地址;
handler != input_handler_list表尾所屬的input_handler結(jié)構(gòu)體地址;
handler++)
即handler第一次指向input_handler_list的頭部所在的input_handler地址。每循環(huán)一次handler就沿著input_handler_list移動到下一個節(jié)點(diǎn),得到下一節(jié)點(diǎn)所屬的handler地址,直到input_handler_list的結(jié)尾。而每次的循環(huán)需要做什么呢?要做的就是遍歷input_handler_list上的每一個handler,看有哪些handler能與該設(shè)備匹配的上。匹配過程:
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))//判斷該handler沒有被列入黑名單或者黑名單匹配不成功的話則繼續(xù)
if ((id = input_match_device(handler->id_table, dev))) //將設(shè)備id與handler的id進(jìn)行匹配,成功則繼續(xù)往下
if ((handle = handler->connect(handler, dev, id))) //鏈接device與handler,成功則繼續(xù)往下
input_link_handle(handle); //將handle鏈入input_handler_list和input_dev_list
繼續(xù)跟蹤進(jìn)這些函數(shù)
static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev)
{
int i;
for (; id->flags || id->driver_info; id++) {
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) //匹配handler和device id的flag標(biāo)志位
if (id->bustype != dev->id.bustype)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
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相關(guān)標(biāo)志位
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);
return id;
}
return NULL;
}
該函數(shù)用于匹配input_dev結(jié)構(gòu)體和input_handler結(jié)構(gòu)體里面定義的input_device_id,若兩者里面有任何相關(guān)變量不一樣則匹配失敗(條件真苛刻)。
再看handle = handler->connect(handler, dev, id),connect函數(shù)即為static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id),為什么呢會是這個呢,我們先看evdev.c中的input_handler結(jié)構(gòu)體定義:
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_handler結(jié)構(gòu)體里邊的connect函數(shù)即為evdev_connect函數(shù)
static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
{
1 struct evdev *evdev; //定義一個evdev結(jié)構(gòu)體指針
struct class_device *cdev;
int minor;
2 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 NULL;
}
3 if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)))
return NULL;
INIT_LIST_HEAD(&evdev->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;
cdev = class_device_create(&input_class, &dev->cdev,
MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
dev->cdev.dev, evdev->name);
/* temporary symlink to keep userspace happy */
sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj,
evdev->name);
return &evdev->handle;
}
先看1處,這里有個定義在evdev.c里邊的新面孔
struct evdev {
int exist;
int open;
int minor;
char name[16];
struct input_handle handle; //關(guān)聯(lián)input_handler和input_dev的input_handle
wait_queue_head_t wait;
struct evdev_list *grab;
struct list_head list;
};
evdev這個結(jié)構(gòu)體就是拿來應(yīng)用開發(fā)操作的,在這里就是觸摸屏對應(yīng)的設(shè)備文件實(shí)體,結(jié)構(gòu)體前邊定義了記錄設(shè)備的一些信息(設(shè)備號,打開狀態(tài)、設(shè)備名字等),這里還定義了一個input_handle的實(shí)體handle,沒錯這個handle就是要用來關(guān)聯(lián)input_dev和input_handler的,后面還有一行加粗的部分后面再做介紹。
再看2處,evdev_table[]是一個全局變量的數(shù)組,在evdev.c中有定義
#define EVDEV_MINORS 32
static struct evdev *evdev_table[EVDEV_MINORS];
在前面已經(jīng)說明了,一個device可以對應(yīng)多個handler,而一個handler也可處理多個device,這里體現(xiàn)出了后者。既然evdev這個結(jié)構(gòu)體是對應(yīng)的設(shè)備文件實(shí)體,因?yàn)檫@個handler可能會處理多個device,因此該handler要處理n個device就會應(yīng)該有n個evdev實(shí)體,而這些實(shí)體的地址存放在evdev_table[]這個指針數(shù)組中,也就是說該handler最多只能處理EVDEV_MINORS個device,而2處的這幾句代碼就是要遍歷evdev_table[]這個數(shù)組看還有沒有空著的位置,有的話才會繼續(xù)進(jìn)行下面的程序。
再看3處,開辟一個evdev結(jié)構(gòu)體的內(nèi)存空間,前面有說明過kzalloc函數(shù),這里不再說明。
后面的代碼就是為evdev結(jié)構(gòu)體變量賦初始值了,其中init_waitqueue_head(&evdev->wait)初始化等待隊(duì)列,具體介紹結(jié)合/linux/wait.h和查看相關(guān)資料(本人不懂)。
函數(shù)最后得到evdev結(jié)構(gòu)體內(nèi)的hanlde地址并返回,此時返回到我們的int input_register_device(struct input_dev *dev)函數(shù)里面,最后一句
input_link_handle(handle),進(jìn)入到該函數(shù)中發(fā)現(xiàn)
static void input_link_handle(struct input_handle *handle)
{
list_add_tail(&handle->d_node, &handle->dev->h_list);
list_add_tail(&handle->h_node, &handle->handler->h_list);
}
在該函數(shù)中也只是將handle中的d_node和h_node分別接入到input_dev和input_handler的h_list中,此時input_dev、input_handler、input_handle三者形成了如圖2所示的關(guān)系。
至此設(shè)備注冊過程算是全部完成了,但是貌似還有點(diǎn)亂。在整個設(shè)備的注冊過程中函數(shù)的嵌套一個接著一個,不僅函數(shù)嵌套,結(jié)構(gòu)體也使用了結(jié)構(gòu)體和函數(shù)嵌套等,在各個函數(shù)內(nèi)也用了大量的結(jié)構(gòu)體指針等等。但是在整個過程中input_dev、input_handler、input_handle這三個結(jié)構(gòu)體變量的實(shí)體也都只有一個。其中input_dev結(jié)構(gòu)體實(shí)體在xxx_ts.c中直接定義了一個input_dev的指針全局變量:
struct input_dev *tsdev;
并在初始化函數(shù)中開辟了一個input_dev的內(nèi)存空間并讓tsdev指針指向它:
w55fa95_dev = input_allocate_device();
input_handler結(jié)構(gòu)體在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,
};
而關(guān)聯(lián)input_dev和input_handler的input_handle則是在調(diào)用鏈接連接函數(shù)static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)的時候,在該函數(shù)的內(nèi)部調(diào)用evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)開辟了一個evdev結(jié)構(gòu)體的內(nèi)存空間時創(chuàng)建了,input_handle就是使用了evdev結(jié)構(gòu)體內(nèi)部定義的input_handle。
一個完整input設(shè)備系統(tǒng)不僅要有設(shè)備,還需要有處理程序input_handler,而上文中主要介紹的是設(shè)備注冊、生成、以及和handler搭配的一個過程,而handler在何時生成的?
第三章、input_handler的注冊。
Input_handler是要和用戶層打交道的,在evdev.c中直接定義了一個input_handler結(jié)構(gòu)體并初始化了一些內(nèi)部成員變量。
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops, //用戶對設(shè)備操作的函數(shù)指針
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids, //指向一個evedev的指針數(shù)組
};
先看第一行加粗的代碼,evedev_fops結(jié)構(gòu)體的定義如下
static struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.flush = evdev_flush
};
相信做過linux設(shè)備驅(qū)動編程的對這都很熟悉了,就是一大堆的用戶接口函數(shù),包括對設(shè)備的open、close、read、write、ioctl等函數(shù)。
再看第二行代碼加粗的部分.id_table = evdev_ids, id.table就是前面所說過要和input_dev的id匹配的這么一個結(jié)構(gòu)體,這里讓它初始化為evdev_ids,在看evdev_ids的定義:
static struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
MODULE_DEVICE_TABLE(input, evdev_ids);
這里是一個結(jié)構(gòu)體數(shù)組,令數(shù)組中第一個結(jié)構(gòu)體的該成員變量driver_info的值為1,其他成員變量均未定義,說明這個handler對所有device的id都能匹配得上。數(shù)組中的第二個結(jié)構(gòu)體為空表示結(jié)束,用來標(biāo)識結(jié)束配合下面的MODULE_DEVICE_TABLE(input, evdev_ids)使用,關(guān)于MODULE_DEVICE_TABLE宏定義介紹自行查看相關(guān)文獻(xiàn)(我也不懂)。
接下來進(jìn)入正題,input_handler的注冊!
Input_handler的注冊和input_dev的注冊很相似,大同小異罷了,在evdev.c源碼中顯示定義并初始化了一個input_handler結(jié)構(gòu)體并直接給相關(guān)的成員變量賦值了,就是本章開始所將的部分,然后再初始化函數(shù)中注冊一個input_handler:
static int __init evdev_init(void)
{
input_register_handler(&evdev_handler);
return 0;
}
這里只調(diào)用了一個input_register_handler()函數(shù),看起來應(yīng)該是很簡單的樣子(相比input_dev的注冊),繼續(xù)跟蹤進(jìn)入到該函數(shù)中:
void input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
struct input_handle *handle;
struct input_device_id *id;
if (!handler) return;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL)
input_table[handler->minor >> 5] = handler;
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node)
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev)))
if ((handle = handler->connect(handler, dev, id)))
input_link_handle(handle);
input_wakeup_procfs_readers();
}
該函數(shù)中代碼加粗的部分,與input_register_device()函數(shù)里的如出一轍,這里不再做說明。
至此input_handler的注冊已經(jīng)結(jié)束。
第四章、input子系統(tǒng)數(shù)據(jù)結(jié)構(gòu)
下圖3是以觸摸屏設(shè)備為例子的input子系統(tǒng)數(shù)據(jù)結(jié)構(gòu)圖。
 |
圖3 input子系統(tǒng)數(shù)據(jù)結(jié)構(gòu)圖 |
進(jìn)行到這里,又多冒出來了一個struct evdev_list的結(jié)構(gòu)體,這在之前并沒有提到過,因?yàn)樵谧詉nput_dev和input_handler過程中并沒有用到過,查看該結(jié)構(gòu)體:
struct evdev_list {
struct input_event buffer[EVDEV_BUFFER_SIZE]; //存放設(shè)備數(shù)據(jù)信息
int head; //buffer的下標(biāo),標(biāo)識從設(shè)備中過來要存放到buffer的數(shù)據(jù)的位置
第一章、了解linux input子系統(tǒng)
Linux輸入設(shè)備總類繁雜,常見的包括有按鍵、鍵盤、觸摸屏、鼠標(biāo)、搖桿等等,他們本身就是字符設(shè)備,而linux內(nèi)核將這些設(shè)備的共同性抽象出來,簡化驅(qū)動開發(fā)建立了一個input子系統(tǒng)。子系統(tǒng)共分為三層,如圖1所示。
 |
圖1 input輸入子系統(tǒng)
|
驅(qū)動層和硬件相關(guān),直接捕捉和獲取硬件設(shè)備的數(shù)據(jù)信息等(包括觸摸屏被按下、按下位置、鼠標(biāo)移動、鍵盤按下等等),然后將數(shù)據(jù)信息報(bào)告到核心層。核心層負(fù)責(zé)連接驅(qū)動層和事件處理層,設(shè)備驅(qū)動(device driver)和處理程序(handler)的注冊需要通過核心層來完成,核心層接收來自驅(qū)動層的數(shù)據(jù)信息,并將數(shù)據(jù)信息選擇對應(yīng)的handler去處理,最終handler將數(shù)據(jù)復(fù)制到用戶空間。
先了解三個定義在/linux/input.h下重要的結(jié)構(gòu)體input_dev、input_handler、input_handle。
struct input_dev {
void *private;
const char *name;
const char *phys;
const char *uniq;
struct input_id id; //與input_handler匹配用的id
unsigned long evbit[NBITS(EV_MAX)]; //設(shè)備支持的事件類型
unsigned long keybit[NBITS(KEY_MAX)]; //按鍵事件支持的子事件類型
unsigned long relbit[NBITS(REL_MAX)];
unsigned long absbit[NBITS(ABS_MAX)]; //絕對坐標(biāo)事件支持的子事件類型
unsigned long mscbit[NBITS(MSC_MAX)];
unsigned long ledbit[NBITS(LED_MAX)];
unsigned long sndbit[NBITS(SND_MAX)];
unsigned long ffbit[NBITS(FF_MAX)];
unsigned long swbit[NBITS(SW_MAX)];
int ff_effects_max;
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
unsigned int repeat_key;
struct timer_list timer;
struct pt_regs *regs;
int state;
int sync;
int abs[ABS_MAX + 1];
int rep[REP_MAX + 1];
unsigned long key[NBITS(KEY_MAX)];
unsigned long led[NBITS(LED_MAX)];
unsigned long snd[NBITS(SND_MAX)];
unsigned long sw[NBITS(SW_MAX)];
int absmax[ABS_MAX + 1]; //絕對坐標(biāo)事件的最大鍵值
int absmin[ABS_MAX + 1]; //絕對坐標(biāo)事件的最小鍵值
int absfuzz[ABS_MAX + 1];
int absflat[ABS_MAX + 1];
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*accept)(struct input_dev *dev, struct file *file);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);
int (*erase_effect)(struct input_dev *dev, int effect_id);
struct input_handle *grab; //當(dāng)前占有該設(shè)備的handle
struct mutex mutex; /* serializes open and close operations */
unsigned int users; //打開該設(shè)備的用戶量
struct class_device cdev;
struct device *dev; /* will be removed soon */
int dynalloc; /* temporarily */
struct list_head h_list; //該鏈表頭用于鏈接該設(shè)備所關(guān)聯(lián)的input_handle
struct list_head node; //該鏈表頭用于將設(shè)備鏈接到input_dev_list
};
Input_dev是一個很強(qiáng)大的結(jié)構(gòu)體,它把所有的input設(shè)備(觸摸屏、鍵盤、鼠標(biāo)等)的信息都考慮到了,對于觸摸屏來說只用到它里面的一部分而已,尤其是加粗的部分,注意該結(jié)構(gòu)體中最后兩行定義的兩個list_head結(jié)構(gòu)體,list_head在/linux/list.h中有定義,深入跟蹤
struct list_head {
struct list_head *next, *prev;
};
該結(jié)構(gòu)體內(nèi)部并沒有定義數(shù)據(jù)而只定義了兩個指向本身結(jié)構(gòu)體的指針,預(yù)先說明一下,所有的input device在注冊后會加入一個input_dev_list(輸入設(shè)備鏈表),所有的eventhandler在注冊后會加入一個input_handler_list(輸入處理程序鏈表),這里的list_head主要的作用是作為input_dev_list和input_handler_list的一個節(jié)點(diǎn)來保存地址。Input_dev_list和input_handler_list之間的對應(yīng)關(guān)系由input_handle結(jié)構(gòu)體橋接,具體后面說明。
struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
const struct file_operations *fops; //提供給用戶對設(shè)備操作的函數(shù)指針
int minor;
char *name;
struct input_device_id *id_table; //與input_dev匹配用的id
struct input_device_id *blacklist; //標(biāo)記的黑名單
struct list_head h_list; //用于鏈接和該handler相關(guān)的handle
struct list_head node; //用于將該handler鏈入input_handler_list
};
input_handler顧名思義,它是用來處理input_dev的一個結(jié)構(gòu)體,相關(guān)的處理函數(shù)在結(jié)構(gòu)里內(nèi)部都有定義,最后兩行定義的list_head結(jié)構(gòu)體作用同input_dev所定義的一樣,這里不再說明。
注:input_device_id結(jié)構(gòu)體在/linux/mod_devicetable.h中有定義
struct input_handle {
void *private;
int open; //記錄設(shè)備打開次數(shù)
char *name;
struct input_dev *dev; //指向所屬的input_dev
struct input_handler *handler; //指向所屬的input_handler
struct list_head d_node; //用于鏈入所指向的input_dev的handle鏈表
struct list_head h_node; //用于鏈入所指向的input_handler的handle鏈表
};
可以看到input_handle中擁有指向input_dev和input_handler的指針,即input_handle是用來關(guān)聯(lián)input_dev和input_handler。為什么用input_handle來關(guān)聯(lián)input_dev和input_handler而不將input_dev和input_handler直接對應(yīng)呢?因?yàn)?/span>一個device可以對應(yīng)多個handler,而一個handler也可處理多個device。就如一個觸摸屏設(shè)備可以對應(yīng)event handler也可以對應(yīng)tseve handler。
input_dev、input_handler、input_handle的關(guān)系如下圖2所示。
 |
圖2 input_dev,input_handler,input_handle關(guān)系圖 |
第二章、input device的注冊
Input device的注冊實(shí)際上僅僅只有幾行代碼,因?yàn)樵趇nput.c中已經(jīng)將大量的代碼封裝好了,主需要調(diào)用幾個關(guān)鍵的函數(shù)就能完成對input device的注冊。
在xxx_ts.c中預(yù)先定義全局變量struct input_dev tsdev;然后進(jìn)入到初始化函數(shù)
static int __init xxx_probe(struct platform_device *pdev)
{
…
if (!(tsdev = input_allocate_device()))
{
printk(KERN_ERR "tsdev: not enough memory\n");
err = -ENOMEM;
goto fail;
}
…
tsdev->name = "xxx TouchScreen"; //xxx為芯片型號
tsdev ->phys = "xxx/event0";
tsdev ->id.bustype = BUS_HOST; //設(shè)備id,用于匹配handler的id
tsdev ->id.vendor = 0x0005;
tsdev ->id.product = 0x0001;
tsdev ->id.version = 0x0100;
tsdev ->open = xxx_open;
tsdev ->close =xxx_close;
tsdev ->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_SYN); //設(shè)置支持的事件類型
tsdev ->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
input_set_abs_params(tsdev, ABS_X, 0, 0x400, 0, 0); //限定絕對坐標(biāo)X的取值范圍
input_set_abs_params(tsdev, ABS_Y, 0, 0x400, 0, 0); //同上
input_set_abs_params(tsdev, ABS_PRESSURE, 0, 1000, 0, 0); //觸摸屏壓力值范圍
…
If(input_register_device(tsdev) == error) //注冊設(shè)備
goto fail;
…
fail:
input_free_device(tsdev);
printk(“ts probe failed\n”);
return err;
}
先看該函數(shù)中的tsdev = input_allocate_device(),深入最終,該函數(shù)在input.c中有定義
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
if (dev) {
dev->dynalloc = 1;
dev->cdev.class = &input_class;
class_device_initialize(&dev->cdev);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
}
return dev;
}
學(xué)過C語言應(yīng)該都知道m(xù)alloc函數(shù)(開辟內(nèi)存空間),而這里的kzalloc也類似于C語言中的malloc一樣,是linux內(nèi)核空間分配內(nèi)存函數(shù),后面的GFP_KERNEL標(biāo)志意為常規(guī)的內(nèi)存分配,更多的分配標(biāo)志可參閱先關(guān)資料(我也不懂)。再回到前面的函數(shù)中來,接著后面的代碼為對tsdev結(jié)構(gòu)體中的成員進(jìn)行賦值初始化,賦值完成后就要開始進(jìn)入主題:注冊設(shè)備了,進(jìn)入到函數(shù)input_register_device(tsdev),該函數(shù)在input.c中有定義。
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0); //定義原子變量,禁止線程并發(fā)訪問
struct input_handle *handle; //定義一些變量備后文使用
struct input_handler *handler;
struct input_device_id *id;
const char *path;
int error;
if (!dev->dynalloc) {
printk(KERN_WARNING "input: device %s is statically allocated, will not register\n"
dev->name ? dev->name : "<Unknown>");
return -EINVAL;
}
mutex_init(&dev->mutex); //互斥鎖初始化,防止臨界區(qū)代碼被并發(fā)訪問
set_bit(EV_SYN, dev->evbit); //設(shè)置支持同步事件,input設(shè)備全部默認(rèn)支持同步事件
/*
* 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;
}
INIT_LIST_HEAD(&dev->h_list); //初始化需要關(guān)聯(lián)的handle鏈表頭
list_add_tail(&dev->node, &input_dev_list); //將設(shè)備添加到input_dev_list中
dev->cdev.class = &input_class;
snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
error = class_device_add(&dev->cdev);
if (error)
return error;
error = sysfs_create_group(&dev->cdev.kobj, &input_dev_attr_group);
if (error)
goto fail1;
error = sysfs_create_group(&dev->cdev.kobj, &input_dev_id_attr_group);
if (error)
goto fail2;
error = sysfs_create_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
if (error)
goto fail3;
__module_get(THIS_MODULE);
path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path);
/*** 遍歷input_handler_list上全部的handler,尋找與該設(shè)備匹配的handler ***/
list_for_each_entry(handler, &input_handler_list, node)
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev)))
if ((handle = handler->connect(handler, dev, id)))
input_link_handle(handle);
input_wakeup_procfs_readers();
return 0;
fail3: sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group);
fail2: sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group);
fail1: class_device_del(&dev->cdev);
return error;
}
先看函數(shù)中前面代碼加粗的部分mutex_init(&dev->mutex),與互斥鎖相關(guān)的東西(我也不太懂),set_bit(EV_SYN, dev->evbit)設(shè)置支持同步事件,linux的input子系統(tǒng)默認(rèn)要支持同步事件。
接著看中間代碼加粗的部分,有關(guān)list操作的在/linux/list.h中有定義
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
可以看得出INIT_LIST_HEAD(struct list_head *list)就是讓list指向結(jié)構(gòu)體的成員再指向其本身完成初始化操作,list_add_tail(struct list_head *new, struct list_head *head)是將new所指向的結(jié)構(gòu)體作為一節(jié)點(diǎn)插入到head所指向鏈表節(jié)點(diǎn)之前。因此函數(shù)中的list_add_tail(&dev->node, &input_dev_list)就是將該設(shè)備添加到input_dev_list中。而input_dev_list這個雙向鏈表在什么時候被定義了呢,且看input.c源代碼開始部分全局部分有定義:
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
而LIST_HEAD的宏定義:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
顯然這在最開始就已經(jīng)定義了input_dev_list和input_handler_list這兩個鏈表,且這兩個鏈表都只有一個節(jié)點(diǎn),而在device和handler注冊的時候會在這兩條鏈表中加入節(jié)點(diǎn),list_add_tail(&dev->node, &input_dev_list)就是將該設(shè)備添加到input_dev_list中。
注意最后代碼加粗的部分,該部分完成了input_dev和input_handler的橋接。先看list_for_each_entry(handler, &input_handler_list, node),list_for_each_entry在list.h中有定義,其作用相當(dāng)于一個for循環(huán)。其約等價于:(個人理解)
for(handler = input_handler_list表頭所屬的input_handler結(jié)構(gòu)體地址;
handler != input_handler_list表尾所屬的input_handler結(jié)構(gòu)體地址;
handler++)
即handler第一次指向input_handler_list的頭部所在的input_handler地址。每循環(huán)一次handler就沿著input_handler_list移動到下一個節(jié)點(diǎn),得到下一節(jié)點(diǎn)所屬的handler地址,直到input_handler_list的結(jié)尾。而每次的循環(huán)需要做什么呢?要做的就是遍歷input_handler_list上的每一個handler,看有哪些handler能與該設(shè)備匹配的上。匹配過程:
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))//判斷該handler沒有被列入黑名單或者黑名單匹配不成功的話則繼續(xù)
if ((id = input_match_device(handler->id_table, dev))) //將設(shè)備id與handler的id進(jìn)行匹配,成功則繼續(xù)往下
if ((handle = handler->connect(handler, dev, id))) //鏈接device與handler,成功則繼續(xù)往下
input_link_handle(handle); //將handle鏈入input_handler_list和input_dev_list
繼續(xù)跟蹤進(jìn)這些函數(shù)
static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev)
{
int i;
for (; id->flags || id->driver_info; id++) {
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) //匹配handler和device id的flag標(biāo)志位
if (id->bustype != dev->id.bustype)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
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相關(guān)標(biāo)志位
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);
return id;
}
return NULL;
}
該函數(shù)用于匹配input_dev結(jié)構(gòu)體和input_handler結(jié)構(gòu)體里面定義的input_device_id,若兩者里面有任何相關(guān)變量不一樣則匹配失敗(條件真苛刻)。
再看handle = handler->connect(handler, dev, id),connect函數(shù)即為static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id),為什么呢會是這個呢,我們先看evdev.c中的input_handler結(jié)構(gòu)體定義:
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_handler結(jié)構(gòu)體里邊的connect函數(shù)即為evdev_connect函數(shù)
static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
{
1 struct evdev *evdev; //定義一個evdev結(jié)構(gòu)體指針
struct class_device *cdev;
int minor;
2 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 NULL;
}
3 if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)))
return NULL;
INIT_LIST_HEAD(&evdev->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;
cdev = class_device_create(&input_class, &dev->cdev,
MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
dev->cdev.dev, evdev->name);
/* temporary symlink to keep userspace happy */
sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj,
evdev->name);
return &evdev->handle;
}
先看1處,這里有個定義在evdev.c里邊的新面孔
struct evdev {
int exist;
int open;
int minor;
char name[16];
struct input_handle handle; //關(guān)聯(lián)input_handler和input_dev的input_handle
wait_queue_head_t wait;
struct evdev_list *grab;
struct list_head list;
};
evdev這個結(jié)構(gòu)體就是拿來應(yīng)用開發(fā)操作的,在這里就是觸摸屏對應(yīng)的設(shè)備文件實(shí)體,結(jié)構(gòu)體前邊定義了記錄設(shè)備的一些信息(設(shè)備號,打開狀態(tài)、設(shè)備名字等),這里還定義了一個input_handle的實(shí)體handle,沒錯這個handle就是要用來關(guān)聯(lián)input_dev和input_handler的,后面還有一行加粗的部分后面再做介紹。
再看2處,evdev_table[]是一個全局變量的數(shù)組,在evdev.c中有定義
#define EVDEV_MINORS 32
static struct evdev *evdev_table[EVDEV_MINORS];
在前面已經(jīng)說明了,一個device可以對應(yīng)多個handler,而一個handler也可處理多個device,這里體現(xiàn)出了后者。既然evdev這個結(jié)構(gòu)體是對應(yīng)的設(shè)備文件實(shí)體,因?yàn)檫@個handler可能會處理多個device,因此該handler要處理n個device就會應(yīng)該有n個evdev實(shí)體,而這些實(shí)體的地址存放在evdev_table[]這個指針數(shù)組中,也就是說該handler最多只能處理EVDEV_MINORS個device,而2處的這幾句代碼就是要遍歷evdev_table[]這個數(shù)組看還有沒有空著的位置,有的話才會繼續(xù)進(jìn)行下面的程序。
再看3處,開辟一個evdev結(jié)構(gòu)體的內(nèi)存空間,前面有說明過kzalloc函數(shù),這里不再說明。
后面的代碼就是為evdev結(jié)構(gòu)體變量賦初始值了,其中init_waitqueue_head(&evdev->wait)初始化等待隊(duì)列,具體介紹結(jié)合/linux/wait.h和查看相關(guān)資料(本人不懂)。
函數(shù)最后得到evdev結(jié)構(gòu)體內(nèi)的hanlde地址并返回,此時返回到我們的int input_register_device(struct input_dev *dev)函數(shù)里面,最后一句
input_link_handle(handle),進(jìn)入到該函數(shù)中發(fā)現(xiàn)
static void input_link_handle(struct input_handle *handle)
{
list_add_tail(&handle->d_node, &handle->dev->h_list);
list_add_tail(&handle->h_node, &handle->handler->h_list);
}
在該函數(shù)中也只是將handle中的d_node和h_node分別接入到input_dev和input_handler的h_list中,此時input_dev、input_handler、input_handle三者形成了如圖2所示的關(guān)系。
至此設(shè)備注冊過程算是全部完成了,但是貌似還有點(diǎn)亂。在整個設(shè)備的注冊過程中函數(shù)的嵌套一個接著一個,不僅函數(shù)嵌套,結(jié)構(gòu)體也使用了結(jié)構(gòu)體和函數(shù)嵌套等,在各個函數(shù)內(nèi)也用了大量的結(jié)構(gòu)體指針等等。但是在整個過程中input_dev、input_handler、input_handle這三個結(jié)構(gòu)體變量的實(shí)體也都只有一個。其中input_dev結(jié)構(gòu)體實(shí)體在xxx_ts.c中直接定義了一個input_dev的指針全局變量:
struct input_dev *tsdev;
并在初始化函數(shù)中開辟了一個input_dev的內(nèi)存空間并讓tsdev指針指向它:
w55fa95_dev = input_allocate_device();
input_handler結(jié)構(gòu)體在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,
};
而關(guān)聯(lián)input_dev和input_handler的input_handle則是在調(diào)用鏈接連接函數(shù)static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)的時候,在該函數(shù)的內(nèi)部調(diào)用evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)開辟了一個evdev結(jié)構(gòu)體的內(nèi)存空間時創(chuàng)建了,input_handle就是使用了evdev結(jié)構(gòu)體內(nèi)部定義的input_handle。
一個完整input設(shè)備系統(tǒng)不僅要有設(shè)備,還需要有處理程序input_handler,而上文中主要介紹的是設(shè)備注冊、生成、以及和handler搭配的一個過程,而handler在何時生成的?
第三章、input_handler的注冊。
Input_handler是要和用戶層打交道的,在evdev.c中直接定義了一個input_handler結(jié)構(gòu)體并初始化了一些內(nèi)部成員變量。
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops, //用戶對設(shè)備操作的函數(shù)指針
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids, //指向一個evedev的指針數(shù)組
};
先看第一行加粗的代碼,evedev_fops結(jié)構(gòu)體的定義如下
static struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.flush = evdev_flush
};
相信做過linux設(shè)備驅(qū)動編程的對這都很熟悉了,就是一大堆的用戶接口函數(shù),包括對設(shè)備的open、close、read、write、ioctl等函數(shù)。
再看第二行代碼加粗的部分.id_table = evdev_ids, id.table就是前面所說過要和input_dev的id匹配的這么一個結(jié)構(gòu)體,這里讓它初始化為evdev_ids,在看evdev_ids的定義:
static struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
MODULE_DEVICE_TABLE(input, evdev_ids);
這里是一個結(jié)構(gòu)體數(shù)組,令數(shù)組中第一個結(jié)構(gòu)體的該成員變量driver_info的值為1,其他成員變量均未定義,說明這個handler對所有device的id都能匹配得上。數(shù)組中的第二個結(jié)構(gòu)體為空表示結(jié)束,用來標(biāo)識結(jié)束配合下面的MODULE_DEVICE_TABLE(input, evdev_ids)使用,關(guān)于MODULE_DEVICE_TABLE宏定義介紹自行查看相關(guān)文獻(xiàn)(我也不懂)。
接下來進(jìn)入正題,input_handler的注冊!
Input_handler的注冊和input_dev的注冊很相似,大同小異罷了,在evdev.c源碼中顯示定義并初始化了一個input_handler結(jié)構(gòu)體并直接給相關(guān)的成員變量賦值了,就是本章開始所將的部分,然后再初始化函數(shù)中注冊一個input_handler:
static int __init evdev_init(void)
{
input_register_handler(&evdev_handler);
return 0;
}
這里只調(diào)用了一個input_register_handler()函數(shù),看起來應(yīng)該是很簡單的樣子(相比input_dev的注冊),繼續(xù)跟蹤進(jìn)入到該函數(shù)中:
void input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
struct input_handle *handle;
struct input_device_id *id;
if (!handler) return;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL)
input_table[handler->minor >> 5] = handler;
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node)
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev)))
if ((handle = handler->connect(handler, dev, id)))
input_link_handle(handle);
input_wakeup_procfs_readers();
}
該函數(shù)中代碼加粗的部分,與input_register_device()函數(shù)里的如出一轍,這里不再做說明。
至此input_handler的注冊已經(jīng)結(jié)束。
第四章、input子系統(tǒng)數(shù)據(jù)結(jié)構(gòu)
下圖3是以觸摸屏設(shè)備為例子的input子系統(tǒng)數(shù)據(jù)結(jié)構(gòu)圖。
 |
圖3 input子系統(tǒng)數(shù)據(jù)結(jié)構(gòu)圖 |
進(jìn)行到這里,又多冒出來了一個struct evdev_list的結(jié)構(gòu)體,這在之前并沒有提到過,因?yàn)樵谧詉nput_dev和input_handler過程中并沒有用到過,查看該結(jié)構(gòu)體:
struct evdev_list {
struct input_event buffer[EVDEV_BUFFER_SIZE]; //存放設(shè)備數(shù)據(jù)信息
int head; //buffer的下標(biāo),標(biāo)識從設(shè)備中過來要存放到buffer的數(shù)據(jù)的位置
|