File Index
Files and directories
Sign in
FILE

rainshadow-cec.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/cec/usb/rainshadow/rainshadow-cec.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * RainShadow Tech HDMI CEC driver
  4 *
  5 * Copyright 2016 Hans Verkuil <hverkuil@kernel.org>
  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
 34MODULE_AUTHOR("Hans Verkuil <hverkuil@kernel.org>");
 35MODULE_DESCRIPTION("RainShadow Tech HDMI CEC driver");
 36MODULE_LICENSE("GPL");
 37
 38#define DATA_SIZE 256
 39
 40struct 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
 65static 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
107static 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
169static 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	spin_lock(&rain->buf_lock);
175	if (rain->buf_len == DATA_SIZE) {
176		spin_unlock(&rain->buf_lock);
177		dev_warn_once(rain->dev, "buffer overflow\n");
178		return IRQ_HANDLED;
179	}
180	rain->buf_len++;
181	rain->buf[rain->buf_wr_idx] = data;
182	rain->buf_wr_idx = (rain->buf_wr_idx + 1) & 0xff;
183	spin_unlock(&rain->buf_lock);
184	schedule_work(&rain->work);
185	return IRQ_HANDLED;
186}
187
188static void rain_disconnect(struct serio *serio)
189{
190	struct rain *rain = serio_get_drvdata(serio);
191
192	cancel_work_sync(&rain->work);
193	cec_unregister_adapter(rain->adap);
194	dev_info(&serio->dev, "disconnected\n");
195	serio_close(serio);
196	serio_set_drvdata(serio, NULL);
197	kfree(rain);
198}
199
200static int rain_send(struct rain *rain, const char *command)
201{
202	int err = serio_write(rain->serio, '!');
203
204	dev_dbg(rain->dev, "send: %s\n", command);
205	while (!err && *command)
206		err = serio_write(rain->serio, *command++);
207	if (!err)
208		err = serio_write(rain->serio, '~');
209
210	return err;
211}
212
213static int rain_send_and_wait(struct rain *rain,
214			      const char *cmd, const char *reply)
215{
216	int err;
217
218	init_completion(&rain->cmd_done);
219
220	mutex_lock(&rain->write_lock);
221	err = rain_send(rain, cmd);
222	if (err)
223		goto err;
224
225	if (!wait_for_completion_timeout(&rain->cmd_done, HZ)) {
226		err = -ETIMEDOUT;
227		goto err;
228	}
229	if (reply && strncmp(rain->cmd_reply, reply, strlen(reply))) {
230		dev_dbg(rain->dev,
231			 "transmit of '%s': received '%s' instead of '%s'\n",
232			 cmd, rain->cmd_reply, reply);
233		err = -EIO;
234	}
235err:
236	mutex_unlock(&rain->write_lock);
237	return err;
238}
239
240static int rain_setup(struct rain *rain, struct serio *serio,
241			struct cec_log_addrs *log_addrs, u16 *pa)
242{
243	int err;
244
245	err = rain_send_and_wait(rain, "R", "REV");
246	if (err)
247		return err;
248	dev_info(rain->dev, "Firmware version %s\n", rain->cmd_reply + 4);
249
250	err = rain_send_and_wait(rain, "Q 1", "QTY");
251	if (err)
252		return err;
253	err = rain_send_and_wait(rain, "c0000", "CFG");
254	if (err)
255		return err;
256	return rain_send_and_wait(rain, "A F 0000", "ADR");
257}
258
259static int rain_cec_adap_enable(struct cec_adapter *adap, bool enable)
260{
261	return 0;
262}
263
264static int rain_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
265{
266	struct rain *rain = cec_get_drvdata(adap);
267	u8 cmd[16];
268
269	if (log_addr == CEC_LOG_ADDR_INVALID)
270		log_addr = CEC_LOG_ADDR_UNREGISTERED;
271	snprintf(cmd, sizeof(cmd), "A %x", log_addr);
272	return rain_send_and_wait(rain, cmd, "ADR");
273}
274
275static int rain_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
276				    u32 signal_free_time, struct cec_msg *msg)
277{
278	struct rain *rain = cec_get_drvdata(adap);
279	char cmd[2 * CEC_MAX_MSG_SIZE + 16];
280	unsigned int i;
281	int err;
282
283	if (msg->len == 1) {
284		snprintf(cmd, sizeof(cmd), "x%x", cec_msg_destination(msg));
285	} else {
286		char hex[3];
287
288		snprintf(cmd, sizeof(cmd), "x%x %02x ",
289			 cec_msg_destination(msg), msg->msg[1]);
290		for (i = 2; i < msg->len; i++) {
291			snprintf(hex, sizeof(hex), "%02x", msg->msg[i]);
292			strlcat(cmd, hex, sizeof(cmd));
293		}
294	}
295	mutex_lock(&rain->write_lock);
296	err = rain_send(rain, cmd);
297	mutex_unlock(&rain->write_lock);
298	return err;
299}
300
301static const struct cec_adap_ops rain_cec_adap_ops = {
302	.adap_enable = rain_cec_adap_enable,
303	.adap_log_addr = rain_cec_adap_log_addr,
304	.adap_transmit = rain_cec_adap_transmit,
305};
306
307static int rain_connect(struct serio *serio, struct serio_driver *drv)
308{
309	u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL;
310	struct rain *rain;
311	int err = -ENOMEM;
312	struct cec_log_addrs log_addrs = {};
313	u16 pa = CEC_PHYS_ADDR_INVALID;
314
315	rain = kzalloc(sizeof(*rain), GFP_KERNEL);
316
317	if (!rain)
318		return -ENOMEM;
319
320	rain->serio = serio;
321	rain->adap = cec_allocate_adapter(&rain_cec_adap_ops, rain,
322					  dev_name(&serio->dev), caps, 1);
323	err = PTR_ERR_OR_ZERO(rain->adap);
324	if (err < 0)
325		goto free_device;
326
327	rain->dev = &serio->dev;
328	serio_set_drvdata(serio, rain);
329	INIT_WORK(&rain->work, rain_irq_work_handler);
330	mutex_init(&rain->write_lock);
331	spin_lock_init(&rain->buf_lock);
332
333	err = serio_open(serio, drv);
334	if (err)
335		goto delete_adap;
336
337	err = rain_setup(rain, serio, &log_addrs, &pa);
338	if (err)
339		goto close_serio;
340
341	err = cec_register_adapter(rain->adap, &serio->dev);
342	if (err < 0)
343		goto close_serio;
344
345	rain->dev = &rain->adap->devnode.dev;
346	return 0;
347
348close_serio:
349	serio_close(serio);
350delete_adap:
351	cec_delete_adapter(rain->adap);
352	serio_set_drvdata(serio, NULL);
353free_device:
354	kfree(rain);
355	return err;
356}
357
358static const struct serio_device_id rain_serio_ids[] = {
359	{
360		.type	= SERIO_RS232,
361		.proto	= SERIO_RAINSHADOW_CEC,
362		.id	= SERIO_ANY,
363		.extra	= SERIO_ANY,
364	},
365	{ 0 }
366};
367
368MODULE_DEVICE_TABLE(serio, rain_serio_ids);
369
370static struct serio_driver rain_drv = {
371	.driver		= {
372		.name	= "rainshadow-cec",
373	},
374	.description	= "RainShadow Tech HDMI CEC driver",
375	.id_table	= rain_serio_ids,
376	.interrupt	= rain_interrupt,
377	.connect	= rain_connect,
378	.disconnect	= rain_disconnect,
379};
380
381module_serio_driver(rain_drv);
~/ Read-only file preview.