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