root/drivers/media/cec/cec-pin-error-inj.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cec_pin_rx_error_inj
  2. cec_pin_tx_error_inj
  3. cec_pin_error_inj_parse_line
  4. cec_pin_show_cmd
  5. cec_pin_error_inj_show

   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 
  13 struct cec_error_inj_cmd {
  14         unsigned int mode_offset;
  15         int arg_idx;
  16         const char *cmd;
  17 };
  18 
  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" },
  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 
  49 u16 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 
  61 u16 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 
  72 bool 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 
 240 static 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 
 262 int 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, "#   <op>[,<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 }

/* [<][>][^][v][top][bottom][index][help] */