media: cec: core: add glitch error injection

This adds support for inserting 'glitches' after a falling and/or
rising edge. This tests what happens when there are little voltage
spikes after falling or rising edges, which can be caused due to
noise or reflections on the CEC line.

A proper CEC implementation will deglitch this, but a poor implementation
can create a Low Drive pulse in response, effectively making CEC unusable.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
This commit is contained in:
Hans Verkuil 2025-06-30 13:08:46 +02:00 committed by Mauro Carvalho Chehab
parent 5bcc50cb55
commit 36e713438a
3 changed files with 83 additions and 1 deletions

View File

@ -95,6 +95,10 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
pin->tx_custom_pulse = false;
pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
pin->tx_glitch_falling_edge = false;
pin->tx_glitch_rising_edge = false;
return true;
}
if (!strcmp(token, "rx-clear")) {
@ -111,6 +115,10 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
pin->tx_custom_pulse = false;
pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
pin->tx_glitch_falling_edge = false;
pin->tx_glitch_rising_edge = false;
return true;
}
if (!strcmp(token, "tx-ignore-nack-until-eom")) {
@ -122,6 +130,14 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
cec_pin_start_timer(pin);
return true;
}
if (!strcmp(token, "tx-glitch-falling-edge")) {
pin->tx_glitch_falling_edge = true;
return true;
}
if (!strcmp(token, "tx-glitch-rising-edge")) {
pin->tx_glitch_rising_edge = true;
return true;
}
if (!p)
return false;
@ -139,7 +155,23 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
return false;
pin->tx_custom_high_usecs = usecs;
pin->tx_glitch_high_usecs = usecs;
return true;
}
if (!strcmp(token, "tx-glitch-low-usecs")) {
u32 usecs;
if (kstrtou32(p, 0, &usecs) || usecs > 100)
return false;
pin->tx_glitch_low_usecs = usecs;
return true;
}
if (!strcmp(token, "tx-glitch-high-usecs")) {
u32 usecs;
if (kstrtou32(p, 0, &usecs) || usecs > 100)
return false;
pin->tx_glitch_high_usecs = usecs;
return true;
}
@ -285,6 +317,10 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
seq_puts(sf, "# tx-custom-low-usecs <usecs> define the 'low' time for the custom pulse\n");
seq_puts(sf, "# tx-custom-high-usecs <usecs> define the 'high' time for the custom pulse\n");
seq_puts(sf, "# tx-custom-pulse transmit the custom pulse once the bus is idle\n");
seq_puts(sf, "# tx-glitch-low-usecs <usecs> define the 'low' time for the glitch pulse\n");
seq_puts(sf, "# tx-glitch-high-usecs <usecs> define the 'high' time for the glitch pulse\n");
seq_puts(sf, "# tx-glitch-falling-edge send the glitch pulse after every falling edge\n");
seq_puts(sf, "# tx-glitch-rising-edge send the glitch pulse after every rising edge\n");
seq_puts(sf, "#\n");
seq_puts(sf, "# TX error injection:\n");
seq_puts(sf, "# <op>[,<mode>] tx-no-eom don't set the EOM bit\n");
@ -334,6 +370,10 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
if (pin->tx_ignore_nack_until_eom)
seq_puts(sf, "tx-ignore-nack-until-eom\n");
if (pin->tx_glitch_falling_edge)
seq_puts(sf, "tx-glitch-falling-edge\n");
if (pin->tx_glitch_rising_edge)
seq_puts(sf, "tx-glitch-rising-edge\n");
if (pin->tx_custom_pulse)
seq_puts(sf, "tx-custom-pulse\n");
if (pin->tx_custom_low_usecs != CEC_TIM_CUSTOM_DEFAULT)
@ -342,5 +382,11 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
if (pin->tx_custom_high_usecs != CEC_TIM_CUSTOM_DEFAULT)
seq_printf(sf, "tx-custom-high-usecs %u\n",
pin->tx_custom_high_usecs);
if (pin->tx_glitch_low_usecs != CEC_TIM_GLITCH_DEFAULT)
seq_printf(sf, "tx-glitch-low-usecs %u\n",
pin->tx_glitch_low_usecs);
if (pin->tx_glitch_high_usecs != CEC_TIM_GLITCH_DEFAULT)
seq_printf(sf, "tx-glitch-high-usecs %u\n",
pin->tx_glitch_high_usecs);
return 0;
}

View File

@ -164,6 +164,9 @@ enum cec_pin_state {
/* The default for the low/high time of the custom pulse */
#define CEC_TIM_CUSTOM_DEFAULT 1000
/* The default for the low/high time of the glitch pulse */
#define CEC_TIM_GLITCH_DEFAULT 1
#define CEC_NUM_PIN_EVENTS 128
#define CEC_PIN_EVENT_FL_IS_HIGH (1 << 0)
#define CEC_PIN_EVENT_FL_DROPPED (1 << 1)
@ -227,10 +230,14 @@ struct cec_pin {
u32 tx_custom_low_usecs;
u32 tx_custom_high_usecs;
u32 tx_glitch_low_usecs;
u32 tx_glitch_high_usecs;
bool tx_ignore_nack_until_eom;
bool tx_custom_pulse;
bool tx_generated_poll;
bool tx_post_eom;
bool tx_glitch_falling_edge;
bool tx_glitch_rising_edge;
u8 tx_extra_bytes;
u32 tx_low_drive_cnt;
#ifdef CONFIG_CEC_PIN_ERROR_INJ

View File

@ -142,15 +142,42 @@ static bool cec_pin_read(struct cec_pin *pin)
return v;
}
static void cec_pin_insert_glitch(struct cec_pin *pin, bool rising_edge)
{
/*
* Insert a short glitch after the falling or rising edge to
* simulate reflections on the CEC line. This can be used to
* test deglitch filters, which should be present in CEC devices
* to deal with noise on the line.
*/
if (!pin->tx_glitch_high_usecs || !pin->tx_glitch_low_usecs)
return;
if (rising_edge) {
udelay(pin->tx_glitch_high_usecs);
call_void_pin_op(pin, low);
udelay(pin->tx_glitch_low_usecs);
call_void_pin_op(pin, high);
} else {
udelay(pin->tx_glitch_low_usecs);
call_void_pin_op(pin, high);
udelay(pin->tx_glitch_high_usecs);
call_void_pin_op(pin, low);
}
}
static void cec_pin_low(struct cec_pin *pin)
{
call_void_pin_op(pin, low);
if (pin->tx_glitch_falling_edge && pin->adap->cec_pin_is_high)
cec_pin_insert_glitch(pin, false);
cec_pin_update(pin, false, false);
}
static bool cec_pin_high(struct cec_pin *pin)
{
call_void_pin_op(pin, high);
if (pin->tx_glitch_rising_edge && !pin->adap->cec_pin_is_high)
cec_pin_insert_glitch(pin, true);
return cec_pin_read(pin);
}
@ -1350,6 +1377,8 @@ struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops,
init_waitqueue_head(&pin->kthread_waitq);
pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
adap = cec_allocate_adapter(&cec_pin_adap_ops, priv, name,
caps | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN,