X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Flib%2Fadapter%2FUSBCECAdapterCommunication.cpp;h=e51a861f28579ebe6d5685c883032626e05add52;hb=f99b4d1a21d99436a3e49733e7b83276a6362cbf;hp=12b8eacde7f70b2197656cf78d8a6cd7c33576d5;hpb=b74fd339e942f438e288f9e26cca404a5a430925;p=deb_libcec.git diff --git a/src/lib/adapter/USBCECAdapterCommunication.cpp b/src/lib/adapter/USBCECAdapterCommunication.cpp index 12b8eac..e51a861 100644 --- a/src/lib/adapter/USBCECAdapterCommunication.cpp +++ b/src/lib/adapter/USBCECAdapterCommunication.cpp @@ -40,14 +40,35 @@ using namespace std; using namespace CEC; using namespace PLATFORM; +void *CUSBCECAdapterProcessor::Process(void) +{ + cec_command command; + while (!IsStopped()) + { + if (m_inBuffer.Pop(command)) + m_callback->OnCommandReceived(command); + Sleep(5); + } + + return NULL; +} + +void CUSBCECAdapterProcessor::AddCommand(cec_command command) +{ + m_inBuffer.Push(command); +} + CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(CCECProcessor *processor, const char *strPort, uint16_t iBaudRate /* = 38400 */) : m_port(NULL), m_processor(processor), + m_bHasData(false), m_iLineTimeout(0), m_iFirmwareVersion(CEC_FW_VERSION_UNKNOWN), m_lastInitiator(CECDEVICE_UNKNOWN), m_bNextIsEscaped(false), - m_bGotStart(false) + m_bGotStart(false), + m_messageProcessor(NULL), + m_bInitialised(false) { m_port = new PLATFORM::CSerialPort(strPort, iBaudRate); } @@ -57,61 +78,131 @@ CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void) Close(); } -bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */) +bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */) { + bool bReturn(false); uint64_t iNow = GetTimeMs(); - uint64_t iTimeout = iNow + iTimeoutMs; + uint64_t iTarget = iTimeoutMs > 0 ? iNow + iTimeoutMs : iNow + CEC_DEFAULT_TRANSMIT_WAIT; - CLockObject lock(m_mutex); + /* try to ping the adapter */ + bool bPinged(false); + unsigned iPingTry(0); + while (iNow < iTarget && (bPinged = PingAdapter()) == false) + { + CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry); + CEvent::Sleep(500); + iNow = GetTimeMs(); + } - if (!m_port) + /* try to read the firmware version */ + m_iFirmwareVersion = CEC_FW_VERSION_UNKNOWN; + unsigned iFwVersionTry(0); + while (bPinged && iNow < iTarget && (m_iFirmwareVersion = GetFirmwareVersion()) == CEC_FW_VERSION_UNKNOWN && iFwVersionTry < 3) { - CLibCEC::AddLog(CEC_LOG_ERROR, "port is NULL"); - return false; + CLibCEC::AddLog(CEC_LOG_WARNING, "the adapter did not respond with a correct firmware version (try %d)", ++iFwVersionTry); + CEvent::Sleep(500); + iNow = GetTimeMs(); } - if (IsOpen()) + if (m_iFirmwareVersion == CEC_FW_VERSION_UNKNOWN) { - CLibCEC::AddLog(CEC_LOG_ERROR, "port is already open"); - return true; + CLibCEC::AddLog(CEC_LOG_DEBUG, "defaulting to firmware version 1"); + m_iFirmwareVersion = 1; } - CStdString strError; - bool bConnected(false); - while (!bConnected && iNow < iTimeout) + if (m_iFirmwareVersion >= 2) { - if ((bConnected = m_port->Open(iTimeout)) == false) + /* try to set controlled mode */ + unsigned iControlledTry(0); + bool bControlled(false); + while (iNow < iTarget && (bControlled = SetControlledMode(true)) == false) { - strError.Format("error opening serial port '%s': %s", m_port->GetName().c_str(), m_port->GetError().c_str()); - Sleep(250); + CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry); + CEvent::Sleep(500); iNow = GetTimeMs(); } + bReturn = bControlled; } + else + bReturn = true; - if (!bConnected) { - CLibCEC::AddLog(CEC_LOG_ERROR, strError); - return false; + CLockObject lock(m_mutex); + m_bInitialised = bReturn; } - CLibCEC::AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting"); + return bReturn; +} + +bool CUSBCECAdapterCommunication::Open(IAdapterCommunicationCallback *cb, uint32_t iTimeoutMs /* = 10000 */, bool bSkipChecks /* = false */) +{ + uint64_t iNow = GetTimeMs(); + uint64_t iTimeout = iNow + iTimeoutMs; - //clear any input bytes - uint8_t buff[1024]; - while (m_port->Read(buff, 1024, 100) > 0) { - CLibCEC::AddLog(CEC_LOG_DEBUG, "data received, clearing it"); - Sleep(250); + CLockObject lock(m_mutex); + + if (!m_port) + { + CLibCEC::AddLog(CEC_LOG_ERROR, "port is NULL"); + return false; + } + + if (IsOpen()) + { + CLibCEC::AddLog(CEC_LOG_ERROR, "port is already open"); + return true; + } + + m_callback = cb; + CStdString strError; + bool bConnected(false); + while (!bConnected && iNow < iTimeout) + { + if ((bConnected = m_port->Open(iTimeout)) == false) + { + strError.Format("error opening serial port '%s': %s", m_port->GetName().c_str(), m_port->GetError().c_str()); + Sleep(250); + iNow = GetTimeMs(); + } + } + + if (!bConnected) + { + CLibCEC::AddLog(CEC_LOG_ERROR, strError); + return false; + } + + CLibCEC::AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting"); + + if (!bSkipChecks) + { + //clear any input bytes + uint8_t buff[1024]; + while (m_port->Read(buff, 1024, 100) > 0) + { + CLibCEC::AddLog(CEC_LOG_DEBUG, "data received, clearing it"); + Sleep(250); + } + } } - if (CreateThread()) + if (!bSkipChecks && !CheckAdapter()) { - CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread started"); - return true; + CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks"); + return false; } else { - CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a communication thread"); + if (CreateThread()) + { + CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread started"); + return true; + } + else + { + CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a communication thread"); + } } return false; @@ -119,23 +210,47 @@ bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */) void CUSBCECAdapterCommunication::Close(void) { - CLockObject lock(m_mutex); - m_rcvCondition.Broadcast(); StopThread(); } void *CUSBCECAdapterCommunication::Process(void) { + m_messageProcessor = new CUSBCECAdapterProcessor(m_callback); + m_messageProcessor->CreateThread(); + + cec_command command; + command.Clear(); + bool bCommandReceived(false); while (!IsStopped()) { - ReadFromDevice(50); - Sleep(5); - WriteNextCommand(); + { + CLockObject lock(m_mutex); + ReadFromDevice(50); + bCommandReceived = m_callback && Read(command, 0) && m_bInitialised; + } + + /* push the next command to the callback method if there is one */ + if (!IsStopped() && bCommandReceived) + m_messageProcessor->AddCommand(command); + + if (!IsStopped()) + { + Sleep(5); + WriteNextCommand(); + } } + /* stop the message processor */ + m_messageProcessor->StopThread(); + delete m_messageProcessor; + + /* notify all threads that are waiting on messages to be sent */ CCECAdapterMessage *msg(NULL); - if (m_outBuffer.Pop(msg)) - msg->condition.Broadcast(); + while (m_outBuffer.Pop(msg)) + msg->event.Broadcast(); + + /* set the ackmask to 0 before closing the connection */ + SetAckMaskInternal(0, true); if (m_port) { @@ -149,6 +264,8 @@ void *CUSBCECAdapterCommunication::Process(void) cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, uint8_t iMaxTries, uint8_t iLineTimeout /* = 3 */, uint8_t iRetryLineTimeout /* = 3 */) { cec_adapter_message_state retVal(ADAPTER_MESSAGE_STATE_UNKNOWN); + if (!IsRunning()) + return retVal; CCECAdapterMessage *output = new CCECAdapterMessage(data); @@ -177,41 +294,25 @@ cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command & bool CUSBCECAdapterCommunication::Write(CCECAdapterMessage *data) { - bool bReturn(false); - - CLockObject lock(data->mutex); data->state = ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT; m_outBuffer.Push(data); - data->condition.Wait(data->mutex); + data->event.Wait(5000); - if (data->state != ADAPTER_MESSAGE_STATE_SENT) - { - CLibCEC::AddLog(CEC_LOG_ERROR, "command was not sent"); - } - else if (data->expectControllerAck) - { - bReturn = WaitForAck(*data); - if (bReturn) - { - if (data->isTransmission) - data->state = ADAPTER_MESSAGE_STATE_SENT_ACKED; - } - else - { - data->state = ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED; - CLibCEC::AddLog(CEC_LOG_DEBUG, "did not receive ack"); - } - } - else + if ((data->expectControllerAck && data->state != ADAPTER_MESSAGE_STATE_SENT_ACKED) || + (!data->expectControllerAck && data->state != ADAPTER_MESSAGE_STATE_SENT)) { - bReturn = true; + CLibCEC::AddLog(CEC_LOG_DEBUG, "command was not %s", data->state == ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED ? "acked" : "sent"); + return false; } - return bReturn; + return true; } bool CUSBCECAdapterCommunication::Read(cec_command &command, uint32_t iTimeout) { + if (!IsRunning()) + return false; + CCECAdapterMessage msg; if (Read(msg, iTimeout)) { @@ -234,15 +335,16 @@ bool CUSBCECAdapterCommunication::Read(CCECAdapterMessage &msg, uint32_t iTimeou if (!m_inBuffer.Pop(buf)) { - if (!m_rcvCondition.Wait(m_mutex, iTimeout)) + if (iTimeout == 0 || !m_rcvCondition.Wait(m_mutex, m_bHasData, iTimeout)) return false; m_inBuffer.Pop(buf); + m_bHasData = !m_inBuffer.IsEmpty(); } if (buf) { msg.packet = buf->packet; - msg.state = msg.state = ADAPTER_MESSAGE_STATE_INCOMING; + msg.state = ADAPTER_MESSAGE_STATE_INCOMING; delete buf; return true; } @@ -280,11 +382,9 @@ bool CUSBCECAdapterCommunication::StartBootloader(void) bool CUSBCECAdapterCommunication::PingAdapter(void) { - bool bReturn(false); - if (!IsRunning()) - return bReturn; - + CLockObject lock(m_mutex); CLibCEC::AddLog(CEC_LOG_DEBUG, "sending ping"); + CCECAdapterMessage *output = new CCECAdapterMessage; output->PushBack(MSGSTART); @@ -292,11 +392,16 @@ bool CUSBCECAdapterCommunication::PingAdapter(void) output->PushBack(MSGEND); output->isTransmission = false; - if ((bReturn = Write(output)) == false) - CLibCEC::AddLog(CEC_LOG_ERROR, "could not ping the adapter"); + SendMessageToAdapter(output); + bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; delete output; + if (!bWriteOk) + { + CLibCEC::AddLog(CEC_LOG_ERROR, "could not ping the adapter"); + return false; + } - return bReturn; + return true; } bool CUSBCECAdapterCommunication::ParseMessage(const CCECAdapterMessage &msg) @@ -307,6 +412,7 @@ bool CUSBCECAdapterCommunication::ParseMessage(const CCECAdapterMessage &msg) if (msg.IsEmpty()) return bEom; + CLockObject adapterLock(m_mutex); switch(msg.Message()) { case MSGCODE_FRAME_START: @@ -340,7 +446,6 @@ bool CUSBCECAdapterCommunication::ParseMessage(const CCECAdapterMessage &msg) m_currentframe.PushBack(msg[1]); m_currentframe.eom = msg.IsEOM(); } - bEom = msg.IsEOM(); } break; default: @@ -348,17 +453,16 @@ bool CUSBCECAdapterCommunication::ParseMessage(const CCECAdapterMessage &msg) } CLibCEC::AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString()); - return bEom; + return msg.IsEOM(); } uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void) { uint16_t iReturn(m_iFirmwareVersion); - if (!IsRunning()) - return iReturn; if (iReturn == CEC_FW_VERSION_UNKNOWN) { + CLockObject lock(m_mutex); CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting the firmware version"); CCECAdapterMessage *output = new CCECAdapterMessage; @@ -369,15 +473,30 @@ uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void) output->expectControllerAck = false; SendMessageToAdapter(output); + bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT; delete output; + if (!bWriteOk) + { + CLibCEC::AddLog(CEC_LOG_ERROR, "could not request the firmware version"); + return iReturn; + } + Sleep(250); // TODO ReadFromDevice() isn't waiting for the timeout to pass on win32 + ReadFromDevice(CEC_DEFAULT_TRANSMIT_WAIT, 5 /* start + msgcode + 2 bytes for fw version + end */); CCECAdapterMessage input; - if (!Read(input, CEC_DEFAULT_TRANSMIT_WAIT) || input.Message() != MSGCODE_FIRMWARE_VERSION || input.Size() != 3) - CLibCEC::AddLog(CEC_LOG_ERROR, "no or invalid firmware version (size = %d, message = %d)", input.Size(), input.Message()); + if (Read(input, 0)) + { + if (input.Message() != MSGCODE_FIRMWARE_VERSION || input.Size() != 3) + CLibCEC::AddLog(CEC_LOG_ERROR, "invalid firmware version (size = %d, message = %d)", input.Size(), input.Message()); + else + { + m_iFirmwareVersion = (input[1] << 8 | input[2]); + iReturn = m_iFirmwareVersion; + } + } else { - m_iFirmwareVersion = (input[1] << 8 | input[2]); - iReturn = m_iFirmwareVersion; + CLibCEC::AddLog(CEC_LOG_ERROR, "no firmware version received"); } } @@ -410,6 +529,11 @@ bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout) } bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask) +{ + return SetAckMaskInternal(iMask, false); +} + +bool CUSBCECAdapterCommunication::SetAckMaskInternal(uint16_t iMask, bool bWriteDirectly /* = false */) { bool bReturn(false); CLibCEC::AddLog(CEC_LOG_DEBUG, "setting ackmask to %2x", iMask); @@ -423,7 +547,9 @@ bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask) output->PushBack(MSGEND); output->isTransmission = false; - if ((bReturn = Write(output)) == false) + if (bWriteDirectly) + SendMessageToAdapter(output); + else if ((bReturn = Write(output)) == false) CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the ackmask"); delete output; @@ -433,7 +559,7 @@ bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask) bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled) { - bool bReturn(false); + CLockObject lock(m_mutex); CLibCEC::AddLog(CEC_LOG_DEBUG, "turning controlled mode %s", controlled ? "on" : "off"); CCECAdapterMessage *output = new CCECAdapterMessage; @@ -444,11 +570,16 @@ bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled) output->PushBack(MSGEND); output->isTransmission = false; - if ((bReturn = Write(output)) == false) - CLibCEC::AddLog(CEC_LOG_ERROR, "could not set controlled mode"); + SendMessageToAdapter(output); + bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT; delete output; + if (!bWriteOk) + { + CLibCEC::AddLog(CEC_LOG_ERROR, "could not set controlled mode"); + return false; + } - return bReturn; + return true; } bool CUSBCECAdapterCommunication::IsOpen(void) @@ -463,16 +594,13 @@ bool CUSBCECAdapterCommunication::WaitForAck(CCECAdapterMessage &message) uint8_t iPacketsLeft(message.Size() / 4); int64_t iNow = GetTimeMs(); - int64_t iTargetTime = iNow + message.transmit_timeout; + int64_t iTargetTime = iNow + (message.transmit_timeout <= 5 ? CEC_DEFAULT_TRANSMIT_WAIT : message.transmit_timeout); - while (!bTransmitSucceeded && !bError && (message.transmit_timeout == 0 || iNow < iTargetTime)) + while (!bTransmitSucceeded && !bError && iNow < iTargetTime) { + ReadFromDevice(50); CCECAdapterMessage msg; - int32_t iWait = (int32_t)(iTargetTime - iNow); - if (iWait <= 5 || message.transmit_timeout <= 5) - iWait = CEC_DEFAULT_TRANSMIT_WAIT; - - if (!Read(msg, iWait)) + if (!Read(msg, 0)) { iNow = GetTimeMs(); continue; @@ -505,11 +633,11 @@ bool CUSBCECAdapterCommunication::WaitForAck(CCECAdapterMessage &message) switch(msg.Message()) { case MSGCODE_COMMAND_ACCEPTED: - CLibCEC::AddLog(CEC_LOG_DEBUG, msg.ToString()); if (iPacketsLeft > 0) iPacketsLeft--; if (!message.isTransmission && iPacketsLeft == 0) bTransmitSucceeded = true; + CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - waiting for %d more", msg.ToString().c_str(), iPacketsLeft); break; case MSGCODE_TRANSMIT_SUCCEEDED: CLibCEC::AddLog(CEC_LOG_DEBUG, msg.ToString()); @@ -526,6 +654,10 @@ bool CUSBCECAdapterCommunication::WaitForAck(CCECAdapterMessage &message) } } + message.state = bTransmitSucceeded && !bError ? + ADAPTER_MESSAGE_STATE_SENT_ACKED : + ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED; + return bTransmitSucceeded && !bError; } @@ -554,7 +686,8 @@ void CUSBCECAdapterCommunication::AddData(uint8_t *data, size_t iLen) m_currentAdapterMessage.Clear(); m_bGotStart = false; m_bNextIsEscaped = false; - m_rcvCondition.Signal(); + m_bHasData = true; + m_rcvCondition.Broadcast(); } else if (m_bNextIsEscaped) { @@ -572,18 +705,21 @@ void CUSBCECAdapterCommunication::AddData(uint8_t *data, size_t iLen) } } -bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout) +bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */) { ssize_t iBytesRead; uint8_t buff[256]; if (!m_port) return false; + if (iSize > 256) + iSize = 256; CLockObject lock(m_mutex); - iBytesRead = m_port->Read(buff, sizeof(buff), iTimeout); + iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout); if (iBytesRead < 0 || iBytesRead > 256) { CLibCEC::AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str()); + StopThread(false); return false; } else if (iBytesRead > 0) @@ -597,7 +733,13 @@ bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout) void CUSBCECAdapterCommunication::SendMessageToAdapter(CCECAdapterMessage *msg) { CLockObject adapterLock(m_mutex); - CLockObject lock(msg->mutex); + if (!m_port->IsOpen()) + { + CLibCEC::AddLog(CEC_LOG_ERROR, "error writing to serial port: the connection is closed"); + msg->state = ADAPTER_MESSAGE_STATE_ERROR; + return; + } + if (msg->tries == 1) SetLineTimeout(msg->lineTimeout); else @@ -612,8 +754,14 @@ void CUSBCECAdapterCommunication::SendMessageToAdapter(CCECAdapterMessage *msg) { CLibCEC::AddLog(CEC_LOG_DEBUG, "command sent"); msg->state = ADAPTER_MESSAGE_STATE_SENT; + + if (msg->expectControllerAck) + { + if (!WaitForAck(*msg)) + CLibCEC::AddLog(CEC_LOG_DEBUG, "did not receive ack"); + } } - msg->condition.Signal(); + msg->event.Signal(); } void CUSBCECAdapterCommunication::WriteNextCommand(void) @@ -622,3 +770,10 @@ void CUSBCECAdapterCommunication::WriteNextCommand(void) if (m_outBuffer.Pop(msg)) SendMessageToAdapter(msg); } + +CStdString CUSBCECAdapterCommunication::GetPortName(void) +{ + CStdString strName; + strName = m_port->GetName(); + return strName; +}