Android系统添加key和keypad

平台:MTK
一.添加一个按键
1.在DCT tool keypad list 文件增加新按键的选项
alps\mediatek\source\dct\Keypad_YuSu.cmp中添加新键,如SMS快捷键
KEY_SYM
KEY_SMS
KEY_0

2.打开DCT tool 在keypad矩阵中在相应定义的按键位中添加新按键,如SMS,然后Save

3.修改linux 键盘码文件input.h
由于preloader\uboot\kernel\factory 等情况分开使用,相应的文件路径下的input.h都应该修改为新按键增加键码值
kernel\include\linux\input.h
bionic\libc\kernel\common\linux\input.h
external\kernel-headers\original\linux\input.h
external\qemu\linux_keycodes.h
mediatek\plaform\mt6575\preloader\src\drivers\inc\linux\input.h
Mediatek\plaform\mt6575\uboot\inc\linux\input.h
如KEY_SMS
    #define KEY_SMS 252

4.增加keypad layout文件键盘映射,linux和android key映射
mediatek\config\<project name>\mt6575-kpd.kl
如:
key 252 SMS 
其中252 是linux键码,SMS是android识别key值 如果是需要唤醒系统,还需要增加WAKE
如果新按键是全键盘的一些生僻字符,修改: mediatek\config\<project name>\mt6575-kpd.kcm
注意:
    如果是accdet也就耳机按键, keypad layout文件键盘映射,linux和android key映射在ACCDET.kl,Generic.kcm',电话监听的就是HEADSETHOOK

5.修改Java识别keycode
framework/base/include/ui/KeyCodelabels.h
KEYCODES数据结构后面增加
{"SMS",220}
framework/base/native/include/android/KeyCodes.h
在按键定义项增加AKEYCODE_SMS = 220;

6.修改Java 键盘事件
framework/base/core/java/android/view/keyevent.java
/**
*@hide
*/
public static final int KEYCODE_SMS =220;
最后的按键为新增的
private static final int LAST_KEYCODE ==KEYCODE_SMS;
以上/**/注释的code是android 非开放API或变量定义的时候,需要添加Java Doc的识别,否则要运行make update-api才能build通过.
如果是系统按键,修改framework/base/libs/ui/input.cpp
isSystemKey()增加case AKEYCODE_SMS:

7.修改XML文件描述符framework/base/core/res/res/values/attr.xml
<enum name="KEYCODE_SMS" value="220" />

8.增加测试验证log在android
framework\base\policy\src\com\android\internal\polidy\impl\phoneWindowManager.java
在interceptKeyBeforeDispatching()增加
if(keycode== KeyEvent.KEYCODE_SMS){
log.d(TAG, "interceptKeyTi KEYCODE_SMS keyCode="+ keyCode + " down=" + down + "
repeatCount=" + repeatCount + “ keyguardOn=” + keyguardOn + “ mHomePressed=” +
mHomePressed + “ cancled=” + canceled)

9.增加按键全局功能响应:
framework\base\policy\src\com\android\internal\polidy\impl\phoneWindow.java
分别在onKeyDown()、onKeyUp()增加对应代码
if(keycode== KeyEvent.KEYCODE_SMS){}
 
 
二.矩阵键盘原理
    不同的MTK平台内部提供的按键数目各有不同,但是扫描原理大致一样,这里以MTK6735为例来讲解记录

在初始化或是没有按键的情况下,ROW设置为输出,COL设置为输入。

 BB芯片的内部设有一个按键中断,在没有任何按键的情况下,所有CLO线为高电平,ROW线为低电平,一旦有按键按下,比如key10(ROW1,COL2)有按下,由于COL2线与ROW1线直接相连,COL2线即被拉低,只要COL线有被拉低的,内部按键中断便会响应,内部按键中断产生后,BB就知道有按键产生,这时就进行按键扫描,判断出事哪个按键按下,然后进行相应的处理。

    中断产生后,BB先发出消息将所有COL线置高,所有ROW线置低,扫描COL线,比如key10(ROW1,COL2)有按下,会扫描到COL2被拉低;再将将所有ROW线置高,所有COL线置低,扫描ROW线,比如key10(ROW1,COL2)有按下,会扫描到ROW1被拉低;那么COL2ROW1组合起来就知道哪个按键按下了。

   但是实际上按键中断产生后,扫描过程已经在BB内部迅速完成,然后把相应的按键位置用寄存器保存起来。每个寄存器16位,每一位代表一个按键,那么总共53个按键,需要4个寄存器去保存。

  寄存器的每个位对应一个按键,默认为1,当有按键按下,那么相应位置0。然后系统去读这四个寄存器,就能知道是哪一个按键按下。

 
 
三.添加矩阵键盘
    这里修改的6735的3*3的矩阵键盘
    1.修改DWS
  
    注意:
        因为原理端口81和82会输出方波,所以要把除了M0和M1两种模式之外的模式取消,而且都要配置in,out模式,因为会扫描两次,就会同时作为输入输出。
 
    
   修改KEYPAD,填入矩阵,这样一般就可以了
 
修改按键键值:
   以dws为主,kernel-3.18/arch/arm64/boot/dts/cust_kpd.dtsi如果在dts中include,覆盖dws里面的
 
四.keypad驱动分析
 Kpd.c (kernel-3.10\drivers\misc\mediatek\keypad)
重要结构体
static struct platform_driver kpd_pdrv = {
.probe = kpd_pdrv_probe,
.remove = kpd_pdrv_remove,
#ifndef USE_EARLY_SUSPEND
.suspend = kpd_pdrv_suspend,
.resume = kpd_pdrv_resume,
#endif
.driver = {
.name = KPD_NAME,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = kpd_of_match,
#endif
},
};
 
kpd_mod_init
    platform_driver_register(&kpd_pdrv); //注册
 
  
进入probe
static struct file_operations kpd_dev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = kpd_dev_ioctl,
.open = kpd_dev_open,
};
 
static struct miscdevice kpd_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = KPD_NAME,
.fops = &kpd_dev_fops,
};
 
kpd_pdrv_probe
    kpd_ldvt_test_init();/* API 2 for kpd LFVT test enviroment settings */
        /* set kpd GPIO to kpd mode */
mtk_kpd_gpio_set();  //单独分析
        temp_reg = readw(KP_SEL); //读取KP_SEL寄存器的值 ,Used to select:1: Single keypad or double keypad 2: Which cols and rows are used when using double keypad

        temp_reg &= ~(0x1); //我们这里single

        /* set kpd enable and sel register */
mt_reg_sync_writew(temp_reg, KP_SEL);
mt_reg_sync_writew(0x1, KP_EN);
        kpd_input_dev = input_allocate_device();
    /* fulfill custom settings */
    kpd_memory_setting
        kpd_init_keymap(kpd_keymap);
            keymap[i] = kpd_keymap[i];  //初始化keymap:kpd_keymap[KPD_NUM_KEYS] = KPD_INIT_KEYMAP();
kpd_init_keymap_state(kpd_keymap_state);
            keymap_state[i] = kpd_keymap_state[i];  //kpd_keymap_state[KPD_NUM_MEMS] = {0xffff, 0xffff, 0xffff, 0xffff, 0x00ff};
    r = input_register_device(kpd_input_dev);
    r = misc_register(&kpd_dev);
    /* register IRQ and EINT */
    kpd_set_debounce(KPD_KEY_DEBOUNCE); //设置防反跳时间
    r = request_irq(kp_irqnr, kpd_irq_handler, IRQF_TRIGGER_NONE, KPD_NAME, NULL); //keypad处理函数
    long_press_reboot_function_setting();/* /API 4 for kpd long press reboot function setting */ 
    hrtimer_init(&aee_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); //初始化高精度定时器,相对模式
    aee_timer.function = aee_timer_func; //处理函数
    hrtimer_init(&aee_timer_5s, CLOCK_MONOTONIC, HRTIMER_MODE_REL); //第二个
    aee_timer_5s.function = aee_timer_5s_func;
    
 
    mtk_kpd_gpio_set();
            mtk_kpd_gpios_get  //得到DWS里面的设置的行和列的gpio和模式
                #ifdef GPIO_KPD_KROW0_PIN
ROW_REG[0] = GPIO_KPD_KROW0_PIN;
GPIO_MODE[0] |= GPIO_KPD_KROW0_PIN_M_KROW;
        #endif
            /* KCOL: GPIO INPUT + PULL ENABLE + PULL UP */ 设置列的模式
    mt_set_gpio_mode(COL_REG[i], ((GPIO_MODE[i] >> 4) & 0x0f));
    mt_set_gpio_dir(COL_REG[i], 0);
    mt_set_gpio_pull_enable(COL_REG[i], 1);
    mt_set_gpio_pull_select(COL_REG[i], 1);
            /* KROW: GPIO output + pull disable + pull down */
    mt_set_gpio_mode(ROW_REG[i], (GPIO_MODE[i] & 0x0f));
    mt_set_gpio_dir(ROW_REG[i], 1);
    mt_set_gpio_pull_enable(ROW_REG[i], 0);
    mt_set_gpio_pull_select(ROW_REG[i], 0);
 
 
 
long_press_reboot_function_setting();/* /API 4 for kpd long press reboot function setting */ 
    if (get_boot_mode() == NORMAL_BOOT)  //如果正常启动
 
            #ifdef ONEKEY_REBOOT_NORMAL_MODE   //一个按键重启
pmic_set_register_value(PMIC_RG_PWRKEY_RST_EN,0x01); //使能
pmic_set_register_value(PMIC_RG_HOMEKEY_RST_EN,0x00); //去掉
pmic_set_register_value(PMIC_RG_PWRKEY_RST_TD,KPD_PMIC_LPRST_TD);  //写入超时时间,11s这里
#endif    
#ifdef TWOKEY_REBOOT_NORMAL_MODE
pmic_set_register_value(PMIC_RG_PWRKEY_RST_EN,0x01);
pmic_set_register_value(PMIC_RG_HOMEKEY_RST_EN,0x01);
pmic_set_register_value(PMIC_RG_PWRKEY_RST_TD,KPD_PMIC_LPRST_TD);
#endif
    esle
         #ifdef ONEKEY_REBOOT_OTHER_MODE
pmic_set_register_value(PMIC_RG_PWRKEY_RST_EN,0x01);
pmic_set_register_value(PMIC_RG_HOMEKEY_RST_EN,0x00);
pmic_set_register_value(PMIC_RG_PWRKEY_RST_TD,KPD_PMIC_LPRST_TD);
#endif
 
#ifdef TWOKEY_REBOOT_OTHER_MODE
pmic_set_register_value(PMIC_RG_PWRKEY_RST_EN,0x01);
pmic_set_register_value(PMIC_RG_HOMEKEY_RST_EN,0x01);
pmic_set_register_value(PMIC_RG_PWRKEY_RST_TD,KPD_PMIC_LPRST_TD);
#endif
 
 
分析keypad处理函数kpd_irq_handler
kpd_irq_handler
     tasklet_schedule(&kpd_keymap_tasklet); //调用tasklet
          kpd_keymap_handler
                kpd_get_keymap_state(new_state); //从寄存器得到keypad状态
                for (i = 0; i < KPD_NUM_MEMS; i++) {
                   change = new_state[i] ^ kpd_keymap_state[i]; //按键有无变化
                   for (j = 0; j < 16; j++) { //具体按键哪个按键变化
mask = 1U << j;
                   } 
                   hw_keycode = (i << 4) + j; //得到按键的在keymap具体值
  /* bit is 1: not pressed, 0: pressed */
  pressed = !(new_state[i] & mask);    //判断是按键还是松开
                  linux_keycode = kpd_keymap[hw_keycode]; //从keymap得到linuxkey
                  kpd_aee_handler(linux_keycode, pressed); //如果是KEY_VOLUMEUP或者KEY_VOLUMEDOWN
                        kpd_update_aee_state(); 
                             /* if volumeup and volumedown was pressed the same time then start the time of ten seconds */
            aee_timer_started = true;
                            hrtimer_start(&aee_timer_5s, ktime_set(AEE_DELAY_TIME_5S, 0), HRTIMER_MODE_REL); //打开5s
                            hrtimer_start(&aee_timer, ktime_set(AEE_DELAY_TIME, 0), HRTIMER_MODE_REL);  //15s
                 input_report_key(kpd_input_dev, linux_keycode, pressed); //上报按键
                 .........处理一些情况
 
 
aee_timer_func
   /* aee_kernel_reminding("manual dump ", "Triggered by press KEY_VOLUMEUP+KEY_VOLUMEDOWN"); */
    aee_trigger_kdb(); //就是使能KDB的操作,一个调试工具,kernel debug bridge
 
 
aee_timer_5s_func
    /* printk("kpd: vol up+vol down AEE manual dump timer 5s !\n"); */
    flags_5s = true;
         
 
 
 
 
 
 
posted @ 2020-11-25 10:14  luoyuna  阅读(1715)  评论(0编辑  收藏  举报