基于MTK平台kpd驱动初步分析
生活随笔
收集整理的這篇文章主要介紹了
基于MTK平台kpd驱动初步分析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、kpd_pdrv_probe函數的分析:
???/*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;?? }??
/*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;
#endiffor (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_AUTOTESTfor (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_EINTmt65xx_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);
#endifif(kpd_enable_lprst && get_boot_mode() == NORMAL_BOOT) {kpd_print("Normal Boot\n");
#ifdef KPD_PMIC_LPRST_TDkpd_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函數進行相關初始化后,這時候,當我們按鍵按下了,就會觸發中斷,進入中斷服務子程序
?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;?? }??
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,下面仔細分析
這個函數,詳細注釋如下:
?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);?? }??
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) = 0if (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函數分析
?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();?? ????}?? }??
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的區別就
是前者是非原子操作,而后者是原子操作,所謂原子操作,意思是最小的執行單位,再其執行過
程中是不會被其他任務打斷的。
四、背光處理函數
?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");?? ????}?? }??
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:該函數的作用是修改一個已經調度的定時器結構的到期時間。
五、背光控制函數
調度的是這個函數
?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");?? ????}?? }??
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");}
}
這里就能夠看到使能和失能背光的函數,繼續跟蹤:
?void?kpd_enable_backlight(void)?? {?? ????/*mt6326_kpled_dim_duty_Full();?? ????mt6326_kpled_Enable();*/?? ????upmu_kpled_dim_duty(31);?????? ????upmu_kpled_en(1);?? }?? upmu_kpled_dim_duty這是控制背光電流大小從而可以控制亮度?? upmu_kpled_en這是控制開關。??
?
?
?
二、當執行完面probe函數進行相關初始化后,這時候,當我們按鍵按下了,就會觸發中斷,進入中斷服務子程序
?
可以看到,中斷服務程序里面執行了 tasklet_schedule(&kpd_keymap_tasklet);
跟蹤代碼可以發現,實際上是執行了這個函數kpd_keymap_handler,下面仔細分析
這個函數,詳細注釋如下:
?
三、kpd_aee_handler函數分析
?
詳細分析:
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.首先用到了一個位操作函數,注意這個函數是原子操作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:該函數的作用是修改一個已經調度的定時器結構的到期時間。
五、背光控制函數
調度的是這個函數
?
這里就能夠看到使能和失能背光的函數,繼續跟蹤:
?
總結
以上是生活随笔為你收集整理的基于MTK平台kpd驱动初步分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微服务(Microservice)那点事
- 下一篇: Scaled-YOLOv4: Scali