* [PATCH 1/8] media: cec/core: max_retries -> max_attempts
2026-07-10 11:07 [PATCH 0/8] media: cec/core: fixes and improvements Hans Verkuil
@ 2026-07-10 11:07 ` Hans Verkuil
2026-07-10 11:07 ` [PATCH 2/8] media: cec/core: drop ABORTED/TIMEOUT check Hans Verkuil
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2026-07-10 11:07 UTC (permalink / raw)
To: linux-media; +Cc: Hans Verkuil
Rename max_retries to max_attempts. This counter really
is for the number of attempts, not the number of retries.
Also increase the number of attempts from 2 to 3.
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
drivers/media/cec/core/cec-adap.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index de0c4fcd8dfe..e9dce0dfd102 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -1317,7 +1317,7 @@ static int cec_config_log_addr(struct cec_adapter *adap,
{
struct cec_log_addrs *las = &adap->log_addrs;
struct cec_msg msg = { };
- const unsigned int max_retries = 2;
+ const unsigned int max_attempts = 3;
unsigned int i;
int err;
@@ -1328,7 +1328,7 @@ static int cec_config_log_addr(struct cec_adapter *adap,
msg.len = 1;
msg.msg[0] = (log_addr << 4) | log_addr;
- for (i = 0; i < max_retries; i++) {
+ for (i = 0; i < max_attempts; i++) {
err = cec_transmit_msg_fh(adap, &msg, NULL, true);
/*
@@ -1357,19 +1357,19 @@ static int cec_config_log_addr(struct cec_adapter *adap,
if (msg.tx_status & CEC_TX_STATUS_NACK)
break;
/*
- * Retry up to max_retries times if the message was neither
+ * Do up to max_attempts if the message was neither
* OKed or NACKed. This can happen due to e.g. a Lost
* Arbitration condition.
*/
}
/*
- * If we are unable to get an OK or a NACK after max_retries attempts
+ * If we are unable to get an OK or a NACK after max_attempts
* (and note that each attempt already consists of four polls), then
* we assume that something is really weird and that it is not a
* good idea to try and claim this logical address.
*/
- if (i == max_retries) {
+ if (i == max_attempts) {
dprintk(0, "polling for LA %u failed with tx_status=0x%04x\n",
log_addr, msg.tx_status);
return 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 2/8] media: cec/core: drop ABORTED/TIMEOUT check
2026-07-10 11:07 [PATCH 0/8] media: cec/core: fixes and improvements Hans Verkuil
2026-07-10 11:07 ` [PATCH 1/8] media: cec/core: max_retries -> max_attempts Hans Verkuil
@ 2026-07-10 11:07 ` Hans Verkuil
2026-07-10 11:07 ` [PATCH 3/8] media: cec/core: handle core events like normal events Hans Verkuil
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2026-07-10 11:07 UTC (permalink / raw)
To: linux-media; +Cc: Hans Verkuil
When claiming a logical address, don't break off the process
when the transmit returns ABORTED or TIMEOUT. Just do another
attempt in that case.
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
drivers/media/cec/core/cec-adap.c | 7 -------
1 file changed, 7 deletions(-)
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index e9dce0dfd102..0c2f4660a7bd 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -1345,13 +1345,6 @@ static int cec_config_log_addr(struct cec_adapter *adap,
if (err)
return err;
- /*
- * The message was aborted or timed out due to a disconnect or
- * unconfigure, just bail out.
- */
- if (msg.tx_status &
- (CEC_TX_STATUS_ABORTED | CEC_TX_STATUS_TIMEOUT))
- return -EINTR;
if (msg.tx_status & CEC_TX_STATUS_OK)
return 0;
if (msg.tx_status & CEC_TX_STATUS_NACK)
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 3/8] media: cec/core: handle core events like normal events
2026-07-10 11:07 [PATCH 0/8] media: cec/core: fixes and improvements Hans Verkuil
2026-07-10 11:07 ` [PATCH 1/8] media: cec/core: max_retries -> max_attempts Hans Verkuil
2026-07-10 11:07 ` [PATCH 2/8] media: cec/core: drop ABORTED/TIMEOUT check Hans Verkuil
@ 2026-07-10 11:07 ` Hans Verkuil
2026-07-10 11:07 ` [PATCH 4/8] media: cec/core: drop cec_post_state_event call Hans Verkuil
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2026-07-10 11:07 UTC (permalink / raw)
To: linux-media; +Cc: Hans Verkuil
Currently there is a distinction between core events
(CEC_EVENT_STATE_CHANGE and CEC_EVENT_LOST_MSGS) and other
events. The core events do not require memory allocations,
so are a bit faster, but they are also limited to just a
single event: if a new event comes in, then that replaces
the old one.
It's all overly complicated, and with only one state change
event it is easy to miss state changes.
So just drop that optimization, and allow for up to 3
state change events.
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
drivers/media/cec/core/cec-adap.c | 34 +++++++++++++++----------------
drivers/media/cec/core/cec-api.c | 5 ++---
include/media/cec.h | 2 --
3 files changed, 18 insertions(+), 23 deletions(-)
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index 0c2f4660a7bd..bbd5395fa67d 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -80,9 +80,9 @@ void cec_queue_event_fh(struct cec_fh *fh,
const struct cec_event *new_ev, u64 ts)
{
static const u16 max_events[CEC_NUM_EVENTS] = {
- 1, 1, 800, 800, 8, 8, 8, 8
+ 3, 1, 800, 800, 8, 8, 8, 8
};
- struct cec_event_entry *entry;
+ struct cec_event_entry *new_entry, *entry;
unsigned int ev_idx = new_ev->event - 1;
if (WARN_ON(ev_idx >= ARRAY_SIZE(fh->events)))
@@ -92,36 +92,34 @@ void cec_queue_event_fh(struct cec_fh *fh,
ts = ktime_get_ns();
mutex_lock(&fh->lock);
- if (ev_idx < CEC_NUM_CORE_EVENTS)
- entry = &fh->core_events[ev_idx];
- else
- entry = kmalloc_obj(*entry);
- if (entry) {
+ new_entry = kmalloc_obj(*new_entry);
+ if (new_entry) {
if (new_ev->event == CEC_EVENT_LOST_MSGS &&
fh->queued_events[ev_idx]) {
+ entry = list_first_entry(&fh->events[ev_idx],
+ struct cec_event_entry, list);
entry->ev.lost_msgs.lost_msgs +=
new_ev->lost_msgs.lost_msgs;
+ kfree(new_entry);
goto unlock;
}
- entry->ev = *new_ev;
- entry->ev.ts = ts;
+ new_entry->ev = *new_ev;
+ new_entry->ev.ts = ts;
if (fh->queued_events[ev_idx] < max_events[ev_idx]) {
/* Add new msg at the end of the queue */
- list_add_tail(&entry->list, &fh->events[ev_idx]);
+ list_add_tail(&new_entry->list, &fh->events[ev_idx]);
fh->queued_events[ev_idx]++;
fh->total_queued_events++;
goto unlock;
}
- if (ev_idx >= CEC_NUM_CORE_EVENTS) {
- list_add_tail(&entry->list, &fh->events[ev_idx]);
- /* drop the oldest event */
- entry = list_first_entry(&fh->events[ev_idx],
- struct cec_event_entry, list);
- list_del(&entry->list);
- kfree(entry);
- }
+ list_add_tail(&new_entry->list, &fh->events[ev_idx]);
+ /* drop the oldest event */
+ entry = list_first_entry(&fh->events[ev_idx],
+ struct cec_event_entry, list);
+ list_del(&entry->list);
+ kfree(entry);
}
/* Mark that events were lost */
entry = list_first_entry_or_null(&fh->events[ev_idx],
diff --git a/drivers/media/cec/core/cec-api.c b/drivers/media/cec/core/cec-api.c
index 103ded79526f..a491d7fc8a71 100644
--- a/drivers/media/cec/core/cec-api.c
+++ b/drivers/media/cec/core/cec-api.c
@@ -345,8 +345,7 @@ static long cec_dqevent(struct cec_adapter *adap, struct cec_fh *fh,
if (copy_to_user(parg, &ev->ev, sizeof(ev->ev)))
err = -EFAULT;
- if (ev_idx >= CEC_NUM_CORE_EVENTS)
- kfree(ev);
+ kfree(ev);
fh->queued_events[ev_idx]--;
fh->total_queued_events--;
@@ -673,7 +672,7 @@ static int cec_release(struct inode *inode, struct file *filp)
list_del(&entry->list);
kfree(entry);
}
- for (i = CEC_NUM_CORE_EVENTS; i < CEC_NUM_EVENTS; i++) {
+ for (i = 0; i < CEC_NUM_EVENTS; i++) {
while (!list_empty(&fh->events[i])) {
struct cec_event_entry *entry =
list_first_entry(&fh->events[i],
diff --git a/include/media/cec.h b/include/media/cec.h
index 0c8e86115b6f..5aff399e69e6 100644
--- a/include/media/cec.h
+++ b/include/media/cec.h
@@ -85,7 +85,6 @@ struct cec_event_entry {
struct cec_event ev;
};
-#define CEC_NUM_CORE_EVENTS 2
#define CEC_NUM_EVENTS CEC_EVENT_PIN_5V_HIGH
struct cec_fh {
@@ -101,7 +100,6 @@ struct cec_fh {
struct list_head events[CEC_NUM_EVENTS]; /* queued events */
u16 queued_events[CEC_NUM_EVENTS];
unsigned int total_queued_events;
- struct cec_event_entry core_events[CEC_NUM_CORE_EVENTS];
struct list_head msgs; /* queued messages */
unsigned int queued_msgs;
};
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 4/8] media: cec/core: drop cec_post_state_event call
2026-07-10 11:07 [PATCH 0/8] media: cec/core: fixes and improvements Hans Verkuil
` (2 preceding siblings ...)
2026-07-10 11:07 ` [PATCH 3/8] media: cec/core: handle core events like normal events Hans Verkuil
@ 2026-07-10 11:07 ` Hans Verkuil
2026-07-10 11:07 ` [PATCH 5/8] media: cec/core: flush stale STATE_CHANGE events Hans Verkuil
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2026-07-10 11:07 UTC (permalink / raw)
To: linux-media; +Cc: Hans Verkuil
No need to call cec_post_state_event() if the next call
is to cec_adap_unconfigure() since that will also call
cec_post_state_event().
This fixes the case when, when the physical address is
invalidated, you get two CEC_EVENT_STATE_CHANGE events:
one with a non-zero log_addr_mask and one with a zero
log_addr_mask.
You just want to see the event with phys_addr set to
f.f.f.f and log_addr_mask set to 0.
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
drivers/media/cec/core/cec-adap.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index bbd5395fa67d..5c7ede88c3df 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -1708,7 +1708,6 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
cec_phys_addr_exp(phys_addr));
if (becomes_invalid || !is_invalid) {
adap->phys_addr = CEC_PHYS_ADDR_INVALID;
- cec_post_state_event(adap);
cec_adap_unconfigure(adap);
if (becomes_invalid) {
cec_adap_enable(adap);
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 5/8] media: cec/core: flush stale STATE_CHANGE events
2026-07-10 11:07 [PATCH 0/8] media: cec/core: fixes and improvements Hans Verkuil
` (3 preceding siblings ...)
2026-07-10 11:07 ` [PATCH 4/8] media: cec/core: drop cec_post_state_event call Hans Verkuil
@ 2026-07-10 11:07 ` Hans Verkuil
2026-07-10 11:07 ` [PATCH 6/8] media: cec/core: add a new CEC_LOG_ADDRS_FL_CONFIG_FAILED flag Hans Verkuil
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2026-07-10 11:07 UTC (permalink / raw)
To: linux-media; +Cc: Hans Verkuil
If the physical address becomes invalid, then flush any
old STATE_CHANGE events since those are no longer relevant.
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
drivers/media/cec/core/cec-adap.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index 5c7ede88c3df..4d14186bfef6 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -103,9 +103,32 @@ void cec_queue_event_fh(struct cec_fh *fh,
kfree(new_entry);
goto unlock;
}
+
new_entry->ev = *new_ev;
new_entry->ev.ts = ts;
+ /*
+ * If the physical address becomes invalid (HPD went low),
+ * then just flush all pending STATE_CHANGE events since
+ * those are all obsoleted.
+ *
+ * This ensures you will not see stale STATE_CHANGE events.
+ */
+ if (new_ev->event == CEC_EVENT_STATE_CHANGE &&
+ new_ev->state_change.phys_addr == CEC_PHYS_ADDR_INVALID &&
+ fh->queued_events[ev_idx]) {
+ /* drop all events */
+ while (!list_empty(&fh->events[ev_idx])) {
+ entry = list_first_entry(&fh->events[ev_idx],
+ struct cec_event_entry, list);
+ list_del(&entry->list);
+ kfree(entry);
+ fh->total_queued_events--;
+ fh->queued_events[ev_idx]--;
+ }
+ new_entry->ev.flags |= CEC_EVENT_FL_DROPPED_EVENTS;
+ }
+
if (fh->queued_events[ev_idx] < max_events[ev_idx]) {
/* Add new msg at the end of the queue */
list_add_tail(&new_entry->list, &fh->events[ev_idx]);
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 6/8] media: cec/core: add a new CEC_LOG_ADDRS_FL_CONFIG_FAILED flag
2026-07-10 11:07 [PATCH 0/8] media: cec/core: fixes and improvements Hans Verkuil
` (4 preceding siblings ...)
2026-07-10 11:07 ` [PATCH 5/8] media: cec/core: flush stale STATE_CHANGE events Hans Verkuil
@ 2026-07-10 11:07 ` Hans Verkuil
2026-07-10 11:07 ` [PATCH 7/8] media: cec/core: cec-pin: toggle rx_toggle when arb lost Hans Verkuil
2026-07-10 11:07 ` [PATCH 8/8] media: cec/core: add error-inj-tx-timeouts debugfs entry Hans Verkuil
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2026-07-10 11:07 UTC (permalink / raw)
To: linux-media; +Cc: Hans Verkuil
If claiming a logical address fails, then set the
CEC_LOG_ADDRS_FL_CONFIG_FAILED flag. This makes it possible for
userspace to detect this corner case.
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
.../userspace-api/media/cec/cec-ioc-adap-g-log-addrs.rst | 7 +++++++
drivers/media/cec/core/cec-adap.c | 3 +++
include/uapi/linux/cec.h | 2 ++
3 files changed, 12 insertions(+)
diff --git a/Documentation/userspace-api/media/cec/cec-ioc-adap-g-log-addrs.rst b/Documentation/userspace-api/media/cec/cec-ioc-adap-g-log-addrs.rst
index f3293a589dd6..8397bf573798 100644
--- a/Documentation/userspace-api/media/cec/cec-ioc-adap-g-log-addrs.rst
+++ b/Documentation/userspace-api/media/cec/cec-ioc-adap-g-log-addrs.rst
@@ -185,6 +185,13 @@ logical address types are already defined will return with error ``EBUSY``.
are CEC devices that can only handle CDC messages.
All other messages are ignored.
+ * .. _`CEC-LOG-ADDRS-FL-CONFIG-FAILED`:
+
+ - ``CEC_LOG_ADDRS_FL_CONFIG_FAILED``
+ - 8
+ - If this flag is set, then the CEC device failed to claim a free logical
+ address and is in the unconfigured state. This can never happen if
+ ``CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK`` was set as well.
.. tabularcolumns:: |p{7.8cm}|p{1.0cm}|p{8.5cm}|
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index 4d14186bfef6..774bf9099183 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -1482,6 +1482,7 @@ static int cec_config_thread_func(void *arg)
dprintk(1, "physical address: %x.%x.%x.%x, claim %d logical addresses\n",
cec_phys_addr_exp(adap->phys_addr), las->num_log_addrs);
las->log_addr_mask = 0;
+ las->flags &= ~CEC_LOG_ADDRS_FL_CONFIG_FAILED;
if (las->log_addr_type[0] == CEC_LOG_ADDR_TYPE_UNREGISTERED)
goto configured;
@@ -1614,6 +1615,8 @@ static int cec_config_thread_func(void *arg)
unconfigure:
for (i = 0; i < las->num_log_addrs; i++)
las->log_addr[i] = CEC_LOG_ADDR_INVALID;
+ if (adap->phys_addr != CEC_PHYS_ADDR_INVALID)
+ las->flags |= CEC_LOG_ADDRS_FL_CONFIG_FAILED;
cec_adap_unconfigure(adap);
adap->is_configuring = false;
adap->must_reconfigure = false;
diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h
index 81a05c9c0706..fdfc97a6e4ec 100644
--- a/include/uapi/linux/cec.h
+++ b/include/uapi/linux/cec.h
@@ -403,6 +403,8 @@ struct cec_log_addrs {
#define CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU (1 << 1)
/* CDC-Only device: supports only CDC messages */
#define CEC_LOG_ADDRS_FL_CDC_ONLY (1 << 2)
+/* Configuration failed */
+#define CEC_LOG_ADDRS_FL_CONFIG_FAILED (1 << 3)
/**
* struct cec_drm_connector_info - tells which drm connector is
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7/8] media: cec/core: cec-pin: toggle rx_toggle when arb lost
2026-07-10 11:07 [PATCH 0/8] media: cec/core: fixes and improvements Hans Verkuil
` (5 preceding siblings ...)
2026-07-10 11:07 ` [PATCH 6/8] media: cec/core: add a new CEC_LOG_ADDRS_FL_CONFIG_FAILED flag Hans Verkuil
@ 2026-07-10 11:07 ` Hans Verkuil
2026-07-10 11:07 ` [PATCH 8/8] media: cec/core: add error-inj-tx-timeouts debugfs entry Hans Verkuil
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2026-07-10 11:07 UTC (permalink / raw)
To: linux-media; +Cc: Hans Verkuil
If we inject an Arbitration Lost error, then manually toggle rx_toggle
instead of waiting for cec_pin_to_idle(). When handling the Arbitration
Lost error injection we are switching to TX mode, and as a result when
cec_pin_to_idle() is called when the transmit ends it would never toggle
rx_toggle since it is no longer in RX mode.
Without this change the 'any,toggle rx-arb-lost' error injection
would, once it is on, always stay on.
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
drivers/media/cec/core/cec-pin.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c
index 085fc12067af..6a0ee32e8401 100644
--- a/drivers/media/cec/core/cec-pin.c
+++ b/drivers/media/cec/core/cec-pin.c
@@ -692,7 +692,6 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
v = cec_pin_read(pin);
if (!v)
break;
- pin->state = CEC_ST_RX_START_BIT_HIGH;
delta = ktime_us_delta(ts, pin->ts);
/* Start bit low is too short, go back to idle */
if (delta < CEC_TIM_START_BIT_LOW_MIN - CEC_TIM_IDLE_SAMPLE) {
@@ -703,7 +702,16 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
cec_pin_to_idle(pin);
break;
}
+ pin->state = CEC_ST_RX_START_BIT_HIGH;
if (rx_arb_lost(pin, &poll)) {
+ /*
+ * Normally rx_toggle is toggled in cec_pin_to_idle()
+ * when we're in an RX state, but here we switch to TX
+ * mode, so cec_pin_to_idle() sees a TX mode and never
+ * toggles rx_toggle. So toggle it here as a special
+ * corner case.
+ */
+ pin->rx_toggle ^= 1;
cec_msg_init(&pin->tx_msg, poll >> 4, poll & 0xf);
pin->tx_generated_poll = true;
pin->tx_extra_bytes = 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 8/8] media: cec/core: add error-inj-tx-timeouts debugfs entry
2026-07-10 11:07 [PATCH 0/8] media: cec/core: fixes and improvements Hans Verkuil
` (6 preceding siblings ...)
2026-07-10 11:07 ` [PATCH 7/8] media: cec/core: cec-pin: toggle rx_toggle when arb lost Hans Verkuil
@ 2026-07-10 11:07 ` Hans Verkuil
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2026-07-10 11:07 UTC (permalink / raw)
To: linux-media; +Cc: Hans Verkuil
Add a new debugfs entry that makes it possible to do
error injection of failing the next N transmits by a
timeout.
This can be used to test what happens in that case during
the claiming of a free logical address.
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
drivers/media/cec/core/cec-adap.c | 7 +++++++
drivers/media/cec/core/cec-core.c | 31 +++++++++++++++++++++++++++++++
include/media/cec.h | 2 ++
3 files changed, 40 insertions(+)
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index 774bf9099183..ee8a200b9803 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -629,6 +629,13 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status,
attempts_made = 1;
mutex_lock(&adap->lock);
+ if (adap->error_inj_tx_timeouts) {
+ dprintk(2, "%s: error_inj_tx_timeouts %u\n",
+ __func__, adap->error_inj_tx_timeouts);
+ adap->error_inj_tx_timeouts--;
+ mutex_unlock(&adap->lock);
+ return;
+ }
data = adap->transmitting;
if (!data) {
/*
diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
index c51e769b4e34..230f8a413e74 100644
--- a/drivers/media/cec/core/cec-core.c
+++ b/drivers/media/cec/core/cec-core.c
@@ -223,6 +223,34 @@ static int cec_error_inj_show(struct seq_file *sf, void *unused)
return call_op(adap, error_inj_show, sf);
}
DEFINE_SHOW_STORE_ATTRIBUTE(cec_error_inj);
+
+static ssize_t cec_error_inj_tx_timeouts_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct seq_file *sf = file->private_data;
+ struct cec_adapter *adap = sf->private;
+ int ret;
+
+ if (count > 5)
+ return -EINVAL;
+
+ mutex_lock(&adap->lock);
+ ret = kstrtou32_from_user(ubuf, count, 0, &adap->error_inj_tx_timeouts);
+ if (ret)
+ adap->error_inj_tx_timeouts = 0;
+ mutex_unlock(&adap->lock);
+ return ret ? : count;
+}
+
+static int cec_error_inj_tx_timeouts_show(struct seq_file *sf, void *unused)
+{
+ struct cec_adapter *adap = sf->private;
+
+ seq_printf(sf, "%u", adap->error_inj_tx_timeouts);
+ return 0;
+}
+
+DEFINE_SHOW_STORE_ATTRIBUTE(cec_error_inj_tx_timeouts);
#endif
struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
@@ -367,6 +395,9 @@ int cec_register_adapter(struct cec_adapter *adap,
debugfs_create_devm_seqfile(&adap->devnode.dev, "status", adap->cec_dir,
cec_adap_status);
+ debugfs_create_file("error-inj-tx-timeouts", 0644, adap->cec_dir, adap,
+ &cec_error_inj_tx_timeouts_fops);
+
if (!adap->ops->error_inj_show || !adap->ops->error_inj_parse_line)
return 0;
debugfs_create_file("error-inj", 0644, adap->cec_dir, adap,
diff --git a/include/media/cec.h b/include/media/cec.h
index 5aff399e69e6..6b462d5efe93 100644
--- a/include/media/cec.h
+++ b/include/media/cec.h
@@ -281,6 +281,8 @@ struct cec_adapter {
u32 tx_low_drive_log_cnt;
u32 tx_error_log_cnt;
+ u32 error_inj_tx_timeouts;
+
#ifdef CONFIG_CEC_NOTIFIER
struct cec_notifier *notifier;
#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread