一、kpd_pdrv_probe函数的分析:

 
  1. /*1. 输入设备实例  kpd_input_dev */
  2. 全局变量:static struct input_dev *kpd_input_dev;
  3. static int kpd_pdrv_probe(struct platform_device *pdev)
  4. {
  5. int i, r;
  6. u16 new_state[KPD_NUM_MEMS];
  7. /* initialize and register input device (/dev/input/eventX) */
  8. /*2. 初始化输入设备并分配内存空间*/
  9. kpd_input_dev = input_allocate_device();
  10. if (!kpd_input_dev)
  11. return -ENOMEM;
  12. /*下面开始填充kpd_input_dev  设备驱动结构体*/
  13. kpd_input_dev->name = KPD_NAME;
  14. kpd_input_dev->id.bustype = BUS_HOST;
  15. kpd_input_dev->id.vendor = 0x2454;
  16. kpd_input_dev->id.product = 0x6575;
  17. kpd_input_dev->id.version = 0x0010;
  18. kpd_input_dev->open = kpd_open;
  19. /*3. 设置某位为1,以第二个参数为起始地址,EV_KEY表示要设置的位
  20. 作用:告诉input子系统支持那些事件, EV_KEY 这里表示告诉input子系统支持
  21. 按键事件
  22. */
  23. __set_bit(EV_KEY, kpd_input_dev->evbit);
  24. #if (KPD_PWRKEY_USE_EINT||KPD_PWRKEY_USE_PMIC)
  25. /*4. 设置某位为1,以第二个参数为起始地址,EV_KEY表示要设置的位
  26. 作用:告诉input子系统支持那些按键, KPD_PWRKEY_MAP 这里表示告诉input子系统支持
  27. 电源按键
  28. */
  29. __set_bit(KPD_PWRKEY_MAP, kpd_input_dev->keybit);
  30. kpd_keymap[8] = 0;
  31. #endif
  32. for (i = 17; i < KPD_NUM_KEYS; i += 9)   /* only [8] works for Power key */
  33. kpd_keymap[i] = 0;
  34. for (i = 0; i < KPD_NUM_KEYS; i++) {
  35. if (kpd_keymap[i] != 0)
  36. __set_bit(kpd_keymap[i], kpd_input_dev->keybit);
  37. }
  38. /*5. 上述几行代码表示设置电源按键 kpd_keymap 为0,其它按键 kpd_keymap 为1*/
  39. __set_bit(250, kpd_input_dev->keybit);
  40. __set_bit(251, kpd_input_dev->keybit);
  41. #if KPD_AUTOTEST
  42. for (i = 0; i < ARRAY_SIZE(kpd_auto_keymap); i++)
  43. __set_bit(kpd_auto_keymap[i], kpd_input_dev->keybit);
  44. #endif
  45. #if KPD_HAS_SLIDE_QWERTY
  46. __set_bit(EV_SW, kpd_input_dev->evbit);
  47. __set_bit(SW_LID, kpd_input_dev->swbit);
  48. __set_bit(SW_LID, kpd_input_dev->sw);    /* 1: lid shut => closed */
  49. #endif
  50. #ifdef KPD_PMIC_RSTKEY_MAP
  51. __set_bit(KPD_PMIC_RSTKEY_MAP, kpd_input_dev->keybit);
  52. #endif
  53. /*6. 指定kpd_input_dev这个平台设备sysfs中的父设备节点*/
  54. kpd_input_dev->dev.parent = &pdev->dev;
  55. /*7. 注册input输入子系统*/
  56. r = input_register_device(kpd_input_dev);
  57. if (r) {
  58. printk(KPD_SAY "register input device failed (%d)\n", r);
  59. input_free_device(kpd_input_dev);
  60. return r;
  61. }
  62. /* register device (/dev/mt6575-kpd) */
  63. /*7. 指定kpd_dev这个平台设备sysfs中的父设备节点*/
  64. kpd_dev.parent = &pdev->dev;
  65. /*8. 注册混杂设备*/
  66. r = misc_register(&kpd_dev);
  67. if (r) {
  68. printk(KPD_SAY "register device failed (%d)\n", r);
  69. input_unregister_device(kpd_input_dev);
  70. return r;
  71. }
  72. /*8. 注册按键中断*/
  73. /* register IRQ and EINT */
  74. /*9. 设置消抖时间*/
  75. kpd_set_debounce(KPD_KEY_DEBOUNCE);
  76. /*10. 设置中断触发方式*/
  77. mt65xx_irq_set_sens(MT6575_KP_IRQ_ID, MT65xx_EDGE_SENSITIVE);
  78. /*11 . 设置中断优先级*/
  79. mt65xx_irq_set_polarity(MT6575_KP_IRQ_ID, MT65xx_POLARITY_LOW);
  80. /*12. 注册中断处理函数*/
  81. r = request_irq(MT6575_KP_IRQ_ID, kpd_irq_handler, 0, KPD_NAME, NULL);
  82. if (r) {
  83. printk(KPD_SAY "register IRQ failed (%d)\n", r);
  84. misc_deregister(&kpd_dev);
  85. input_unregister_device(kpd_input_dev);
  86. return r;
  87. }
  88. /*13. 以下为电源键中断函数的注册*/
  89. #if KPD_PWRKEY_USE_EINT
  90. mt65xx_eint_set_sens(KPD_PWRKEY_EINT, KPD_PWRKEY_SENSITIVE);
  91. mt65xx_eint_set_hw_debounce(KPD_PWRKEY_EINT, KPD_PWRKEY_DEBOUNCE);
  92. mt65xx_eint_registration(KPD_PWRKEY_EINT, true, KPD_PWRKEY_POLARITY,
  93. kpd_pwrkey_eint_handler, false);
  94. #endif
  95. if(kpd_enable_lprst && get_boot_mode() == NORMAL_BOOT) {
  96. kpd_print("Normal Boot\n");
  97. #ifdef KPD_PMIC_LPRST_TD
  98. kpd_print("Enable LPRST\n");
  99. /*14. 以下为设置按键唤醒的时间*/
  100. upmu_testmode_pwrkey_rst_en(0x01);
  101. upmu_testmode_homekey_rst_en(0x01);
  102. upmu_testmode_pwrkey_rst_td(KPD_PMIC_LPRST_TD);
  103. #endif
  104. } else {
  105. kpd_print("Disable LPRST %d\n", kpd_enable_lprst);
  106. }
  107. /*15. 设置一个高精度定时器,并且定义了时间到期的回调函数 aee_timer_func*/
  108. hrtimer_init(&aee_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
  109. aee_timer.function = aee_timer_func;
  110. /*以下为三个按键的初始化,也就是配置
  111. 注意,默认值gpio输出是0*/
  112. #if 1 // ylliu add. dct default value does not work...
  113. /* KCOL0: GPIO103: KCOL1: GPIO108, KCOL2: GPIO105, KCOL4: GPIO102 input + pull enable + pull up */
  114. mt_set_gpio_mode(GPIO_KPD_KCOL0_PIN, GPIO_KPD_KCOL0_PIN_M_KP_COL);
  115. mt_set_gpio_dir(GPIO_KPD_KCOL0_PIN, GPIO_DIR_IN);
  116. mt_set_gpio_pull_enable(GPIO_KPD_KCOL0_PIN, GPIO_PULL_ENABLE);
  117. mt_set_gpio_pull_select(GPIO_KPD_KCOL0_PIN, GPIO_PULL_UP);
  118. mt_set_gpio_mode(GPIO_KPD_KCOL1_PIN, GPIO_KPD_KCOL1_PIN_M_KP_COL);
  119. mt_set_gpio_dir(GPIO_KPD_KCOL1_PIN, GPIO_DIR_IN);
  120. mt_set_gpio_pull_enable(GPIO_KPD_KCOL1_PIN, GPIO_PULL_ENABLE);
  121. mt_set_gpio_pull_select(GPIO_KPD_KCOL1_PIN, GPIO_PULL_UP);
  122. mt_set_gpio_mode(GPIO_KPD_KCOL2_PIN, GPIO_KPD_KCOL2_PIN_M_KP_COL);
  123. mt_set_gpio_dir(GPIO_KPD_KCOL2_PIN, GPIO_DIR_IN);
  124. mt_set_gpio_pull_enable(GPIO_KPD_KCOL2_PIN, GPIO_PULL_ENABLE);
  125. mt_set_gpio_pull_select(GPIO_KPD_KCOL2_PIN, GPIO_PULL_UP);
  126. mt_set_gpio_mode(GPIO_KPD_KCOL4_PIN, GPIO_KPD_KCOL4_PIN_M_KP_COL);
  127. mt_set_gpio_dir(GPIO_KPD_KCOL4_PIN, GPIO_DIR_IN);
  128. mt_set_gpio_pull_enable(GPIO_KPD_KCOL4_PIN, GPIO_PULL_ENABLE);
  129. mt_set_gpio_pull_select(GPIO_KPD_KCOL4_PIN, GPIO_PULL_UP);
  130. /* KROW0: GPIO98, KROW1: GPIO97: KROW2: GPIO95 output + pull disable + pull down */
  131. mt_set_gpio_mode(GPIO_KPD_KROW0_PIN, GPIO_KPD_KROW0_PIN_M_KP_ROW);
  132. mt_set_gpio_dir(GPIO_KPD_KROW0_PIN, GPIO_DIR_OUT);
  133. mt_set_gpio_pull_enable(GPIO_KPD_KROW0_PIN, GPIO_PULL_DISABLE);
  134. mt_set_gpio_pull_select(GPIO_KPD_KROW0_PIN, GPIO_PULL_DOWN);
  135. #endif
  1. // default disable backlight. reboot from recovery need this.
  2. kpd_disable_backlight();
  3. // store default state, resolve recovery bugs.
  4. kpd_get_keymap_state(new_state);
  5. memcpy(kpd_keymap_state, new_state, sizeof(new_state));
  6. return 0;
  7. }
  /*1. 输入设备实例  kpd_input_dev */
全局变量:static struct input_dev *kpd_input_dev;
static int kpd_pdrv_probe(struct platform_device *pdev)
{
int i, r;
u16 new_state[KPD_NUM_MEMS];
/* initialize and register input device (/dev/input/eventX) */
/*2. 初始化输入设备并分配内存空间*/
kpd_input_dev = input_allocate_device();
if (!kpd_input_dev)
return -ENOMEM;
/*下面开始填充kpd_input_dev  设备驱动结构体*/
kpd_input_dev->name = KPD_NAME;
kpd_input_dev->id.bustype = BUS_HOST;
kpd_input_dev->id.vendor = 0x2454;
kpd_input_dev->id.product = 0x6575;
kpd_input_dev->id.version = 0x0010;
kpd_input_dev->open = kpd_open;
/*3. 设置某位为1,以第二个参数为起始地址,EV_KEY表示要设置的位
作用:告诉input子系统支持那些事件, EV_KEY 这里表示告诉input子系统支持
按键事件
*/
__set_bit(EV_KEY, kpd_input_dev->evbit);
#if (KPD_PWRKEY_USE_EINT||KPD_PWRKEY_USE_PMIC)
/*4. 设置某位为1,以第二个参数为起始地址,EV_KEY表示要设置的位
作用:告诉input子系统支持那些按键, KPD_PWRKEY_MAP 这里表示告诉input子系统支持
电源按键
*/
__set_bit(KPD_PWRKEY_MAP, kpd_input_dev->keybit);
kpd_keymap[8] = 0;
#endif
for (i = 17; i < KPD_NUM_KEYS; i += 9)    /* only [8] works for Power key */
kpd_keymap[i] = 0;
for (i = 0; i < KPD_NUM_KEYS; i++) {
if (kpd_keymap[i] != 0)
__set_bit(kpd_keymap[i], kpd_input_dev->keybit);
}
/*5. 上述几行代码表示设置电源按键 kpd_keymap 为0,其它按键 kpd_keymap 为1*/
__set_bit(250, kpd_input_dev->keybit);
__set_bit(251, kpd_input_dev->keybit);
#if KPD_AUTOTEST
for (i = 0; i < ARRAY_SIZE(kpd_auto_keymap); i++)
__set_bit(kpd_auto_keymap[i], kpd_input_dev->keybit);
#endif
#if KPD_HAS_SLIDE_QWERTY
__set_bit(EV_SW, kpd_input_dev->evbit);
__set_bit(SW_LID, kpd_input_dev->swbit);
__set_bit(SW_LID, kpd_input_dev->sw);    /* 1: lid shut => closed */
#endif
#ifdef KPD_PMIC_RSTKEY_MAP
__set_bit(KPD_PMIC_RSTKEY_MAP, kpd_input_dev->keybit);
#endif
/*6. 指定kpd_input_dev这个平台设备sysfs中的父设备节点*/
kpd_input_dev->dev.parent = &pdev->dev;
/*7. 注册input输入子系统*/
r = input_register_device(kpd_input_dev);
if (r) {
printk(KPD_SAY "register input device failed (%d)\n", r);
input_free_device(kpd_input_dev);
return r;
}
/* register device (/dev/mt6575-kpd) */
/*7. 指定kpd_dev这个平台设备sysfs中的父设备节点*/
kpd_dev.parent = &pdev->dev;
/*8. 注册混杂设备*/
r = misc_register(&kpd_dev);
if (r) {
printk(KPD_SAY "register device failed (%d)\n", r);
input_unregister_device(kpd_input_dev);
return r;
}
/*8. 注册按键中断*/
/* register IRQ and EINT */
/*9. 设置消抖时间*/
kpd_set_debounce(KPD_KEY_DEBOUNCE);
/*10. 设置中断触发方式*/
mt65xx_irq_set_sens(MT6575_KP_IRQ_ID, MT65xx_EDGE_SENSITIVE);
/*11 . 设置中断优先级*/
mt65xx_irq_set_polarity(MT6575_KP_IRQ_ID, MT65xx_POLARITY_LOW);
/*12. 注册中断处理函数*/
r = request_irq(MT6575_KP_IRQ_ID, kpd_irq_handler, 0, KPD_NAME, NULL);
if (r) {
printk(KPD_SAY "register IRQ failed (%d)\n", r);
misc_deregister(&kpd_dev);
input_unregister_device(kpd_input_dev);
return r;
}
/*13. 以下为电源键中断函数的注册*/
#if KPD_PWRKEY_USE_EINT
mt65xx_eint_set_sens(KPD_PWRKEY_EINT, KPD_PWRKEY_SENSITIVE);
mt65xx_eint_set_hw_debounce(KPD_PWRKEY_EINT, KPD_PWRKEY_DEBOUNCE);
mt65xx_eint_registration(KPD_PWRKEY_EINT, true, KPD_PWRKEY_POLARITY,
kpd_pwrkey_eint_handler, false);
#endif
if(kpd_enable_lprst && get_boot_mode() == NORMAL_BOOT) {
kpd_print("Normal Boot\n");
#ifdef KPD_PMIC_LPRST_TD
kpd_print("Enable LPRST\n");
/*14. 以下为设置按键唤醒的时间*/
upmu_testmode_pwrkey_rst_en(0x01);
upmu_testmode_homekey_rst_en(0x01);
upmu_testmode_pwrkey_rst_td(KPD_PMIC_LPRST_TD);
#endif
} else {
kpd_print("Disable LPRST %d\n", kpd_enable_lprst);
}
/*15. 设置一个高精度定时器,并且定义了时间到期的回调函数 aee_timer_func*/
hrtimer_init(&aee_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
aee_timer.function = aee_timer_func;
/*以下为三个按键的初始化,也就是配置
注意,默认值gpio输出是0*/
#if 1 // ylliu add. dct default value does not work...
/* KCOL0: GPIO103: KCOL1: GPIO108, KCOL2: GPIO105, KCOL4: GPIO102 input + pull enable + pull up */
mt_set_gpio_mode(GPIO_KPD_KCOL0_PIN, GPIO_KPD_KCOL0_PIN_M_KP_COL);
mt_set_gpio_dir(GPIO_KPD_KCOL0_PIN, GPIO_DIR_IN);
mt_set_gpio_pull_enable(GPIO_KPD_KCOL0_PIN, GPIO_PULL_ENABLE);
mt_set_gpio_pull_select(GPIO_KPD_KCOL0_PIN, GPIO_PULL_UP);
mt_set_gpio_mode(GPIO_KPD_KCOL1_PIN, GPIO_KPD_KCOL1_PIN_M_KP_COL);
mt_set_gpio_dir(GPIO_KPD_KCOL1_PIN, GPIO_DIR_IN);
mt_set_gpio_pull_enable(GPIO_KPD_KCOL1_PIN, GPIO_PULL_ENABLE);
mt_set_gpio_pull_select(GPIO_KPD_KCOL1_PIN, GPIO_PULL_UP);
mt_set_gpio_mode(GPIO_KPD_KCOL2_PIN, GPIO_KPD_KCOL2_PIN_M_KP_COL);
mt_set_gpio_dir(GPIO_KPD_KCOL2_PIN, GPIO_DIR_IN);
mt_set_gpio_pull_enable(GPIO_KPD_KCOL2_PIN, GPIO_PULL_ENABLE);
mt_set_gpio_pull_select(GPIO_KPD_KCOL2_PIN, GPIO_PULL_UP);
mt_set_gpio_mode(GPIO_KPD_KCOL4_PIN, GPIO_KPD_KCOL4_PIN_M_KP_COL);
mt_set_gpio_dir(GPIO_KPD_KCOL4_PIN, GPIO_DIR_IN);
mt_set_gpio_pull_enable(GPIO_KPD_KCOL4_PIN, GPIO_PULL_ENABLE);
mt_set_gpio_pull_select(GPIO_KPD_KCOL4_PIN, GPIO_PULL_UP);
/* KROW0: GPIO98, KROW1: GPIO97: KROW2: GPIO95 output + pull disable + pull down */
mt_set_gpio_mode(GPIO_KPD_KROW0_PIN, GPIO_KPD_KROW0_PIN_M_KP_ROW);
mt_set_gpio_dir(GPIO_KPD_KROW0_PIN, GPIO_DIR_OUT);
mt_set_gpio_pull_enable(GPIO_KPD_KROW0_PIN, GPIO_PULL_DISABLE);
mt_set_gpio_pull_select(GPIO_KPD_KROW0_PIN, GPIO_PULL_DOWN);
#endif
// default disable backlight. reboot from recovery need this.
kpd_disable_backlight();
// store default state, resolve recovery bugs.
kpd_get_keymap_state(new_state);
memcpy(kpd_keymap_state, new_state, sizeof(new_state));
return 0;
}

二、当执行完面probe函数进行相关初始化后,这时候,当我们按键按下了,就会触发中断,进入中断服务子程序

 
  1. static irqreturn_t __tcmfunc kpd_irq_handler(int irq, void *dev_id)
  2. {
  3. /* use _nosync to avoid deadlock */
  4. disable_irq_nosync(MT6575_KP_IRQ_ID);
  5. tasklet_schedule(&kpd_keymap_tasklet);
  6. return IRQ_HANDLED;
  7. }
static irqreturn_t __tcmfunc kpd_irq_handler(int irq, void *dev_id)
{
/* use _nosync to avoid deadlock */
disable_irq_nosync(MT6575_KP_IRQ_ID);
tasklet_schedule(&kpd_keymap_tasklet);
return IRQ_HANDLED;
}

可以看到,中断服务程序里面执行了 tasklet_schedule(&kpd_keymap_tasklet);
跟踪代码可以发现,实际上是执行了这个函数kpd_keymap_handler,下面仔细分析
这个函数,详细注释如下:

 
  1. static void kpd_keymap_handler(unsigned long data)
  2. {
  3. int i, j;
  4. bool pressed;
  5. u16 new_state[KPD_NUM_MEMS], change, mask;
  6. u16 hw_keycode, linux_keycode;
  7. kpd_get_keymap_state(new_state);                                                                    //首先读取键值,并且存放于new_state中
  8. if (pmic_get_acc_state() == 1) {
  9. for (i = 0; i < KPD_NUM_MEMS; i++) {
  10. change = new_state[i] ^ kpd_keymap_state[i];                                        //进行异或操作,就是为了取出两者不同的值
  11. if (!change)
  12. continue;
  13. for (j = 0; j < 16; j++) {
  14. mask = 1U << j;
  15. if (!(change & mask))
  16. continue;
  17. hw_keycode = (i << 4) + j;        //i = 0, j = 1;                                 //这里是得到hw_keycode的值
  18. printk("hw_keycode = %d ,i = %d, j = %d \n",hw_keycode,i,j);
  19. /* bit is 1: not pressed, 0: pressed */
  20. pressed = !(new_state[i] & mask);   //(new_state[i] & mask) = 0
  21. if (kpd_show_hw_keycode) {
  22. printk(KPD_SAY "(%s) HW keycode = %u\n",
  23. pressed ? "pressed" : "released",
  24. hw_keycode);
  25. }
  26. BUG_ON(hw_keycode >= KPD_NUM_KEYS);
  27. linux_keycode = kpd_keymap[hw_keycode];                                             //这里的linux_keycode恒为零。
  28. printk("linux_keycode = %d  \n",linux_keycode);
  29. if(unlikely(linux_keycode == 0)) {
  30. if (hw_keycode == 1 && pressed) { // special key, SOS.
  31. struct device *dev = &(kpd_input_dev->dev);
  32. char *envp[] = { "SOS_pressed", NULL };
  33. kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);               //建立设备文件?
  34. printk(KPD_SAY "SOS_pressed\n");
  35. // used by recovery.
  36. /*这个接口会向INPUT子系统上报按键(该按键被按下)*/
  37. input_report_key(kpd_input_dev, 251, pressed);                      //如果上层检测到SOS_pressed就会做相应处理。
  38. } else if (hw_keycode == 2 && pressed) { // special key, background.
  39. struct device *dev = &(kpd_input_dev->dev);
  40. char *envp[] = { "background_pressed", NULL };
  41. kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
  42. printk(KPD_SAY "background_pressed\n");
  43. // used by recovery.
  44. input_report_key(kpd_input_dev, 8, pressed);
  45. } else if (hw_keycode == 4 && pressed) { // special key, mode.
  46. struct device *dev = &(kpd_input_dev->dev);
  47. char *envp[] = { "mode_pressed", NULL };
  48. kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
  49. printk(KPD_SAY "mode_pressed\n");
  50. } else if (hw_keycode == 1 || hw_keycode == 2 || hw_keycode == 4) { // add this to turn off backlight.
  51. printk(KPD_SAY "background or SOS or mode release!\n");
  52. // used by recovery.
  53. if (hw_keycode == 1)
  54. input_report_key(kpd_input_dev, 251, pressed);
  55. else if (hw_keycode == 2)
  56. input_report_key(kpd_input_dev, 8, pressed);
  57. } else {
  58. kpd_print("Linux keycode = 0\n");
  59. continue;
  60. }
  61. }
  62. kpd_aee_handler(linux_keycode, pressed);
  63. kpd_backlight_handler(pressed, linux_keycode);
  64. input_report_key(kpd_input_dev, linux_keycode, pressed);
  65. }
  66. }
  67. } else {
  68. printk(KPD_SAY "acc off, ignore and key...\n");
  69. }
  70. memcpy(kpd_keymap_state, new_state, sizeof(new_state));
  71. kpd_print("save new keymap state\n");
  72. enable_irq(MT6575_KP_IRQ_ID);
  73. }
static void kpd_keymap_handler(unsigned long data)
{
int i, j;
bool pressed;
u16 new_state[KPD_NUM_MEMS], change, mask;
u16 hw_keycode, linux_keycode;
kpd_get_keymap_state(new_state);                                                                    //首先读取键值,并且存放于new_state中
if (pmic_get_acc_state() == 1) {
for (i = 0; i < KPD_NUM_MEMS; i++) {
change = new_state[i] ^ kpd_keymap_state[i];                                       //进行异或操作,就是为了取出两者不同的值
if (!change)
continue;
for (j = 0; j < 16; j++) {
mask = 1U << j;
if (!(change & mask))
continue;
hw_keycode = (i << 4) + j;      //i = 0, j = 1;                               //这里是得到hw_keycode的值
printk("hw_keycode = %d ,i = %d, j = %d \n",hw_keycode,i,j);
/* bit is 1: not pressed, 0: pressed */
pressed = !(new_state[i] & mask);  //(new_state[i] & mask) = 0
if (kpd_show_hw_keycode) {
printk(KPD_SAY "(%s) HW keycode = %u\n",
pressed ? "pressed" : "released",
hw_keycode);
}
BUG_ON(hw_keycode >= KPD_NUM_KEYS);
linux_keycode = kpd_keymap[hw_keycode];                                                //这里的linux_keycode恒为零。
printk("linux_keycode = %d  \n",linux_keycode);
if(unlikely(linux_keycode == 0)) {
if (hw_keycode == 1 && pressed) { // special key, SOS.
struct device *dev = &(kpd_input_dev->dev);
char *envp[] = { "SOS_pressed", NULL };
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);               //建立设备文件?
printk(KPD_SAY "SOS_pressed\n");
// used by recovery.
/*这个接口会向INPUT子系统上报按键(该按键被按下)*/
input_report_key(kpd_input_dev, 251, pressed);                      //如果上层检测到SOS_pressed就会做相应处理。
} else if (hw_keycode == 2 && pressed) { // special key, background.
struct device *dev = &(kpd_input_dev->dev);
char *envp[] = { "background_pressed", NULL };
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
printk(KPD_SAY "background_pressed\n");
// used by recovery.
input_report_key(kpd_input_dev, 8, pressed);
} else if (hw_keycode == 4 && pressed) { // special key, mode.
struct device *dev = &(kpd_input_dev->dev);
char *envp[] = { "mode_pressed", NULL };
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
printk(KPD_SAY "mode_pressed\n");
} else if (hw_keycode == 1 || hw_keycode == 2 || hw_keycode == 4) { // add this to turn off backlight.
printk(KPD_SAY "background or SOS or mode release!\n");
// used by recovery.
if (hw_keycode == 1)
input_report_key(kpd_input_dev, 251, pressed);
else if (hw_keycode == 2)
input_report_key(kpd_input_dev, 8, pressed);
} else {
kpd_print("Linux keycode = 0\n");
continue;
}
}
kpd_aee_handler(linux_keycode, pressed);
kpd_backlight_handler(pressed, linux_keycode);
input_report_key(kpd_input_dev, linux_keycode, pressed);
}
}
} else {
printk(KPD_SAY "acc off, ignore and key...\n");
}
memcpy(kpd_keymap_state, new_state, sizeof(new_state));
kpd_print("save new keymap state\n");
enable_irq(MT6575_KP_IRQ_ID);
}

三、kpd_aee_handler函数分析

 
  1. static void kpd_aee_handler(u32 keycode, u16 pressed) {
  2. if(pressed) {
  3. if(keycode == KEY_VOLUMEUP) {
  4. __set_bit(0, &aee_pressed_keys);
  5. } else if(keycode == KEY_VOLUMEDOWN) {
  6. __set_bit(1, &aee_pressed_keys);
  7. } else {
  8. return;
  9. }
  10. kpd_update_aee_state();
  11. } else {
  12. if(keycode == KEY_VOLUMEUP) {
  13. __clear_bit(0, &aee_pressed_keys);
  14. } else if(keycode == KEY_VOLUMEDOWN) {
  15. __clear_bit(1, &aee_pressed_keys);
  16. } else {
  17. return;
  18. }
  19. kpd_update_aee_state();
  20. }
  21. }
static void kpd_aee_handler(u32 keycode, u16 pressed) {
if(pressed) {
if(keycode == KEY_VOLUMEUP) {
__set_bit(0, &aee_pressed_keys);
} else if(keycode == KEY_VOLUMEDOWN) {
__set_bit(1, &aee_pressed_keys);
} else {
return;
}
kpd_update_aee_state();
} else {
if(keycode == KEY_VOLUMEUP) {
__clear_bit(0, &aee_pressed_keys);
} else if(keycode == KEY_VOLUMEDOWN) {
__clear_bit(1, &aee_pressed_keys);
} else {
return;
}
kpd_update_aee_state();
}
}

详细分析:
1.__set_bit(0, &aee_pressed_keys),定义了一个:static u16 aee_pressed_keys;
所以__set_bit的意思是将aee_pressed_keys的bit0设置为1
2.相应的__clear_bit(0, &aee_pressed_keys);就是把aee_pressed_keys的bit0清零,
3.还有在内核的non-atomic.h文件中还有一些其它的位操作,记住__set_bit和set_bit的区别就
是前者是非原子操作,而后者是原子操作,所谓原子操作,意思是最小的执行单位,再其执行过
程中是不会被其他任务打断的。

四、背光处理函数

 
  1. void kpd_backlight_handler(bool pressed, u16 linux_keycode)
  2. {
  3. if (kpd_suspend && !test_bit(linux_keycode, kpd_wake_keybit)) {
  4. kpd_print("Linux keycode %u is not WAKE key\n", linux_keycode);
  5. return;
  6. }
  7. /* not in suspend or the key pressed is WAKE key */
  8. if (pressed) {
  9. atomic_inc(&kpd_key_pressed);
  10. kpd_backlight_on = !!atomic_read(&kpd_key_pressed);
  11. schedule_work(&kpd_backlight_work);     //点亮背光灯
  12. kpd_print("switch backlight on\n");
  13. } else {
  14. atomic_dec(&kpd_key_pressed);
  15. mod_timer(&kpd_backlight_timer,             //KPD_BACKLIGHT_TIME控制背光时间,单位为sec,如果注释掉这句,背光将不灭
  16. jiffies + KPD_BACKLIGHT_TIME * HZ);
  17. kpd_print("activate backlight timer\n");
  18. }
  19. }
void kpd_backlight_handler(bool pressed, u16 linux_keycode)
{
if (kpd_suspend && !test_bit(linux_keycode, kpd_wake_keybit)) {
kpd_print("Linux keycode %u is not WAKE key\n", linux_keycode);
return;
}
/* not in suspend or the key pressed is WAKE key */
if (pressed) {
atomic_inc(&kpd_key_pressed);
kpd_backlight_on = !!atomic_read(&kpd_key_pressed);
schedule_work(&kpd_backlight_work);     //点亮背光灯
kpd_print("switch backlight on\n");
} else {
atomic_dec(&kpd_key_pressed);
mod_timer(&kpd_backlight_timer,             //KPD_BACKLIGHT_TIME控制背光时间,单位为sec,如果注释掉这句,背光将不灭
jiffies + KPD_BACKLIGHT_TIME * HZ);
kpd_print("activate backlight timer\n");
}
}

详细分析
1.首先用到了一个位操作函数,注意这个函数是原子操作test_bit
2.全局变量static atomic_t kpd_key_pressed = ATOMIC_INIT(0);这是原子操作的初始化,kpd_key_pressed初始化为0
3.上述函数涉及到一些原子操作函数,解释如下:
atomic_inc(&kpd_key_pressed); 是对变量进行加1操作
atomic_dec(&kpd_key_pressed); 是对变量进行减1操作
!!atomic_read(&kpd_key_pressed);是读取变量的值,前面两个 !!强调该返回值不是1就是0:bool类型
4.mod_timer:该函数的作用是修改一个已经调度的定时器结构的到期时间。

五、背光控制函数

调度的是这个函数

 
  1. static void kpd_switch_backlight(struct work_struct *work)
  2. {
  3. if (kpd_backlight_on) {
  4. kpd_enable_backlight();
  5. kpd_print("backlight is on\n");
  6. } else {
  7. kpd_disable_backlight();
  8. kpd_print("backlight is off\n");
  9. }
  10. }
static void kpd_switch_backlight(struct work_struct *work)
{
if (kpd_backlight_on) {
kpd_enable_backlight();
kpd_print("backlight is on\n");
} else {
kpd_disable_backlight();
kpd_print("backlight is off\n");
}
}

这里就能够看到使能和失能背光的函数,继续跟踪:

 
  1. void kpd_enable_backlight(void)
  2. {
  3. /*mt6326_kpled_dim_duty_Full();
  4. mt6326_kpled_Enable();*/
  5. upmu_kpled_dim_duty(31);
  6. upmu_kpled_en(1);
  7. }
  8. upmu_kpled_dim_duty这是控制背光电流大小从而可以控制亮度
  9. upmu_kpled_en这是控制开关。

基于MTK平台kpd驱动初步分析相关推荐

  1. 基于MTK平台kpd 驱动解析

    ---> http://www.unjeep.com/q/625260162.htm http://blog.csdn.net/aree/article/details/28683741 htt ...

  2. linux驱动学习2(kpd驱动初步分析)

    一.kpd_pdrv_probe函数的分析: /*1. 输入设备实例 kpd_input_dev */ 全局变量:static struct input_dev *kpd_input_dev; sta ...

  3. MTK 平台lcm驱动框架分析1

    源码路径: kernel-3.18/arch/arm/boot/dts/mt6580.dtsi kernel-3.18/drivers/misc/mediatek/video/common/mtkfb ...

  4. MTK平台闪光灯驱动分析

    MTK平台闪光灯驱动分析   以前没写过博客,总想写着来着,把之前学到的做过的东西都记录下来,但是一直没有时间也没那么大的决心.这次趁着刚换工作,正在学习熟悉平台不是太忙的机会,把自己总结的文档写下来 ...

  5. MTK平台TP驱动框架解析

    一,TP驱动代码的组成 MTK平台TP驱动主要包括两处文件: 1,kernel-3.18\drivers\input\touchscreen\mediatek\mtk_tpd.c 平台设备驱动,主要为 ...

  6. MTK 驱动(69)---MTK平台 电池驱动相关

    MTK平台 电池驱动相关 MTK平台 电池驱动 一.相关代码路径(mt6755/Android6.0) alps/kernel-3.18/drivers/misc/mediatek/include/m ...

  7. mtk平台lcd驱动移植

    mtk平台lcd驱动分为两个部分:lk和kernel.两部分基本流程相同,除了GPIO操作及头文件. 1. lk:需要修改的目录 vendor/mediatek/proprietary/bootabl ...

  8. MTK 驱动---MTK平台 电池驱动相关

    MTK平台 电池驱动 一.相关代码路径(mt6755/Android6.0) alps/kernel-3.18/drivers/misc/mediatek/include/mt-plat/mt6755 ...

  9. 基于MTK 的 TP 驱动分析

    1. 克隆服务器工程源码并切换分支 git clone git@192.168.20.2: mt658292_kk v9 git checkout -b submit_v9_dongxf_tp_mod ...

最新文章

  1. Java基础知识练习02
  2. input按钮中的onclick事件
  3. Xamarin.Android之封装个简单的网络请求类
  4. 前端HTML5css3阴影凹凸文字
  5. 使用Spring Boot进行面向方面的编程
  6. deepin中mysql数据库的连接_教你如何典雅的用Python连接MySQL数据库
  7. 可变大小、颜色边框、样式的UISwitch
  8. 我们很需要“企业即时通讯”
  9. 感知器原理及线性分类技术实现
  10. [转]关于Microsoft.Office.Interop组件接口的调用方法
  11. Cartographer ROS for Turtlebots 初探
  12. sokit socket调试工具
  13. C/C++程序员是什么让你有如此优势?音视频开发该怎么学?
  14. 寒假每日一题题解(1.29)摘花生(DP水题)
  15. 国产手机已经用上了 120W 快充技术,苹果还在用20W的原因
  16. Java使用Hutools工具类发送腾讯企业邮箱
  17. Meth | 关闭mac自带apache的启动
  18. 这家公司把裁员写进OKR,脸都不要了
  19. 用筛选法求100之内的素数。谭浩强《c语言程序设计》第六章第一题
  20. 多网段情况下配置windows防火墙允许ping以及远程打印和网络发现

热门文章

  1. Android和风SDK,AndroidSDK——和风天气使用初体验
  2. 社交战争下半场:从流量战到内容战
  3. 蛮力法分硬币问题 c++ cpp
  4. MATLAB_LSB_隐藏水印和提取,附代码
  5. 通过一个Kafka故障解决过程阐述架构师必须具备的架构思维
  6. LessonTableViewEdit
  7. Driver的prob的调用顺序
  8. C4D R19 图文安装教程
  9. mysql的cpu使用率突然增高_mysql cpu使用率过高解决方法
  10. 黑马程序员————高新技术————内省(了解JavaBean)