media: cec: core: add new CEC_MSG_FL_REPLY_VENDOR_ID flag
authorHans Verkuil <hverkuil-cisco@xs4all.nl>
Thu, 4 Jul 2024 09:01:51 +0000 (11:01 +0200)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Mon, 5 Aug 2024 07:38:46 +0000 (09:38 +0200)
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>
Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst
Documentation/userspace-api/media/cec/cec-ioc-receive.rst
drivers/media/cec/core/cec-adap.c
drivers/media/cec/core/cec-core.c
include/media/cec.h
include/uapi/linux/cec.h

index d5e014ce19b5814c3f4f1ff4d5bc029d92f9540f..1d5248979a6dab38bf4361518757d6465ad1b8bd 100644 (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
 ============
index 364938ad34df91dde335b628e163934f44022086..3e6c511e054f40db2aa0cde0d3f40a7bd2384f34 100644 (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:
index da09834990b875c91a66d501520617b9a0b68ccf..c81b1ed7c08ae83b9a9b3470451719eec5758833 100644 (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);
        }
 
index 6f940df0230c95469a99a83387c4b3cfdbbb3d97..e0756826d629b999d48bbd9c5ec2591651cf19c4 100644 (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;
index d131514032f2c595e25e194323d05f2627b28185..07d2ee8a3904134fa5b0c86e5f46800191b67858 100644 (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;
index b8e071abaea5acfa6d2fde13bb1ec6fb286fc1a0..894fffc66f2cb7a898529bd0ff3d61119fe4ee56 100644 (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.