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都应该修改为新按键增加键码值
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.hbionic\libc\kernel\common\linux\input.hexternal\kernel-headers\original\linux\input.hexternal\qemu\linux_keycodes.hmediatek\plaform\mt6575\preloader\src\drivers\inc\linux\input.hMediatek\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
如:
#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被拉低;那么COL2和ROW1组合起来就知道哪个按键按下了。
但是实际上按键中断产生后,扫描过程已经在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_set_gpio_dir(COL_REG[i], 0);
mt_set_gpio_pull_enable(COL_REG[i], 1);
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_timer_5s_func
/* printk("kpd: vol up+vol down AEE manual dump timer 5s !\n"); */
flags_5s = true;