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->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 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 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