/*
  * A pending CEC transmit needs to be cancelled, either because the CEC
  * adapter is disabled or the transmit takes an impossibly long time to
- * finish.
+ * finish, or the reply timed out.
  *
  * This function is called with adap->lock held.
  */
-static void cec_data_cancel(struct cec_data *data, u8 tx_status)
+static void cec_data_cancel(struct cec_data *data, u8 tx_status, u8 rx_status)
 {
+       struct cec_adapter *adap = data->adap;
+
        /*
         * It's either the current transmit, or it is a pending
         * transmit. Take the appropriate action to clear it.
         */
-       if (data->adap->transmitting == data) {
-               data->adap->transmitting = NULL;
+       if (adap->transmitting == data) {
+               adap->transmitting = NULL;
        } else {
                list_del_init(&data->list);
                if (!(data->msg.tx_status & CEC_TX_STATUS_OK))
-                       if (!WARN_ON(!data->adap->transmit_queue_sz))
-                               data->adap->transmit_queue_sz--;
+                       if (!WARN_ON(!adap->transmit_queue_sz))
+                               adap->transmit_queue_sz--;
        }
 
        if (data->msg.tx_status & CEC_TX_STATUS_OK) {
                data->msg.rx_ts = ktime_get_ns();
-               data->msg.rx_status = CEC_RX_STATUS_ABORTED;
+               data->msg.rx_status = rx_status;
+               if (!data->blocking)
+                       data->msg.tx_status = 0;
        } else {
                data->msg.tx_ts = ktime_get_ns();
                data->msg.tx_status |= tx_status |
                                       CEC_TX_STATUS_MAX_RETRIES;
                data->msg.tx_error_cnt++;
                data->attempts = 0;
+               if (!data->blocking)
+                       data->msg.rx_status = 0;
        }
 
        /* Queue transmitted message for monitoring purposes */
-       cec_queue_msg_monitor(data->adap, &data->msg, 1);
+       cec_queue_msg_monitor(adap, &data->msg, 1);
+
+       if (!data->blocking && data->msg.sequence && adap->ops->received)
+               /* Allow drivers to process the message first */
+               adap->ops->received(adap, &data->msg);
 
        cec_data_completed(data);
 }
        while (!list_empty(&adap->transmit_queue)) {
                data = list_first_entry(&adap->transmit_queue,
                                        struct cec_data, list);
-               cec_data_cancel(data, CEC_TX_STATUS_ABORTED);
+               cec_data_cancel(data, CEC_TX_STATUS_ABORTED, 0);
        }
        if (adap->transmitting)
                adap->transmit_in_progress_aborted = true;
        /* 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_TX_STATUS_OK);
+                       cec_data_cancel(data, CEC_TX_STATUS_OK, CEC_RX_STATUS_ABORTED);
                /*
                 * If cancel_delayed_work returned false, then
                 * the cec_wait_timeout function is running,
                                        adap->transmitting->msg.msg);
                                /* Just give up on this. */
                                cec_data_cancel(adap->transmitting,
-                                               CEC_TX_STATUS_TIMEOUT);
+                                               CEC_TX_STATUS_TIMEOUT, 0);
                        } else {
                                pr_warn("cec-%s: transmit timed out\n", adap->name);
                        }
                /* 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_TX_STATUS_ABORTED);
+                       cec_data_cancel(data, CEC_TX_STATUS_ABORTED, 0);
                else
                        adap->transmit_in_progress = true;
 
 
        /* Mark the message as timed out */
        list_del_init(&data->list);
-       data->msg.rx_ts = ktime_get_ns();
-       data->msg.rx_status = CEC_RX_STATUS_TIMEOUT;
-       cec_data_completed(data);
+       cec_data_cancel(data, CEC_TX_STATUS_OK, CEC_RX_STATUS_TIMEOUT);
 unlock:
        mutex_unlock(&adap->lock);
 }
        mutex_lock(&adap->lock);
 
        /* Cancel the transmit if it was interrupted */
-       if (!data->completed)
-               cec_data_cancel(data, CEC_TX_STATUS_ABORTED);
+       if (!data->completed) {
+               if (data->msg.tx_status & CEC_TX_STATUS_OK)
+                       cec_data_cancel(data, CEC_TX_STATUS_OK, CEC_RX_STATUS_ABORTED);
+               else
+                       cec_data_cancel(data, CEC_TX_STATUS_ABORTED, 0);
+       }
 
        /* The transmit completed (possibly with an error) */
        *msg = data->msg;
        adap->transmit_in_progress = false;
        adap->transmit_in_progress_aborted = false;
        if (adap->transmitting)
-               cec_data_cancel(adap->transmitting, CEC_TX_STATUS_ABORTED);
+               cec_data_cancel(adap->transmitting, CEC_TX_STATUS_ABORTED, 0);
        mutex_unlock(&adap->devnode.lock);
 }