brintos

brintos / linux-shallow public Read only

0
0
Text · 11.1 KiB · fc0968b Raw
346 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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		else171			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	else245		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}346