media: cec: core: add new CEC_MSG_FL_REPLY_VENDOR_ID flag

If this flag is set, then the reply is expected to consist of
the CEC_MSG_VENDOR_COMMAND_WITH_ID opcode followed by the Vendor ID (as
used in bytes 1-4 of the message), followed by the struct cec_msg reply
field.

Note that this assumes that the byte after the Vendor ID is a
vendor-specific opcode.

This flag makes it easier to wait for replies to vendor commands,
using the same CEC framework support for waiting for regular replies.

Support for this flag is indicated by setting the new
CEC_CAP_REPLY_VENDOR_ID capability.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
This commit is contained in:
Hans Verkuil 2024-07-04 11:01:51 +02:00 committed by Mauro Carvalho Chehab
parent 2c25dcc236
commit 613f21505b
6 changed files with 64 additions and 16 deletions

View File

@ -137,6 +137,12 @@ returns the information to the application. The ioctl never fails.
- 0x00000100
- If this capability is set, then :ref:`CEC_ADAP_G_CONNECTOR_INFO` can
be used.
* .. _`CEC-CAP-REPLY-VENDOR-ID`:
- ``CEC_CAP_REPLY_VENDOR_ID``
- 0x00000200
- If this capability is set, then
:ref:`CEC_MSG_FL_REPLY_VENDOR_ID <cec-msg-flags>` can be used.
Return Value
============

View File

@ -232,6 +232,21 @@ View On' messages from initiator 0xf ('Unregistered') to destination 0 ('TV').
capability. If that is not set, then the ``EPERM`` error code is
returned.
* .. _`CEC-MSG-FL-REPLY-VENDOR-ID`:
- ``CEC_MSG_FL_REPLY_VENDOR_ID``
- 4
- This flag is only available if the ``CEC_CAP_REPLY_VENDOR_ID`` capability
is set. If this flag is set, then the reply is expected to consist of
the ``CEC_MSG_VENDOR_COMMAND_WITH_ID`` opcode followed by the Vendor ID
(in bytes 1-4 of the message), followed by the ``struct cec_msg``
``reply`` field.
Note that this assumes that the byte after the Vendor ID is a
vendor-specific opcode.
This flag makes it easier to wait for replies to vendor commands.
.. tabularcolumns:: |p{5.6cm}|p{0.9cm}|p{10.8cm}|
.. _cec-tx-status:

View File

@ -673,8 +673,9 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status,
/* Retry this message */
data->attempts -= attempts_made;
if (msg->timeout)
dprintk(2, "retransmit: %*ph (attempts: %d, wait for 0x%02x)\n",
msg->len, msg->msg, data->attempts, msg->reply);
dprintk(2, "retransmit: %*ph (attempts: %d, wait for %*ph)\n",
msg->len, msg->msg, data->attempts,
data->match_len, data->match_reply);
else
dprintk(2, "retransmit: %*ph (attempts: %d)\n",
msg->len, msg->msg, data->attempts);
@ -780,6 +781,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
{
struct cec_data *data;
bool is_raw = msg_is_raw(msg);
bool reply_vendor_id = msg->flags & CEC_MSG_FL_REPLY_VENDOR_ID;
int err;
if (adap->devnode.unregistered)
@ -794,12 +796,13 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
msg->tx_low_drive_cnt = 0;
msg->tx_error_cnt = 0;
msg->sequence = 0;
msg->flags &= CEC_MSG_FL_REPLY_TO_FOLLOWERS | CEC_MSG_FL_RAW |
CEC_MSG_FL_REPLY_VENDOR_ID;
if (msg->reply && msg->timeout == 0) {
if ((reply_vendor_id || msg->reply) && msg->timeout == 0) {
/* Make sure the timeout isn't 0. */
msg->timeout = 1000;
}
msg->flags &= CEC_MSG_FL_REPLY_TO_FOLLOWERS | CEC_MSG_FL_RAW;
if (!msg->timeout)
msg->flags &= ~CEC_MSG_FL_REPLY_TO_FOLLOWERS;
@ -809,6 +812,11 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
dprintk(1, "%s: invalid length %d\n", __func__, msg->len);
return -EINVAL;
}
if (reply_vendor_id &&
(msg->len < 6 || msg->msg[1] != CEC_MSG_VENDOR_COMMAND_WITH_ID)) {
dprintk(1, "%s: message too short or not <Vendor Command With ID>\n", __func__);
return -EINVAL;
}
memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len);
@ -900,8 +908,9 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
__func__);
return -ENONET;
}
if (msg->reply) {
dprintk(1, "%s: invalid msg->reply\n", __func__);
if (reply_vendor_id || msg->reply) {
dprintk(1, "%s: adapter is unconfigured so reply is not supported\n",
__func__);
return -EINVAL;
}
}
@ -923,6 +932,14 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
data->fh = fh;
data->adap = adap;
data->blocking = block;
if (reply_vendor_id) {
memcpy(data->match_reply, msg->msg + 1, 4);
data->match_reply[4] = msg->reply;
data->match_len = 5;
} else if (msg->timeout) {
data->match_reply[0] = msg->reply;
data->match_len = 1;
}
init_completion(&data->c);
INIT_DELAYED_WORK(&data->work, cec_wait_timeout);
@ -1211,13 +1228,15 @@ void cec_received_msg_ts(struct cec_adapter *adap,
if (!abort && dst->msg[1] == CEC_MSG_INITIATE_ARC &&
(cmd == CEC_MSG_REPORT_ARC_INITIATED ||
cmd == CEC_MSG_REPORT_ARC_TERMINATED) &&
(dst->reply == CEC_MSG_REPORT_ARC_INITIATED ||
dst->reply == CEC_MSG_REPORT_ARC_TERMINATED))
(data->match_reply[0] == CEC_MSG_REPORT_ARC_INITIATED ||
data->match_reply[0] == CEC_MSG_REPORT_ARC_TERMINATED)) {
dst->reply = cmd;
data->match_reply[0] = cmd;
}
/* Does the command match? */
if ((abort && cmd != dst->msg[1]) ||
(!abort && cmd != dst->reply))
(!abort && memcmp(data->match_reply, msg->msg + 1, data->match_len)))
continue;
/* Does the addressing match? */
@ -2318,18 +2337,21 @@ int cec_adap_status(struct seq_file *file, void *priv)
}
data = adap->transmitting;
if (data)
seq_printf(file, "transmitting message: %*ph (reply: %02x, timeout: %ums)\n",
data->msg.len, data->msg.msg, data->msg.reply,
seq_printf(file, "transmitting message: %*ph (reply: %*ph, timeout: %ums)\n",
data->msg.len, data->msg.msg,
data->match_len, data->match_reply,
data->msg.timeout);
seq_printf(file, "pending transmits: %u\n", adap->transmit_queue_sz);
list_for_each_entry(data, &adap->transmit_queue, list) {
seq_printf(file, "queued tx message: %*ph (reply: %02x, timeout: %ums)\n",
data->msg.len, data->msg.msg, data->msg.reply,
seq_printf(file, "queued tx message: %*ph (reply: %*ph, timeout: %ums)\n",
data->msg.len, data->msg.msg,
data->match_len, data->match_reply,
data->msg.timeout);
}
list_for_each_entry(data, &adap->wait_queue, list) {
seq_printf(file, "message waiting for reply: %*ph (reply: %02x, timeout: %ums)\n",
data->msg.len, data->msg.msg, data->msg.reply,
seq_printf(file, "message waiting for reply: %*ph (reply: %*ph, timeout: %ums)\n",
data->msg.len, data->msg.msg,
data->match_len, data->match_reply,
data->msg.timeout);
}

View File

@ -273,7 +273,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
adap->cec_pin_is_high = true;
adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;
adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE;
adap->capabilities = caps;
adap->capabilities = caps | CEC_CAP_REPLY_VENDOR_ID;
if (debug_phys_addr)
adap->capabilities |= CEC_CAP_PHYS_ADDR;
adap->needs_hpd = caps & CEC_CAP_NEEDS_HPD;

View File

@ -66,6 +66,8 @@ struct cec_data {
struct list_head xfer_list;
struct cec_adapter *adap;
struct cec_msg msg;
u8 match_len;
u8 match_reply[5];
struct cec_fh *fh;
struct delayed_work work;
struct completion c;

View File

@ -165,6 +165,7 @@ static inline int cec_msg_recv_is_rx_result(const struct cec_msg *msg)
/* cec_msg flags field */
#define CEC_MSG_FL_REPLY_TO_FOLLOWERS (1 << 0)
#define CEC_MSG_FL_RAW (1 << 1)
#define CEC_MSG_FL_REPLY_VENDOR_ID (1 << 2)
/* cec_msg tx/rx_status field */
#define CEC_TX_STATUS_OK (1 << 0)
@ -339,6 +340,8 @@ static inline int cec_is_unconfigured(__u16 log_addr_mask)
#define CEC_CAP_MONITOR_PIN (1 << 7)
/* CEC_ADAP_G_CONNECTOR_INFO is available */
#define CEC_CAP_CONNECTOR_INFO (1 << 8)
/* CEC_MSG_FL_REPLY_VENDOR_ID is available */
#define CEC_CAP_REPLY_VENDOR_ID (1 << 9)
/**
* struct cec_caps - CEC capabilities structure.