1. 程式人生 > >Linux輸入子系統

Linux輸入子系統

為何需要input子系統

Linux系統支援的許多裝置,都是輸入性的裝置,如按鍵、觸控式螢幕、鍵盤、滑鼠。本質上都是一個個的字元裝置驅動,Linux系統為這些裝置統一管理實現了input子系統,方便驅動和應用的開發。

input子系統軟體架構

linux輸入子系統主要分三層:driver層,core層, event hander層。
Driver層根據core層提供的介面,向上報告發生的按鍵動作。然後core根據驅動的型別,分派這個報告給對應的event hander進行處理。
event hander層把資料變化反應到裝置模型的檔案中(事件緩衝區)。並通知在這些裝置模型檔案上等待的程序。
這裡寫圖片描述

input子系統工作原理

註冊匹配過程

這裡寫圖片描述

device註冊:

int input_register_device(struct input_dev *dev)
{
    ...

    //加到input_dev_list
    list_add_tail(&dev->node, &input_dev_list);

    //從input_handler_list匹配
    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);
    ...
}

handle註冊:

int input_register_handler(struct input_handler *handler)
{
    struct input_dev *dev;
    int error;

    error = mutex_lock_interruptible(&input_mutex);
    if (error)
        return error;

    INIT_LIST_HEAD(&handler->h_list);

    //加入input_handler_list
    list_add_tail(&handler->node, &input_handler_list);

    //依次對input_dev_list的dev做匹配
list_for_each_entry(dev, &input_dev_list, node) input_attach_handler(dev, handler); input_wakeup_procfs_readers(); mutex_unlock(&input_mutex); return 0; }

input_attach_handler就是根據id匹配的過程。

讀取過程

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;
size_t read = 0;
int error;

if (count != 0 && count < input_event_size())
    return -EINVAL;

for (;;) {
    if (!evdev->exist)
        return -ENODEV;

    if (client->packet_head == client->tail &&
        (file->f_flags & O_NONBLOCK))
        return -EAGAIN;

    /*
     * count == 0 is special - no IO is done but we check
     * for error conditions (see above).
     */
    if (count == 0)
        break;

    while (read + input_event_size() <= count &&
           evdev_fetch_next_event(client, &event)) {

        if (input_event_to_user(buffer + read, &event))
            return -EFAULT;

        read += input_event_size();
    }

    if (read)
        break;
    //睡眠等待資料
    if (!(file->f_flags & O_NONBLOCK)) {
        error = wait_event_interruptible(evdev->wait,
                client->packet_head != client->tail ||
                !evdev->exist);
        if (error)
            return error;
    }
}

return read;

}

事件上報

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
    input_event(dev, EV_KEY, code, !!value);
}

static inline void input_sync(struct input_dev *dev)
{
    input_event(dev, EV_SYN, SYN_REPORT, 0);
}

其實都是呼叫到input_event,最後呼叫handler->event

static void evdev_events(struct input_handle *handle,
             const struct input_value *vals, unsigned int count)
{
    struct evdev *evdev = handle->private;
    struct evdev_client *client;
    ktime_t time_mono, time_real;

    time_mono = ktime_get();
    time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());

    rcu_read_lock();

    client = rcu_dereference(evdev->grab);

    if (client)
        evdev_pass_values(client, vals, count, time_mono, time_real);
    else
        list_for_each_entry_rcu(client, &evdev->client_list, node)
            evdev_pass_values(client, vals, count,
                      time_mono, time_real);

    rcu_read_unlock();
}

static void evdev_pass_values(struct evdev_client *client,
            const struct input_value *vals, unsigned int count,
            ktime_t mono, ktime_t real)
{
    struct evdev *evdev = client->evdev;
    const struct input_value *v;
    struct input_event event;
    bool wakeup = false;

    event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
                      mono : real);

    /* Interrupts are disabled, just acquire the lock. */
    spin_lock(&client->buffer_lock);

    for (v = vals; v != vals + count; v++) {
        event.type = v->type;
        event.code = v->code;
        event.value = v->value;
        __pass_event(client, &event);
        if (v->type == EV_SYN && v->code == SYN_REPORT)
            wakeup = true;
    }

    spin_unlock(&client->buffer_lock);

    if (wakeup)
        wake_up_interruptible(&evdev->wait);//喚醒程序
}

如何寫input子系統裝置驅動

分配一個input_dev結構體

ts->input_dev = input_allocate_device();

設定

ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
ts->input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
input_set_abs_params(ts->input_dev, ABS_X, 0, SCREEN_MAX_X, 0, 0);
input_set_abs_params(ts->input_dev, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);
input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, 0, 0);

註冊

ret = input_register_device(ts->input_dev);

硬體相關的程式碼,比如在中斷服務程式裡上報事件

static irqreturn_t goodix_ts_irq_hanbler(int irq, void *dev_id)
{
    struct goodix_ts_data *ts = (struct goodix_ts_data *)dev_id;

    dprintk(DEBUG_INT_INFO,"==========------TS Interrupt-----============\n");
    queue_work(goodix_wq, &ts->work);
    return IRQ_HANDLED;
}

static void goodix_ts_work_func(struct work_struct *work)
{
    for (idx= 0; idx < GTP_MAX_KEY_NUM; idx++){
        input_report_key(ts->input_dev, touch_key_array[idx], key_value & (0x01<<idx));
    }
    //input_report_key(ts->input_dev, BTN_TOUCH, (touch_num || key_value));
    input_sync(ts->input_dev);
}

相關推薦

linux 輸入子系統

gpo sdn font width idt hid -s OS div 1、分層結構     用戶層     事件處理層     輸入核心層     設備驅動層 2、步驟: https://blog.csdn.net/woshidahuaidan2011/art

linux 輸入子系統之電阻式觸控式螢幕驅動

一、輸入子系統情景回憶ING...... 在Linux中,輸入子系統是由輸入子系統裝置驅動層、輸入子系統核心層(Input Core)和輸入子系統事件處理層(Event Handler)組成。其中裝置驅動層提供對硬體各暫存器的讀寫訪問和將底層硬體對使用者輸入訪問的響應轉換為標準的輸入事件,再

Linux 輸入子系統分析 Linux輸入子系統分析(詳解)

為什麼要引入輸入子系統? 在前面我們寫了一些簡單的字元裝置的驅動程式,我們是怎麼樣開啟一個裝置並操作的呢? 一般都是在執行應用程式時,open一個特定的裝置檔案,如:/dev/buttons 1 ..... 2 int main(int argc, char **argv) 3 {

Linux輸入子系統

為何需要input子系統 Linux系統支援的許多裝置,都是輸入性的裝置,如按鍵、觸控式螢幕、鍵盤、滑鼠。本質上都是一個個的字元裝置驅動,Linux系統為這些裝置統一管理實現了input子系統,方便驅動和應用的開發。 input子系統軟體架構 linux輸

Linux輸入子系統:事件的編碼 -- event-codes.txt

輸入系統協議用型別types和編碼codecs來表示輸入裝置的值並用此來通知使用者空間的應用程式。這篇文件對這些型別和編碼進行了說明並且指出什麼時候和如何使用這些型別和編碼。  一個單一的硬體事件可以產生多個輸入事件,每個輸入事件包含一個單一資料項的新的資料值。EV_SYN

linux輸入子系統——按鍵

轉載linux之輸入子系統分析 實現步驟 1、定義、分配一個input-dev結構體 static struct input_dev *buttons_dev; buttons_dev=input_allocate_device(); 2、設定 buttons_dev->n

linux input輸入子系統分析《四》:input子系統整體流程全面分析

總線 返回值 分代 並不是 事件 等等 lag pri 位置 1 input輸入子系統整體流程 本節分析input子系統在內核中的實現,包括輸入子系統(Input Core),事件處理層(Event Handler)和設備驅動層。由於上節代碼講解了設備驅動層的寫法

Linux驅動之輸入子系統簡析

ans 沒有 procfs 通過 sel spa 函數 minor ifdef 輸入子系統由驅動層、輸入子系統核心、事件處理層三部分組成。一個輸入事件,如鼠標移動、鍵盤按下等通過Driver->Inputcore->Event handler->users

Linux輸入子系統分析

在此節之前,我們學的都是簡單的字元驅動,涉及的內容有字元驅動的框架、自動建立裝置節點、linux中斷、poll機制、非同步通知、同步互斥/非阻塞、定時器去抖動。 其中驅動框架如下: 1)寫file_operations結構體的成員函式: .open()、.read()、.write()

輸入子系統------鍵盤按鍵驅動程式 13.Linux鍵盤按鍵驅動 (詳解)

由上一節的輸入子系統的框架分析可知,其分三層:裝置驅動層,核心層,事件驅動層 我們在為某種裝置的編寫驅動層,只需要關心裝置驅動層,即如何驅動裝置並獲得硬體資料(如按下的按鍵資料),然後呼叫核心層提供的介面,核心層就會自動把資料提交給事件處理層。在輸入子系統中,事件驅動是標準的,適用於所有輸入類的。

linux驅動由淺入深系列:輸入子系統之二(編寫一個gpio_key驅動)

本系列導航: 在上一篇文章中我們大致瞭解了linux input subsystem的功能及應用層的使用,本文我們一起來看一看驅動程式碼的編寫。接下來一篇,計劃寫一下應用層如何模擬按鍵訊息,產生與按下實際按鍵相同的效果。 在“linux驅動由淺入深系列:驅動程式的基

linux驅動由淺入深系列:輸入子系統之三(應用層模擬input_event)

本系列導航: 在上一篇文章中編寫了gpio_key的驅動,可以看到每次gpio_key按下會上報event到/dev/input /event7節點。其實在應用層是可以完全模擬出這個按鍵過程的,原理是向EV_KEY型別的eventX訊息節點write event將

關於linux input device輸入子系統架構及android中的框架

con 窗口 color manage rop windows pub att 從數據 關於linux input device輸入子系統架構及android中的框架 應用app和windowmanagerservice的input event通信方式 在Native層的

Linux input子系統編程、分析與模板

linux輸入設備都有共性:中斷驅動+字符IO,基於分層的思想,Linux內核將這些設備的公有的部分提取出來,基於cdev提供接口,設計了輸入子系統,所有使用輸入子系統構建的設備都使用主設備號13,同時輸入子系統也支持自動創建設備文件,這些文件采用阻塞的IO讀寫方式,被創建在"/dev/input/"下。如下

Linux鄰居子系統的細節之confirm-OpenVPN server模式的MAC地址學習

虛擬 關於 剖析 定時 sdn -m mac ets ntop 在《Linux實現的ARP緩存老化時間原理解析》一文中。我剖析了Linux協議棧IPv4的鄰居子系統的轉化,再次貼出那個狀態機轉化圖,可是這個圖更具體了些,因為它有一個外部輸入,那就是confirm:

Linux時間子系統專題匯總

sub hone http 分辨 .html blog details sleep 維護 DroidPhone關於Linux時間子系統專題: http://blog.csdn.net/DroidPhone/article/category/1263459 Linux時間子系

Linux時間子系統之八:動態時鐘框架(CONFIG_NO_HZ、tickless)

sleep file rup linux時間 load 曾經 大致 獲取 conf 在前面章節的討論中,我們一直基於一個假設:Linux中的時鐘事件都是由一個周期時鐘提供,不管系統中的clock_event_device是工作於周期觸發模式,還是工作於單觸發模式,也不管定時

Linux時間子系統之四:定時器的引擎:clock_event_device

到來 開始 register 工作模式 統一 10個 net 說過 序列 早期的內核版本中,進程的調度基於一個稱之為tick的時鐘滴答,通常使用時鐘中斷來定時地產生tick信號,每次tick定時中斷都會進行進程的統計和調度,並對tick進行計數,記錄在一個jiffies變量

Linux時間子系統之六:高精度定時器(HRTIMER)的原理和實現

3.4 size 屬於 running return repr 而是 復雜度 ctu 上一篇文章,我介紹了傳統的低分辨率定時器的實現原理。而隨著內核的不斷演進,大牛們已經對這種低分辨率定時器的精度不再滿足,而且,硬件也在不斷地發展,系統中的定時器硬件的精度也越來越高,這也給

Linux時間子系統之七:定時器的應用--msleep(),hrtimer_nanosleep()

get 關系 警告 mov signed num wakeup sch switch 我們已經在前面幾章介紹了低分辨率定時器和高精度定時器的實現原理,內核為了方便其它子系統,在時間子系統中提供了一些用於延時或調度的API,例如msleep,hrtimer_nanosleep