5.5 用户输入BSP的实现

不同平台的键盘等输入设备,通常是处理器SOC之外的硬件。对于Android输入系统中的BSP支持部分的驱动程序和配置文件,和处理器的关系不大,而和其外部连接的硬件关系较大。

↘5.5.1 模拟器中的实现

1.驱动程序

goldfish虚拟处理器键盘输入部分的驱动程序是Event驱动程序,代码路径为:drivers/input/keyboard/goldfish_events.c。这个驱动程序是一个标准的Event驱动程序,在用户空间的设备节点为/dev/input/event0。

goldfish_events.c中实现其核心的内容:

    static irqreturn_t events_interrupt(int irq, void *dev_id) {
        struct event_dev *edev = dev_id;
        unsigned type, code, value;
        type = __raw_readl(edev->addr + REG_READ);         // 类型
        code = __raw_readl(edev->addr + REG_READ);         // 码
        value = __raw_readl(edev->addr + REG_READ);        // 数值
        input_event(edev->input, type, code, value);
        return IRQ_HANDLED;
    }

events_interrupt实现的是按键事件的中断处理函数,当中断发生后,读取虚拟寄存器的内容,将信息上报。实际上,虚拟寄存器中的内容由模拟器根据主机环境键盘按下的情况得到。

2.用户空间的配置文件

在模拟器环境中,使用了默认的所有的kl和kcm文件,由于模拟器环境支持全键盘,因此基本上包含了大部分的功能。在模拟器环境中,实际上按键的扫描码对应的是桌面电脑的键盘(效果和鼠标点击模拟器的控制面板类似),键盘的某些按键按下后,转换为驱动程序中的扫描码,然后再由上层的用户空间处理,这个过程和实际系统中是类似的。显然,通过更改默认的kl文件,也可以更改实际按键的映射关系。

tuttle2.kl和tuttle2.kcm是额外的按键布局和按键字符影射文件。

↘5.5.2 Nexus One系统中的实现

1.触摸屏、轨迹球和按键驱动程序

Nexus One系统使用MSM的Mahimahi平台的用户输入设备包括以下几个和用户输入相关的Event设备。

·/dev/input/event4:几个硬件的按键。

·/dev/input/event2:触摸屏设备。

·/dev/input/event5:轨迹球设备。

触摸屏的驱动程序在drivers/input/touchscreen目录中的synaptics_i2c_rmi.c,这是一个I2C触摸屏的驱动程序。

按键和轨迹球的功能,具体的驱动程序在arch/arm/mach-msm/目录board-mahimahi-keypad.c文件中实现。

board-mahimahi-keypad.c中的全局定义如下所示:

    static struct gpio_event_info *mahimahi_input_info[] = {
      &mahimahi_keypad_matrix_info.info,                    // 键盘矩阵
      &mahimahi_keypad_key_info.info,                       // 键盘信息
      &jogball_x_axis.info.info,                            // 轨迹球X方向信息
      &jogball_y_axis.info.info,                            // 轨迹球Y方向信息
    };
    static struct gpio_event_platform_data mahimahi_input_data = {
      .names = {"mahimahi-keypad", "mahimahi-nav", NULL, }, // 按键轨迹球的设备名称
      .info = mahimahi_input_info,
      .info_count = ARRAY_SIZE(mahimahi_input_info),
      .power = jogball_power,
    };
    static struct platform_device mahimahi_input_device = { // GPIO的平台设备定义
      .name = GPIO_EVENT_DEV_NAME,
      .id = 0,
      .dev = { .platform_data = &mahimahi_input_data,  },
    };

按键和轨迹球是通过GPIO系统来实现的,因此定义了gpio_event_info类型的数组。"mahimahi-keypad"和"mahimahi-nav"分别是两个设备的名称。gpio_event_info类型的指针数组mahimahi_input_info中包含了mahimahi_keypad_matrix_info.info,mahimahi_keypad_key_info.info、jogball_x_axis.info.info和jogball_y_axis.info.info。

按键驱动是一个利用GPIO矩阵的驱动,由gpio_event_matrix_info矩阵定义,定义还需要包含按键的GPIO矩阵和input设备的信息,内容如下所示:

    static unsigned int mahimahi_col_gpios[] = { 33, 32, 3l };
    static unsigned int mahimahi_row_gpios[] = { 42, 4l, 40 };
    #define KEYMAP_INDEX(col, row)  ((col)*ARRAY_SIZE(mahimahi_row_gpios) + (row))
    #define KEYMAP_SIZE         (ARRAY_SIZE(mahimahi_col_gpios) * \
                    ARRAY_SIZE(mahimahi_row_gpios))
    static const unsigned short mahimahi_keymap[KEYMAP_SIZE] = {           // 按键映射关系
      [KEYMAP_INDEX(0, 0)] = KEY_VOLUMEUP,                                 // KEY_VOLUMEUP == ll5
      [KEYMAP_INDEX(0, l)] = KEY_VOLUMEDOWN,                               // KEY_VOLUMEDOWN == ll4
      [KEYMAP_INDEX(l, l)] = MATRIX_KEY(l, BTN_MOUSE),
    };
    static struct gpio_event_matrix_info mahimahi_keypad_matrix_info = {
      .info.func = gpio_event_matrix_func,                                 // 关键函数实现
      .keymap = mahimahi_keymap,
      .output_gpios = mahimahi_col_gpios,                                  // GPIO的行列实现
      .input_gpios = mahimahi_row_gpios,
      .noutputs = ARRAY_SIZE(mahimahi_col_gpios),
      .ninputs = ARRAY_SIZE(mahimahi_row_gpios),
      .settle_time.tv.nsec = 40 * NSEC_PER_USEC,
      .poll_time.tv.nsec = 20 * NSEC_PER_MSEC,
      .flags = (GPIOKPF_LEVEL_TRIGGERED_IRQ |
            GPIOKPF_REMOVE_PHANTOM_KEYS | GPIOKPF_PRINT_UNMAPPED_KEYS),
    };
    static struct gpio_event_direct_entry mahimahi_keypad_key_map[] = {    // Power按键
      {    .gpio    = MAHIMAHI_GPIO_POWER_KEY,.code    = KEY_POWER, },
    };
    static struct gpio_event_input_info mahimahi_keypad_key_info = {
      .info.func = gpio_event_input_func,                                  // 关键函数实现
      .info.no_suspend = true,
      .flags = 0,
      .type = EV_KEY,
      .keymap = mahimahi_keypad_key_map,
      .keymap_size = ARRAY_SIZE(mahimahi_keypad_key_map)
    };

mahimahi_keypad_key_matrix_info和mahimahi_keypad_info是gpio_event_matrix_info类型的结构体,分别负责两个和一个按键的处理,实际上,MSM的Mahimahi平台硬件上只有三个按键:Power、音量增加和音量减少。音量增加和音量减少的扫描码分别是KEY_VOLUMEUP(115)和KEY_VOLUMEDOWN(114)。

提示:音量控制的两个按键在全键盘的qwerty.kl有所定义,同时符合Linux的Input设备和Android的按键的数值。

轨迹球也是由GPIO实现的,由X方向和Y方向两部分组成,内容如下所示:

    static uint32_t jogball_x_gpios[] = {
      MAHIMAHI_GPIO_BALL_LEFT, MAHIMAHI_GPIO_BALL_RIGHT,   // 左键、右键
    };
    static uint32_t jogball_y_gpios[] = {
      MAHIMAHI_GPIO_BALL_UP, MAHIMAHI_GPIO_BALL_DOWN,      // 上键、下键
    };
    static struct jog_axis_info jogball_x_axis = {         // X轴的内容
      .info = {
    .info.func = gpio_event_axis_func,                     // 关键函数实现
    .count = ARRAY_SIZE(jogball_x_gpios),
    .dev = l, .type = EV_REL, .code = REL_X,               // 相对设备,X轴
    .decoded_size = lU << ARRAY_SIZE(jogball_x_gpios),
    .map = jogball_axis_map,    .gpio = jogball_x_gpios,
    .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION,
      }
    };
    static struct jog_axis_info jogball_y_axis = {         // Y轴的内容
      .info = {
    .info.func = gpio_event_axis_func,                     // 关键函数实现
    .count = ARRAY_SIZE(jogball_y_gpios)
    .dev = l, .type = EV_REL, .code = REL_Y,               // 相对设备,Y轴
    .decoded_size = lU << ARRAY_SIZE(jogball_y_gpios),
    .map = jogball_axis_map,    .gpio = jogball_y_gpios,
    .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION,
      }
    };

这里的轨迹球是用jog_axis_info类型的结构体进行定义的,这种设备的类型(type)是相对设备EV_REL。

2.用户空间的配置文件

Nexus One系统平台增加了h2w_headset.kl和mahimahi-keypad.kl作为按键布局的配置文件,代码的路径为:device/htc/passion-common/。

h2w_headset.kl文件的内容如下所示:

    key l07   ENDCALL          WAKE_DROPPED
    key ll3   MUTE             WAKE
    key ll4   VOLUME_DOWN      WAKE
    key ll5   VOLUME_UP        WAKE
    key l63   MEDIA_NEXT       WAKE
    key l64   MEDIA_PLAY_PAUSE WAKE
    key l65   MEDIA_PREVIOUS   WAKE
    key 226   HEADSETHOOK      WAKE
    key 23l   CALL             WAKE_DROPPED

mahimahi-keypad.kl与默认的qwerty.kl文件类似,属于全键盘的定义,只是个别的键值的内容有所不同。

3.虚拟按键

虚拟按键将触摸屏边缘作为按键的方式,在这种系统中一般触摸屏都比显示屏略大,在触摸屏下方多处显示屏的区域,可以用作虚拟按键。

使用虚拟按键需要将触摸屏的区域影射成按键的坐标,Android的输入系统要使用虚拟按键定义文件。虚拟按键定义文件是/sys/board_properties/目录中的virtualkeys.<设备>文件。这是sys文件系统的文件,由内核中的代码生成,被Android的输入系统使用。

虚拟按键定义文件的格式如下所示:

    0xl:扫描码:X:Y:W:H:0xl: ……

例如,在Nexus One的系统上查看虚拟按键的配置文件如下所示:

    # cat /sys/board_properties/virtualkeys.synaptics-rmi-touchscreen
    0x0l:l58:55:835:90:55:0x0l:l39:l72:835:l25:55:0x0l:l02:298:835:ll5:55:0x0l:2l7:4l2
  :835:95:55

这里定义了4个按键,每个按键的内容如下所示。

·0x01:158:55:835:90:55:最左(90×55),Back按键,扫描码158。

·0x01:139:172:835:125:55:中左(125×55),Menu按键,扫描码139。

·0x01:102:298:835:115:55:中右(115×55),Home按键,扫描码102。

·0x01:217:412:835:95:55:最右(95×55),Search按键,扫描码217。

Msm内核生成sys文件系统的文件是arch/arm/mach-msm/board-mahimahi.c。它针对synaptics-rmi-touchscreen触摸屏的区域生成的虚拟按键文件。

↘5.5.3 Nexus S系统中的实现

1.驱动程序

Nexus One系统使用三星的herring平台的用户输入设备包括以下几个和用户输入相关的Event设备。

·/dev/input/event0:mxt224_ts_input触摸屏设备。

·/dev/input/event2:herring-keypad键盘设备。

·/dev/input/event5:cypress-touchkey虚拟按键,I2C总线的地址为10-0020。

herring-keypad键盘驱动的定义在板级移植文件arch/arm/mach-s5pv210/mach-herring.c中,内容如下所示:

    static struct gpio_event_platform_data herring_input_data = {
      .names = {"herring-keypad", NULL, },
      .info = herring_input_info,
      .info_count = ARRAY_SIZE(herring_input_info),
    };

mxt224_ts_input触摸屏设备的代码路径为:drivers/input/touchscreen/mxt224.c。mxt224.c通过控制I2C总线来实现触摸屏Input设备,也实现了一个名为"Atmel MXT224"的I2C驱动。

cypress-touchkey是虚拟按键的设备,驱动实现的路径为drivers/input/keyboard/目录中的cypress-touchkey.c和cypress-touchkey-firmware.c文件,cypress-touchkey.c中通过读取I2C总线获得按键的信息,并且调用input_report_key()等函数向上层汇报,其中还实现了一个I2C的驱动模块,cypress-touchkey-firmware.c则通过GPIO进行控制。设备相关的内容则在板级文件arch/arm/mach-s5pv210/mach-herring.c中定义,即touchkey_platform_data结构类型的数据。

2.用户空间的配置文件

除了默认的文件外,Nexus S系统增加了cypress-touchkey.kl、herring-keypad.kl、s3c-keypad.kl和sec_jack.kl作为按键布局的配置文件,路径为:device/samsung/crespo。

herring-keypad.kl是平台的键盘的配置文件,内容如下所示:

    key ll5    VOLUME_UP       WAKE
    key ll4    VOLUME_DOWN     WAKE
    key ll6    POWER           WAKE
    key l39    MENU            VIRTUAL
    key l02    HOME            VIRTUAL
    key l58    BACK            VIRTUAL
    key 2l7    SEARCH          VIRTUAL

Nexus S仅有电源(用于开机关机)、音量增加和音量减少3个硬件按键。cypress-touchkey.kl是按键触摸区域的专用文件,内容如下所示:

    key l39    MENU          VIRTUAL
    key l02    HOME          VIRTUAL
    key l58    BACK          VIRTUAL
    key 2l7    SEARCH        VIRTUAL

其中定义的几个键值就是菜单、桌面、回退和搜索Nexus S显示屏下方的按键。

↘5.5.4 Galaxy Nexus系统中的实现

Galaxy Nexus系统的输入设备包括一个触摸屏,仅有的3个硬件按键,还可以支持耳机的线控。它们与处理器的连接方式各自不同。

1.驱动程序

Galaxy Nexus的几个输入设备在内核中都表现成Event输入设备,如下所示。

·/dev/input/event1:名称为"Melfas MMSxxx Touchscreen"的触摸屏。

·/dev/input/event2:名称为"tuna-gpio-keypad"的GPIO连接的键盘。

·/dev/input/event5:名称为"Tuna Headset Jack"的耳机线控输入设备。

触摸屏是连接在OMAP处理器的第3个I2C总线上的设备,地址为3-0048。其代码路径为:drivers/input/touchscreen/mms_ts.c,在sys文件系统看到这个设备的信息是:

    shell@android:/sys/devices/platform/omap/omap_i2c.3/i2c-3/3-0048 $ cat name
    mms_ts

GPIO键盘的内容在Tuna板级支持的arch/arm/mach-omap2/board-tuna-input.c文件中定义,包括以下内容:

    static struct gpio_event_direct_entry tuna_gpio_keypad_keys_map_high[] = {
      {  .code = KEY_POWER, .gpio    = 3,},                   // Power按键
    };
    static struct gpio_event_input_info tuna_gpio_keypad_keys_info_high = {
      .info.func = gpio_event_input_func,   .info.no_suspend = true,
      .type = EV_KEY,                                         // 为Event设备中的按键类型
      .keymap = tuna_gpio_keypad_keys_map_high,
      .keymap_size = ARRAY_SIZE(tuna_gpio_keypad_keys_map_high),
      .flags = GPIOEDF_ACTIVE_HIGH,
      .debounce_time.tv64 = 2 * NSEC_PER_MSEC,
    };
    static struct gpio_event_direct_entry tuna_gpio_keypad_keys_map_low[] = {
      {    .code    = KEY_VOLUMEDOWN,     .gpio     = 8,},     // 两个音量按键
      {    .code    = KEY_VOLUMEUP,       .gpio     = 30,},
    };
    static struct gpio_event_input_info tuna_gpio_keypad_keys_info_low = {
      .info.func = gpio_event_input_func,   .info.no_suspend = true,
      .type = EV_KEY,                                          // 为Event设备中的按键类型
      .keymap = tuna_gpio_keypad_keys_map_low,
      .keymap_size = ARRAY_SIZE(tuna_gpio_keypad_keys_map_low),
      .debounce_time.tv64 = 2 * NSEC_PER_MSEC,
    };
    static struct gpio_event_info *tuna_gpio_keypad_info[] = { // GPIO的事件的定义
      &tuna_gpio_keypad_keys_info_high.info, &tuna_gpio_keypad_keys_info_low.info,
    };
    static struct gpio_event_platform_data tuna_gpio_keypad_data = {
      .name = "tuna-gpio-keypad",                               // 驱动的名称
      .info = tuna_gpio_keypad_info,
      .info_count = ARRAY_SIZE(tuna_gpio_keypad_info)
    };

其中包括了音量增加键、音量减少按键和电源按键(Power)的定义,它们都是连接在GPIO之上的。在驱动程序中,使用gpio_event_input_info结构直接将其实现成Event设备。

2.用户空间的配置文件

Galaxy Nexus系统平台增加了tuna-gpio-keypad.kl为Tuna平台的键盘布局文件,文件码路径为device/samsung/tuna/tuna-gpio-keypad.kl,内容如下所示:

    $ cat tuna-gpio-keypad.kl
    key ll4   VOLUME_DOWN      WAKE
    key ll5   VOLUME_UP        WAKE
    key ll6   POWER            WAKE

这里的3个按键均有WAKE,表示Galaxy Nexus的3个硬件的按键均可以唤醒。文件sec_jack.kl用于耳机上面的线控按键,内容如下所示:

此处的3个按键分别定义为KeycodeLabels.h中的87("MEDIA_NEXT")、88("MEDIA_PREVIOUS")和79("HEADSETHOOK")3个按键码。

另一个键盘布局文件sii9234_rcp.kl用于HDMI控制芯片MHD SiI9234。其中包括小键盘(NUMPAD)和多媒体控制的各个按键。

    key l63   MEDIA_NEXT       WAKE
    key l65   MEDIA_PREVIOUS   WAKE
    key 226   HEADSETHOOK      WAKE