summaryrefslogtreecommitdiffstats
path: root/drivers/media/cec
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/cec')
-rw-r--r--drivers/media/cec/Makefile2
-rw-r--r--drivers/media/cec/cec-adap.c107
-rw-r--r--drivers/media/cec/cec-api.c24
-rw-r--r--drivers/media/cec/cec-core.c8
-rw-r--r--drivers/media/cec/cec-edid.c155
-rw-r--r--drivers/media/cec/cec-pin.c20
6 files changed, 85 insertions, 231 deletions
diff --git a/drivers/media/cec/Makefile b/drivers/media/cec/Makefile
index 29a2ab9e77c5..ad8677d8c896 100644
--- a/drivers/media/cec/Makefile
+++ b/drivers/media/cec/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-cec-objs := cec-core.o cec-adap.o cec-api.o cec-edid.o
+cec-objs := cec-core.o cec-adap.o cec-api.o
ifeq ($(CONFIG_CEC_NOTIFIER),y)
cec-objs += cec-notifier.o
diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c
index 030b2602faf0..31d1f4ab915e 100644
--- a/drivers/media/cec/cec-adap.c
+++ b/drivers/media/cec/cec-adap.c
@@ -62,6 +62,19 @@ static unsigned int cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr
return adap->log_addrs.primary_device_type[i < 0 ? 0 : i];
}
+u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size,
+ unsigned int *offset)
+{
+ unsigned int loc = cec_get_edid_spa_location(edid, size);
+
+ if (offset)
+ *offset = loc;
+ if (loc == 0)
+ return CEC_PHYS_ADDR_INVALID;
+ return (edid[loc] << 8) | edid[loc + 1];
+}
+EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr);
+
/*
* Queue a new event for this filehandle. If ts == 0, then set it
* to the current time.
@@ -341,7 +354,7 @@ static void cec_data_completed(struct cec_data *data)
*
* This function is called with adap->lock held.
*/
-static void cec_data_cancel(struct cec_data *data)
+static void cec_data_cancel(struct cec_data *data, u8 tx_status)
{
/*
* It's either the current transmit, or it is a pending
@@ -356,13 +369,11 @@ static void cec_data_cancel(struct cec_data *data)
}
if (data->msg.tx_status & CEC_TX_STATUS_OK) {
- /* Mark the canceled RX as a timeout */
data->msg.rx_ts = ktime_get_ns();
- data->msg.rx_status = CEC_RX_STATUS_TIMEOUT;
+ data->msg.rx_status = CEC_RX_STATUS_ABORTED;
} else {
- /* Mark the canceled TX as an error */
data->msg.tx_ts = ktime_get_ns();
- data->msg.tx_status |= CEC_TX_STATUS_ERROR |
+ data->msg.tx_status |= tx_status |
CEC_TX_STATUS_MAX_RETRIES;
data->msg.tx_error_cnt++;
data->attempts = 0;
@@ -390,15 +401,15 @@ static void cec_flush(struct cec_adapter *adap)
while (!list_empty(&adap->transmit_queue)) {
data = list_first_entry(&adap->transmit_queue,
struct cec_data, list);
- cec_data_cancel(data);
+ cec_data_cancel(data, CEC_TX_STATUS_ABORTED);
}
if (adap->transmitting)
- cec_data_cancel(adap->transmitting);
+ cec_data_cancel(adap->transmitting, CEC_TX_STATUS_ABORTED);
/* Cancel the pending timeout work. */
list_for_each_entry_safe(data, n, &adap->wait_queue, list) {
if (cancel_delayed_work(&data->work))
- cec_data_cancel(data);
+ cec_data_cancel(data, CEC_TX_STATUS_OK);
/*
* If cancel_delayed_work returned false, then
* the cec_wait_timeout function is running,
@@ -474,12 +485,13 @@ int cec_thread_func(void *_adap)
* so much traffic on the bus that the adapter was
* unable to transmit for CEC_XFER_TIMEOUT_MS (2.1s).
*/
- dprintk(1, "%s: message %*ph timed out\n", __func__,
+ pr_warn("cec-%s: message %*ph timed out\n", adap->name,
adap->transmitting->msg.len,
adap->transmitting->msg.msg);
adap->tx_timeouts++;
/* Just give up on this. */
- cec_data_cancel(adap->transmitting);
+ cec_data_cancel(adap->transmitting,
+ CEC_TX_STATUS_TIMEOUT);
goto unlock;
}
@@ -514,9 +526,11 @@ int cec_thread_func(void *_adap)
if (data->attempts) {
/* should be >= 3 data bit periods for a retry */
signal_free_time = CEC_SIGNAL_FREE_TIME_RETRY;
- } else if (data->new_initiator) {
+ } else if (adap->last_initiator !=
+ cec_msg_initiator(&data->msg)) {
/* should be >= 5 data bit periods for new initiator */
signal_free_time = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
+ adap->last_initiator = cec_msg_initiator(&data->msg);
} else {
/*
* should be >= 7 data bit periods for sending another
@@ -530,7 +544,7 @@ int cec_thread_func(void *_adap)
/* Tell the adapter to transmit, cancel on error */
if (adap->ops->adap_transmit(adap, data->attempts,
signal_free_time, &data->msg))
- cec_data_cancel(data);
+ cec_data_cancel(data, CEC_TX_STATUS_ABORTED);
unlock:
mutex_unlock(&adap->lock);
@@ -701,9 +715,6 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
struct cec_fh *fh, bool block)
{
struct cec_data *data;
- u8 last_initiator = 0xff;
- unsigned int timeout;
- int res = 0;
msg->rx_ts = 0;
msg->tx_ts = 0;
@@ -813,23 +824,6 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
data->adap = adap;
data->blocking = block;
- /*
- * Determine if this message follows a message from the same
- * initiator. Needed to determine the free signal time later on.
- */
- if (msg->len > 1) {
- if (!(list_empty(&adap->transmit_queue))) {
- const struct cec_data *last;
-
- last = list_last_entry(&adap->transmit_queue,
- const struct cec_data, list);
- last_initiator = cec_msg_initiator(&last->msg);
- } else if (adap->transmitting) {
- last_initiator =
- cec_msg_initiator(&adap->transmitting->msg);
- }
- }
- data->new_initiator = last_initiator != cec_msg_initiator(msg);
init_completion(&data->c);
INIT_DELAYED_WORK(&data->work, cec_wait_timeout);
@@ -846,47 +840,22 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
return 0;
/*
- * If we don't get a completion before this time something is really
- * wrong and we time out.
- */
- timeout = CEC_XFER_TIMEOUT_MS;
- /* Add the requested timeout if we have to wait for a reply as well */
- if (msg->timeout)
- timeout += msg->timeout;
-
- /*
* Release the lock and wait, retake the lock afterwards.
*/
mutex_unlock(&adap->lock);
- res = wait_for_completion_killable_timeout(&data->c,
- msecs_to_jiffies(timeout));
+ wait_for_completion_killable(&data->c);
+ if (!data->completed)
+ cancel_delayed_work_sync(&data->work);
mutex_lock(&adap->lock);
- if (data->completed) {
- /* The transmit completed (possibly with an error) */
- *msg = data->msg;
- kfree(data);
- return 0;
- }
- /*
- * The wait for completion timed out or was interrupted, so mark this
- * as non-blocking and disconnect from the filehandle since it is
- * still 'in flight'. When it finally completes it will just drop the
- * result silently.
- */
- data->blocking = false;
- if (data->fh)
- list_del(&data->xfer_list);
- data->fh = NULL;
+ /* Cancel the transmit if it was interrupted */
+ if (!data->completed)
+ cec_data_cancel(data, CEC_TX_STATUS_ABORTED);
- if (res == 0) { /* timed out */
- /* Check if the reply or the transmit failed */
- if (msg->timeout && (msg->tx_status & CEC_TX_STATUS_OK))
- msg->rx_status = CEC_RX_STATUS_TIMEOUT;
- else
- msg->tx_status = CEC_TX_STATUS_MAX_RETRIES;
- }
- return res > 0 ? 0 : res;
+ /* The transmit completed (possibly with an error) */
+ *msg = data->msg;
+ kfree(data);
+ return 0;
}
/* Helper function to be used by drivers and this framework. */
@@ -1044,6 +1013,8 @@ void cec_received_msg_ts(struct cec_adapter *adap,
mutex_lock(&adap->lock);
dprintk(2, "%s: %*ph\n", __func__, msg->len, msg->msg);
+ adap->last_initiator = 0xff;
+
/* Check if this message was for us (directed or broadcast). */
if (!cec_msg_is_broadcast(msg))
valid_la = cec_has_log_addr(adap, msg_dest);
@@ -1506,6 +1477,8 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
}
mutex_lock(&adap->devnode.lock);
+ adap->last_initiator = 0xff;
+
if ((adap->needs_hpd || list_empty(&adap->devnode.fhs)) &&
adap->ops->adap_enable(adap, true)) {
mutex_unlock(&adap->devnode.lock);
diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c
index b6536bbad530..391b6fd483e1 100644
--- a/drivers/media/cec/cec-api.c
+++ b/drivers/media/cec/cec-api.c
@@ -77,9 +77,9 @@ static long cec_adap_g_caps(struct cec_adapter *adap,
{
struct cec_caps caps = {};
- strlcpy(caps.driver, adap->devnode.dev.parent->driver->name,
+ strscpy(caps.driver, adap->devnode.dev.parent->driver->name,
sizeof(caps.driver));
- strlcpy(caps.name, adap->name, sizeof(caps.name));
+ strscpy(caps.name, adap->name, sizeof(caps.name));
caps.available_log_addrs = adap->available_log_addrs;
caps.capabilities = adap->capabilities;
caps.version = LINUX_VERSION_CODE;
@@ -101,6 +101,23 @@ static long cec_adap_g_phys_addr(struct cec_adapter *adap,
return 0;
}
+static int cec_validate_phys_addr(u16 phys_addr)
+{
+ int i;
+
+ if (phys_addr == CEC_PHYS_ADDR_INVALID)
+ return 0;
+ for (i = 0; i < 16; i += 4)
+ if (phys_addr & (0xf << i))
+ break;
+ if (i == 16)
+ return 0;
+ for (i += 4; i < 16; i += 4)
+ if ((phys_addr & (0xf << i)) == 0)
+ return -EINVAL;
+ return 0;
+}
+
static long cec_adap_s_phys_addr(struct cec_adapter *adap, struct cec_fh *fh,
bool block, __u16 __user *parg)
{
@@ -112,7 +129,7 @@ static long cec_adap_s_phys_addr(struct cec_adapter *adap, struct cec_fh *fh,
if (copy_from_user(&phys_addr, parg, sizeof(phys_addr)))
return -EFAULT;
- err = cec_phys_addr_validate(phys_addr, NULL, NULL);
+ err = cec_validate_phys_addr(phys_addr);
if (err)
return err;
mutex_lock(&adap->lock);
@@ -665,6 +682,7 @@ const struct file_operations cec_devnode_fops = {
.owner = THIS_MODULE,
.open = cec_open,
.unlocked_ioctl = cec_ioctl,
+ .compat_ioctl = cec_ioctl,
.release = cec_release,
.poll = cec_poll,
.llseek = no_llseek,
diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c
index b278ab90b387..e4edc930d4ed 100644
--- a/drivers/media/cec/cec-core.c
+++ b/drivers/media/cec/cec-core.c
@@ -264,7 +264,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
adap = kzalloc(sizeof(*adap), GFP_KERNEL);
if (!adap)
return ERR_PTR(-ENOMEM);
- strlcpy(adap->name, name, sizeof(adap->name));
+ strscpy(adap->name, name, sizeof(adap->name));
adap->phys_addr = CEC_PHYS_ADDR_INVALID;
adap->cec_pin_is_high = true;
adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;
@@ -307,12 +307,10 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
return ERR_PTR(-ENOMEM);
}
- snprintf(adap->device_name, sizeof(adap->device_name),
- "RC for %s", name);
snprintf(adap->input_phys, sizeof(adap->input_phys),
- "%s/input0", name);
+ "%s/input0", adap->name);
- adap->rc->device_name = adap->device_name;
+ adap->rc->device_name = adap->name;
adap->rc->input_phys = adap->input_phys;
adap->rc->input_id.bustype = BUS_CEC;
adap->rc->input_id.vendor = 0;
diff --git a/drivers/media/cec/cec-edid.c b/drivers/media/cec/cec-edid.c
deleted file mode 100644
index ec72ac1c0b91..000000000000
--- a/drivers/media/cec/cec-edid.c
+++ /dev/null
@@ -1,155 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * cec-edid - HDMI Consumer Electronics Control EDID & CEC helper functions
- *
- * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <media/cec.h>
-
-/*
- * This EDID is expected to be a CEA-861 compliant, which means that there are
- * at least two blocks and one or more of the extensions blocks are CEA-861
- * blocks.
- *
- * The returned location is guaranteed to be < size - 1.
- */
-static unsigned int cec_get_edid_spa_location(const u8 *edid, unsigned int size)
-{
- unsigned int blocks = size / 128;
- unsigned int block;
- u8 d;
-
- /* Sanity check: at least 2 blocks and a multiple of the block size */
- if (blocks < 2 || size % 128)
- return 0;
-
- /*
- * If there are fewer extension blocks than the size, then update
- * 'blocks'. It is allowed to have more extension blocks than the size,
- * since some hardware can only read e.g. 256 bytes of the EDID, even
- * though more blocks are present. The first CEA-861 extension block
- * should normally be in block 1 anyway.
- */
- if (edid[0x7e] + 1 < blocks)
- blocks = edid[0x7e] + 1;
-
- for (block = 1; block < blocks; block++) {
- unsigned int offset = block * 128;
-
- /* Skip any non-CEA-861 extension blocks */
- if (edid[offset] != 0x02 || edid[offset + 1] != 0x03)
- continue;
-
- /* search Vendor Specific Data Block (tag 3) */
- d = edid[offset + 2] & 0x7f;
- /* Check if there are Data Blocks */
- if (d <= 4)
- continue;
- if (d > 4) {
- unsigned int i = offset + 4;
- unsigned int end = offset + d;
-
- /* Note: 'end' is always < 'size' */
- do {
- u8 tag = edid[i] >> 5;
- u8 len = edid[i] & 0x1f;
-
- if (tag == 3 && len >= 5 && i + len <= end &&
- edid[i + 1] == 0x03 &&
- edid[i + 2] == 0x0c &&
- edid[i + 3] == 0x00)
- return i + 4;
- i += len + 1;
- } while (i < end);
- }
- }
- return 0;
-}
-
-u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size,
- unsigned int *offset)
-{
- unsigned int loc = cec_get_edid_spa_location(edid, size);
-
- if (offset)
- *offset = loc;
- if (loc == 0)
- return CEC_PHYS_ADDR_INVALID;
- return (edid[loc] << 8) | edid[loc + 1];
-}
-EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr);
-
-void cec_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr)
-{
- unsigned int loc = cec_get_edid_spa_location(edid, size);
- u8 sum = 0;
- unsigned int i;
-
- if (loc == 0)
- return;
- edid[loc] = phys_addr >> 8;
- edid[loc + 1] = phys_addr & 0xff;
- loc &= ~0x7f;
-
- /* update the checksum */
- for (i = loc; i < loc + 127; i++)
- sum += edid[i];
- edid[i] = 256 - sum;
-}
-EXPORT_SYMBOL_GPL(cec_set_edid_phys_addr);
-
-u16 cec_phys_addr_for_input(u16 phys_addr, u8 input)
-{
- /* Check if input is sane */
- if (WARN_ON(input == 0 || input > 0xf))
- return CEC_PHYS_ADDR_INVALID;
-
- if (phys_addr == 0)
- return input << 12;
-
- if ((phys_addr & 0x0fff) == 0)
- return phys_addr | (input << 8);
-
- if ((phys_addr & 0x00ff) == 0)
- return phys_addr | (input << 4);
-
- if ((phys_addr & 0x000f) == 0)
- return phys_addr | input;
-
- /*
- * All nibbles are used so no valid physical addresses can be assigned
- * to the input.
- */
- return CEC_PHYS_ADDR_INVALID;
-}
-EXPORT_SYMBOL_GPL(cec_phys_addr_for_input);
-
-int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port)
-{
- int i;
-
- if (parent)
- *parent = phys_addr;
- if (port)
- *port = 0;
- if (phys_addr == CEC_PHYS_ADDR_INVALID)
- return 0;
- for (i = 0; i < 16; i += 4)
- if (phys_addr & (0xf << i))
- break;
- if (i == 16)
- return 0;
- if (parent)
- *parent = phys_addr & (0xfff0 << i);
- if (port)
- *port = (phys_addr >> i) & 0xf;
- for (i += 4; i < 16; i += 4)
- if ((phys_addr & (0xf << i)) == 0)
- return -EINVAL;
- return 0;
-}
-EXPORT_SYMBOL_GPL(cec_phys_addr_validate);
diff --git a/drivers/media/cec/cec-pin.c b/drivers/media/cec/cec-pin.c
index 6e311424f0dc..635db8e70ead 100644
--- a/drivers/media/cec/cec-pin.c
+++ b/drivers/media/cec/cec-pin.c
@@ -935,6 +935,17 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
/* Start bit, switch to receive state */
pin->ts = ts;
pin->state = CEC_ST_RX_START_BIT_LOW;
+ /*
+ * If a transmit is pending, then that transmit should
+ * use a signal free time of no more than
+ * CEC_SIGNAL_FREE_TIME_NEW_INITIATOR since it will
+ * have a new initiator due to the receive that is now
+ * starting.
+ */
+ if (pin->tx_msg.len && pin->tx_signal_free_time >
+ CEC_SIGNAL_FREE_TIME_NEW_INITIATOR)
+ pin->tx_signal_free_time =
+ CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
break;
}
if (ktime_to_ns(pin->ts) == 0)
@@ -1157,6 +1168,15 @@ static int cec_pin_adap_transmit(struct cec_adapter *adap, u8 attempts,
{
struct cec_pin *pin = adap->pin;
+ /*
+ * If a receive is in progress, then this transmit should use
+ * a signal free time of max CEC_SIGNAL_FREE_TIME_NEW_INITIATOR
+ * since when it starts transmitting it will have a new initiator.
+ */
+ if (pin->state != CEC_ST_IDLE &&
+ signal_free_time > CEC_SIGNAL_FREE_TIME_NEW_INITIATOR)
+ signal_free_time = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
+
pin->tx_signal_free_time = signal_free_time;
pin->tx_extra_bytes = 0;
pin->tx_msg = *msg;
OpenPOWER on IntegriCloud