Input子系统(二)【转】
轉(zhuǎn)自:http://blog.chinaunix.net/uid-25047042-id-4192368.html
上一篇中粗略的分析了下input_dev,input_handle,input_handler這三者之間的關(guān)系,而在實(shí)際系統(tǒng)當(dāng)中input子系統(tǒng)是如何工作的呢,當(dāng)然我們知道,故事肯定是圍繞著它們?nèi)齻€(gè)發(fā)生,下面我們來(lái)看看具體的input設(shè)備的工作流程。同樣以觸摸屏為例。
在觸摸屏驅(qū)動(dòng)中,當(dāng)有觸摸事件產(chǎn)生(手接觸到觸摸屏的時(shí)候),觸摸屏相關(guān)IC會(huì)產(chǎn)生中斷,在中斷處理函數(shù)當(dāng)中,kernel或者說(shuō)tp?driver會(huì)讀取此次中斷產(chǎn)生的數(shù)據(jù)(對(duì)于一個(gè)支持多點(diǎn)觸摸的觸摸屏來(lái)說(shuō)就是每條觸摸軌跡的坐標(biāo)),driver將數(shù)據(jù)組織好,然后向input子系統(tǒng)report數(shù)據(jù):
……
ret?=?gtp_i2c_read(ts->client,?buf,?2?+?8?*?(touch_num?-?1));?
memcpy(&point_data[12],?&buf[2],?8?*?(touch_num?-?1));//從硬件讀取數(shù)據(jù)
……
input_x??=?coor_data[pos?+?1]?|?coor_data[pos?+?2]?<<?8;
input_y??=?coor_data[pos?+?3]?|?coor_data[pos?+?4]?<<?8;
input_w??=?coor_data[pos?+?5]?|?coor_data[pos?+?6]?<<?8;//組織相關(guān)數(shù)據(jù)
……
input_mt_slot(ts->input_dev,?id);
input_report_abs(ts->input_dev,?ABS_MT_TRACKING_ID,?id);
input_report_abs(ts->input_dev,?ABS_MT_POSITION_X,?x);
input_report_abs(ts->input_dev,?ABS_MT_POSITION_Y,?y);
input_report_abs(ts->input_dev,?ABS_MT_TOUCH_MAJOR,?w);
input_report_abs(ts->input_dev,?ABS_MT_WIDTH_MAJOR,?w);//向input子系統(tǒng)report數(shù)據(jù)
input_sync(ts->input_dev);//同步相關(guān)數(shù)據(jù)
……
Input子系統(tǒng)對(duì)于不同類(lèi)型的事件有不同的處理方法,以多點(diǎn)觸摸絕對(duì)坐標(biāo)為例:
input_report_abs(ts->input_dev,?ABS_MT_POSITION_X,?x);
ts->input_dev是report數(shù)據(jù)的這個(gè)設(shè)備,在上一篇input設(shè)備初始化有提過(guò),ABS_MT_POSITION_X表明report的這個(gè)事件是一個(gè)多點(diǎn)觸摸的X軸絕對(duì)坐標(biāo)事件,x就是從硬件中讀取的某個(gè)觸摸點(diǎn)的X軸也就是這次report的值了。
static?inline?void?input_report_abs(struct?input_dev?*dev,?unsigned?int?code,?int?value)
{
input_event(dev,?EV_ABS,?code,?value);
}
Input子系統(tǒng)用input_report_abs()封裝了對(duì)絕對(duì)坐標(biāo)類(lèi)型事件,在input.h中我們可以看到,input子系統(tǒng)還封裝了其他很多種類(lèi)型事件接口,它們中間都調(diào)用了input_event接口。
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))?{
/*
首先會(huì)判斷該input設(shè)備是否具有report?type類(lèi)型事件的能力,只有設(shè)備支持report?type類(lèi)型事件,才會(huì)向input子系統(tǒng)report相關(guān)數(shù)據(jù)。而支持該種類(lèi)型事件是在設(shè)備的初始化的時(shí)候做的,上一篇中也有提過(guò),也就是:
ts->input_dev->evbit[0]?=?BIT_MASK(EV_SYN)?|?BIT_MASK(EV_KEY)?|?BIT_MASK(EV_ABS)?;
在設(shè)備的probe當(dāng)中會(huì)設(shè)置設(shè)備支持的處理的事件類(lèi)型,在這里設(shè)備是支持處理坐標(biāo)類(lèi)型事件的能力的。
*/
spin_lock_irqsave(&dev->event_lock,?flags);
add_input_randomness(type,?code,?value);
/*
將事件數(shù)據(jù)做為一個(gè)隨機(jī)數(shù)產(chǎn)生熵,因?yàn)橛|摸事件可等同視為一個(gè)隨機(jī)事件,人手觸摸屏幕點(diǎn)是隨機(jī)的,沒(méi)有什么規(guī)定一定要總?cè)ビ|摸屏上某個(gè)位置
*/
input_handle_event(dev,?type,?code,?value);//處理這次input事件
spin_unlock_irqrestore(&dev->event_lock,?flags);
}
}
static?void?input_handle_event(struct?input_dev?*dev,
???????unsigned?int?type,?unsigned?int?code,?int?value)
/*
在input_handle_event()當(dāng)中,根據(jù)事件類(lèi)型的不同,分別進(jìn)行不同的處理,下面只選取了EV_ABS類(lèi)型事件列出。
*/
{
int?disposition?=?INPUT_IGNORE_EVENT;
?
switch?(type)?{
……
case?EV_ABS:
if?(is_event_supported(code,?dev->absbit,?ABS_MAX))
/*
這里同樣還會(huì)做出一個(gè)判斷,因?yàn)樽鴺?biāo)類(lèi)型事件同樣分為很多種類(lèi)型的坐標(biāo)事件,我們這里要處理的是多點(diǎn)觸摸的絕對(duì)坐標(biāo)類(lèi)型事件,如果設(shè)備不支持處理這種事件的話(huà),那也不會(huì)向input子系統(tǒng)report事件。對(duì)于該類(lèi)型事件是否支持的設(shè)置同樣放在了設(shè)備初始化的時(shí)候:
input_set_abs_params(ts->input_dev,?ABS_MT_POSITION_X,?0,?ts->abs_x_max,?0,?0);
這里同樣設(shè)置了該設(shè)備report的最大的x軸絕對(duì)坐標(biāo)值。
*/
disposition?=?input_handle_abs_event(dev,?code,?&value);
/*
對(duì)于絕對(duì)坐標(biāo)類(lèi)型事件在很多時(shí)候是會(huì)非常頻繁的產(chǎn)生,像觸摸屏它的報(bào)點(diǎn)率一般會(huì)有50-60HZ,那么它產(chǎn)生的數(shù)據(jù)相對(duì)來(lái)說(shuō)是非常大的,對(duì)于連續(xù)的重復(fù)的坐標(biāo),input子系統(tǒng)會(huì)進(jìn)行過(guò)濾,只處理一次,以及可以過(guò)濾由于硬件noise產(chǎn)生的誤數(shù)據(jù)。
*/
break;
……
if?(disposition?!=?INPUT_IGNORE_EVENT?&&?type?!=?EV_SYN)
dev->sync?=?false;
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);//事件處理成功,將事件傳遞給input_handler處理。
}
通過(guò)以上一系列接口的傳遞和處理,input?event終于被傳遞給了input_handler。
static?void?input_pass_event(struct?input_dev?*dev,
?????unsigned?int?type,?unsigned?int?code,?int?value)
{
struct?input_handler?*handler;
struct?input_handle?*handle;
?
rcu_read_lock();
handle?=?rcu_dereference(dev->grab);
if?(handle)
handle->handler->event(handle,?type,?code,?value);
else?{
bool?filtered?=?false;
list_for_each_entry_rcu(handle,?&dev->h_list,?d_node)?{
/*
在這里是不是看到了很眼熟的東西,在上一篇當(dāng)中我們有說(shuō)過(guò),在注冊(cè)Input_device的時(shí)候,會(huì)將input_handle通過(guò)d_node保存在input_dev的h_list上,在這里就是通過(guò)d_node從input_dev的h_list獲取其對(duì)應(yīng)的input_handle。
*/
if?(!handle->open)
continue;
handler?=?handle->handler;//通過(guò)handle獲取到對(duì)應(yīng)的input_handler。
if?(!handler->filter)?{
if?(filtered)
break;
handler->event(handle,?type,?code,?value);//input_handler處理input事件。
}?else?if?(handler->filter(handle,?type,?code,?value))
filtered?=?true;
}
}
rcu_read_unlock();
}
到這里,我們就要進(jìn)入input_handler了,input_handler調(diào)用其event接口對(duì)數(shù)據(jù)進(jìn)行處理。
Input_handler
上一篇有說(shuō)過(guò),input_handler是數(shù)據(jù)的消耗者,負(fù)責(zé)kernel設(shè)備數(shù)據(jù)與上層應(yīng)用的交接。Linux內(nèi)核為我們提供了一個(gè)默認(rèn)的input_handler,它支持處理所有的input設(shè)備,它就是evdev。相關(guān)代碼在evdev.c當(dāng)中。
在evdev.c當(dāng)中為我們定義了一個(gè)input_handler:
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,
};
其中event是事件處理接口,當(dāng)input設(shè)備傳遞數(shù)據(jù)過(guò)來(lái)的時(shí)候,event負(fù)責(zé)處理。
Connect在上一篇有粗略分析過(guò),當(dāng)注冊(cè)一個(gè)input_device或者input_handler的時(shí)候,它就會(huì)被調(diào)用,關(guān)聯(lián)對(duì)應(yīng)的input_handler或者input_device。
Disconnect斷開(kāi)input_device和input_handler時(shí)被調(diào)用。
Fops是input_handler提供的文件操作接口集合。在linux系統(tǒng)當(dāng)中,所有的設(shè)備對(duì)上層來(lái)說(shuō)都是文件,而這里定義了應(yīng)用對(duì)設(shè)備文件所有的接口。
Minor是input_handler處理的設(shè)備的次設(shè)備號(hào)的起始設(shè)備號(hào)。
Name,很簡(jiǎn)單,該input_handler的name。
Id_table,定義了該input_handler支持處理的設(shè)備id,evdev支持所有類(lèi)型的input_device設(shè)備類(lèi)型。
Evdev.c模塊加載的時(shí)候會(huì)注冊(cè)當(dāng)input_handler:
static?int?__init?evdev_init(void)
{
return?input_register_handler(&evdev_handler);
}
以下是注冊(cè)input_handler內(nèi)核的動(dòng)作:
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;
}
list_add_tail(&handler->node,?&input_handler_list);
list_for_each_entry(dev,?&input_dev_list,?node)?//在注冊(cè)input_device的時(shí)候會(huì)將input_device掛載在input_dev_list鏈表上。
input_attach_handler(dev,?handler);//關(guān)聯(lián)input_dev和input_handler
input_wakeup_procfs_readers();
?out:
mutex_unlock(&input_mutex);
return?retval;
}
這里可以看到,當(dāng)注冊(cè)input_handler的時(shí)候,會(huì)關(guān)聯(lián)內(nèi)核當(dāng)前已經(jīng)存在的input_device,因此不用擔(dān)心有注冊(cè)在input_handler注冊(cè)之前的input_device沒(méi)有關(guān)聯(lián)相關(guān)input_handler。
這里還需要介紹另外兩個(gè)重要的數(shù)據(jù)結(jié)構(gòu):
struct?evdev?{
int?open;
int?minor;//次設(shè)備號(hào)
struct?input_handle?handle;//對(duì)應(yīng)input_device關(guān)聯(lián)的input_handle
wait_queue_head_t?wait;//等待隊(duì)列頭
struct?evdev_client?__rcu?*grab;
struct?list_head?client_list;
spinlock_t?client_lock;?/*?protects?client_list?*/
struct?mutex?mutex;
struct?device?dev;
bool?exist;
};
Evdev是input_device?eventn設(shè)備的定義,eventn設(shè)備是Input_device的一個(gè)子設(shè)備,在linux內(nèi)核系統(tǒng)當(dāng)中,/dev/input/目錄下每個(gè)input_device都對(duì)應(yīng)著一個(gè)eventn(n=0,1,2……)文件,而input_handler所處理的就是這個(gè)evdev設(shè)備。
struct?evdev_client?{
unsigned?int?head;
unsigned?int?tail;
unsigned?int?packet_head;?/*?[future]?position?of?the?first?element?of?next?packet?*/
spinlock_t?buffer_lock;?/*?protects?access?to?buffer,?head?and?tail?*/
struct?wake_lock?wake_lock;
char?name[28];
struct?fasync_struct?*fasync;
struct?evdev?*evdev;//指向的evdev,該evdev_client處理的eventn對(duì)象
struct?list_head?node;
unsigned?int?bufsize;
struct?input_event?buffer[];//保存input_device傳遞過(guò)來(lái)的數(shù)據(jù)的buffer
};
Evdev_client是evdev提供處理數(shù)據(jù)的一個(gè)客戶(hù)端,當(dāng)有多個(gè)應(yīng)用進(jìn)程打開(kāi)同一個(gè)eventn設(shè)備文件的時(shí)候,內(nèi)核為每個(gè)應(yīng)用進(jìn)程提供一個(gè)event_client客戶(hù)端,該event_client為對(duì)應(yīng)進(jìn)程負(fù)責(zé)數(shù)據(jù)處理傳輸。
我們知道在注冊(cè)input_device的時(shí)候會(huì)匹配evdev,然后關(guān)聯(lián)input_device和input_handler,即調(diào)用evdev的connect接口evdev_connect:
static?int?evdev_connect(struct?input_handler?*handler,?struct?input_dev?*dev,
?const?struct?input_device_id?*id)
{
struct?evdev?*evdev;
int?minor;
int?error;
?
for?(minor?=?0;?minor?<?EVDEV_MINORS;?minor++)
if?(!evdev_table[minor])
break;
/*
首先進(jìn)行判斷evdev_table數(shù)組是否還有剩余空間留給新的evdev,evdev_table數(shù)組當(dāng)中的每一個(gè)元素都代表著一個(gè)evdev設(shè)備,系統(tǒng)最多只能存在32個(gè)evdev設(shè)備
*/
if?(minor?==?EVDEV_MINORS)?{
pr_err("no?more?free?evdev?devices\n");
return?-ENFILE;
}
evdev?=?kzalloc(sizeof(struct?evdev),?GFP_KERNEL);
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);
?
dev_set_name(&evdev->dev,?"event%d",?minor);//通過(guò)次設(shè)備號(hào)設(shè)置設(shè)備文件名,可在/dev/input/目錄下查看
evdev->exist?=?true;//設(shè)置evdev存在標(biāo)志
evdev->minor?=?minor;//從evdev_table空閑空間中選取一個(gè)做為該evdev的次設(shè)備號(hào)。
?
evdev->handle.dev?=?input_get_device(dev);
evdev->handle.name?=?dev_name(&evdev->dev);
evdev->handle.handler?=?handler;
evdev->handle.private?=?evdev;//初始化evdev的input_handle結(jié)構(gòu)
?
evdev->dev.devt?=?MKDEV(INPUT_MAJOR,?EVDEV_MINOR_BASE?+?minor);//通過(guò)input_device主設(shè)備號(hào)和evdev次設(shè)備號(hào)組合該設(shè)備的設(shè)備號(hào)
evdev->dev.class?=?&input_class;
evdev->dev.parent?=?&dev->dev;//將evdev設(shè)備父設(shè)備設(shè)置為input_device
evdev->dev.release?=?evdev_free;
device_initialize(&evdev->dev);
?
error?=?input_register_handle(&evdev->handle);
if?(error)
goto?err_free_evdev;
?
error?=?evdev_install_chrdev(evdev);
/*
static?int?evdev_install_chrdev(struct?evdev?*evdev)
{
evdev_table[evdev->minor]?=?evdev;//將該evdev保存在evdev_table數(shù)組中,minor作為數(shù)組下標(biāo),因此,evdev_table的第minor個(gè)元素被占用了。
return?0;
}
*/
if?(error)
goto?err_unregister_handle;
?
error?=?device_add(&evdev->dev);//向系統(tǒng)添加一個(gè)設(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;
}
因此,當(dāng)input_device和input_handler關(guān)聯(lián)起來(lái)之后,便會(huì)在/dev/input/目錄下生成一個(gè)evdev設(shè)備文件eventn(n=0,1,2,3……)。
而當(dāng)上層應(yīng)用需要獲取input_device數(shù)據(jù)的時(shí)候就會(huì)打開(kāi)eventn設(shè)備文件,就會(huì)調(diào)用static?int?evdev_open(struct?inode?*inode,?struct?file?*file)
{
struct?evdev?*evdev;
struct?evdev_client?*client;
int?i?=?iminor(inode)?-?EVDEV_MINOR_BASE;//獲取該設(shè)備文件的次設(shè)備號(hào)
unsigned?int?bufsize;
int?error;
?
if?(i?>=?EVDEV_MINORS)
return?-ENODEV;
error?=?mutex_lock_interruptible(&evdev_table_mutex);
if?(error)
return?error;
evdev?=?evdev_table[i];//通過(guò)次設(shè)備號(hào)取得其evdev結(jié)構(gòu)
if?(evdev)
get_device(&evdev->dev);
mutex_unlock(&evdev_table_mutex);
if?(!evdev)
return?-ENODEV;
bufsize?=?evdev_compute_buffer_size(evdev->handle.dev);
client?=?kzalloc(sizeof(struct?evdev_client)?+
bufsize?*?sizeof(struct?input_event),
?GFP_KERNEL);//分配一個(gè)evdev_client客戶(hù)端
if?(!client)?{
error?=?-ENOMEM;
goto?err_put_evdev;
}
client->bufsize?=?bufsize;
spin_lock_init(&client->buffer_lock);
snprintf(client->name,?sizeof(client->name),?"%s-%d",
dev_name(&evdev->dev),?task_tgid_vnr(current));
wake_lock_init(&client->wake_lock,?WAKE_LOCK_SUSPEND,?client->name);
client->evdev?=?evdev;//初始化evdev_client相關(guān)結(jié)構(gòu)
evdev_attach_client(evdev,?client);//關(guān)聯(lián)event_client和evdev,將event_client保存在evdev的client_list鏈表上。
error?=?evdev_open_device(evdev);//調(diào)用input_device?open接口來(lái)做一些初始化工作。
if?(error)
goto?err_free_client;
file->private_data?=?client;//將evdev_client保存在file的私有域。
nonseekable_open(inode,?file);
return?0;
?err_free_client:
evdev_detach_client(evdev,?client);
wake_lock_destroy(&client->wake_lock);
kfree(client);
?err_put_evdev:
put_device(&evdev->dev);
return?error;
}
以上準(zhǔn)備工作做好了,再回到前面的input_device的數(shù)據(jù)被傳遞到input_handler,input_handler調(diào)用其event接口對(duì)數(shù)據(jù)處理,我們來(lái)看看evdev的event接口evdev_event:
static?void?evdev_event(struct?input_handle?*handle,
unsigned?int?type,?unsigned?int?code,?int?value)
{
struct?evdev?*evdev?=?handle->private;//通過(guò)handle獲取該input_device對(duì)應(yīng)的evdev
struct?evdev_client?*client;
struct?input_event?event;
struct?timespec?ts;
?
ktime_get_ts(&ts);
event.time.tv_sec?=?ts.tv_sec;
event.time.tv_usec?=?ts.tv_nsec?/?NSEC_PER_USEC;
event.type?=?type;
event.code?=?code;
event.value?=?value;
/*
Evdev用一個(gè)input_event結(jié)構(gòu)將傳遞過(guò)來(lái)的事件類(lèi)型type,事件編碼code,事件值value以及該事件產(chǎn)生的時(shí)間保存起來(lái)
*/
rcu_read_lock();
?
client?=?rcu_dereference(evdev->grab);
if?(client)
evdev_pass_event(client,?&event);
else{
list_for_each_entry_rcu(client,?&evdev->client_list,?node)
/*
遍歷evdev的client_list鏈表,將事件event傳遞給每一個(gè)evdev客戶(hù)端去處理,這樣所有打開(kāi)該eventn設(shè)備文件的進(jìn)程都將得到響應(yīng)。
*/
evdev_pass_event(client,?&event);
}
?
rcu_read_unlock();
?
if?(type?==?EV_SYN?&&?code?==?SYN_REPORT)
/*
接受到同步信號(hào)之后也就是input_sync(ts->input_dev)調(diào)用之后,系統(tǒng)喚醒等待隊(duì)列,所有阻塞在該等待隊(duì)列的進(jìn)程得到響應(yīng),告訴它們現(xiàn)在有數(shù)據(jù)可以讀取了。
進(jìn)程通過(guò)poll系統(tǒng)調(diào)用,等待數(shù)據(jù),直到input_device將數(shù)據(jù)傳遞過(guò)來(lái)且通過(guò)上面的evdev_pass_event將數(shù)據(jù)傳遞到第一個(gè)evdev客戶(hù)端處理完之后:
static?unsigned?int?evdev_poll(struct?file?*file,?poll_table?*wait)
{
struct?evdev_client?*client?=?file->private_data;
struct?evdev?*evdev?=?client->evdev;
unsigned?int?mask;
poll_wait(file,?&evdev->wait,?wait);//進(jìn)程阻塞,直到evdev_pass_event處理完之后被喚醒
?
mask?=?evdev->exist???POLLOUT?|?POLLWRNORM?:?POLLHUP?|?POLLERR;
if?(client->packet_head?!=?client->tail)
mask?|=?POLLIN?|?POLLRDNORM;
return?mask;
}
*/
wake_up_interruptible(&evdev->wait);//喚醒阻塞進(jìn)程,進(jìn)程返回后上層應(yīng)用就能意識(shí)到數(shù)據(jù)已經(jīng)準(zhǔn)備好了,可以讀取了。
}
?
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);
?
wake_lock_timeout(&client->wake_lock,?5?*?HZ);
client->buffer[client->head++]?=?*event;//將event保存在evdev_client的buffer當(dāng)中
client->head?&=?client->bufsize?-?1;
?
if?(unlikely(client->head?==?client->tail))?{
client->tail?=?(client->head?-?2)?&?(client->bufsize?-?1);
client->buffer[client->tail].time?=?event->time;
client->buffer[client->tail].type?=?EV_SYN;
client->buffer[client->tail].code?=?SYN_DROPPED;
client->buffer[client->tail].value?=?0;
client->packet_head?=?client->tail;
}
if?(event->type?==?EV_SYN?&&?event->code?==?SYN_REPORT)?{
client->packet_head?=?client->head;
kill_fasync(&client->fasync,?SIGIO,?POLL_IN);//接收到input_device的同步信號(hào)之后evdev_client發(fā)送同步信號(hào)。
}
spin_unlock(&client->buffer_lock);
}
return?retval;
}
下面看看數(shù)據(jù)是怎么被讀取的:
static?ssize_t?evdev_read(struct?file?*file,?char?__user?*buffer,
??size_t?count,?loff_t?*ppos)
{
struct?evdev_client?*client?=?file->private_data;
struct?evdev?*evdev?=?client->evdev;
struct?input_event?event;
int?retval;
if?(count?<?input_event_size())//如果要讀取的數(shù)據(jù)量少于一個(gè)input_event的字長(zhǎng),說(shuō)明讀取參數(shù)有誤
return?-EINVAL;
if?(client->packet_head?==?client->tail?&&?evdev->exist?&&
????(file->f_flags?&?O_NONBLOCK))//如果客戶(hù)端buffer沒(méi)有input_event且evdev有效且以非阻塞方式讀取數(shù)據(jù),剛返回重新讀取錯(cuò)誤
return?-EAGAIN;
?
retval?=?wait_event_interruptible(evdev->wait,
client->packet_head?!=?client->tail?||?!evdev->exist);//客戶(hù)端evdev_client?buffer有數(shù)據(jù)或者evdev無(wú)效,進(jìn)程繼續(xù)執(zhí)行
if?(retval)
return?retval;
if?(!evdev->exist)
return?-ENODEV;
while?(retval?+?input_event_size()?<=?count?&&
???????evdev_fetch_next_event(client,?&event))?{
if?(input_event_to_user(buffer?+?retval,?&event))//每次向用戶(hù)空間copy一個(gè)input_event,直到evdev_client?buffer中所有input_event?copy完畢
return?-EFAULT;
retval?+=?input_event_size();
}
return?retval;
}
根據(jù)對(duì)evdev的分析,我們可知,當(dāng)input_device?report數(shù)據(jù)的時(shí)候,數(shù)據(jù)首先被組織成一個(gè)input_event事件,并記錄該事件的詳細(xì)時(shí)間,然后input_event會(huì)被傳遞給每一個(gè)打開(kāi)該設(shè)備文件eventn的evdev_client客戶(hù)端的buffer中保存,當(dāng)input_device?report?type為EV_SYN且code為SYN_ERPORT數(shù)據(jù)的時(shí)候,說(shuō)明在input_dev該次已全部完成數(shù)據(jù)的report,喚醒阻塞進(jìn)程(應(yīng)用進(jìn)程通過(guò)調(diào)用poll()或者select()系統(tǒng)調(diào)用阻塞在evdev的evdev_poll()當(dāng)中,檢測(cè)是否存在數(shù)據(jù)可以讀取),開(kāi)始在evdev_read()中將各自event_client?的buffer中的input_event?copy到用戶(hù)空間,這樣用戶(hù)空間就能獲取到input_device從硬件讀取然后傳遞給input_handler的數(shù)據(jù)了。詳細(xì)的上層應(yīng)用是怎么讀取input設(shè)備數(shù)據(jù)流程,且聽(tīng)下回分解。
轉(zhuǎn)載于:https://www.cnblogs.com/sky-heaven/p/10783643.html
總結(jié)
以上是生活随笔為你收集整理的Input子系统(二)【转】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Spring Boot + Vue 前后
- 下一篇: 持续集成工具jenkins的部署--Wi