天天看點

輸入子系統(一)

本次所涉及到的檔案:

include/linux/input.h

drivers/input/input.c

arch/arm/mach-rk30/board-rk30-sdk.c

drivers/input/keyboard/rk29_keys.c

Boot Log:

[ 1.853285] input: rk29-keypad as /devices/platform/rk29-keypad/input/input0

match name → “rk29-keypad”

1、相關配置:

Device Drivers  --->
    Input device support  --->
        [*]   Keyboards  --->
            <*>   rk29 keyboard

按鍵驅動對應的源碼位置為:   drivers/input/keyboard/rk29_keys.c
按鍵相關的參數從board檔案中傳遞給驅動程式:
           

2、相關按鍵屬性定義:

arch/arm/mach-rk30/board-rk30-sdk.c

static struct rk29_keys_button key_button[] = {
    /***{
                .desc   = "vol-",
                .code   = KEY_VOLUMEDOWN,
                .gpio   = RK30_PIN4_PC5,
                .active_low = PRESS_LEV_LOW,
        },
        {
                .desc   = "play",
                .code   = KEY_POWER,
                .gpio   = RK30_PIN6_PA2,
                .active_low = PRESS_LEV_LOW,
                //.code_long_press = EV_ENCALL,
                .wakeup = 1,
        },
        {
                .desc   = "vol+",
                .code   = KEY_VOLUMEUP,
                .adc_value      = 1,
                .gpio = INVALID_GPIO,
                .active_low = PRESS_LEV_LOW,
        },*/
#if defined(CONFIG_PROJECT_D200)
        {
                .desc   = "play",
                .code   =   KEY_POWER,
                .gpio   =  RK30_PIN6_PA0,///RK30_PIN2_PA1,///
                .active_low = PRESS_LEV_LOW,
                //.code_long_press = EV_ENCALL,
                .wakeup = ,
        },
#endif
#ifndef RK3000_SDK
/***    {
                .desc   = "menu",
                .code   = EV_MENU,
                .adc_value      = ,
                .gpio = INVALID_GPIO,
                .active_low = PRESS_LEV_LOW,
        },
        {
                .desc   = "home",
                .code   = KEY_HOME,
                .adc_value      = ,
                .gpio = INVALID_GPIO,
                .active_low = PRESS_LEV_LOW,
        },*/
        {
                .desc   = "esc",
                .code   =  KEY_HOME,///KEY_BACK,
                .adc_value      =  ,///334,
                .gpio = RK30_PIN2_PC7,///INVALID_GPIO,
                .active_low = PRESS_LEV_LOW,
        },
/***    {
                .desc   = "camera",
                .code   = KEY_CAMERA,
                .adc_value      = 743,
                .gpio = INVALID_GPIO,
                .active_low = PRESS_LEV_LOW,
        },*/
#else
/***    {
                .desc   = "menu",
                .code   = EV_MENU,
                .adc_value      = ,
                .gpio = INVALID_GPIO,
                .active_low = PRESS_LEV_LOW,
        },
        {
                .desc   = "home",
                .code   = KEY_HOME,
                .adc_value      = ,
                .gpio = INVALID_GPIO,
                .active_low = PRESS_LEV_LOW,
        },*/
        {
                .desc   = "home",
                .code   = KEY_BACK,
                .adc_value      = 386,
                .gpio =  RK30_PIN2_PC7,///INVALID_GPIO,
                .active_low = PRESS_LEV_LOW,
        },
/***    {
                .desc   = "camera",
                .code   = KEY_CAMERA,
                .adc_value      = 827,
                .gpio = INVALID_GPIO,
                .active_low = PRESS_LEV_LOW,
        },*/
#endif
};
           

3、相關按鍵操作:

drivers/input/keyboard/rk29_keys.c

input = input_allocate_device();

配置設定input_dev結構體,并調用input_register_device(input)對其進行注冊,

3.1、input_allocate_device( ):

struct input_dev *input_allocate_device(void)
{
    struct input_dev *dev;

    dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
    if (dev) {
        dev->dev.type = &input_dev_type;    /* 初始化裝置結構類型 */
        dev->dev.class = &input_class;      /* 初始化為輸入裝置類 */
        device_initialize(&dev->dev);       /* 初始化device結構體 */
        mutex_init(&dev->mutex);            /* 初始化互斥鎖 */
        spin_lock_init(&dev->event_lock);   /* 初始化事件自旋鎖 */
        INIT_LIST_HEAD(&dev->h_list);       /* 初始化連結清單 */
        INIT_LIST_HEAD(&dev->node);

        __module_get(THIS_MODULE);          /* 子產品引用 */
    }

    return dev;
}
           

注:input_dev類型的指針,該結構體屬于輸入裝置結構體,包含輸入裝置的一些資訊,如裝置支援的按鍵碼、裝置名稱、裝置支援的事件等。

關于input_dev結構體更詳盡的解釋參見:http://bbs.21ic.com/icview-309385-1-1.html

3.2、注冊函數input_register_device( )

int input_register_device(struct input_dev *dev)
{
    static atomic_t input_no = ATOMIC_INIT();
    struct input_handler *handler;
    const char *path;
    int error;

    /* Every input device generates EV_SYN/SYN_REPORT events. */
    __set_bit(EV_SYN, dev->evbit);
    /*
        #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    /* LED燈裝置 */
        #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    /* 事件類型最大個數和提供位掩碼支援 */
        #define EV_CNT          (EV_MAX+1)
        詳細輸入子系統的事件編碼資訊參見http://blog.csdn.net/droidphone/article/details/8432055
     */

    /* KEY_RESERVED is not supposed to be transmitted to userspace. */
    __clear_bit(KEY_RESERVED, dev->keybit);

    /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
    input_cleanse_bitmasks(dev);

    if (!dev->hint_events_per_packet)
        dev->hint_events_per_packet =
                input_estimate_events_per_packet(dev);

    /* 初始化timer定時器,為處理重複擊鍵而定義 */
    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] = ;
        dev->rep[REP_PERIOD] = ;
    }/* 自動處理重複按鍵定義 */

    if (!dev->getkeycode)
        dev->getkeycode = input_default_getkeycode;
    /* 檢查是否定義,如未定義則使用預設值 */
    if (!dev->setkeycode)
        dev->setkeycode = input_default_setkeycode;

    dev_set_name(&dev->dev, "input%ld",
             (unsigned long) atomic_inc_return(&input_no) - );
    /* 将input_dev包含的device注冊到裝置模型中 */
    error = device_add(&dev->dev);
    if (error)
        return error;
    /* 列印裝置路徑,輸出調試資訊 */
    path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
    pr_info("%s as %s\n",
        dev->name ? dev->name : "Unspecified device",
        path ? path : "N/A");
    kfree(path);

    error = mutex_lock_interruptible(&input_mutex);
    if (error) {
        device_del(&dev->dev);
        return error;
    }
    /* 将input_dev加入到input_dev_list連結清單中 */
    list_add_tail(&dev->node, &input_dev_list);

    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);

    input_wakeup_procfs_readers();

    mutex_unlock(&input_mutex);

    return ;
}
           

3.3、input_attach_handler( )

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    const struct input_device_id *id;           /* 輸入裝置指針 */
    int error;

    id = input_match_device(handler, dev);
    if (!id)
        return -ENODEV;

    error = handler->connect(handler, dev, id); /* 連接配接裝置和處理函數 */
    if (error && error != -ENODEV)
        pr_err("failed to attach handler %s to device %s, error: %d\n",
               handler->name, kobject_name(&dev->dev.kobj), error);

    return error;
}
           

3.4、input_match_device( )

static const struct input_device_id *input_match_device(struct input_handler *handler,
                            struct input_dev *dev)
{
    const struct input_device_id *id;
    int i;

    for (id = handler->id_table; id->flags || id->driver_info; id++) {

        if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
            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比對項 */
        MATCH_BIT(evbit,  EV_MAX);
        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);

        if (!handler->match || handler->match(handler, dev))
            return id;
    }

    return NULL;
}
           

注:注冊input_dev的過程就是為input_device設定預設值,并将其挂到input_dev_list。與挂載在input_handler_list中的handler相比對。如比對成功,則調用handler的connect函數。

關于此輸入子系統部分,發現網上有更為詳盡的解釋和說明,故貼出位址以備參考:

http://blog.csdn.net/tianxiawuzhei/article/details/7607068

http://blog.csdn.net/weiqing1981127/article/details/8138389

繼續閱讀