cec-pin-error-inj.c (11402B)
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/slab.h> 8#include <linux/sched/types.h> 9 10#include <media/cec-pin.h> 11#include "cec-pin-priv.h" 12 13struct cec_error_inj_cmd { 14 unsigned int mode_offset; 15 int arg_idx; 16 const char *cmd; 17}; 18 19static 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" }, 27 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" }, 46 { 0, -1, NULL } 47}; 48 49u16 cec_pin_rx_error_inj(struct cec_pin *pin) 50{ 51 u16 cmd = CEC_ERROR_INJ_OP_ANY; 52 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) && 55 pin->rx_bit >= 18) 56 cmd = pin->rx_msg.msg[1]; 57 return (pin->error_inj[cmd] & CEC_ERROR_INJ_RX_MASK) ? cmd : 58 CEC_ERROR_INJ_OP_ANY; 59} 60 61u16 cec_pin_tx_error_inj(struct cec_pin *pin) 62{ 63 u16 cmd = CEC_ERROR_INJ_OP_ANY; 64 65 if (!(pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) && 66 pin->tx_msg.len > 1) 67 cmd = pin->tx_msg.msg[1]; 68 return (pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) ? cmd : 69 CEC_ERROR_INJ_OP_ANY; 70} 71 72bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line) 73{ 74 static const char *delims = " \t\r"; 75 struct cec_pin *pin = adap->pin; 76 unsigned int i; 77 bool has_pos = false; 78 char *p = line; 79 char *token; 80 char *comma; 81 u64 *error; 82 u8 *args; 83 bool has_op; 84 u8 op; 85 u8 mode; 86 u8 pos; 87 88 p = skip_spaces(p); 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; 97 return true; 98 } 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; 103 return true; 104 } 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; 113 return true; 114 } 115 if (!strcmp(token, "tx-ignore-nack-until-eom")) { 116 pin->tx_ignore_nack_until_eom = true; 117 return true; 118 } 119 if (!strcmp(token, "tx-custom-pulse")) { 120 pin->tx_custom_pulse = true; 121 cec_pin_start_timer(pin); 122 return true; 123 } 124 if (!p) 125 return false; 126 127 p = skip_spaces(p); 128 if (!strcmp(token, "tx-custom-low-usecs")) { 129 u32 usecs; 130 131 if (kstrtou32(p, 0, &usecs) || usecs > 10000000) 132 return false; 133 pin->tx_custom_low_usecs = usecs; 134 return true; 135 } 136 if (!strcmp(token, "tx-custom-high-usecs")) { 137 u32 usecs; 138 139 if (kstrtou32(p, 0, &usecs) || usecs > 10000000) 140 return false; 141 pin->tx_custom_high_usecs = usecs; 142 return true; 143 } 144 145 comma = strchr(token, ','); 146 if (comma) 147 *comma++ = '\0'; 148 if (!strcmp(token, "any")) { 149 has_op = false; 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)) { 153 has_op = true; 154 error = pin->error_inj + op; 155 args = pin->error_inj_args[op]; 156 } else { 157 return false; 158 } 159 160 mode = CEC_ERROR_INJ_MODE_ONCE; 161 if (comma) { 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; 170 else 171 return false; 172 } 173 174 token = strsep(&p, delims); 175 if (p) { 176 p = skip_spaces(p); 177 has_pos = !kstrtou8(p, 0, &pos); 178 } 179 180 if (!strcmp(token, "clear")) { 181 *error = 0; 182 return true; 183 } 184 if (!strcmp(token, "rx-clear")) { 185 *error &= ~CEC_ERROR_INJ_RX_MASK; 186 return true; 187 } 188 if (!strcmp(token, "tx-clear")) { 189 *error &= ~CEC_ERROR_INJ_TX_MASK; 190 return true; 191 } 192 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; 196 u64 mode_mask; 197 int arg_idx; 198 bool is_bit_pos = true; 199 200 if (strcmp(token, cmd)) 201 continue; 202 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; 206 207 if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET) { 208 if (has_op) 209 return false; 210 if (!has_pos) 211 pos = 0x0f; 212 is_bit_pos = false; 213 } else if (mode_offset == CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET) { 214 if (!has_pos || !pos) 215 return false; 216 is_bit_pos = false; 217 } 218 219 if (arg_idx >= 0 && is_bit_pos) { 220 if (!has_pos || pos >= 160) 221 return false; 222 if (has_op && pos < 10 + 8) 223 return false; 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) && 228 (pos % 10) == 9) 229 return false; 230 } 231 *error &= ~mode_mask; 232 *error |= (u64)mode << mode_offset; 233 if (arg_idx >= 0) 234 args[arg_idx] = pos; 235 return true; 236 } 237 return false; 238} 239 240static void cec_pin_show_cmd(struct seq_file *sf, u32 cmd, u8 mode) 241{ 242 if (cmd == CEC_ERROR_INJ_OP_ANY) 243 seq_puts(sf, "any,"); 244 else 245 seq_printf(sf, "0x%02x,", cmd); 246 switch (mode) { 247 case CEC_ERROR_INJ_MODE_ONCE: 248 seq_puts(sf, "once "); 249 break; 250 case CEC_ERROR_INJ_MODE_ALWAYS: 251 seq_puts(sf, "always "); 252 break; 253 case CEC_ERROR_INJ_MODE_TOGGLE: 254 seq_puts(sf, "toggle "); 255 break; 256 default: 257 seq_puts(sf, "off "); 258 break; 259 } 260} 261 262int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf) 263{ 264 struct cec_pin *pin = adap->pin; 265 unsigned int i, j; 266 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"); 274 seq_puts(sf, "#\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, "# any[,<mode>] rx-arb-lost [<poll>] generate a POLL message to trigger an arbitration lost\n"); 281 seq_puts(sf, "#\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"); 287 seq_puts(sf, "#\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"); 301 seq_puts(sf, "#\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"); 308 309 seq_puts(sf, "\nclear\n"); 310 311 for (i = 0; i < ARRAY_SIZE(pin->error_inj); i++) { 312 u64 e = pin->error_inj[i]; 313 314 for (j = 0; cec_error_inj_cmds[j].cmd; j++) { 315 const char *cmd = cec_error_inj_cmds[j].cmd; 316 unsigned int mode; 317 unsigned int mode_offset; 318 int arg_idx; 319 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; 323 if (!mode) 324 continue; 325 cec_pin_show_cmd(sf, i, mode); 326 seq_puts(sf, cmd); 327 if (arg_idx >= 0) 328 seq_printf(sf, " %u", 329 pin->error_inj_args[i][arg_idx]); 330 seq_puts(sf, "\n"); 331 } 332 } 333 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); 344 return 0; 345}