]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
media: cec: core: add glitch error injection
authorHans Verkuil <hverkuil@xs4all.nl>
Mon, 30 Jun 2025 11:08:46 +0000 (13:08 +0200)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Tue, 8 Jul 2025 06:35:26 +0000 (08:35 +0200)
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>
drivers/media/cec/core/cec-pin-error-inj.c
drivers/media/cec/core/cec-pin-priv.h
drivers/media/cec/core/cec-pin.c

index 6e61a04b81687657a9458851444a637e6370edea..68cbd83dfd6e0a6fa66a45e1e492d7308f90cfcf 100644 (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;
 }
index 156a9f81be9411e57da3ece0077ec4743029dabb..88eefcb60ab8a502d01d3621e37928d1d452527f 100644 (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
index 59ac12113f3a28091e6dbc58e9f5b9d1743163ee..f3b0febf0f1c87dd5f0411f44dc2a64ef26dc064 100644 (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,