日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于MTK平台kpd驱动初步分析

發布時間:2023/12/14 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于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這是控制開關。??
  • 總結

    以上是生活随笔為你收集整理的基于MTK平台kpd驱动初步分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。