diff options
Diffstat (limited to 'drivers/media/cec/core/cec-adap.c')
-rw-r--r-- | drivers/media/cec/core/cec-adap.c | 82 |
1 files changed, 61 insertions, 21 deletions
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 559a172ebc6c..ba6828ef540e 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -7,12 +7,13 @@ #include <linux/errno.h> #include <linux/init.h> -#include <linux/module.h> #include <linux/kernel.h> #include <linux/kmod.h> #include <linux/ktime.h> -#include <linux/slab.h> #include <linux/mm.h> +#include <linux/module.h> +#include <linux/seq_file.h> +#include <linux/slab.h> #include <linux/string.h> #include <linux/types.h> @@ -490,6 +491,15 @@ int cec_thread_func(void *_adap) goto unlock; } + if (adap->transmit_in_progress && + adap->transmit_in_progress_aborted) { + if (adap->transmitting) + cec_data_cancel(adap->transmitting, + CEC_TX_STATUS_ABORTED, 0); + adap->transmit_in_progress = false; + adap->transmit_in_progress_aborted = false; + goto unlock; + } if (adap->transmit_in_progress && timeout) { /* * If we timeout, then log that. Normally this does @@ -664,8 +674,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); @@ -771,6 +782,9 @@ 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) && + msg->len > 1 && msg->msg[1] == CEC_MSG_VENDOR_COMMAND_WITH_ID; + int err; if (adap->devnode.unregistered) return -ENODEV; @@ -784,12 +798,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 | + (reply_vendor_id ? CEC_MSG_FL_REPLY_VENDOR_ID : 0); - 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; @@ -799,6 +814,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) { + dprintk(1, "%s: <Vendor Command With ID> message too short\n", + __func__); + return -EINVAL; + } memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len); @@ -890,8 +910,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; } } @@ -913,6 +934,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); @@ -935,11 +964,13 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, * Release the lock and wait, retake the lock afterwards. */ mutex_unlock(&adap->lock); - wait_for_completion_killable(&data->c); - if (!data->completed) - cancel_delayed_work_sync(&data->work); + err = wait_for_completion_killable(&data->c); + cancel_delayed_work_sync(&data->work); mutex_lock(&adap->lock); + if (err) + adap->transmit_in_progress_aborted = true; + /* Cancel the transmit if it was interrupted */ if (!data->completed) { if (data->msg.tx_status & CEC_TX_STATUS_OK) @@ -1199,13 +1230,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? */ @@ -1575,9 +1608,12 @@ unconfigure: */ static void cec_claim_log_addrs(struct cec_adapter *adap, bool block) { - if (WARN_ON(adap->is_configuring || adap->is_configured)) + if (WARN_ON(adap->is_claiming_log_addrs || + adap->is_configuring || adap->is_configured)) return; + adap->is_claiming_log_addrs = true; + init_completion(&adap->config_completion); /* Ready to kick off the thread */ @@ -1592,6 +1628,7 @@ static void cec_claim_log_addrs(struct cec_adapter *adap, bool block) wait_for_completion(&adap->config_completion); mutex_lock(&adap->lock); } + adap->is_claiming_log_addrs = false; } /* @@ -2302,18 +2339,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); } |