xref: /linux/drivers/input/misc/qnap-mcu-input.c (revision 909fd2b89f2edab8ec440a67dcf8627614e272b6)
1*bb7e3611SHeiko Stuebner // SPDX-License-Identifier: GPL-2.0-only
2*bb7e3611SHeiko Stuebner 
3*bb7e3611SHeiko Stuebner /*
4*bb7e3611SHeiko Stuebner  * Driver for input events on QNAP-MCUs
5*bb7e3611SHeiko Stuebner  *
6*bb7e3611SHeiko Stuebner  * Copyright (C) 2024 Heiko Stuebner <heiko@sntech.de>
7*bb7e3611SHeiko Stuebner  */
8*bb7e3611SHeiko Stuebner 
9*bb7e3611SHeiko Stuebner #include <linux/input.h>
10*bb7e3611SHeiko Stuebner #include <linux/mfd/qnap-mcu.h>
11*bb7e3611SHeiko Stuebner #include <linux/module.h>
12*bb7e3611SHeiko Stuebner #include <linux/platform_device.h>
13*bb7e3611SHeiko Stuebner #include <linux/slab.h>
14*bb7e3611SHeiko Stuebner #include <uapi/linux/input-event-codes.h>
15*bb7e3611SHeiko Stuebner 
16*bb7e3611SHeiko Stuebner /*
17*bb7e3611SHeiko Stuebner  * The power-key needs to be pressed for a while to create an event,
18*bb7e3611SHeiko Stuebner  * so there is no use for overly frequent polling.
19*bb7e3611SHeiko Stuebner  */
20*bb7e3611SHeiko Stuebner #define POLL_INTERVAL		500
21*bb7e3611SHeiko Stuebner 
22*bb7e3611SHeiko Stuebner struct qnap_mcu_input_dev {
23*bb7e3611SHeiko Stuebner 	struct input_dev *input;
24*bb7e3611SHeiko Stuebner 	struct qnap_mcu *mcu;
25*bb7e3611SHeiko Stuebner 	struct device *dev;
26*bb7e3611SHeiko Stuebner 
27*bb7e3611SHeiko Stuebner 	struct work_struct beep_work;
28*bb7e3611SHeiko Stuebner 	int beep_type;
29*bb7e3611SHeiko Stuebner };
30*bb7e3611SHeiko Stuebner 
31*bb7e3611SHeiko Stuebner static void qnap_mcu_input_poll(struct input_dev *input)
32*bb7e3611SHeiko Stuebner {
33*bb7e3611SHeiko Stuebner 	struct qnap_mcu_input_dev *idev = input_get_drvdata(input);
34*bb7e3611SHeiko Stuebner 	static const u8 cmd[] = { '@', 'C', 'V' };
35*bb7e3611SHeiko Stuebner 	u8 reply[4];
36*bb7e3611SHeiko Stuebner 	int state, ret;
37*bb7e3611SHeiko Stuebner 
38*bb7e3611SHeiko Stuebner 	/* poll the power button */
39*bb7e3611SHeiko Stuebner 	ret = qnap_mcu_exec(idev->mcu, cmd, sizeof(cmd), reply, sizeof(reply));
40*bb7e3611SHeiko Stuebner 	if (ret)
41*bb7e3611SHeiko Stuebner 		return;
42*bb7e3611SHeiko Stuebner 
43*bb7e3611SHeiko Stuebner 	/* First bytes must mirror the sent command */
44*bb7e3611SHeiko Stuebner 	if (memcmp(cmd, reply, sizeof(cmd))) {
45*bb7e3611SHeiko Stuebner 		dev_err(idev->dev, "malformed data received\n");
46*bb7e3611SHeiko Stuebner 		return;
47*bb7e3611SHeiko Stuebner 	}
48*bb7e3611SHeiko Stuebner 
49*bb7e3611SHeiko Stuebner 	state = reply[3] - 0x30;
50*bb7e3611SHeiko Stuebner 	input_event(input, EV_KEY, KEY_POWER, state);
51*bb7e3611SHeiko Stuebner 	input_sync(input);
52*bb7e3611SHeiko Stuebner }
53*bb7e3611SHeiko Stuebner 
54*bb7e3611SHeiko Stuebner static void qnap_mcu_input_beeper_work(struct work_struct *work)
55*bb7e3611SHeiko Stuebner {
56*bb7e3611SHeiko Stuebner 	struct qnap_mcu_input_dev *idev =
57*bb7e3611SHeiko Stuebner 		container_of(work, struct qnap_mcu_input_dev, beep_work);
58*bb7e3611SHeiko Stuebner 	const u8 cmd[] = { '@', 'C', (idev->beep_type == SND_TONE) ? '3' : '2' };
59*bb7e3611SHeiko Stuebner 
60*bb7e3611SHeiko Stuebner 	qnap_mcu_exec_with_ack(idev->mcu, cmd, sizeof(cmd));
61*bb7e3611SHeiko Stuebner }
62*bb7e3611SHeiko Stuebner 
63*bb7e3611SHeiko Stuebner static int qnap_mcu_input_event(struct input_dev *input, unsigned int type,
64*bb7e3611SHeiko Stuebner 				unsigned int code, int value)
65*bb7e3611SHeiko Stuebner {
66*bb7e3611SHeiko Stuebner 	struct qnap_mcu_input_dev *idev = input_get_drvdata(input);
67*bb7e3611SHeiko Stuebner 
68*bb7e3611SHeiko Stuebner 	if (type != EV_SND || (code != SND_BELL && code != SND_TONE))
69*bb7e3611SHeiko Stuebner 		return -EOPNOTSUPP;
70*bb7e3611SHeiko Stuebner 
71*bb7e3611SHeiko Stuebner 	if (value < 0)
72*bb7e3611SHeiko Stuebner 		return -EINVAL;
73*bb7e3611SHeiko Stuebner 
74*bb7e3611SHeiko Stuebner 	/* beep runtime is determined by the MCU */
75*bb7e3611SHeiko Stuebner 	if (value == 0)
76*bb7e3611SHeiko Stuebner 		return 0;
77*bb7e3611SHeiko Stuebner 
78*bb7e3611SHeiko Stuebner 	/* Schedule work to actually turn the beeper on */
79*bb7e3611SHeiko Stuebner 	idev->beep_type = code;
80*bb7e3611SHeiko Stuebner 	schedule_work(&idev->beep_work);
81*bb7e3611SHeiko Stuebner 
82*bb7e3611SHeiko Stuebner 	return 0;
83*bb7e3611SHeiko Stuebner }
84*bb7e3611SHeiko Stuebner 
85*bb7e3611SHeiko Stuebner static void qnap_mcu_input_close(struct input_dev *input)
86*bb7e3611SHeiko Stuebner {
87*bb7e3611SHeiko Stuebner 	struct qnap_mcu_input_dev *idev = input_get_drvdata(input);
88*bb7e3611SHeiko Stuebner 
89*bb7e3611SHeiko Stuebner 	cancel_work_sync(&idev->beep_work);
90*bb7e3611SHeiko Stuebner }
91*bb7e3611SHeiko Stuebner 
92*bb7e3611SHeiko Stuebner static int qnap_mcu_input_probe(struct platform_device *pdev)
93*bb7e3611SHeiko Stuebner {
94*bb7e3611SHeiko Stuebner 	struct qnap_mcu *mcu = dev_get_drvdata(pdev->dev.parent);
95*bb7e3611SHeiko Stuebner 	struct qnap_mcu_input_dev *idev;
96*bb7e3611SHeiko Stuebner 	struct device *dev = &pdev->dev;
97*bb7e3611SHeiko Stuebner 	struct input_dev *input;
98*bb7e3611SHeiko Stuebner 	int ret;
99*bb7e3611SHeiko Stuebner 
100*bb7e3611SHeiko Stuebner 	idev = devm_kzalloc(dev, sizeof(*idev), GFP_KERNEL);
101*bb7e3611SHeiko Stuebner 	if (!idev)
102*bb7e3611SHeiko Stuebner 		return -ENOMEM;
103*bb7e3611SHeiko Stuebner 
104*bb7e3611SHeiko Stuebner 	input = devm_input_allocate_device(dev);
105*bb7e3611SHeiko Stuebner 	if (!input)
106*bb7e3611SHeiko Stuebner 		return dev_err_probe(dev, -ENOMEM, "no memory for input device\n");
107*bb7e3611SHeiko Stuebner 
108*bb7e3611SHeiko Stuebner 	idev->input = input;
109*bb7e3611SHeiko Stuebner 	idev->dev = dev;
110*bb7e3611SHeiko Stuebner 	idev->mcu = mcu;
111*bb7e3611SHeiko Stuebner 
112*bb7e3611SHeiko Stuebner 	input_set_drvdata(input, idev);
113*bb7e3611SHeiko Stuebner 
114*bb7e3611SHeiko Stuebner 	input->name		= "qnap-mcu";
115*bb7e3611SHeiko Stuebner 	input->phys		= "qnap-mcu-input/input0";
116*bb7e3611SHeiko Stuebner 	input->id.bustype	= BUS_HOST;
117*bb7e3611SHeiko Stuebner 	input->id.vendor	= 0x0001;
118*bb7e3611SHeiko Stuebner 	input->id.product	= 0x0001;
119*bb7e3611SHeiko Stuebner 	input->id.version	= 0x0100;
120*bb7e3611SHeiko Stuebner 	input->event		= qnap_mcu_input_event;
121*bb7e3611SHeiko Stuebner 	input->close		= qnap_mcu_input_close;
122*bb7e3611SHeiko Stuebner 
123*bb7e3611SHeiko Stuebner 	input_set_capability(input, EV_KEY, KEY_POWER);
124*bb7e3611SHeiko Stuebner 	input_set_capability(input, EV_SND, SND_BELL);
125*bb7e3611SHeiko Stuebner 	input_set_capability(input, EV_SND, SND_TONE);
126*bb7e3611SHeiko Stuebner 
127*bb7e3611SHeiko Stuebner 	INIT_WORK(&idev->beep_work, qnap_mcu_input_beeper_work);
128*bb7e3611SHeiko Stuebner 
129*bb7e3611SHeiko Stuebner 	ret = input_setup_polling(input, qnap_mcu_input_poll);
130*bb7e3611SHeiko Stuebner 	if (ret)
131*bb7e3611SHeiko Stuebner 		return dev_err_probe(dev, ret, "unable to set up polling\n");
132*bb7e3611SHeiko Stuebner 
133*bb7e3611SHeiko Stuebner 	input_set_poll_interval(input, POLL_INTERVAL);
134*bb7e3611SHeiko Stuebner 
135*bb7e3611SHeiko Stuebner 	ret = input_register_device(input);
136*bb7e3611SHeiko Stuebner 	if (ret)
137*bb7e3611SHeiko Stuebner 		return dev_err_probe(dev, ret, "unable to register input device\n");
138*bb7e3611SHeiko Stuebner 
139*bb7e3611SHeiko Stuebner 	return 0;
140*bb7e3611SHeiko Stuebner }
141*bb7e3611SHeiko Stuebner 
142*bb7e3611SHeiko Stuebner static struct platform_driver qnap_mcu_input_driver = {
143*bb7e3611SHeiko Stuebner 	.probe = qnap_mcu_input_probe,
144*bb7e3611SHeiko Stuebner 	.driver = {
145*bb7e3611SHeiko Stuebner 		.name = "qnap-mcu-input",
146*bb7e3611SHeiko Stuebner 	},
147*bb7e3611SHeiko Stuebner };
148*bb7e3611SHeiko Stuebner module_platform_driver(qnap_mcu_input_driver);
149*bb7e3611SHeiko Stuebner 
150*bb7e3611SHeiko Stuebner MODULE_ALIAS("platform:qnap-mcu-input");
151*bb7e3611SHeiko Stuebner MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
152*bb7e3611SHeiko Stuebner MODULE_DESCRIPTION("QNAP MCU input driver");
153*bb7e3611SHeiko Stuebner MODULE_LICENSE("GPL");
154