Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Input: add driver for the input part of qnap-mcu devices

The MCU controls the power-button and beeper, so expose them as input
device. There is of course no interrupt line, so the status of the
power-button needs to be polled. To generate an event the power-button
also needs to be held for 1-2 seconds, so the polling interval does
not need to be overly fast.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Link: https://lore.kernel.org/r/20241107114712.538976-7-heiko@sntech.de
Signed-off-by: Lee Jones <lee@kernel.org>

authored by

Heiko Stuebner and committed by
Lee Jones
bb7e3611 2ec8bb47

+167
+1
MAINTAINERS
··· 19108 19108 QNAP MCU DRIVER 19109 19109 M: Heiko Stuebner <heiko@sntech.de> 19110 19110 S: Maintained 19111 + F: drivers/input/misc/qnap-mcu-input.c 19111 19112 F: drivers/leds/leds-qnap-mcu.c 19112 19113 F: drivers/mfd/qnap-mcu.c 19113 19114 F: include/linux/qnap-mcu.h
+12
drivers/input/misc/Kconfig
··· 917 917 To compile this driver as a module, choose M here: the 918 918 module will be called hisi_powerkey. 919 919 920 + config INPUT_QNAP_MCU 921 + tristate "Input Support for QNAP MCU controllers" 922 + depends on MFD_QNAP_MCU 923 + help 924 + This option enables support for input elements available on 925 + embedded controllers used in QNAP NAS devices. 926 + 927 + This includes a polled power-button as well as a beeper. 928 + 929 + To compile this driver as a module, choose M here: the 930 + module will be called qnap-mcu-input. 931 + 920 932 config INPUT_RAVE_SP_PWRBUTTON 921 933 tristate "RAVE SP Power button Driver" 922 934 depends on RAVE_SP_CORE
+1
drivers/input/misc/Makefile
··· 68 68 obj-$(CONFIG_INPUT_POWERMATE) += powermate.o 69 69 obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o 70 70 obj-$(CONFIG_INPUT_PWM_VIBRA) += pwm-vibra.o 71 + obj-$(CONFIG_INPUT_QNAP_MCU) += qnap-mcu-input.o 71 72 obj-$(CONFIG_INPUT_RAVE_SP_PWRBUTTON) += rave-sp-pwrbutton.o 72 73 obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o 73 74 obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
+153
drivers/input/misc/qnap-mcu-input.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + /* 4 + * Driver for input events on QNAP-MCUs 5 + * 6 + * Copyright (C) 2024 Heiko Stuebner <heiko@sntech.de> 7 + */ 8 + 9 + #include <linux/input.h> 10 + #include <linux/mfd/qnap-mcu.h> 11 + #include <linux/module.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/slab.h> 14 + #include <uapi/linux/input-event-codes.h> 15 + 16 + /* 17 + * The power-key needs to be pressed for a while to create an event, 18 + * so there is no use for overly frequent polling. 19 + */ 20 + #define POLL_INTERVAL 500 21 + 22 + struct qnap_mcu_input_dev { 23 + struct input_dev *input; 24 + struct qnap_mcu *mcu; 25 + struct device *dev; 26 + 27 + struct work_struct beep_work; 28 + int beep_type; 29 + }; 30 + 31 + static void qnap_mcu_input_poll(struct input_dev *input) 32 + { 33 + struct qnap_mcu_input_dev *idev = input_get_drvdata(input); 34 + static const u8 cmd[] = { '@', 'C', 'V' }; 35 + u8 reply[4]; 36 + int state, ret; 37 + 38 + /* poll the power button */ 39 + ret = qnap_mcu_exec(idev->mcu, cmd, sizeof(cmd), reply, sizeof(reply)); 40 + if (ret) 41 + return; 42 + 43 + /* First bytes must mirror the sent command */ 44 + if (memcmp(cmd, reply, sizeof(cmd))) { 45 + dev_err(idev->dev, "malformed data received\n"); 46 + return; 47 + } 48 + 49 + state = reply[3] - 0x30; 50 + input_event(input, EV_KEY, KEY_POWER, state); 51 + input_sync(input); 52 + } 53 + 54 + static void qnap_mcu_input_beeper_work(struct work_struct *work) 55 + { 56 + struct qnap_mcu_input_dev *idev = 57 + container_of(work, struct qnap_mcu_input_dev, beep_work); 58 + const u8 cmd[] = { '@', 'C', (idev->beep_type == SND_TONE) ? '3' : '2' }; 59 + 60 + qnap_mcu_exec_with_ack(idev->mcu, cmd, sizeof(cmd)); 61 + } 62 + 63 + static int qnap_mcu_input_event(struct input_dev *input, unsigned int type, 64 + unsigned int code, int value) 65 + { 66 + struct qnap_mcu_input_dev *idev = input_get_drvdata(input); 67 + 68 + if (type != EV_SND || (code != SND_BELL && code != SND_TONE)) 69 + return -EOPNOTSUPP; 70 + 71 + if (value < 0) 72 + return -EINVAL; 73 + 74 + /* beep runtime is determined by the MCU */ 75 + if (value == 0) 76 + return 0; 77 + 78 + /* Schedule work to actually turn the beeper on */ 79 + idev->beep_type = code; 80 + schedule_work(&idev->beep_work); 81 + 82 + return 0; 83 + } 84 + 85 + static void qnap_mcu_input_close(struct input_dev *input) 86 + { 87 + struct qnap_mcu_input_dev *idev = input_get_drvdata(input); 88 + 89 + cancel_work_sync(&idev->beep_work); 90 + } 91 + 92 + static int qnap_mcu_input_probe(struct platform_device *pdev) 93 + { 94 + struct qnap_mcu *mcu = dev_get_drvdata(pdev->dev.parent); 95 + struct qnap_mcu_input_dev *idev; 96 + struct device *dev = &pdev->dev; 97 + struct input_dev *input; 98 + int ret; 99 + 100 + idev = devm_kzalloc(dev, sizeof(*idev), GFP_KERNEL); 101 + if (!idev) 102 + return -ENOMEM; 103 + 104 + input = devm_input_allocate_device(dev); 105 + if (!input) 106 + return dev_err_probe(dev, -ENOMEM, "no memory for input device\n"); 107 + 108 + idev->input = input; 109 + idev->dev = dev; 110 + idev->mcu = mcu; 111 + 112 + input_set_drvdata(input, idev); 113 + 114 + input->name = "qnap-mcu"; 115 + input->phys = "qnap-mcu-input/input0"; 116 + input->id.bustype = BUS_HOST; 117 + input->id.vendor = 0x0001; 118 + input->id.product = 0x0001; 119 + input->id.version = 0x0100; 120 + input->event = qnap_mcu_input_event; 121 + input->close = qnap_mcu_input_close; 122 + 123 + input_set_capability(input, EV_KEY, KEY_POWER); 124 + input_set_capability(input, EV_SND, SND_BELL); 125 + input_set_capability(input, EV_SND, SND_TONE); 126 + 127 + INIT_WORK(&idev->beep_work, qnap_mcu_input_beeper_work); 128 + 129 + ret = input_setup_polling(input, qnap_mcu_input_poll); 130 + if (ret) 131 + return dev_err_probe(dev, ret, "unable to set up polling\n"); 132 + 133 + input_set_poll_interval(input, POLL_INTERVAL); 134 + 135 + ret = input_register_device(input); 136 + if (ret) 137 + return dev_err_probe(dev, ret, "unable to register input device\n"); 138 + 139 + return 0; 140 + } 141 + 142 + static struct platform_driver qnap_mcu_input_driver = { 143 + .probe = qnap_mcu_input_probe, 144 + .driver = { 145 + .name = "qnap-mcu-input", 146 + }, 147 + }; 148 + module_platform_driver(qnap_mcu_input_driver); 149 + 150 + MODULE_ALIAS("platform:qnap-mcu-input"); 151 + MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); 152 + MODULE_DESCRIPTION("QNAP MCU input driver"); 153 + MODULE_LICENSE("GPL");