]>
Commit | Line | Data |
---|---|---|
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 | ||
39 | MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>"); | |
40 | MODULE_DESCRIPTION("RainShadow Tech HDMI CEC driver"); | |
41 | MODULE_LICENSE("GPL"); | |
42 | ||
43 | #define DATA_SIZE 256 | |
44 | ||
45 | struct 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 | ||
70 | static 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 | ||
112 | static 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 | ||
173 | static 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 | ||
191 | static 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 | ||
203 | static 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 | ||
216 | static 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 | } | |
238 | err: | |
239 | mutex_unlock(&rain->write_lock); | |
240 | return err; | |
241 | } | |
242 | ||
243 | static 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 | ||
262 | static int rain_cec_adap_enable(struct cec_adapter *adap, bool enable) | |
263 | { | |
264 | return 0; | |
265 | } | |
266 | ||
267 | static 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 | ||
278 | static 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 | ||
304 | static 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 | ||
310 | static 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 | ||
352 | close_serio: | |
353 | serio_close(serio); | |
354 | delete_adap: | |
355 | cec_delete_adapter(rain->adap); | |
356 | serio_set_drvdata(serio, NULL); | |
357 | free_device: | |
358 | kfree(rain); | |
359 | return err; | |
360 | } | |
361 | ||
362 | static 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 | ||
372 | MODULE_DEVICE_TABLE(serio, rain_serio_ids); | |
373 | ||
374 | static 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 | ||
385 | module_serio_driver(rain_drv); |