1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
6 #include <linux/delay.h>
7 #include <linux/slab.h>
8 #include <linux/sched/types.h>
10 #include <media/cec-pin.h>
11 #include "cec-pin-priv.h"
13 struct cec_error_inj_cmd
{
14 unsigned int mode_offset
;
19 static const struct cec_error_inj_cmd cec_error_inj_cmds
[] = {
20 { CEC_ERROR_INJ_RX_NACK_OFFSET
, -1, "rx-nack" },
21 { CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET
,
22 CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX
, "rx-low-drive" },
23 { CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET
, -1, "rx-add-byte" },
24 { CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET
, -1, "rx-remove-byte" },
25 { CEC_ERROR_INJ_RX_ARB_LOST_OFFSET
,
26 CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX
, "rx-arb-lost" },
28 { CEC_ERROR_INJ_TX_NO_EOM_OFFSET
, -1, "tx-no-eom" },
29 { CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET
, -1, "tx-early-eom" },
30 { CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET
,
31 CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX
, "tx-add-bytes" },
32 { CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET
, -1, "tx-remove-byte" },
33 { CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET
,
34 CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX
, "tx-short-bit" },
35 { CEC_ERROR_INJ_TX_LONG_BIT_OFFSET
,
36 CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX
, "tx-long-bit" },
37 { CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET
,
38 CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX
, "tx-custom-bit" },
39 { CEC_ERROR_INJ_TX_SHORT_START_OFFSET
, -1, "tx-short-start" },
40 { CEC_ERROR_INJ_TX_LONG_START_OFFSET
, -1, "tx-long-start" },
41 { CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET
, -1, "tx-custom-start" },
42 { CEC_ERROR_INJ_TX_LAST_BIT_OFFSET
,
43 CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX
, "tx-last-bit" },
44 { CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET
,
45 CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX
, "tx-low-drive" },
49 u16
cec_pin_rx_error_inj(struct cec_pin
*pin
)
51 u16 cmd
= CEC_ERROR_INJ_OP_ANY
;
53 /* Only when 18 bits have been received do we have a valid cmd */
54 if (!(pin
->error_inj
[cmd
] & CEC_ERROR_INJ_RX_MASK
) &&
56 cmd
= pin
->rx_msg
.msg
[1];
57 return (pin
->error_inj
[cmd
] & CEC_ERROR_INJ_RX_MASK
) ? cmd
:
61 u16
cec_pin_tx_error_inj(struct cec_pin
*pin
)
63 u16 cmd
= CEC_ERROR_INJ_OP_ANY
;
65 if (!(pin
->error_inj
[cmd
] & CEC_ERROR_INJ_TX_MASK
) &&
67 cmd
= pin
->tx_msg
.msg
[1];
68 return (pin
->error_inj
[cmd
] & CEC_ERROR_INJ_TX_MASK
) ? cmd
:
72 bool cec_pin_error_inj_parse_line(struct cec_adapter
*adap
, char *line
)
74 static const char *delims
= " \t\r";
75 struct cec_pin
*pin
= adap
->pin
;
89 token
= strsep(&p
, delims
);
90 if (!strcmp(token
, "clear")) {
91 memset(pin
->error_inj
, 0, sizeof(pin
->error_inj
));
92 pin
->rx_toggle
= pin
->tx_toggle
= false;
93 pin
->tx_ignore_nack_until_eom
= false;
94 pin
->tx_custom_pulse
= false;
95 pin
->tx_custom_low_usecs
= CEC_TIM_CUSTOM_DEFAULT
;
96 pin
->tx_custom_high_usecs
= CEC_TIM_CUSTOM_DEFAULT
;
99 if (!strcmp(token
, "rx-clear")) {
100 for (i
= 0; i
<= CEC_ERROR_INJ_OP_ANY
; i
++)
101 pin
->error_inj
[i
] &= ~CEC_ERROR_INJ_RX_MASK
;
102 pin
->rx_toggle
= false;
105 if (!strcmp(token
, "tx-clear")) {
106 for (i
= 0; i
<= CEC_ERROR_INJ_OP_ANY
; i
++)
107 pin
->error_inj
[i
] &= ~CEC_ERROR_INJ_TX_MASK
;
108 pin
->tx_toggle
= false;
109 pin
->tx_ignore_nack_until_eom
= false;
110 pin
->tx_custom_pulse
= false;
111 pin
->tx_custom_low_usecs
= CEC_TIM_CUSTOM_DEFAULT
;
112 pin
->tx_custom_high_usecs
= CEC_TIM_CUSTOM_DEFAULT
;
115 if (!strcmp(token
, "tx-ignore-nack-until-eom")) {
116 pin
->tx_ignore_nack_until_eom
= true;
119 if (!strcmp(token
, "tx-custom-pulse")) {
120 pin
->tx_custom_pulse
= true;
121 cec_pin_start_timer(pin
);
128 if (!strcmp(token
, "tx-custom-low-usecs")) {
131 if (kstrtou32(p
, 0, &usecs
) || usecs
> 10000000)
133 pin
->tx_custom_low_usecs
= usecs
;
136 if (!strcmp(token
, "tx-custom-high-usecs")) {
139 if (kstrtou32(p
, 0, &usecs
) || usecs
> 10000000)
141 pin
->tx_custom_high_usecs
= usecs
;
145 comma
= strchr(token
, ',');
148 if (!strcmp(token
, "any")) {
150 error
= pin
->error_inj
+ CEC_ERROR_INJ_OP_ANY
;
151 args
= pin
->error_inj_args
[CEC_ERROR_INJ_OP_ANY
];
152 } else if (!kstrtou8(token
, 0, &op
)) {
154 error
= pin
->error_inj
+ op
;
155 args
= pin
->error_inj_args
[op
];
160 mode
= CEC_ERROR_INJ_MODE_ONCE
;
162 if (!strcmp(comma
, "off"))
163 mode
= CEC_ERROR_INJ_MODE_OFF
;
164 else if (!strcmp(comma
, "once"))
165 mode
= CEC_ERROR_INJ_MODE_ONCE
;
166 else if (!strcmp(comma
, "always"))
167 mode
= CEC_ERROR_INJ_MODE_ALWAYS
;
168 else if (!strcmp(comma
, "toggle"))
169 mode
= CEC_ERROR_INJ_MODE_TOGGLE
;
174 token
= strsep(&p
, delims
);
177 has_pos
= !kstrtou8(p
, 0, &pos
);
180 if (!strcmp(token
, "clear")) {
184 if (!strcmp(token
, "rx-clear")) {
185 *error
&= ~CEC_ERROR_INJ_RX_MASK
;
188 if (!strcmp(token
, "tx-clear")) {
189 *error
&= ~CEC_ERROR_INJ_TX_MASK
;
193 for (i
= 0; cec_error_inj_cmds
[i
].cmd
; i
++) {
194 const char *cmd
= cec_error_inj_cmds
[i
].cmd
;
195 unsigned int mode_offset
;
198 bool is_bit_pos
= true;
200 if (strcmp(token
, cmd
))
203 mode_offset
= cec_error_inj_cmds
[i
].mode_offset
;
204 mode_mask
= CEC_ERROR_INJ_MODE_MASK
<< mode_offset
;
205 arg_idx
= cec_error_inj_cmds
[i
].arg_idx
;
207 if (mode_offset
== CEC_ERROR_INJ_RX_ARB_LOST_OFFSET
) {
213 } else if (mode_offset
== CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET
) {
214 if (!has_pos
|| !pos
)
219 if (arg_idx
>= 0 && is_bit_pos
) {
220 if (!has_pos
|| pos
>= 160)
222 if (has_op
&& pos
< 10 + 8)
224 /* Invalid bit position may not be the Ack bit */
225 if ((mode_offset
== CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET
||
226 mode_offset
== CEC_ERROR_INJ_TX_LONG_BIT_OFFSET
||
227 mode_offset
== CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET
) &&
231 *error
&= ~mode_mask
;
232 *error
|= (u64
)mode
<< mode_offset
;
240 static void cec_pin_show_cmd(struct seq_file
*sf
, u32 cmd
, u8 mode
)
242 if (cmd
== CEC_ERROR_INJ_OP_ANY
)
243 seq_puts(sf
, "any,");
245 seq_printf(sf
, "0x%02x,", cmd
);
247 case CEC_ERROR_INJ_MODE_ONCE
:
248 seq_puts(sf
, "once ");
250 case CEC_ERROR_INJ_MODE_ALWAYS
:
251 seq_puts(sf
, "always ");
253 case CEC_ERROR_INJ_MODE_TOGGLE
:
254 seq_puts(sf
, "toggle ");
257 seq_puts(sf
, "off ");
262 int cec_pin_error_inj_show(struct cec_adapter
*adap
, struct seq_file
*sf
)
264 struct cec_pin
*pin
= adap
->pin
;
267 seq_puts(sf
, "# Clear error injections:\n");
268 seq_puts(sf
, "# clear clear all rx and tx error injections\n");
269 seq_puts(sf
, "# rx-clear clear all rx error injections\n");
270 seq_puts(sf
, "# tx-clear clear all tx error injections\n");
271 seq_puts(sf
, "# <op> clear clear all rx and tx error injections for <op>\n");
272 seq_puts(sf
, "# <op> rx-clear clear all rx error injections for <op>\n");
273 seq_puts(sf
, "# <op> tx-clear clear all tx error injections for <op>\n");
275 seq_puts(sf
, "# RX error injection:\n");
276 seq_puts(sf
, "# <op>[,<mode>] rx-nack NACK the message instead of sending an ACK\n");
277 seq_puts(sf
, "# <op>[,<mode>] rx-low-drive <bit> force a low-drive condition at this bit position\n");
278 seq_puts(sf
, "# <op>[,<mode>] rx-add-byte add a spurious byte to the received CEC message\n");
279 seq_puts(sf
, "# <op>[,<mode>] rx-remove-byte remove the last byte from the received CEC message\n");
280 seq_puts(sf
, "# <op>[,<mode>] rx-arb-lost <poll> generate a POLL message to trigger an arbitration lost\n");
282 seq_puts(sf
, "# TX error injection settings:\n");
283 seq_puts(sf
, "# tx-ignore-nack-until-eom ignore early NACKs until EOM\n");
284 seq_puts(sf
, "# tx-custom-low-usecs <usecs> define the 'low' time for the custom pulse\n");
285 seq_puts(sf
, "# tx-custom-high-usecs <usecs> define the 'high' time for the custom pulse\n");
286 seq_puts(sf
, "# tx-custom-pulse transmit the custom pulse once the bus is idle\n");
288 seq_puts(sf
, "# TX error injection:\n");
289 seq_puts(sf
, "# <op>[,<mode>] tx-no-eom don't set the EOM bit\n");
290 seq_puts(sf
, "# <op>[,<mode>] tx-early-eom set the EOM bit one byte too soon\n");
291 seq_puts(sf
, "# <op>[,<mode>] tx-add-bytes <num> append <num> (1-255) spurious bytes to the message\n");
292 seq_puts(sf
, "# <op>[,<mode>] tx-remove-byte drop the last byte from the message\n");
293 seq_puts(sf
, "# <op>[,<mode>] tx-short-bit <bit> make this bit shorter than allowed\n");
294 seq_puts(sf
, "# <op>[,<mode>] tx-long-bit <bit> make this bit longer than allowed\n");
295 seq_puts(sf
, "# <op>[,<mode>] tx-custom-bit <bit> send the custom pulse instead of this bit\n");
296 seq_puts(sf
, "# <op>[,<mode>] tx-short-start send a start pulse that's too short\n");
297 seq_puts(sf
, "# <op>[,<mode>] tx-long-start send a start pulse that's too long\n");
298 seq_puts(sf
, "# <op>[,<mode>] tx-custom-start send the custom pulse instead of the start pulse\n");
299 seq_puts(sf
, "# <op>[,<mode>] tx-last-bit <bit> stop sending after this bit\n");
300 seq_puts(sf
, "# <op>[,<mode>] tx-low-drive <bit> force a low-drive condition at this bit position\n");
302 seq_puts(sf
, "# <op> CEC message opcode (0-255) or 'any'\n");
303 seq_puts(sf
, "# <mode> 'once' (default), 'always', 'toggle' or 'off'\n");
304 seq_puts(sf
, "# <bit> CEC message bit (0-159)\n");
305 seq_puts(sf
, "# 10 bits per 'byte': bits 0-7: data, bit 8: EOM, bit 9: ACK\n");
306 seq_puts(sf
, "# <poll> CEC poll message used to test arbitration lost (0x00-0xff, default 0x0f)\n");
307 seq_puts(sf
, "# <usecs> microseconds (0-10000000, default 1000)\n");
309 seq_puts(sf
, "\nclear\n");
311 for (i
= 0; i
< ARRAY_SIZE(pin
->error_inj
); i
++) {
312 u64 e
= pin
->error_inj
[i
];
314 for (j
= 0; cec_error_inj_cmds
[j
].cmd
; j
++) {
315 const char *cmd
= cec_error_inj_cmds
[j
].cmd
;
317 unsigned int mode_offset
;
320 mode_offset
= cec_error_inj_cmds
[j
].mode_offset
;
321 arg_idx
= cec_error_inj_cmds
[j
].arg_idx
;
322 mode
= (e
>> mode_offset
) & CEC_ERROR_INJ_MODE_MASK
;
325 cec_pin_show_cmd(sf
, i
, mode
);
328 seq_printf(sf
, " %u",
329 pin
->error_inj_args
[i
][arg_idx
]);
334 if (pin
->tx_ignore_nack_until_eom
)
335 seq_puts(sf
, "tx-ignore-nack-until-eom\n");
336 if (pin
->tx_custom_pulse
)
337 seq_puts(sf
, "tx-custom-pulse\n");
338 if (pin
->tx_custom_low_usecs
!= CEC_TIM_CUSTOM_DEFAULT
)
339 seq_printf(sf
, "tx-custom-low-usecs %u\n",
340 pin
->tx_custom_low_usecs
);
341 if (pin
->tx_custom_high_usecs
!= CEC_TIM_CUSTOM_DEFAULT
)
342 seq_printf(sf
, "tx-custom-high-usecs %u\n",
343 pin
->tx_custom_high_usecs
);