// SPDX-License-Identifier: GPL-2.0-only /* * vivid-cec.c - A Virtual Video Test Driver, cec emulation * * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*/
if (adap == dev_tx->cec_tx_adap[hdmi_output]) continue; if (!dev_tx->cec_tx_adap[hdmi_output]->is_configured) continue; if (cec_has_log_addr(dev_tx->cec_tx_adap[hdmi_output], dest)) returntrue;
} returnfalse;
}
spin_lock(&dev->cec_xfers_slock); for (i = 0; i < ARRAY_SIZE(dev->xfers); i++) { if (dev->xfers[i].sft &&
dev->xfers[i].sft <= dev->cec_sft) {
ready = true; break;
}
}
spin_unlock(&dev->cec_xfers_slock);
return ready;
}
/* * If an adapter tries to send successive messages, it must wait for the * longest signal-free time between its transmissions. But, if another * adapter sends a message in the interim, then the wait can be reduced * because the messages are no longer successive. Make these adjustments * if necessary. Should be called holding cec_xfers_slock.
*/ staticvoid adjust_sfts(struct vivid_dev *dev)
{ unsignedint i;
u8 initiator;
for (i = 0; i < ARRAY_SIZE(dev->xfers); i++) { if (dev->xfers[i].sft <= CEC_SIGNAL_FREE_TIME_RETRY) continue;
initiator = dev->xfers[i].msg[0] >> 4; if (initiator == dev->last_initiator)
dev->xfers[i].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER; else
dev->xfers[i].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
}
}
/* * The main emulation of the bus on which CEC adapters attempt to send * messages to each other. The bus keeps track of how long it has been * signal-free and accepts a pending transmission only if the state of * the bus matches the transmission's signal-free requirements. It calls * cec_transmit_attempt_done() for all transmits that enter the bus and * cec_received_msg() for successful transmits.
*/ int vivid_cec_bus_thread(void *_dev)
{
u32 last_sft; unsignedint i, j; unsignedint dest;
ktime_t start, end;
s64 delta_us, retry_us; struct vivid_dev *dev = _dev;
dev->cec_sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER; for (;;) { bool first = true; int wait_xfer_us = 0; bool valid_dest = false; int wait_arb_lost_us = 0; unsignedint first_idx = 0; unsignedint first_status = 0; struct cec_msg first_msg = {}; struct xfer_on_bus xfers_on_bus[MAX_OUTPUTS] = {};
wait_event_interruptible(dev->kthread_waitq_cec, xfer_ready(dev) ||
kthread_should_stop()); if (kthread_should_stop()) break;
last_sft = dev->cec_sft;
dev->cec_sft = 0; /* * Move the messages that are ready onto the bus. The adapter with * the most leading zeros will win control of the bus and any other * adapters will lose arbitration.
*/
spin_lock(&dev->cec_xfers_slock); for (i = 0; i < ARRAY_SIZE(dev->xfers); i++) { if (!dev->xfers[i].sft || dev->xfers[i].sft > last_sft) continue; if (first) {
first = false;
first_idx = i;
xfers_on_bus[first_idx].adap = dev->xfers[i].adap;
memcpy(first_msg.msg, dev->xfers[i].msg, dev->xfers[i].len);
first_msg.len = dev->xfers[i].len;
} else {
xfers_on_bus[i].adap = dev->xfers[i].adap;
xfers_on_bus[i].status = CEC_TX_STATUS_ARB_LOST; /* * For simplicity wait for all 4 bits of the initiator's * address even though HDMI specification uses bit-level * precision.
*/
wait_arb_lost_us = 4 * CEC_DATA_BIT_US + CEC_START_BIT_US;
}
dev->xfers[i].sft = 0;
}
dev->last_initiator = cec_msg_initiator(&first_msg);
adjust_sfts(dev);
spin_unlock(&dev->cec_xfers_slock);
dest = cec_msg_destination(&first_msg);
valid_dest = cec_msg_is_broadcast(&first_msg); if (!valid_dest)
valid_dest = find_dest_adap(dev, xfers_on_bus[first_idx].adap, dest); if (valid_dest) {
first_status = CEC_TX_STATUS_OK; /* * Message length is in bytes, but each byte is transmitted in * a block of 10 bits.
*/
wait_xfer_us = first_msg.len * 10 * CEC_DATA_BIT_US;
} else {
first_status = CEC_TX_STATUS_NACK; /* * A message that is not acknowledged stops transmitting after * the header block of 10 bits.
*/
wait_xfer_us = 10 * CEC_DATA_BIT_US;
}
wait_xfer_us += CEC_START_BIT_US;
xfers_on_bus[first_idx].status = first_status;
/* Sleep as if sending messages on a real hardware bus. */
start = ktime_get(); if (wait_arb_lost_us) {
usleep_range(wait_arb_lost_us - CEC_MARGIN_US, wait_arb_lost_us); for (i = 0; i < ARRAY_SIZE(xfers_on_bus); i++) { if (xfers_on_bus[i].status != CEC_TX_STATUS_ARB_LOST) continue;
cec_transmit_attempt_done(xfers_on_bus[i].adap,
CEC_TX_STATUS_ARB_LOST);
} if (kthread_should_stop()) break;
}
wait_xfer_us -= wait_arb_lost_us;
usleep_range(wait_xfer_us - CEC_MARGIN_US, wait_xfer_us);
cec_transmit_attempt_done(xfers_on_bus[first_idx].adap, first_status); if (kthread_should_stop()) break; if (first_status == CEC_TX_STATUS_OK) { if (xfers_on_bus[first_idx].adap != dev->cec_rx_adap)
cec_received_msg(dev->cec_rx_adap, &first_msg); for (i = 0, j = 0; i < dev->num_inputs; i++) { unsignedint menu_idx =
dev->input_is_connected_to_output[i];
if (dev->input_type[i] != HDMI) continue;
j++; if (menu_idx < FIXED_MENU_ITEMS) continue;
if (xfers_on_bus[first_idx].adap != dev_tx->cec_tx_adap[hdmi_output])
cec_received_msg(dev_tx->cec_tx_adap[hdmi_output], &first_msg);
}
}
end = ktime_get(); /* * If the emulated transfer took more or less time than it should * have, then compensate by adjusting the wait time needed for the * bus to be signal-free for 3 bit periods (the retry time).
*/
delta_us = div_s64(end - start, 1000);
delta_us -= wait_xfer_us + wait_arb_lost_us;
retry_us = CEC_SIGNAL_FREE_TIME_RETRY * CEC_DATA_BIT_US - delta_us; if (retry_us > CEC_MARGIN_US)
usleep_range(retry_us - CEC_MARGIN_US, retry_us);
dev->cec_sft = CEC_SIGNAL_FREE_TIME_RETRY; /* * If there are no messages that need to be retried, check if any * adapters that did not just transmit a message are ready to * transmit. If none of these adapters are ready, then increase * the signal-free time so that the bus is available to all * adapters and go back to waiting for a transmission.
*/ while (dev->cec_sft >= CEC_SIGNAL_FREE_TIME_RETRY &&
dev->cec_sft < CEC_SIGNAL_FREE_TIME_NEXT_XFER &&
!xfer_ready(dev) && !kthread_should_stop()) {
usleep_range(2 * CEC_DATA_BIT_US - CEC_MARGIN_US,
2 * CEC_DATA_BIT_US);
dev->cec_sft += 2;
}
} return 0;
}
/* * If we receive <Vendor Command With ID> with our vendor ID * and with a payload of size 1, and the payload value is odd, * then we reply with the same message, but with the payload * byte incremented by 1. * * If the size is 1 and the payload value is even, then we * ignore the message. * * The reason we reply to odd instead of even payload values * is that it allows for testing of the corner case where the * reply value is 0 (0xff + 1 % 256). * * For other sizes we Feature Abort. * * This is added for the specific purpose of testing the * CEC_MSG_FL_REPLY_VENDOR_ID flag using vivid.
*/
cec_ops_vendor_command_with_id(msg, &vendor_id, &size, &vendor_cmd); if (vendor_id != adap->log_addrs.vendor_id) break; if (size == 1) { // Ignore even op values if (!(vendor_cmd[0] & 1)) return 0;
reply.len = msg->len;
memcpy(reply.msg + 1, msg->msg + 1, msg->len - 1);
reply.msg[msg->len - 1]++;
} else {
cec_msg_feature_abort(&reply, cec_msg_opcode(msg),
CEC_OP_ABORT_INVALID_OP);
}
cec_transmit_msg(adap, &reply, false); return 0;
}
} return -ENOMSG;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.