日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > windows >内容正文

windows

Input子系统(二)【转】

發(fā)布時(shí)間:2024/9/21 windows 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Input子系统(二)【转】 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(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)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。