]> Git Repo - linux.git/blob - drivers/media/cec/core/cec-pin-error-inj.c
Linux 6.14-rc3
[linux.git] / drivers / media / cec / core / cec-pin-error-inj.c
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
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
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
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->tx_ignore_nack_until_eom = false;
95                 pin->tx_custom_pulse = false;
96                 pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
97                 pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
98                 return true;
99         }
100         if (!strcmp(token, "rx-clear")) {
101                 for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
102                         pin->error_inj[i] &= ~CEC_ERROR_INJ_RX_MASK;
103                 pin->rx_toggle = false;
104                 return true;
105         }
106         if (!strcmp(token, "tx-clear")) {
107                 for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
108                         pin->error_inj[i] &= ~CEC_ERROR_INJ_TX_MASK;
109                 pin->tx_toggle = false;
110                 pin->tx_ignore_nack_until_eom = false;
111                 pin->tx_custom_pulse = false;
112                 pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
113                 pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
114                 return true;
115         }
116         if (!strcmp(token, "tx-ignore-nack-until-eom")) {
117                 pin->tx_ignore_nack_until_eom = true;
118                 return true;
119         }
120         if (!strcmp(token, "tx-custom-pulse")) {
121                 pin->tx_custom_pulse = true;
122                 cec_pin_start_timer(pin);
123                 return true;
124         }
125         if (!p)
126                 return false;
127
128         p = skip_spaces(p);
129         if (!strcmp(token, "tx-custom-low-usecs")) {
130                 u32 usecs;
131
132                 if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
133                         return false;
134                 pin->tx_custom_low_usecs = usecs;
135                 return true;
136         }
137         if (!strcmp(token, "tx-custom-high-usecs")) {
138                 u32 usecs;
139
140                 if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
141                         return false;
142                 pin->tx_custom_high_usecs = usecs;
143                 return true;
144         }
145
146         comma = strchr(token, ',');
147         if (comma)
148                 *comma++ = '\0';
149         if (!strcmp(token, "any")) {
150                 has_op = false;
151                 error = pin->error_inj + CEC_ERROR_INJ_OP_ANY;
152                 args = pin->error_inj_args[CEC_ERROR_INJ_OP_ANY];
153         } else if (!kstrtou8(token, 0, &op)) {
154                 has_op = true;
155                 error = pin->error_inj + op;
156                 args = pin->error_inj_args[op];
157         } else {
158                 return false;
159         }
160
161         mode = CEC_ERROR_INJ_MODE_ONCE;
162         if (comma) {
163                 if (!strcmp(comma, "off"))
164                         mode = CEC_ERROR_INJ_MODE_OFF;
165                 else if (!strcmp(comma, "once"))
166                         mode = CEC_ERROR_INJ_MODE_ONCE;
167                 else if (!strcmp(comma, "always"))
168                         mode = CEC_ERROR_INJ_MODE_ALWAYS;
169                 else if (!strcmp(comma, "toggle"))
170                         mode = CEC_ERROR_INJ_MODE_TOGGLE;
171                 else
172                         return false;
173         }
174
175         token = strsep(&p, delims);
176         if (p) {
177                 p = skip_spaces(p);
178                 has_pos = !kstrtou8(p, 0, &pos);
179         }
180
181         if (!strcmp(token, "clear")) {
182                 *error = 0;
183                 return true;
184         }
185         if (!strcmp(token, "rx-clear")) {
186                 *error &= ~CEC_ERROR_INJ_RX_MASK;
187                 return true;
188         }
189         if (!strcmp(token, "tx-clear")) {
190                 *error &= ~CEC_ERROR_INJ_TX_MASK;
191                 return true;
192         }
193
194         for (i = 0; cec_error_inj_cmds[i].cmd; i++) {
195                 const char *cmd = cec_error_inj_cmds[i].cmd;
196                 unsigned int mode_offset;
197                 u64 mode_mask;
198                 int arg_idx;
199                 bool is_bit_pos = true;
200
201                 if (strcmp(token, cmd))
202                         continue;
203
204                 mode_offset = cec_error_inj_cmds[i].mode_offset;
205                 mode_mask = CEC_ERROR_INJ_MODE_MASK << mode_offset;
206                 arg_idx = cec_error_inj_cmds[i].arg_idx;
207
208                 if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET) {
209                         if (has_op)
210                                 return false;
211                         if (!has_pos)
212                                 pos = 0x0f;
213                         is_bit_pos = false;
214                 } else if (mode_offset == CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET) {
215                         if (!has_pos || !pos)
216                                 return false;
217                         is_bit_pos = false;
218                 }
219
220                 if (arg_idx >= 0 && is_bit_pos) {
221                         if (!has_pos || pos >= 160)
222                                 return false;
223                         if (has_op && pos < 10 + 8)
224                                 return false;
225                         /* Invalid bit position may not be the Ack bit */
226                         if ((mode_offset == CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET ||
227                              mode_offset == CEC_ERROR_INJ_TX_LONG_BIT_OFFSET ||
228                              mode_offset == CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET) &&
229                             (pos % 10) == 9)
230                                 return false;
231                 }
232                 *error &= ~mode_mask;
233                 *error |= (u64)mode << mode_offset;
234                 if (arg_idx >= 0)
235                         args[arg_idx] = pos;
236                 return true;
237         }
238         return false;
239 }
240
241 static void cec_pin_show_cmd(struct seq_file *sf, u32 cmd, u8 mode)
242 {
243         if (cmd == CEC_ERROR_INJ_OP_ANY)
244                 seq_puts(sf, "any,");
245         else
246                 seq_printf(sf, "0x%02x,", cmd);
247         switch (mode) {
248         case CEC_ERROR_INJ_MODE_ONCE:
249                 seq_puts(sf, "once ");
250                 break;
251         case CEC_ERROR_INJ_MODE_ALWAYS:
252                 seq_puts(sf, "always ");
253                 break;
254         case CEC_ERROR_INJ_MODE_TOGGLE:
255                 seq_puts(sf, "toggle ");
256                 break;
257         default:
258                 seq_puts(sf, "off ");
259                 break;
260         }
261 }
262
263 int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
264 {
265         struct cec_pin *pin = adap->pin;
266         unsigned int i, j;
267
268         seq_puts(sf, "# Clear error injections:\n");
269         seq_puts(sf, "#   clear          clear all rx and tx error injections\n");
270         seq_puts(sf, "#   rx-clear       clear all rx error injections\n");
271         seq_puts(sf, "#   tx-clear       clear all tx error injections\n");
272         seq_puts(sf, "#   <op> clear     clear all rx and tx error injections for <op>\n");
273         seq_puts(sf, "#   <op> rx-clear  clear all rx error injections for <op>\n");
274         seq_puts(sf, "#   <op> tx-clear  clear all tx error injections for <op>\n");
275         seq_puts(sf, "#\n");
276         seq_puts(sf, "# RX error injection:\n");
277         seq_puts(sf, "#   <op>[,<mode>] rx-nack              NACK the message instead of sending an ACK\n");
278         seq_puts(sf, "#   <op>[,<mode>] rx-low-drive <bit>   force a low-drive condition at this bit position\n");
279         seq_puts(sf, "#   <op>[,<mode>] rx-add-byte          add a spurious byte to the received CEC message\n");
280         seq_puts(sf, "#   <op>[,<mode>] rx-remove-byte       remove the last byte from the received CEC message\n");
281         seq_puts(sf, "#    any[,<mode>] rx-arb-lost [<poll>] generate a POLL message to trigger an arbitration lost\n");
282         seq_puts(sf, "#\n");
283         seq_puts(sf, "# TX error injection settings:\n");
284         seq_puts(sf, "#   tx-ignore-nack-until-eom           ignore early NACKs until EOM\n");
285         seq_puts(sf, "#   tx-custom-low-usecs <usecs>        define the 'low' time for the custom pulse\n");
286         seq_puts(sf, "#   tx-custom-high-usecs <usecs>       define the 'high' time for the custom pulse\n");
287         seq_puts(sf, "#   tx-custom-pulse                    transmit the custom pulse once the bus is idle\n");
288         seq_puts(sf, "#\n");
289         seq_puts(sf, "# TX error injection:\n");
290         seq_puts(sf, "#   <op>[,<mode>] tx-no-eom            don't set the EOM bit\n");
291         seq_puts(sf, "#   <op>[,<mode>] tx-early-eom         set the EOM bit one byte too soon\n");
292         seq_puts(sf, "#   <op>[,<mode>] tx-add-bytes <num>   append <num> (1-255) spurious bytes to the message\n");
293         seq_puts(sf, "#   <op>[,<mode>] tx-remove-byte       drop the last byte from the message\n");
294         seq_puts(sf, "#   <op>[,<mode>] tx-short-bit <bit>   make this bit shorter than allowed\n");
295         seq_puts(sf, "#   <op>[,<mode>] tx-long-bit <bit>    make this bit longer than allowed\n");
296         seq_puts(sf, "#   <op>[,<mode>] tx-custom-bit <bit>  send the custom pulse instead of this bit\n");
297         seq_puts(sf, "#   <op>[,<mode>] tx-short-start       send a start pulse that's too short\n");
298         seq_puts(sf, "#   <op>[,<mode>] tx-long-start        send a start pulse that's too long\n");
299         seq_puts(sf, "#   <op>[,<mode>] tx-custom-start      send the custom pulse instead of the start pulse\n");
300         seq_puts(sf, "#   <op>[,<mode>] tx-last-bit <bit>    stop sending after this bit\n");
301         seq_puts(sf, "#   <op>[,<mode>] tx-low-drive <bit>   force a low-drive condition at this bit position\n");
302         seq_puts(sf, "#\n");
303         seq_puts(sf, "# <op>       CEC message opcode (0-255) or 'any'\n");
304         seq_puts(sf, "# <mode>     'once' (default), 'always', 'toggle' or 'off'\n");
305         seq_puts(sf, "# <bit>      CEC message bit (0-159)\n");
306         seq_puts(sf, "#            10 bits per 'byte': bits 0-7: data, bit 8: EOM, bit 9: ACK\n");
307         seq_puts(sf, "# <poll>     CEC poll message used to test arbitration lost (0x00-0xff, default 0x0f)\n");
308         seq_puts(sf, "# <usecs>    microseconds (0-10000000, default 1000)\n");
309
310         seq_puts(sf, "\nclear\n");
311
312         for (i = 0; i < ARRAY_SIZE(pin->error_inj); i++) {
313                 u64 e = pin->error_inj[i];
314
315                 for (j = 0; cec_error_inj_cmds[j].cmd; j++) {
316                         const char *cmd = cec_error_inj_cmds[j].cmd;
317                         unsigned int mode;
318                         unsigned int mode_offset;
319                         int arg_idx;
320
321                         mode_offset = cec_error_inj_cmds[j].mode_offset;
322                         arg_idx = cec_error_inj_cmds[j].arg_idx;
323                         mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK;
324                         if (!mode)
325                                 continue;
326                         cec_pin_show_cmd(sf, i, mode);
327                         seq_puts(sf, cmd);
328                         if (arg_idx >= 0)
329                                 seq_printf(sf, " %u",
330                                            pin->error_inj_args[i][arg_idx]);
331                         seq_puts(sf, "\n");
332                 }
333         }
334
335         if (pin->tx_ignore_nack_until_eom)
336                 seq_puts(sf, "tx-ignore-nack-until-eom\n");
337         if (pin->tx_custom_pulse)
338                 seq_puts(sf, "tx-custom-pulse\n");
339         if (pin->tx_custom_low_usecs != CEC_TIM_CUSTOM_DEFAULT)
340                 seq_printf(sf, "tx-custom-low-usecs %u\n",
341                            pin->tx_custom_low_usecs);
342         if (pin->tx_custom_high_usecs != CEC_TIM_CUSTOM_DEFAULT)
343                 seq_printf(sf, "tx-custom-high-usecs %u\n",
344                            pin->tx_custom_high_usecs);
345         return 0;
346 }
This page took 0.049767 seconds and 4 git commands to generate.