• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * RainShadow Tech HDMI CEC driver
4  *
5  * Copyright 2016 Hans Verkuil <hverkuil@xs4all.nl
6  */
7 
8 /*
9  * Notes:
10  *
11  * The higher level protocols are currently disabled. This can be added
12  * later, similar to how this is done for the Pulse Eight CEC driver.
13  *
14  * Documentation of the protocol is available here:
15  *
16  * http://rainshadowtech.com/doc/HDMICECtoUSBandRS232v2.0.pdf
17  */
18 
19 #include <linux/completion.h>
20 #include <linux/ctype.h>
21 #include <linux/delay.h>
22 #include <linux/init.h>
23 #include <linux/interrupt.h>
24 #include <linux/kernel.h>
25 #include <linux/module.h>
26 #include <linux/serio.h>
27 #include <linux/slab.h>
28 #include <linux/spinlock.h>
29 #include <linux/time.h>
30 #include <linux/workqueue.h>
31 
32 #include <media/cec.h>
33 
34 MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
35 MODULE_DESCRIPTION("RainShadow Tech HDMI CEC driver");
36 MODULE_LICENSE("GPL");
37 
38 #define DATA_SIZE 256
39 
40 struct rain {
41 	struct device *dev;
42 	struct serio *serio;
43 	struct cec_adapter *adap;
44 	struct completion cmd_done;
45 	struct work_struct work;
46 
47 	/* Low-level ringbuffer, collecting incoming characters */
48 	char buf[DATA_SIZE];
49 	unsigned int buf_rd_idx;
50 	unsigned int buf_wr_idx;
51 	unsigned int buf_len;
52 	spinlock_t buf_lock;
53 
54 	/* command buffer */
55 	char cmd[DATA_SIZE];
56 	unsigned int cmd_idx;
57 	bool cmd_started;
58 
59 	/* reply to a command, only used to store the firmware version */
60 	char cmd_reply[DATA_SIZE];
61 
62 	struct mutex write_lock;
63 };
64 
rain_process_msg(struct rain * rain)65 static void rain_process_msg(struct rain *rain)
66 {
67 	struct cec_msg msg = {};
68 	const char *cmd = rain->cmd + 3;
69 	int stat = -1;
70 
71 	for (; *cmd; cmd++) {
72 		if (!isxdigit(*cmd))
73 			continue;
74 		if (isxdigit(cmd[0]) && isxdigit(cmd[1])) {
75 			if (msg.len == CEC_MAX_MSG_SIZE)
76 				break;
77 			if (hex2bin(msg.msg + msg.len, cmd, 1))
78 				continue;
79 			msg.len++;
80 			cmd++;
81 			continue;
82 		}
83 		if (!cmd[1])
84 			stat = hex_to_bin(cmd[0]);
85 		break;
86 	}
87 
88 	if (rain->cmd[0] == 'R') {
89 		if (stat == 1 || stat == 2)
90 			cec_received_msg(rain->adap, &msg);
91 		return;
92 	}
93 
94 	switch (stat) {
95 	case 1:
96 		cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_OK);
97 		break;
98 	case 2:
99 		cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_NACK);
100 		break;
101 	default:
102 		cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_LOW_DRIVE);
103 		break;
104 	}
105 }
106 
rain_irq_work_handler(struct work_struct * work)107 static void rain_irq_work_handler(struct work_struct *work)
108 {
109 	struct rain *rain =
110 		container_of(work, struct rain, work);
111 
112 	while (true) {
113 		unsigned long flags;
114 		char data;
115 
116 		spin_lock_irqsave(&rain->buf_lock, flags);
117 		if (!rain->buf_len) {
118 			spin_unlock_irqrestore(&rain->buf_lock, flags);
119 			break;
120 		}
121 
122 		data = rain->buf[rain->buf_rd_idx];
123 		rain->buf_len--;
124 		rain->buf_rd_idx = (rain->buf_rd_idx + 1) & 0xff;
125 
126 		spin_unlock_irqrestore(&rain->buf_lock, flags);
127 
128 		if (!rain->cmd_started && data != '?')
129 			continue;
130 
131 		switch (data) {
132 		case '\r':
133 			rain->cmd[rain->cmd_idx] = '\0';
134 			dev_dbg(rain->dev, "received: %s\n", rain->cmd);
135 			if (!memcmp(rain->cmd, "REC", 3) ||
136 			    !memcmp(rain->cmd, "STA", 3)) {
137 				rain_process_msg(rain);
138 			} else {
139 				strscpy(rain->cmd_reply, rain->cmd,
140 					sizeof(rain->cmd_reply));
141 				complete(&rain->cmd_done);
142 			}
143 			rain->cmd_idx = 0;
144 			rain->cmd_started = false;
145 			break;
146 
147 		case '\n':
148 			rain->cmd_idx = 0;
149 			rain->cmd_started = false;
150 			break;
151 
152 		case '?':
153 			rain->cmd_idx = 0;
154 			rain->cmd_started = true;
155 			break;
156 
157 		default:
158 			if (rain->cmd_idx >= DATA_SIZE - 1) {
159 				dev_dbg(rain->dev,
160 					"throwing away %d bytes of garbage\n", rain->cmd_idx);
161 				rain->cmd_idx = 0;
162 			}
163 			rain->cmd[rain->cmd_idx++] = data;
164 			break;
165 		}
166 	}
167 }
168 
rain_interrupt(struct serio * serio,unsigned char data,unsigned int flags)169 static irqreturn_t rain_interrupt(struct serio *serio, unsigned char data,
170 				    unsigned int flags)
171 {
172 	struct rain *rain = serio_get_drvdata(serio);
173 
174 	if (rain->buf_len == DATA_SIZE) {
175 		dev_warn_once(rain->dev, "buffer overflow\n");
176 		return IRQ_HANDLED;
177 	}
178 	spin_lock(&rain->buf_lock);
179 	rain->buf_len++;
180 	rain->buf[rain->buf_wr_idx] = data;
181 	rain->buf_wr_idx = (rain->buf_wr_idx + 1) & 0xff;
182 	spin_unlock(&rain->buf_lock);
183 	schedule_work(&rain->work);
184 	return IRQ_HANDLED;
185 }
186 
rain_disconnect(struct serio * serio)187 static void rain_disconnect(struct serio *serio)
188 {
189 	struct rain *rain = serio_get_drvdata(serio);
190 
191 	cancel_work_sync(&rain->work);
192 	cec_unregister_adapter(rain->adap);
193 	dev_info(&serio->dev, "disconnected\n");
194 	serio_close(serio);
195 	serio_set_drvdata(serio, NULL);
196 	kfree(rain);
197 }
198 
rain_send(struct rain * rain,const char * command)199 static int rain_send(struct rain *rain, const char *command)
200 {
201 	int err = serio_write(rain->serio, '!');
202 
203 	dev_dbg(rain->dev, "send: %s\n", command);
204 	while (!err && *command)
205 		err = serio_write(rain->serio, *command++);
206 	if (!err)
207 		err = serio_write(rain->serio, '~');
208 
209 	return err;
210 }
211 
rain_send_and_wait(struct rain * rain,const char * cmd,const char * reply)212 static int rain_send_and_wait(struct rain *rain,
213 			      const char *cmd, const char *reply)
214 {
215 	int err;
216 
217 	init_completion(&rain->cmd_done);
218 
219 	mutex_lock(&rain->write_lock);
220 	err = rain_send(rain, cmd);
221 	if (err)
222 		goto err;
223 
224 	if (!wait_for_completion_timeout(&rain->cmd_done, HZ)) {
225 		err = -ETIMEDOUT;
226 		goto err;
227 	}
228 	if (reply && strncmp(rain->cmd_reply, reply, strlen(reply))) {
229 		dev_dbg(rain->dev,
230 			 "transmit of '%s': received '%s' instead of '%s'\n",
231 			 cmd, rain->cmd_reply, reply);
232 		err = -EIO;
233 	}
234 err:
235 	mutex_unlock(&rain->write_lock);
236 	return err;
237 }
238 
rain_setup(struct rain * rain,struct serio * serio,struct cec_log_addrs * log_addrs,u16 * pa)239 static int rain_setup(struct rain *rain, struct serio *serio,
240 			struct cec_log_addrs *log_addrs, u16 *pa)
241 {
242 	int err;
243 
244 	err = rain_send_and_wait(rain, "R", "REV");
245 	if (err)
246 		return err;
247 	dev_info(rain->dev, "Firmware version %s\n", rain->cmd_reply + 4);
248 
249 	err = rain_send_and_wait(rain, "Q 1", "QTY");
250 	if (err)
251 		return err;
252 	err = rain_send_and_wait(rain, "c0000", "CFG");
253 	if (err)
254 		return err;
255 	return rain_send_and_wait(rain, "A F 0000", "ADR");
256 }
257 
rain_cec_adap_enable(struct cec_adapter * adap,bool enable)258 static int rain_cec_adap_enable(struct cec_adapter *adap, bool enable)
259 {
260 	return 0;
261 }
262 
rain_cec_adap_log_addr(struct cec_adapter * adap,u8 log_addr)263 static int rain_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
264 {
265 	struct rain *rain = cec_get_drvdata(adap);
266 	u8 cmd[16];
267 
268 	if (log_addr == CEC_LOG_ADDR_INVALID)
269 		log_addr = CEC_LOG_ADDR_UNREGISTERED;
270 	snprintf(cmd, sizeof(cmd), "A %x", log_addr);
271 	return rain_send_and_wait(rain, cmd, "ADR");
272 }
273 
rain_cec_adap_transmit(struct cec_adapter * adap,u8 attempts,u32 signal_free_time,struct cec_msg * msg)274 static int rain_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
275 				    u32 signal_free_time, struct cec_msg *msg)
276 {
277 	struct rain *rain = cec_get_drvdata(adap);
278 	char cmd[2 * CEC_MAX_MSG_SIZE + 16];
279 	unsigned int i;
280 	int err;
281 
282 	if (msg->len == 1) {
283 		snprintf(cmd, sizeof(cmd), "x%x", cec_msg_destination(msg));
284 	} else {
285 		char hex[3];
286 
287 		snprintf(cmd, sizeof(cmd), "x%x %02x ",
288 			 cec_msg_destination(msg), msg->msg[1]);
289 		for (i = 2; i < msg->len; i++) {
290 			snprintf(hex, sizeof(hex), "%02x", msg->msg[i]);
291 			strlcat(cmd, hex, sizeof(cmd));
292 		}
293 	}
294 	mutex_lock(&rain->write_lock);
295 	err = rain_send(rain, cmd);
296 	mutex_unlock(&rain->write_lock);
297 	return err;
298 }
299 
300 static const struct cec_adap_ops rain_cec_adap_ops = {
301 	.adap_enable = rain_cec_adap_enable,
302 	.adap_log_addr = rain_cec_adap_log_addr,
303 	.adap_transmit = rain_cec_adap_transmit,
304 };
305 
rain_connect(struct serio * serio,struct serio_driver * drv)306 static int rain_connect(struct serio *serio, struct serio_driver *drv)
307 {
308 	u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL;
309 	struct rain *rain;
310 	int err = -ENOMEM;
311 	struct cec_log_addrs log_addrs = {};
312 	u16 pa = CEC_PHYS_ADDR_INVALID;
313 
314 	rain = kzalloc(sizeof(*rain), GFP_KERNEL);
315 
316 	if (!rain)
317 		return -ENOMEM;
318 
319 	rain->serio = serio;
320 	rain->adap = cec_allocate_adapter(&rain_cec_adap_ops, rain,
321 					  dev_name(&serio->dev), caps, 1);
322 	err = PTR_ERR_OR_ZERO(rain->adap);
323 	if (err < 0)
324 		goto free_device;
325 
326 	rain->dev = &serio->dev;
327 	serio_set_drvdata(serio, rain);
328 	INIT_WORK(&rain->work, rain_irq_work_handler);
329 	mutex_init(&rain->write_lock);
330 	spin_lock_init(&rain->buf_lock);
331 
332 	err = serio_open(serio, drv);
333 	if (err)
334 		goto delete_adap;
335 
336 	err = rain_setup(rain, serio, &log_addrs, &pa);
337 	if (err)
338 		goto close_serio;
339 
340 	err = cec_register_adapter(rain->adap, &serio->dev);
341 	if (err < 0)
342 		goto close_serio;
343 
344 	rain->dev = &rain->adap->devnode.dev;
345 	return 0;
346 
347 close_serio:
348 	serio_close(serio);
349 delete_adap:
350 	cec_delete_adapter(rain->adap);
351 	serio_set_drvdata(serio, NULL);
352 free_device:
353 	kfree(rain);
354 	return err;
355 }
356 
357 static const struct serio_device_id rain_serio_ids[] = {
358 	{
359 		.type	= SERIO_RS232,
360 		.proto	= SERIO_RAINSHADOW_CEC,
361 		.id	= SERIO_ANY,
362 		.extra	= SERIO_ANY,
363 	},
364 	{ 0 }
365 };
366 
367 MODULE_DEVICE_TABLE(serio, rain_serio_ids);
368 
369 static struct serio_driver rain_drv = {
370 	.driver		= {
371 		.name	= "rainshadow-cec",
372 	},
373 	.description	= "RainShadow Tech HDMI CEC driver",
374 	.id_table	= rain_serio_ids,
375 	.interrupt	= rain_interrupt,
376 	.connect	= rain_connect,
377 	.disconnect	= rain_disconnect,
378 };
379 
380 module_serio_driver(rain_drv);
381