Linux4.9 A100 加载 gpio-keys 驱动可以使用,但是装载过程驱动报错,并且无法卸载。
-
kernel/linux-4.9/.config 配置:
CONFIG_KEYBOARD_GPIO=m
board.dts 配置:
gpio_keys { device_type = "gpiokey"; compatible = "gpio-keys"; status = "okay"; power_key { device_type = "ok_key"; label = "gpio key power"; linux,code = <KEY_POWER>; gpios = <&pio PJ 1 0 1 0 1>; wakeup-source = <0x1>; }; };
安装驱动 insmod /lib/modules/4.9.170/gpio_keys.ko:
root@dragonboard:~# insmod /lib/modules/4.9.170/gpio_keys.ko [ 35.432556] input: gpiokey as /devices/platform/soc/gpiokey/input/input4 [ 35.441194] Unable to handle kernel paging request at virtual address 100000000 [ 35.449447] pgd = ffffffc03c8e8000 [ 35.453317] [100000000] *pgd=0000000000000000, *pud=0000000000000000 [ 35.460308] Internal error: Oops: 86000005 [#1] PREEMPT SMP Segmentation fault [ 35.460311] Modules linked in:root@dragonboard:~# gpio_keys(+) 8723ds gt9xxnew_ts [ 35.460325] CPU: 3 PID: 2010 Comm: insmod Not tainted 4.9.170 #41 [ 35.460327] Hardware name: sun50iw10 (DT) [ 35.460330] task: ffffffc03ad20e00 task.stack: ffffffc03ca28000 [ 35.460335] PC is at 0x100000000 [ 35.460337] LR is at 0x100000000 [ 35.460341] pc : [<0000000100000000>] lr : [<0000000100000000>] pstate: 40000145 [ 35.460342] sp : ffffffc03ca2bab0 [ 35.460347] x29: 0000000100000000 x28: 0000000000000001 [ 35.460352] x27: 0000000000000000 x26: 0000000000000001 [ 35.460356] x25: ffffffc03b32e7d0 x24: ffffff80008d49d0 [ 35.460361] x23: 0000000000000005 x22: ffffff80008d4818 [ 35.460365] x21: 0000000000000000 x20: ffffff8008ae5000 [ 35.460369] x19: ffffffc03d3dec90 x18: 000000000000000a [ 35.460374] x17: 0000000000000007 x16: 0000000000000001 [ 35.460378] x15: 000000000000033b x14: ffffff8008abd100 [ 35.460383] x13: 000000000214d000 x12: 0000000000000038 [ 35.460387] x11: 0101010101010101 x10: 7f7f7f7f7f7f7f7f [ 35.460391] x9 : 0000000000000000 x8 : ffffffc03c5c2538 [ 35.460396] x7 : 0000000000000000 x6 : 000000000000448e [ 35.460400] x5 : 000000003600e0f6 x4 : ffffffc03b0467b0 [ 35.460404] x3 : 0000000000000001 x2 : 0000000000000000 [ 35.460408] x1 : 0000000000000000 x0 : 0000000000000000 [ 35.460411] [ 35.460411] SP: 0xffffffc03ca2ba30: [ 35.460424] ba30 008d4818 ffffff80 00000005 00000000 008d49d0 ffffff80 3b32e7d0 ffffffc0 [ 35.460435] ba50 00000001 00000000 00000000 00000000 00000001 00000000 00000000 00000001 [ 35.460446] ba70 00000000 00000001 3ca2bab0 ffffffc0 00000000 00000001 40000145 00000000 [ 35.460458] ba90 3d3dec90 ffffffc0 08ae5000 ffffff80 ffffffff 0000007f 08ae5000 ffffff80 [ 35.460469] bab0 3ca2baf0 ffffffc0 08380314 ffffff80 3d3dec90 ffffffc0 3d3decf0 ffffffc0 [ 35.460481] bad0 008d4818 ffffff80 08a74000 ffffff80 08a74cf0 ffffff80 08a74000 ffffff80 [ 35.460492] baf0 3ca2bb20 ffffffc0 0837e738 ffffff80 00000000 00000000 008d4818 ffffff80 [ 35.460504] bb10 083802a0 ffffff80 0837e724 ffffff80 3ca2bb60 ffffffc0 0837fc0c ffffff80 [ 35.460508] [ 35.460508] X4: 0xffffffc03b046730: [ 35.460523] 6730 cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc [ 35.460535] 6750 cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc [ 35.460546] 6770 cccccccc cccccccc cccccccc cccccccc 3b03e600 ffffffc0 3d3d9ae0 ffffffc0 [ 35.460558] 6790 08383148 ffffff80 00000001 00000166 00000164 00000010 0887ecba ffffff80 [ 35.460569] 67b0 3b045e88 ffffffc0 3c5c2488 ffffffc0 00000000 00000000 00000000 00000000 [ 35.460580] 67d0 00000000 00000000 00000000 00000000 00000000 00000000 0838de4c ffffff80 [ 35.460591] 67f0 3b0467a8 ffffffc0 00000003 00000000 00000000 00000000 00000000 00000000 [ 35.460602] 6810 00000000 00000000 8f313930 00000000 00000000 00000000 00000000 00000000 [ 35.460606] [ 35.460606] X8: 0xffffffc03c5c24b8: [ 35.460618] 24b8 00000000 00000000 0838de4c ffffff80 3c5c2480 ffffffc0 00000003 00000000 [ 35.460628] 24d8 00000000 00000000 00000000 00000000 00000000 00000000 3cb04c66 00000008 [ 35.460639] 24f8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 35.460650] 2518 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 35.460662] 2538 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b [ 35.460673] 2558 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b [ 35.460685] 2578 6b6b6b6b a56b6b6b cccccccc cccccccc 3c5c2d80 ffffffc0 0838d5dc ffffff80 [ 35.460696] 2598 081527c0 ffffff80 08153b94 ffffff80 08153cec ffffff80 08153da0 ffffff80 [ 35.460703] [ 35.460703] X19: 0xffffffc03d3dec10: [ 35.460714] ec10 cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc [ 35.460726] ec30 cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc [ 35.460738] ec50 cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc [ 35.460749] ec70 cccccccc cccccccc cccccccc cccccccc 3d3d6b00 ffffffc0 ffffffff 00000000 [ 35.460761] ec90 3d006690 ffffffc0 3d36b080 ffffffc0 3d3d6b00 ffffffc0 3d3d92a8 ffffffc0 [ 35.460772] ecb0 3d3d8ca8 ffffffc0 3d0066a0 ffffffc0 3e234800 ffffffc0 08a74770 ffffff80 [ 35.460783] ecd0 3d3f11d8 ffffffc0 00000005 00000007 00000000 00000000 00000000 00000000 [ 35.460794] ecf0 00000000 00030003 3d3decf8 ffffffc0 3d3decf8 ffffffc0 3ad20e00 ffffffc0 [ 35.460799] [ 35.460799] X25: 0xffffffc03b32e750: [ 35.460811] e750 00000000 00000000 00000000 00000000 088227db ffffff80 00000124 00000000 [ 35.460822] e770 080fcee0 ffffff80 00000000 00000000 00000000 00000000 00000000 00000000 [ 35.460833] e790 00000000 00000000 0881e79f ffffff80 00000124 00000000 080fcc6c ffffff80 [ 35.460844] e7b0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 35.460855] e7d0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 35.460866] e7f0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 35.460877] e810 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 35.460887] e830 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 35.460890] [ 35.460894] Process insmod (pid: 2010, stack limit = 0xffffffc03ca28000) [ 35.460897] Stack: (0xffffffc03ca2bab0 to 0xffffffc03ca2c000) [ 35.460902] baa0: ffffffc03ca2baf0 ffffff8008380314 [ 35.460907] bac0: ffffffc03d3dec90 ffffffc03d3decf0 ffffff80008d4818 ffffff8008a74000 [ 35.460912] bae0: ffffff8008a74cf0 ffffff8008a74000 ffffffc03ca2bb20 ffffff800837e738 [ 35.460917] bb00: 0000000000000000 ffffff80008d4818 ffffff80083802a0 ffffff800837e724 [ 35.460922] bb20: ffffffc03ca2bb60 ffffff800837fc0c ffffff80008d4818 0000000000000000 [ 35.460927] bb40: ffffffc03cb15280 ffffff80086e0484 ffffffc03e197d28 ffffffc03d36b0e8 [ 35.460932] bb60: ffffffc03ca2bb70 ffffff800837f7c8 ffffffc03ca2bbb0 ffffff8008380bd8 [ 35.460937] bb80: ffffff80008d4818 ffffffc03ad20e00 0000000000000000 ffffff800897d000 [ 35.460942] bba0: ffffff80008d7000 ffffff800829e09c ffffffc03ca2bbe0 ffffff8008381a8c [ 35.460947] bbc0: 00000000ffffffff ffffffc03ad20e00 0000000000000000 0000000100150014 [ 35.460952] bbe0: ffffffc03ca2bbf0 ffffff80008d7018 ffffffc03ca2bc00 ffffff80080839e0 [ 35.460957] bc00: ffffffc03ca2bc90 ffffff8008110f88 ffffff80008d4980 ffffff8008990000 [ 35.460962] bc20: ffffffc03c623048 ffffffc03c621200 0000000000000000 ffffff8008110f5c [ 35.460967] bc40: ffffff80008d4980 ffffff8008990000 ffffffc03c623048 ffffff8008990000 [ 35.460972] bc60: 0000000000000000 ffffff80008d49d0 ffffffc03b32e7d0 0000000000000001 [ 35.460977] bc80: 0000000000000000 00000000000409aa ffffffc03ca2bcc0 ffffff80080ff83c [ 35.460982] bca0: ffffff80008d4980 ffffffc03ca2be58 ffffffc03c623048 ffffff8008990000 [ 35.460987] bcc0: ffffffc03ca2be00 ffffff80080ffd50 00000000000001b8 00000000f6f931c0 [ 35.460992] bce0: ffffff800a0ec1b8 0000000000000000 0000000001555008 0000000000010000 [ 35.460997] bd00: ffffffc03ad20e00 0000000000000080 ffffff8008705000 ffffffc03ad20e00 [ 35.461002] bd20: ffffffc03b32aa80 ffffff8008ab2080 ffffff8008705000 ffffff8000000064 [ 35.461007] bd40: ffffff8000000072 ffffff800897d000 ffffffc00000006e 0000000001555008 [ 35.461012] bd60: ffffff8000000124 ffffff80080fccf4 ffffff800870a000 00000000024000c0 [ 35.461017] bd80: ffffffc03ca2bde0 ffffff8008147094 0000000000000000 0000000000000000 [ 35.461022] bda0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [ 35.461027] bdc0: 00006c656e72656b 0000000000000000 0000000000000000 0000000000000000 [ 35.461031] bde0: 0000000000000000 0000000000000000 0000000000000000 00000000000409aa [ 35.461036] be00: 0000000000000000 ffffff8008083200 fffffffffffffe76 0000004036dd6000 [ 35.461041] be20: ffffffffffffffff 00000000f7067ad0 0000000060000010 0000000000000011 [ 35.461046] be40: 000000000000018a ffffff80080877c8 0000000000400004 ffffff800a0ac000 [ 35.461051] be60: 00000000000401b8 ffffff800a0eb7f8 ffffff800a0cc280 ffffff800a0cd510 [ 35.461056] be80: 0000000000001c00 00000000000022f0 0000000000000000 0000000000000000 [ 35.461060] bea0: 00000000000018f0 0000002600000025 0000000000000010 000000000000000c [ 35.461065] bec0: 00000000f6f53008 00000000000401b8 0000000001555008 00000000ff9c3dd1 [ 35.461069] bee0: 00000000000401b8 0000000000000002 00000000ff9c3dd1 0000000000000080 [ 35.461074] bf00: 00000000000b17f8 0000000000000000 00000000f70d7000 0000000000000000 [ 35.461079] bf20: 00000000ff9c3018 00000000ff9c3008 0000000000019a5c 0000000000000000 [ 35.461083] bf40: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [ 35.461087] bf60: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [ 35.461092] bf80: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [ 35.461096] bfa0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [ 35.461101] bfc0: 00000000f7067ad0 0000000060000010 00000000f6f53008 0000000000000080 [ 35.461105] bfe0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [ 35.461108] Call trace: [ 35.461111] Exception stack(0xffffffc03ca2b8e0 to 0xffffffc03ca2ba10) [ 35.461116] b8e0: ffffffc03d3dec90 0000007fffffffff ffffffc03ca2bab0 0000000100000000 [ 35.461121] b900: ffffffc03ca2b940 ffffff80080b7f64 ffffff8008ae5bb0 0000000000000000 [ 35.461126] b920: ffffff800838e294 ffffffc03ca2b980 ffffffc03ca2b970 ffffff800838e294 [ 35.461131] b940: ffffffc03ca2b960 ffffff80086e7ddc ffffffc03d3ded40 ffffffc03d3ded40 [ 35.461136] b960: ffffffc03ca2b970 ffffff80086e819c ffffffc03ca2b980 ffffff800838e2d0 [ 35.461140] b980: 0000000000000000 0000000000000000 0000000000000000 0000000000000001 [ 35.461145] b9a0: ffffffc03b0467b0 000000003600e0f6 000000000000448e 0000000000000000 [ 35.461149] b9c0: ffffffc03c5c2538 0000000000000000 7f7f7f7f7f7f7f7f 0101010101010101 [ 35.461154] b9e0: 0000000000000038 000000000214d000 ffffff8008abd100 000000000000033b [ 35.461157] ba00: 0000000000000001 0000000000000007 [ 35.461160] [<0000000100000000>] 0x100000000 [ 35.461168] Code: bad PC value [ 35.461172] ---[ end trace 907da43ec18b52e9 ]--- root@dragonboard:~#
使用测试:
root@dragonboard:~# evtest /dev/input/event4 Input driver version is 1.0.1 Input device ID: bus 0x19 vendor 0x1 product 0x1 version 0x100 Input device name: "gpiokey" Supported events: Event type 0 (Sync) Event type 1 (Key) Event code 116 (Power) Testing ... (interrupt to exit) Event: time 1329177649.837142, type 1 (Key), code 116 (Power), value 1 Event: time 1329177649.837142, -------------- Report Sync ------------ Event: time 1329177649.884949, type 1 (Key), code 116 (Power), value 0 Event: time 1329177649.884949, -------------- Report Sync ------------ Event: time 1329177654.314768, type 1 (Key), code 116 (Power), value 1 Event: time 1329177654.314768, -------------- Report Sync ------------ Event: time 1329177654.538283, type 1 (Key), code 116 (Power), value 0 Event: time 1329177654.538283, -------------- Report Sync ------------ Event: time 1329177654.538466, type 1 (Key), code 116 (Power), value 1 Event: time 1329177654.538466, -------------- Report Sync ------------ Event: time 1329177654.539263, type 1 (Key), code 116 (Power), value 0 Event: time 1329177654.539263, -------------- Report Sync ------------ Event: time 1329177654.828112, type 1 (Key), code 116 (Power), value 1 Event: time 1329177654.828112, -------------- Report Sync ------------ Event: time 1329177655.048398, type 1 (Key), code 116 (Power), value 0 Event: time 1329177655.048398, -------------- Report Sync ------------ Event: time 1329177655.282155, type 1 (Key), code 116 (Power), value 1 Event: time 1329177655.282155, -------------- Report Sync ------------ Event: time 1329177655.475772, type 1 (Key), code 116 (Power), value 0 Event: time 1329177655.475772, -------------- Report Sync ------------
中断正常:
root@dragonboard:~# cat /proc/interrupts CPU0 CPU1 CPU2 CPU3 5: 15931 16899 32881 12213 GIC-0 27 Level arch_timer 7: 0 0 0 0 wakeupgen 119 Level ppu_interrupt 8: 0 0 0 0 wakeupgen 66 Level 30f0000.iommu 17: 0 0 0 0 sunxi_pio_edge 7 Edge rtw_wifi_gpio_wakeup 185: 1 0 0 0 sunxi_pio_edge 134 Edge sdc0 cd 308: 13 0 0 0 sunxi_pio_edge 257 Edge gpio key power
不能卸载:
root@dragonboard:~# rmmod gpio_keys rmmod: can't unload 'gpio_keys': Device or resource busy root@dragonboard:~#
-
参考链接: Linux_GPIO_开发指南.pdf
-
但是同样的配置用在旋转编码器 CONFIG_INPUT_GPIO_ROTARY_ENCODER=m 完全正常,也是中断模式。
-
我开始以为这个问题可以不解决,结果Linux是可以用,但是进入安卓系统会直接挂掉:
[ 3.123534] input: soc@03000000:gpio_keys as /devices/platform/soc/soc@03000000:gpio_keys/input/input2 [ 3.134354] Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: gpio_keys_probe+0x844/0x848
然后就没然后了。
-
改一下驱动
From 35d26bbc1975aa3b79bce8bcbcde15c0857093dd Mon Sep 17 00:00:00 2001 From: YuzukiTsuru <gloomyghost@gloomyghost.com> Date: Tue, 5 Apr 2022 00:19:12 +0800 Subject: [PATCH] fix gpio keys sunxi driver Signed-off-by: YuzukiTsuru <gloomyghost@gloomyghost.com> --- gpio_keys.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/gpio_keys.c b/gpio_keys.c index 9b8079c..077e50b 100644 --- a/gpio_keys.c +++ b/gpio_keys.c @@ -33,6 +33,22 @@ #include <linux/of_irq.h> #include <linux/spinlock.h> +#ifdef CONFIG_ARCH_SUNXI +#include <linux/sunxi-gpio.h> +#ifdef CONFIG_GPIOKEYS_AS_POWERKEY +#include <linux/kthread.h> +#include <linux/reboot.h> +#include <linux/jiffies.h> +struct long_press_key { + struct delayed_work long_work; + unsigned long start; + int press_sta; +}; + +static struct long_press_key long_press_key; +#endif +#endif + struct gpio_button_data { const struct gpio_keys_button *button; struct input_dev *input; @@ -142,10 +158,14 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata) */ disable_irq(bdata->irq); - if (bdata->gpiod) + if (bdata->gpiod) { cancel_delayed_work_sync(&bdata->work); - else +#ifdef CONFIG_GPIOKEYS_AS_POWERKEY + cancel_delayed_work_sync(&long_press_key.long_work); +#endif + } else { del_timer_sync(&bdata->release_timer); + } bdata->disabled = true; } @@ -355,12 +375,33 @@ static struct attribute_group gpio_keys_attr_group = { .attrs = gpio_keys_attrs, }; +#ifdef CONFIG_GPIOKEYS_AS_POWERKEY +static void gpio_keys_long_press_func(struct work_struct *work) +{ + struct long_press_key *key = &long_press_key; + unsigned long end; + unsigned long diff_time; + + end = jiffies; + diff_time = jiffies_to_msecs(end - key->start); + if ((key->press_sta == 1) && (diff_time >= 5000)) { + orderly_poweroff(true); + return; + } else { + return; + } +} +#endif + static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) { const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; int state; +#ifdef CONFIG_GPIOKEYS_AS_POWERKEY + struct long_press_key *key = &long_press_key; +#endif state = gpiod_get_value_cansleep(bdata->gpiod); if (state < 0) { @@ -373,7 +414,29 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) if (state) input_event(input, type, button->code, button->value); } else { +#ifdef CONFIG_GPIOKEYS_AS_POWERKEY + if (button->code != KEY_POWER) { + input_event(input, type, button->code, state); + } else { + if (state) { + key->start = jiffies; + key->press_sta = 1; + queue_delayed_work(system_wq, &key->long_work, + msecs_to_jiffies(5 * 1000)); + } else if ((!state) && (key->start != 0)) { + key->press_sta = 0; + cancel_delayed_work(&key->long_work); + input_event(input, EV_KEY, button->code, 1); + input_sync(input); + udelay(5000); + input_event(input, EV_KEY, button->code, 0); + input_sync(input); + } + } +#else input_event(input, type, button->code, state); +#endif + } input_sync(input); } @@ -459,10 +522,14 @@ static void gpio_keys_quiesce_key(void *data) { struct gpio_button_data *bdata = data; - if (bdata->gpiod) + if (bdata->gpiod) { cancel_delayed_work_sync(&bdata->work); - else +#ifdef CONFIG_GPIOKEYS_AS_POWERKEY + cancel_delayed_work_sync(&long_press_key.long_work); +#endif + } else { del_timer_sync(&bdata->release_timer); + } } static int gpio_keys_setup_key(struct platform_device *pdev, @@ -502,7 +569,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev, bdata->gpiod = gpio_to_desc(button->gpio); if (!bdata->gpiod) return -EINVAL; - +#if 0 if (button->debounce_interval) { error = gpiod_set_debounce(bdata->gpiod, button->debounce_interval * 1000); @@ -511,6 +578,9 @@ static int gpio_keys_setup_key(struct platform_device *pdev, bdata->software_debounce = button->debounce_interval; } +#else + bdata->software_debounce = button->debounce_interval; +#endif if (button->irq) { bdata->irq = button->irq; @@ -526,6 +596,10 @@ static int gpio_keys_setup_key(struct platform_device *pdev, bdata->irq = irq; } +#ifdef CONFIG_GPIOKEYS_AS_POWERKEY + INIT_DELAYED_WORK(&long_press_key.long_work, + gpio_keys_long_press_func); +#endif INIT_DELAYED_WORK(&bdata->work, gpio_keys_gpio_work_func); isr = gpio_keys_gpio_isr; @@ -664,11 +738,15 @@ gpio_keys_get_devtree_pdata(struct device *dev) i = 0; for_each_available_child_of_node(node, pp) { +#ifdef CONFIG_ARCH_SUNXI + struct gpio_config flags; +#else enum of_gpio_flags flags; +#endif button = &pdata->buttons[i++]; - button->gpio = of_get_gpio_flags(pp, 0, &flags); + button->gpio = of_get_gpio_flags(pp, 0, (enum of_gpio_flags *)&flags); if (button->gpio < 0) { error = button->gpio; if (error != -ENOENT) { @@ -679,7 +757,11 @@ gpio_keys_get_devtree_pdata(struct device *dev) return ERR_PTR(error); } } else { +#ifdef CONFIG_ARCH_SUNXI + button->active_low = flags.data & OF_GPIO_ACTIVE_LOW; +#else button->active_low = flags & OF_GPIO_ACTIVE_LOW; +#endif } button->irq = irq_of_parse_and_map(pp, 0); -- 2.33.0
-
-
@yuzukitsuru
意外发现 R818 TINA SDK 里面的 gpio_keys.c 和你的一模一样。 -
Kconfig:
config GPIOKEYS_AS_POWERKEY bool "gpio-keys used as power-key" default n depends on KEYBOARD_GPIO help Say Y here to enable the function that can add long-press funciton in gpio-keys and use it as power-key.
gpio_keys.c:
/* * Driver for keys on GPIO lines capable of generating interrupts. * * Copyright 2005 Phil Blundell * Copyright 2010, 2011 David Jander <david@protonic.nl> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/sched.h> #include <linux/pm.h> #include <linux/slab.h> #include <linux/sysctl.h> #include <linux/proc_fs.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/gpio_keys.h> #include <linux/workqueue.h> #include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> #include <linux/of_irq.h> #include <linux/spinlock.h> #ifdef CONFIG_ARCH_SUNXI #include <linux/sunxi-gpio.h> #ifdef CONFIG_GPIOKEYS_AS_POWERKEY #include <linux/kthread.h> #include <linux/reboot.h> #include <linux/jiffies.h> struct long_press_key { struct delayed_work long_work; unsigned long start; int press_sta; }; static struct long_press_key long_press_key; #endif #endif struct gpio_button_data { const struct gpio_keys_button *button; struct input_dev *input; struct gpio_desc *gpiod; struct timer_list release_timer; unsigned int release_delay; /* in msecs, for IRQ-only buttons */ struct delayed_work work; unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */ unsigned int irq; spinlock_t lock; bool disabled; bool key_pressed; }; struct gpio_keys_drvdata { const struct gpio_keys_platform_data *pdata; struct input_dev *input; struct mutex disable_lock; struct gpio_button_data data[0]; }; /* * SYSFS interface for enabling/disabling keys and switches: * * There are 4 attributes under /sys/devices/platform/gpio-keys/ * keys [ro] - bitmap of keys (EV_KEY) which can be * disabled * switches [ro] - bitmap of switches (EV_SW) which can be * disabled * disabled_keys [rw] - bitmap of keys currently disabled * disabled_switches [rw] - bitmap of switches currently disabled * * Userland can change these values and hence disable event generation * for each key (or switch). Disabling a key means its interrupt line * is disabled. * * For example, if we have following switches set up as gpio-keys: * SW_DOCK = 5 * SW_CAMERA_LENS_COVER = 9 * SW_KEYPAD_SLIDE = 10 * SW_FRONT_PROXIMITY = 11 * This is read from switches: * 11-9,5 * Next we want to disable proximity (11) and dock (5), we write: * 11,5 * to file disabled_switches. Now proximity and dock IRQs are disabled. * This can be verified by reading the file disabled_switches: * 11,5 * If we now want to enable proximity (11) switch we write: * 5 * to disabled_switches. * * We can disable only those keys which don't allow sharing the irq. */ /** * get_n_events_by_type() - returns maximum number of events per @type * @type: type of button (%EV_KEY, %EV_SW) * * Return value of this function can be used to allocate bitmap * large enough to hold all bits for given type. */ static int get_n_events_by_type(int type) { BUG_ON(type != EV_SW && type != EV_KEY); return (type == EV_KEY) ? KEY_CNT : SW_CNT; } /** * get_bm_events_by_type() - returns bitmap of supported events per @type * @input: input device from which bitmap is retrieved * @type: type of button (%EV_KEY, %EV_SW) * * Return value of this function can be used to allocate bitmap * large enough to hold all bits for given type. */ static const unsigned long *get_bm_events_by_type(struct input_dev *dev, int type) { BUG_ON(type != EV_SW && type != EV_KEY); return (type == EV_KEY) ? dev->keybit : dev->swbit; } /** * gpio_keys_disable_button() - disables given GPIO button * @bdata: button data for button to be disabled * * Disables button pointed by @bdata. This is done by masking * IRQ line. After this function is called, button won't generate * input events anymore. Note that one can only disable buttons * that don't share IRQs. * * Make sure that @bdata->disable_lock is locked when entering * this function to avoid races when concurrent threads are * disabling buttons at the same time. */ static void gpio_keys_disable_button(struct gpio_button_data *bdata) { if (!bdata->disabled) { /* * Disable IRQ and associated timer/work structure. */ disable_irq(bdata->irq); if (bdata->gpiod) { cancel_delayed_work_sync(&bdata->work); #ifdef CONFIG_GPIOKEYS_AS_POWERKEY cancel_delayed_work_sync(&long_press_key.long_work); #endif } else { del_timer_sync(&bdata->release_timer); } bdata->disabled = true; } } /** * gpio_keys_enable_button() - enables given GPIO button * @bdata: button data for button to be disabled * * Enables given button pointed by @bdata. * * Make sure that @bdata->disable_lock is locked when entering * this function to avoid races with concurrent threads trying * to enable the same button at the same time. */ static void gpio_keys_enable_button(struct gpio_button_data *bdata) { if (bdata->disabled) { enable_irq(bdata->irq); bdata->disabled = false; } } /** * gpio_keys_attr_show_helper() - fill in stringified bitmap of buttons * @ddata: pointer to drvdata * @buf: buffer where stringified bitmap is written * @type: button type (%EV_KEY, %EV_SW) * @only_disabled: does caller want only those buttons that are * currently disabled or all buttons that can be * disabled * * This function writes buttons that can be disabled to @buf. If * @only_disabled is true, then @buf contains only those buttons * that are currently disabled. Returns 0 on success or negative * errno on failure. */ static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata, char *buf, unsigned int type, bool only_disabled) { int n_events = get_n_events_by_type(type); unsigned long *bits; ssize_t ret; int i; bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL); if (!bits) return -ENOMEM; for (i = 0; i < ddata->pdata->nbuttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; if (bdata->button->type != type) continue; if (only_disabled && !bdata->disabled) continue; __set_bit(bdata->button->code, bits); } ret = scnprintf(buf, PAGE_SIZE - 1, "%*pbl", n_events, bits); buf[ret++] = '\n'; buf[ret] = '\0'; kfree(bits); return ret; } /** * gpio_keys_attr_store_helper() - enable/disable buttons based on given bitmap * @ddata: pointer to drvdata * @buf: buffer from userspace that contains stringified bitmap * @type: button type (%EV_KEY, %EV_SW) * * This function parses stringified bitmap from @buf and disables/enables * GPIO buttons accordingly. Returns 0 on success and negative error * on failure. */ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, const char *buf, unsigned int type) { int n_events = get_n_events_by_type(type); const unsigned long *bitmap = get_bm_events_by_type(ddata->input, type); unsigned long *bits; ssize_t error; int i; bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL); if (!bits) return -ENOMEM; error = bitmap_parselist(buf, bits, n_events); if (error) goto out; /* First validate */ if (!bitmap_subset(bits, bitmap, n_events)) { error = -EINVAL; goto out; } for (i = 0; i < ddata->pdata->nbuttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; if (bdata->button->type != type) continue; if (test_bit(bdata->button->code, bits) && !bdata->button->can_disable) { error = -EINVAL; goto out; } } mutex_lock(&ddata->disable_lock); for (i = 0; i < ddata->pdata->nbuttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; if (bdata->button->type != type) continue; if (test_bit(bdata->button->code, bits)) gpio_keys_disable_button(bdata); else gpio_keys_enable_button(bdata); } mutex_unlock(&ddata->disable_lock); out: kfree(bits); return error; } #define ATTR_SHOW_FN(name, type, only_disabled) \ static ssize_t gpio_keys_show_##name(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct platform_device *pdev = to_platform_device(dev); \ struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); \ \ return gpio_keys_attr_show_helper(ddata, buf, \ type, only_disabled); \ } ATTR_SHOW_FN(keys, EV_KEY, false); ATTR_SHOW_FN(switches, EV_SW, false); ATTR_SHOW_FN(disabled_keys, EV_KEY, true); ATTR_SHOW_FN(disabled_switches, EV_SW, true); /* * ATTRIBUTES: * * /sys/devices/platform/gpio-keys/keys [ro] * /sys/devices/platform/gpio-keys/switches [ro] */ static DEVICE_ATTR(keys, S_IRUGO, gpio_keys_show_keys, NULL); static DEVICE_ATTR(switches, S_IRUGO, gpio_keys_show_switches, NULL); #define ATTR_STORE_FN(name, type) \ static ssize_t gpio_keys_store_##name(struct device *dev, \ struct device_attribute *attr, \ const char *buf, \ size_t count) \ { \ struct platform_device *pdev = to_platform_device(dev); \ struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); \ ssize_t error; \ \ error = gpio_keys_attr_store_helper(ddata, buf, type); \ if (error) \ return error; \ \ return count; \ } ATTR_STORE_FN(disabled_keys, EV_KEY); ATTR_STORE_FN(disabled_switches, EV_SW); /* * ATTRIBUTES: * * /sys/devices/platform/gpio-keys/disabled_keys [rw] * /sys/devices/platform/gpio-keys/disables_switches [rw] */ static DEVICE_ATTR(disabled_keys, S_IWUSR | S_IRUGO, gpio_keys_show_disabled_keys, gpio_keys_store_disabled_keys); static DEVICE_ATTR(disabled_switches, S_IWUSR | S_IRUGO, gpio_keys_show_disabled_switches, gpio_keys_store_disabled_switches); static struct attribute *gpio_keys_attrs[] = { &dev_attr_keys.attr, &dev_attr_switches.attr, &dev_attr_disabled_keys.attr, &dev_attr_disabled_switches.attr, NULL, }; static struct attribute_group gpio_keys_attr_group = { .attrs = gpio_keys_attrs, }; #ifdef CONFIG_GPIOKEYS_AS_POWERKEY static void gpio_keys_long_press_func(struct work_struct *work) { struct long_press_key *key = &long_press_key; unsigned long end; unsigned long diff_time; end = jiffies; diff_time = jiffies_to_msecs(end - key->start); if ((key->press_sta == 1) && (diff_time >= 5000)) { orderly_poweroff(true); return; } else { return; } } #endif static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) { const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; int state; #ifdef CONFIG_GPIOKEYS_AS_POWERKEY struct long_press_key *key = &long_press_key; #endif state = gpiod_get_value_cansleep(bdata->gpiod); if (state < 0) { dev_err(input->dev.parent, "failed to get gpio state: %d\n", state); return; } if (type == EV_ABS) { if (state) input_event(input, type, button->code, button->value); } else { #ifdef CONFIG_GPIOKEYS_AS_POWERKEY if (button->code != KEY_POWER) { input_event(input, type, button->code, state); } else { if (state) { key->start = jiffies; key->press_sta = 1; queue_delayed_work(system_wq, &key->long_work, msecs_to_jiffies(5 * 1000)); } else if ((!state) && (key->start != 0)) { key->press_sta = 0; cancel_delayed_work(&key->long_work); input_event(input, EV_KEY, button->code, 1); input_sync(input); udelay(5000); input_event(input, EV_KEY, button->code, 0); input_sync(input); } } #else input_event(input, type, button->code, state); #endif } input_sync(input); } static void gpio_keys_gpio_work_func(struct work_struct *work) { struct gpio_button_data *bdata = container_of(work, struct gpio_button_data, work.work); gpio_keys_gpio_report_event(bdata); if (bdata->button->wakeup) pm_relax(bdata->input->dev.parent); } static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; BUG_ON(irq != bdata->irq); if (bdata->button->wakeup) pm_stay_awake(bdata->input->dev.parent); mod_delayed_work(system_wq, &bdata->work, msecs_to_jiffies(bdata->software_debounce)); return IRQ_HANDLED; } static void gpio_keys_irq_timer(unsigned long _data) { struct gpio_button_data *bdata = (struct gpio_button_data *)_data; struct input_dev *input = bdata->input; unsigned long flags; spin_lock_irqsave(&bdata->lock, flags); if (bdata->key_pressed) { input_event(input, EV_KEY, bdata->button->code, 0); input_sync(input); bdata->key_pressed = false; } spin_unlock_irqrestore(&bdata->lock, flags); } static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned long flags; BUG_ON(irq != bdata->irq); spin_lock_irqsave(&bdata->lock, flags); if (!bdata->key_pressed) { if (bdata->button->wakeup) pm_wakeup_event(bdata->input->dev.parent, 0); input_event(input, EV_KEY, button->code, 1); input_sync(input); if (!bdata->release_delay) { input_event(input, EV_KEY, button->code, 0); input_sync(input); goto out; } bdata->key_pressed = true; } if (bdata->release_delay) mod_timer(&bdata->release_timer, jiffies + msecs_to_jiffies(bdata->release_delay)); out: spin_unlock_irqrestore(&bdata->lock, flags); return IRQ_HANDLED; } static void gpio_keys_quiesce_key(void *data) { struct gpio_button_data *bdata = data; if (bdata->gpiod) { cancel_delayed_work_sync(&bdata->work); #ifdef CONFIG_GPIOKEYS_AS_POWERKEY cancel_delayed_work_sync(&long_press_key.long_work); #endif } else { del_timer_sync(&bdata->release_timer); } } static int gpio_keys_setup_key(struct platform_device *pdev, struct input_dev *input, struct gpio_button_data *bdata, const struct gpio_keys_button *button) { const char *desc = button->desc ? button->desc : "gpio_keys"; struct device *dev = &pdev->dev; irq_handler_t isr; unsigned long irqflags; int irq; int error; bdata->input = input; bdata->button = button; spin_lock_init(&bdata->lock); /* * Legacy GPIO number, so request the GPIO here and * convert it to descriptor. */ if (gpio_is_valid(button->gpio)) { unsigned flags = GPIOF_IN; if (button->active_low) flags |= GPIOF_ACTIVE_LOW; error = devm_gpio_request_one(&pdev->dev, button->gpio, flags, desc); if (error < 0) { dev_err(dev, "Failed to request GPIO %d, error %d\n", button->gpio, error); return error; } bdata->gpiod = gpio_to_desc(button->gpio); if (!bdata->gpiod) return -EINVAL; #if 0 if (button->debounce_interval) { error = gpiod_set_debounce(bdata->gpiod, button->debounce_interval * 1000); /* use timer if gpiolib doesn't provide debounce */ if (error < 0) bdata->software_debounce = button->debounce_interval; } #else bdata->software_debounce = button->debounce_interval; #endif if (button->irq) { bdata->irq = button->irq; } else { irq = gpiod_to_irq(bdata->gpiod); if (irq < 0) { error = irq; dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n", button->gpio, error); return error; } bdata->irq = irq; } #ifdef CONFIG_GPIOKEYS_AS_POWERKEY INIT_DELAYED_WORK(&long_press_key.long_work, gpio_keys_long_press_func); #endif INIT_DELAYED_WORK(&bdata->work, gpio_keys_gpio_work_func); isr = gpio_keys_gpio_isr; irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; } else { if (!button->irq) { dev_err(dev, "No IRQ specified\n"); return -EINVAL; } bdata->irq = button->irq; if (button->type && button->type != EV_KEY) { dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n"); return -EINVAL; } bdata->release_delay = button->debounce_interval; setup_timer(&bdata->release_timer, gpio_keys_irq_timer, (unsigned long)bdata); isr = gpio_keys_irq_isr; irqflags = 0; } input_set_capability(input, button->type ?: EV_KEY, button->code); /* * Install custom action to cancel release timer and * workqueue item. */ error = devm_add_action(&pdev->dev, gpio_keys_quiesce_key, bdata); if (error) { dev_err(&pdev->dev, "failed to register quiesce action, error: %d\n", error); return error; } /* * If platform has specified that the button can be disabled, * we don't want it to share the interrupt line. */ if (!button->can_disable) irqflags |= IRQF_SHARED; error = devm_request_any_context_irq(&pdev->dev, bdata->irq, isr, irqflags, desc, bdata); if (error < 0) { dev_err(dev, "Unable to claim irq %d; error %d\n", bdata->irq, error); return error; } return 0; } static void gpio_keys_report_state(struct gpio_keys_drvdata *ddata) { struct input_dev *input = ddata->input; int i; for (i = 0; i < ddata->pdata->nbuttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; if (bdata->gpiod) gpio_keys_gpio_report_event(bdata); } input_sync(input); } static int gpio_keys_open(struct input_dev *input) { struct gpio_keys_drvdata *ddata = input_get_drvdata(input); const struct gpio_keys_platform_data *pdata = ddata->pdata; int error; if (pdata->enable) { error = pdata->enable(input->dev.parent); if (error) return error; } /* Report current state of buttons that are connected to GPIOs */ gpio_keys_report_state(ddata); return 0; } static void gpio_keys_close(struct input_dev *input) { struct gpio_keys_drvdata *ddata = input_get_drvdata(input); const struct gpio_keys_platform_data *pdata = ddata->pdata; if (pdata->disable) pdata->disable(input->dev.parent); } /* * Handlers for alternative sources of platform_data */ #ifdef CONFIG_OF /* * Translate OpenFirmware node properties into platform_data */ static struct gpio_keys_platform_data * gpio_keys_get_devtree_pdata(struct device *dev) { struct device_node *node, *pp; struct gpio_keys_platform_data *pdata; struct gpio_keys_button *button; int error; int nbuttons; int i; node = dev->of_node; if (!node) return ERR_PTR(-ENODEV); nbuttons = of_get_available_child_count(node); if (nbuttons == 0) return ERR_PTR(-ENODEV); pdata = devm_kzalloc(dev, sizeof(*pdata) + nbuttons * sizeof(*button), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); pdata->buttons = (struct gpio_keys_button *)(pdata + 1); pdata->nbuttons = nbuttons; pdata->rep = !!of_get_property(node, "autorepeat", NULL); of_property_read_string(node, "label", &pdata->name); i = 0; for_each_available_child_of_node(node, pp) { #ifdef CONFIG_ARCH_SUNXI struct gpio_config flags; #else enum of_gpio_flags flags; #endif button = &pdata->buttons[i++]; button->gpio = of_get_gpio_flags(pp, 0, (enum of_gpio_flags *)&flags); if (button->gpio < 0) { error = button->gpio; if (error != -ENOENT) { if (error != -EPROBE_DEFER) dev_err(dev, "Failed to get gpio flags, error: %d\n", error); return ERR_PTR(error); } } else { #ifdef CONFIG_ARCH_SUNXI button->active_low = flags.data & OF_GPIO_ACTIVE_LOW; #else button->active_low = flags & OF_GPIO_ACTIVE_LOW; #endif } button->irq = irq_of_parse_and_map(pp, 0); if (!gpio_is_valid(button->gpio) && !button->irq) { dev_err(dev, "Found button without gpios or irqs\n"); return ERR_PTR(-EINVAL); } if (of_property_read_u32(pp, "linux,code", &button->code)) { dev_err(dev, "Button without keycode: 0x%x\n", button->gpio); return ERR_PTR(-EINVAL); } button->desc = of_get_property(pp, "label", NULL); if (of_property_read_u32(pp, "linux,input-type", &button->type)) button->type = EV_KEY; button->wakeup = of_property_read_bool(pp, "wakeup-source") || /* legacy name */ of_property_read_bool(pp, "gpio-key,wakeup"); button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL); if (of_property_read_u32(pp, "debounce-interval", &button->debounce_interval)) button->debounce_interval = 5; } if (pdata->nbuttons == 0) return ERR_PTR(-EINVAL); return pdata; } static const struct of_device_id gpio_keys_of_match[] = { { .compatible = "gpio-keys", }, { }, }; MODULE_DEVICE_TABLE(of, gpio_keys_of_match); #else static inline struct gpio_keys_platform_data * gpio_keys_get_devtree_pdata(struct device *dev) { return ERR_PTR(-ENODEV); } #endif static int gpio_keys_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev); struct gpio_keys_drvdata *ddata; struct input_dev *input; size_t size; int i, error; int wakeup = 0; if (!pdata) { pdata = gpio_keys_get_devtree_pdata(dev); if (IS_ERR(pdata)) return PTR_ERR(pdata); } size = sizeof(struct gpio_keys_drvdata) + pdata->nbuttons * sizeof(struct gpio_button_data); ddata = devm_kzalloc(dev, size, GFP_KERNEL); if (!ddata) { dev_err(dev, "failed to allocate state\n"); return -ENOMEM; } input = devm_input_allocate_device(dev); if (!input) { dev_err(dev, "failed to allocate input device\n"); return -ENOMEM; } ddata->pdata = pdata; ddata->input = input; mutex_init(&ddata->disable_lock); platform_set_drvdata(pdev, ddata); input_set_drvdata(input, ddata); input->name = pdata->name ? : pdev->name; input->phys = "gpio-keys/input0"; input->dev.parent = &pdev->dev; input->open = gpio_keys_open; input->close = gpio_keys_close; input->id.bustype = BUS_HOST; input->id.vendor = 0x0001; input->id.product = 0x0001; input->id.version = 0x0100; /* Enable auto repeat feature of Linux input subsystem */ if (pdata->rep) __set_bit(EV_REP, input->evbit); for (i = 0; i < pdata->nbuttons; i++) { const struct gpio_keys_button *button = &pdata->buttons[i]; struct gpio_button_data *bdata = &ddata->data[i]; error = gpio_keys_setup_key(pdev, input, bdata, button); if (error) return error; if (button->wakeup) wakeup = 1; } error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); if (error) { dev_err(dev, "Unable to export keys/switches, error: %d\n", error); return error; } error = input_register_device(input); if (error) { dev_err(dev, "Unable to register input device, error: %d\n", error); goto err_remove_group; } device_init_wakeup(&pdev->dev, wakeup); return 0; err_remove_group: sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); return error; } static int gpio_keys_remove(struct platform_device *pdev) { sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); device_init_wakeup(&pdev->dev, 0); return 0; } #ifdef CONFIG_PM_SLEEP static int gpio_keys_suspend(struct device *dev) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); struct input_dev *input = ddata->input; int i; if (device_may_wakeup(dev)) { for (i = 0; i < ddata->pdata->nbuttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; if (bdata->button->wakeup) enable_irq_wake(bdata->irq); } } else { mutex_lock(&input->mutex); if (input->users) gpio_keys_close(input); mutex_unlock(&input->mutex); } return 0; } static int gpio_keys_resume(struct device *dev) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); struct input_dev *input = ddata->input; int error = 0; int i; if (device_may_wakeup(dev)) { for (i = 0; i < ddata->pdata->nbuttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; if (bdata->button->wakeup) disable_irq_wake(bdata->irq); } } else { mutex_lock(&input->mutex); if (input->users) error = gpio_keys_open(input); mutex_unlock(&input->mutex); } if (error) return error; gpio_keys_report_state(ddata); return 0; } #endif static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume); static struct platform_driver gpio_keys_device_driver = { .probe = gpio_keys_probe, .remove = gpio_keys_remove, .driver = { .name = "gpio-keys", .pm = &gpio_keys_pm_ops, .of_match_table = of_match_ptr(gpio_keys_of_match), } }; static int __init gpio_keys_init(void) { return platform_driver_register(&gpio_keys_device_driver); } static void __exit gpio_keys_exit(void) { platform_driver_unregister(&gpio_keys_device_driver); } late_initcall(gpio_keys_init); module_exit(gpio_keys_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>"); MODULE_DESCRIPTION("Keyboard driver for GPIOs"); MODULE_ALIAS("platform:gpio-keys");
看了一下驱动,按键长按是 电源键。
-
我现在也遇到了这个情况,gpio-key模块可以正常加载,但是CONFIG_INPUT_GPIO_ROTARY_ENCODER=m,加载的时候报错,然后evtest还是正常使用,这个要怎样修改啊?
-
@tigger 我最近也有遇到相同的问题,不过当时没看到有这个贴。走了点弯路,不过搞清楚原因了。
https://bbs.aw-ol.com/topic/2099/全志平台-gpio-keys-驱动应用和-stack-crash-解决?_=1663384404172
-
@yuzukitsuru 看来以后得多逛逛论坛了,前段时间我也遇到这个问题,走了弯路,没想到论坛早有补丁发出来了。哈哈。
Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号