On Mon Jun 30 13:08:46 2025 +0200, Hans Verkuil wrote:
> 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 <[email protected]>
> Signed-off-by: Mauro Carvalho Chehab <[email protected]>
Patch committed.
Thanks,
Mauro Carvalho Chehab
drivers/media/cec/core/cec-pin-error-inj.c | 48 +++++++++++++++++++++++++++++-
drivers/media/cec/core/cec-pin-priv.h | 7 +++++
drivers/media/cec/core/cec-pin.c | 29 ++++++++++++++++++
3 files changed, 83 insertions(+), 1 deletion(-)
---
diff --git a/drivers/media/cec/core/cec-pin-error-inj.c
b/drivers/media/cec/core/cec-pin-error-inj.c
index 6e61a04b8168..68cbd83dfd6e 100644
--- a/drivers/media/cec/core/cec-pin-error-inj.c
+++ b/drivers/media/cec/core/cec-pin-error-inj.c
@@ -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;
}
diff --git a/drivers/media/cec/core/cec-pin-priv.h
b/drivers/media/cec/core/cec-pin-priv.h
index 156a9f81be94..88eefcb60ab8 100644
--- a/drivers/media/cec/core/cec-pin-priv.h
+++ b/drivers/media/cec/core/cec-pin-priv.h
@@ -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
diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c
index 59ac12113f3a..f3b0febf0f1c 100644
--- a/drivers/media/cec/core/cec-pin.c
+++ b/drivers/media/cec/core/cec-pin.c
@@ -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,