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