1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
4 */
5
6 #include <linux/delay.h>
7 #include <linux/sched/types.h>
8 #include <linux/seq_file.h>
9 #include <linux/slab.h>
10
11 #include <media/cec-pin.h>
12 #include "cec-pin-priv.h"
13
14 struct cec_error_inj_cmd {
15 unsigned int mode_offset;
16 int arg_idx;
17 const char *cmd;
18 };
19
20 static const struct cec_error_inj_cmd cec_error_inj_cmds[] = {
21 { CEC_ERROR_INJ_RX_NACK_OFFSET, -1, "rx-nack" },
22 { CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET,
23 CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX, "rx-low-drive" },
24 { CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET, -1, "rx-add-byte" },
25 { CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET, -1, "rx-remove-byte" },
26 { CEC_ERROR_INJ_RX_ARB_LOST_OFFSET,
27 CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX, "rx-arb-lost" },
28
29 { CEC_ERROR_INJ_TX_NO_EOM_OFFSET, -1, "tx-no-eom" },
30 { CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET, -1, "tx-early-eom" },
31 { CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET,
32 CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX, "tx-add-bytes" },
33 { CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET, -1, "tx-remove-byte" },
34 { CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET,
35 CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX, "tx-short-bit" },
36 { CEC_ERROR_INJ_TX_LONG_BIT_OFFSET,
37 CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX, "tx-long-bit" },
38 { CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET,
39 CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX, "tx-custom-bit" },
40 { CEC_ERROR_INJ_TX_SHORT_START_OFFSET, -1, "tx-short-start" },
41 { CEC_ERROR_INJ_TX_LONG_START_OFFSET, -1, "tx-long-start" },
42 { CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET, -1, "tx-custom-start" },
43 { CEC_ERROR_INJ_TX_LAST_BIT_OFFSET,
44 CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX, "tx-last-bit" },
45 { CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET,
46 CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX, "tx-low-drive" },
47 { 0, -1, NULL }
48 };
49
cec_pin_rx_error_inj(struct cec_pin * pin)50 u16 cec_pin_rx_error_inj(struct cec_pin *pin)
51 {
52 u16 cmd = CEC_ERROR_INJ_OP_ANY;
53
54 /* Only when 18 bits have been received do we have a valid cmd */
55 if (!(pin->error_inj[cmd] & CEC_ERROR_INJ_RX_MASK) &&
56 pin->rx_bit >= 18)
57 cmd = pin->rx_msg.msg[1];
58 return (pin->error_inj[cmd] & CEC_ERROR_INJ_RX_MASK) ? cmd :
59 CEC_ERROR_INJ_OP_ANY;
60 }
61
cec_pin_tx_error_inj(struct cec_pin * pin)62 u16 cec_pin_tx_error_inj(struct cec_pin *pin)
63 {
64 u16 cmd = CEC_ERROR_INJ_OP_ANY;
65
66 if (!(pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) &&
67 pin->tx_msg.len > 1)
68 cmd = pin->tx_msg.msg[1];
69 return (pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) ? cmd :
70 CEC_ERROR_INJ_OP_ANY;
71 }
72
cec_pin_error_inj_parse_line(struct cec_adapter * adap,char * line)73 bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
74 {
75 static const char *delims = " \t\r";
76 struct cec_pin *pin = adap->pin;
77 unsigned int i;
78 bool has_pos = false;
79 char *p = line;
80 char *token;
81 char *comma;
82 u64 *error;
83 u8 *args;
84 bool has_op;
85 u8 op;
86 u8 mode;
87 u8 pos;
88
89 p = skip_spaces(p);
90 token = strsep(&p, delims);
91 if (!strcmp(token, "clear")) {
92 memset(pin->error_inj, 0, sizeof(pin->error_inj));
93 pin->rx_toggle = pin->tx_toggle = false;
94 pin->rx_no_low_drive = false;
95 pin->tx_ignore_nack_until_eom = false;
96 pin->tx_custom_pulse = false;
97 pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
98 pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
99 pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
100 pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
101 pin->tx_glitch_falling_edge = false;
102 pin->tx_glitch_rising_edge = false;
103 return true;
104 }
105 if (!strcmp(token, "rx-clear")) {
106 for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
107 pin->error_inj[i] &= ~CEC_ERROR_INJ_RX_MASK;
108 pin->rx_toggle = false;
109 pin->rx_no_low_drive = false;
110 return true;
111 }
112 if (!strcmp(token, "tx-clear")) {
113 for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
114 pin->error_inj[i] &= ~CEC_ERROR_INJ_TX_MASK;
115 pin->tx_toggle = false;
116 pin->tx_ignore_nack_until_eom = false;
117 pin->tx_custom_pulse = false;
118 pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
119 pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
120 pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
121 pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
122 pin->tx_glitch_falling_edge = false;
123 pin->tx_glitch_rising_edge = false;
124 return true;
125 }
126 if (!strcmp(token, "rx-no-low-drive")) {
127 pin->rx_no_low_drive = true;
128 return true;
129 }
130 if (!strcmp(token, "tx-ignore-nack-until-eom")) {
131 pin->tx_ignore_nack_until_eom = true;
132 return true;
133 }
134 if (!strcmp(token, "tx-custom-pulse")) {
135 pin->tx_custom_pulse = true;
136 cec_pin_start_timer(pin);
137 return true;
138 }
139 if (!strcmp(token, "tx-glitch-falling-edge")) {
140 pin->tx_glitch_falling_edge = true;
141 return true;
142 }
143 if (!strcmp(token, "tx-glitch-rising-edge")) {
144 pin->tx_glitch_rising_edge = true;
145 return true;
146 }
147 if (!p)
148 return false;
149
150 p = skip_spaces(p);
151 if (!strcmp(token, "tx-custom-low-usecs")) {
152 u32 usecs;
153
154 if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
155 return false;
156 pin->tx_custom_low_usecs = usecs;
157 return true;
158 }
159 if (!strcmp(token, "tx-custom-high-usecs")) {
160 u32 usecs;
161
162 if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
163 return false;
164 pin->tx_glitch_high_usecs = usecs;
165 return true;
166 }
167 if (!strcmp(token, "tx-glitch-low-usecs")) {
168 u32 usecs;
169
170 if (kstrtou32(p, 0, &usecs) || usecs > 100)
171 return false;
172 pin->tx_glitch_low_usecs = usecs;
173 return true;
174 }
175 if (!strcmp(token, "tx-glitch-high-usecs")) {
176 u32 usecs;
177
178 if (kstrtou32(p, 0, &usecs) || usecs > 100)
179 return false;
180 pin->tx_glitch_high_usecs = usecs;
181 return true;
182 }
183
184 comma = strchr(token, ',');
185 if (comma)
186 *comma++ = '\0';
187 if (!strcmp(token, "any")) {
188 has_op = false;
189 error = pin->error_inj + CEC_ERROR_INJ_OP_ANY;
190 args = pin->error_inj_args[CEC_ERROR_INJ_OP_ANY];
191 } else if (!kstrtou8(token, 0, &op)) {
192 has_op = true;
193 error = pin->error_inj + op;
194 args = pin->error_inj_args[op];
195 } else {
196 return false;
197 }
198
199 mode = CEC_ERROR_INJ_MODE_ONCE;
200 if (comma) {
201 if (!strcmp(comma, "off"))
202 mode = CEC_ERROR_INJ_MODE_OFF;
203 else if (!strcmp(comma, "once"))
204 mode = CEC_ERROR_INJ_MODE_ONCE;
205 else if (!strcmp(comma, "always"))
206 mode = CEC_ERROR_INJ_MODE_ALWAYS;
207 else if (!strcmp(comma, "toggle"))
208 mode = CEC_ERROR_INJ_MODE_TOGGLE;
209 else
210 return false;
211 }
212
213 token = strsep(&p, delims);
214 if (p) {
215 p = skip_spaces(p);
216 has_pos = !kstrtou8(p, 0, &pos);
217 }
218
219 if (!strcmp(token, "clear")) {
220 *error = 0;
221 return true;
222 }
223 if (!strcmp(token, "rx-clear")) {
224 *error &= ~CEC_ERROR_INJ_RX_MASK;
225 return true;
226 }
227 if (!strcmp(token, "tx-clear")) {
228 *error &= ~CEC_ERROR_INJ_TX_MASK;
229 return true;
230 }
231
232 for (i = 0; cec_error_inj_cmds[i].cmd; i++) {
233 const char *cmd = cec_error_inj_cmds[i].cmd;
234 unsigned int mode_offset;
235 u64 mode_mask;
236 int arg_idx;
237 bool is_bit_pos = true;
238
239 if (strcmp(token, cmd))
240 continue;
241
242 mode_offset = cec_error_inj_cmds[i].mode_offset;
243 mode_mask = CEC_ERROR_INJ_MODE_MASK << mode_offset;
244 arg_idx = cec_error_inj_cmds[i].arg_idx;
245
246 if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET) {
247 if (has_op)
248 return false;
249 if (!has_pos)
250 pos = 0x0f;
251 is_bit_pos = false;
252 } else if (mode_offset == CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET) {
253 if (!has_pos || !pos)
254 return false;
255 is_bit_pos = false;
256 }
257
258 if (arg_idx >= 0 && is_bit_pos) {
259 if (!has_pos || pos >= 160)
260 return false;
261 if (has_op && pos < 10 + 8)
262 return false;
263 /* Invalid bit position may not be the Ack bit */
264 if ((mode_offset == CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET ||
265 mode_offset == CEC_ERROR_INJ_TX_LONG_BIT_OFFSET ||
266 mode_offset == CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET) &&
267 (pos % 10) == 9)
268 return false;
269 }
270 *error &= ~mode_mask;
271 *error |= (u64)mode << mode_offset;
272 if (arg_idx >= 0)
273 args[arg_idx] = pos;
274 return true;
275 }
276 return false;
277 }
278
cec_pin_show_cmd(struct seq_file * sf,u32 cmd,u8 mode)279 static void cec_pin_show_cmd(struct seq_file *sf, u32 cmd, u8 mode)
280 {
281 if (cmd == CEC_ERROR_INJ_OP_ANY)
282 seq_puts(sf, "any,");
283 else
284 seq_printf(sf, "0x%02x,", cmd);
285 switch (mode) {
286 case CEC_ERROR_INJ_MODE_ONCE:
287 seq_puts(sf, "once ");
288 break;
289 case CEC_ERROR_INJ_MODE_ALWAYS:
290 seq_puts(sf, "always ");
291 break;
292 case CEC_ERROR_INJ_MODE_TOGGLE:
293 seq_puts(sf, "toggle ");
294 break;
295 default:
296 seq_puts(sf, "off ");
297 break;
298 }
299 }
300
cec_pin_error_inj_show(struct cec_adapter * adap,struct seq_file * sf)301 int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
302 {
303 struct cec_pin *pin = adap->pin;
304 unsigned int i, j;
305
306 seq_puts(sf, "# Clear error injections:\n");
307 seq_puts(sf, "# clear clear all rx and tx error injections\n");
308 seq_puts(sf, "# rx-clear clear all rx error injections\n");
309 seq_puts(sf, "# tx-clear clear all tx error injections\n");
310 seq_puts(sf, "# <op> clear clear all rx and tx error injections for <op>\n");
311 seq_puts(sf, "# <op> rx-clear clear all rx error injections for <op>\n");
312 seq_puts(sf, "# <op> tx-clear clear all tx error injections for <op>\n");
313 seq_puts(sf, "#\n");
314 seq_puts(sf, "# RX error injection settings:\n");
315 seq_puts(sf, "# rx-no-low-drive do not generate low-drive pulses\n");
316 seq_puts(sf, "#\n");
317 seq_puts(sf, "# RX error injection:\n");
318 seq_puts(sf, "# <op>[,<mode>] rx-nack NACK the message instead of sending an ACK\n");
319 seq_puts(sf, "# <op>[,<mode>] rx-low-drive <bit> force a low-drive condition at this bit position\n");
320 seq_puts(sf, "# <op>[,<mode>] rx-add-byte add a spurious byte to the received CEC message\n");
321 seq_puts(sf, "# <op>[,<mode>] rx-remove-byte remove the last byte from the received CEC message\n");
322 seq_puts(sf, "# any[,<mode>] rx-arb-lost [<poll>] generate a POLL message to trigger an arbitration lost\n");
323 seq_puts(sf, "#\n");
324 seq_puts(sf, "# TX error injection settings:\n");
325 seq_puts(sf, "# tx-ignore-nack-until-eom ignore early NACKs until EOM\n");
326 seq_puts(sf, "# tx-custom-low-usecs <usecs> define the 'low' time for the custom pulse\n");
327 seq_puts(sf, "# tx-custom-high-usecs <usecs> define the 'high' time for the custom pulse\n");
328 seq_puts(sf, "# tx-custom-pulse transmit the custom pulse once the bus is idle\n");
329 seq_puts(sf, "# tx-glitch-low-usecs <usecs> define the 'low' time for the glitch pulse\n");
330 seq_puts(sf, "# tx-glitch-high-usecs <usecs> define the 'high' time for the glitch pulse\n");
331 seq_puts(sf, "# tx-glitch-falling-edge send the glitch pulse after every falling edge\n");
332 seq_puts(sf, "# tx-glitch-rising-edge send the glitch pulse after every rising edge\n");
333 seq_puts(sf, "#\n");
334 seq_puts(sf, "# TX error injection:\n");
335 seq_puts(sf, "# <op>[,<mode>] tx-no-eom don't set the EOM bit\n");
336 seq_puts(sf, "# <op>[,<mode>] tx-early-eom set the EOM bit one byte too soon\n");
337 seq_puts(sf, "# <op>[,<mode>] tx-add-bytes <num> append <num> (1-255) spurious bytes to the message\n");
338 seq_puts(sf, "# <op>[,<mode>] tx-remove-byte drop the last byte from the message\n");
339 seq_puts(sf, "# <op>[,<mode>] tx-short-bit <bit> make this bit shorter than allowed\n");
340 seq_puts(sf, "# <op>[,<mode>] tx-long-bit <bit> make this bit longer than allowed\n");
341 seq_puts(sf, "# <op>[,<mode>] tx-custom-bit <bit> send the custom pulse instead of this bit\n");
342 seq_puts(sf, "# <op>[,<mode>] tx-short-start send a start pulse that's too short\n");
343 seq_puts(sf, "# <op>[,<mode>] tx-long-start send a start pulse that's too long\n");
344 seq_puts(sf, "# <op>[,<mode>] tx-custom-start send the custom pulse instead of the start pulse\n");
345 seq_puts(sf, "# <op>[,<mode>] tx-last-bit <bit> stop sending after this bit\n");
346 seq_puts(sf, "# <op>[,<mode>] tx-low-drive <bit> force a low-drive condition at this bit position\n");
347 seq_puts(sf, "#\n");
348 seq_puts(sf, "# <op> CEC message opcode (0-255) or 'any'\n");
349 seq_puts(sf, "# <mode> 'once' (default), 'always', 'toggle' or 'off'\n");
350 seq_puts(sf, "# <bit> CEC message bit (0-159)\n");
351 seq_puts(sf, "# 10 bits per 'byte': bits 0-7: data, bit 8: EOM, bit 9: ACK\n");
352 seq_puts(sf, "# <poll> CEC poll message used to test arbitration lost (0x00-0xff, default 0x0f)\n");
353 seq_puts(sf, "# <usecs> microseconds (0-10000000, default 1000)\n");
354
355 seq_puts(sf, "\nclear\n");
356
357 for (i = 0; i < ARRAY_SIZE(pin->error_inj); i++) {
358 u64 e = pin->error_inj[i];
359
360 for (j = 0; cec_error_inj_cmds[j].cmd; j++) {
361 const char *cmd = cec_error_inj_cmds[j].cmd;
362 unsigned int mode;
363 unsigned int mode_offset;
364 int arg_idx;
365
366 mode_offset = cec_error_inj_cmds[j].mode_offset;
367 arg_idx = cec_error_inj_cmds[j].arg_idx;
368 mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK;
369 if (!mode)
370 continue;
371 cec_pin_show_cmd(sf, i, mode);
372 seq_puts(sf, cmd);
373 if (arg_idx >= 0)
374 seq_printf(sf, " %u",
375 pin->error_inj_args[i][arg_idx]);
376 seq_puts(sf, "\n");
377 }
378 }
379
380 if (pin->rx_no_low_drive)
381 seq_puts(sf, "rx-no-low-drive\n");
382 if (pin->tx_ignore_nack_until_eom)
383 seq_puts(sf, "tx-ignore-nack-until-eom\n");
384 if (pin->tx_glitch_falling_edge)
385 seq_puts(sf, "tx-glitch-falling-edge\n");
386 if (pin->tx_glitch_rising_edge)
387 seq_puts(sf, "tx-glitch-rising-edge\n");
388 if (pin->tx_custom_pulse)
389 seq_puts(sf, "tx-custom-pulse\n");
390 if (pin->tx_custom_low_usecs != CEC_TIM_CUSTOM_DEFAULT)
391 seq_printf(sf, "tx-custom-low-usecs %u\n",
392 pin->tx_custom_low_usecs);
393 if (pin->tx_custom_high_usecs != CEC_TIM_CUSTOM_DEFAULT)
394 seq_printf(sf, "tx-custom-high-usecs %u\n",
395 pin->tx_custom_high_usecs);
396 if (pin->tx_glitch_low_usecs != CEC_TIM_GLITCH_DEFAULT)
397 seq_printf(sf, "tx-glitch-low-usecs %u\n",
398 pin->tx_glitch_low_usecs);
399 if (pin->tx_glitch_high_usecs != CEC_TIM_GLITCH_DEFAULT)
400 seq_printf(sf, "tx-glitch-high-usecs %u\n",
401 pin->tx_glitch_high_usecs);
402 return 0;
403 }
404