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"); 154