cec: don't wait 1 second when clearing input when there is no input
[deb_libcec.git] / src / lib / adapter / USBCECAdapterCommunication.cpp
index 82c4252d27eb314d993c3178357cc18dfddc026e..5e4a1740bfc0d285c37a69dcf0503eefdcd6218e 100644 (file)
@@ -31,6 +31,8 @@
  */
 
 #include "USBCECAdapterCommunication.h"
+#include "USBCECAdapterCommands.h"
+#include "USBCECAdapterMessageQueue.h"
 #include "../platform/sockets/serialport.h"
 #include "../platform/util/timeutils.h"
 #include "../LibCEC.h"
@@ -42,236 +44,138 @@ using namespace PLATFORM;
 
 #define CEC_ADAPTER_PING_TIMEOUT 15000
 
-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 */) :
+CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint16_t iBaudRate /* = 38400 */) :
+    IAdapterCommunication(callback),
     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_messageProcessor(NULL),
-    m_bInitialised(false)
-{
+    m_lastPollDestination(CECDEVICE_UNKNOWN),
+    m_bInitialised(false),
+    m_pingThread(NULL),
+    m_commands(NULL),
+    m_adapterMessageQueue(NULL)
+{
+  for (unsigned int iPtr = 0; iPtr < 15; iPtr++)
+    m_bWaitingForAck[iPtr] = false;
   m_port = new CSerialPort(strPort, iBaudRate);
 }
 
 CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void)
 {
   Close();
+  delete m_commands;
+  delete m_adapterMessageQueue;
 }
 
-bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */)
+bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = 10000 */, bool bSkipChecks /* = false */, bool bStartListening /* = true */)
 {
-  bool bReturn(false);
-  uint64_t iNow = GetTimeMs();
-  uint64_t iTarget = iTimeoutMs > 0 ? iNow + iTimeoutMs : iNow + CEC_DEFAULT_TRANSMIT_WAIT;
-
-  /* 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();
-  }
-
-  /* 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_WARNING, "the adapter did not respond with a correct firmware version (try %d)", ++iFwVersionTry);
-    CEvent::Sleep(500);
-    iNow = GetTimeMs();
-  }
-
-  if (m_iFirmwareVersion == CEC_FW_VERSION_UNKNOWN)
-  {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "defaulting to firmware version 1");
-    m_iFirmwareVersion = 1;
-  }
-
-  if (m_iFirmwareVersion >= 2)
-  {
-    /* try to set controlled mode */
-    unsigned iControlledTry(0);
-    bool bControlled(false);
-    while (iNow < iTarget && (bControlled = SetControlledMode(true)) == false)
-    {
-      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;
-
-  {
-    CLockObject lock(m_mutex);
-    m_bInitialised = bReturn;
-  }
-
-  return bReturn;
-}
-
-bool CUSBCECAdapterCommunication::Open(IAdapterCommunicationCallback *cb, uint32_t iTimeoutMs /* = 10000 */, bool bSkipChecks /* = false */)
-{
-  uint64_t iNow = GetTimeMs();
-  uint64_t iTimeout = iNow + iTimeoutMs;
-
+  bool bConnectionOpened(false);
   {
     CLockObject lock(m_mutex);
 
+    /* we need the port settings here */
     if (!m_port)
     {
       CLibCEC::AddLog(CEC_LOG_ERROR, "port is NULL");
-      return false;
+      return bConnectionOpened;
     }
 
+    /* return true when the port is already open */
     if (IsOpen())
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "port is already open");
+      CLibCEC::AddLog(CEC_LOG_WARNING, "port is already open");
       return true;
     }
 
-    m_callback = cb;
+    /* adapter commands */
+    if (!m_commands)
+      m_commands = new CUSBCECAdapterCommands(this);
+
+    if (!m_adapterMessageQueue)
+      m_adapterMessageQueue = new CCECAdapterMessageQueue(this);
+
+    /* try to open the connection */
     CStdString strError;
-    bool bConnected(false);
-    while (!bConnected && iNow < iTimeout)
+    CTimeout timeout(iTimeoutMs);
+    while (!bConnectionOpened && timeout.TimeLeft() > 0)
     {
-      if ((bConnected = m_port->Open(iTimeout)) == false)
+      if ((bConnectionOpened = m_port->Open(timeout.TimeLeft())) == false)
       {
         strError.Format("error opening serial port '%s': %s", m_port->GetName().c_str(), m_port->GetError().c_str());
         Sleep(250);
-        iNow = GetTimeMs();
       }
+      /* and retry every 250ms until the timeout passed */
     }
 
-    if (!bConnected)
+    /* return false when we couldn't connect */
+    if (!bConnectionOpened)
     {
       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);
-      }
-    }
+    ClearInputBytes();
   }
 
-  if (!bSkipChecks && !CheckAdapter())
+  if (!CreateThread())
+  {
+    bConnectionOpened = false;
+    CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a communication thread");
+  }
+  else if (!bSkipChecks && !CheckAdapter())
   {
+    bConnectionOpened = false;
     CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
-    return false;
   }
-  else
+  else if (bStartListening)
   {
-    if (CreateThread())
+    /* start a ping thread, that will ping the adapter every 15 seconds
+       if it doesn't receive any ping for 30 seconds, it'll switch to auto mode */
+    m_pingThread = new CAdapterPingThread(this, CEC_ADAPTER_PING_TIMEOUT);
+    if (m_pingThread->CreateThread())
     {
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread started");
-      return true;
+      bConnectionOpened = true;
     }
     else
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a communication thread");
+      bConnectionOpened = false;
+      CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a ping thread");
     }
   }
 
-  return false;
-}
+  if (!bConnectionOpened || !bStartListening)
+    StopThread(0);
+  if (!bConnectionOpened)
+  {
+    delete m_port;
+    m_port = NULL;
+  }
 
-void CUSBCECAdapterCommunication::Close(void)
-{
-  StopThread();
+  return bConnectionOpened;
 }
 
-void *CUSBCECAdapterCommunication::Process(void)
+void CUSBCECAdapterCommunication::Close(void)
 {
-  m_messageProcessor = new CUSBCECAdapterProcessor(m_callback);
-  m_messageProcessor->CreateThread();
-
-  cec_command command;
-  command.Clear();
-  bool bCommandReceived(false);
-  CTimeout pingTimeout(CEC_ADAPTER_PING_TIMEOUT);
-  while (!IsStopped())
+  /* set the ackmask to 0 before closing the connection */
+  if (IsRunning())
   {
-    {
-      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);
-
-    /* ping the adapter every 15 seconds */
-    if (pingTimeout.TimeLeft() == 0)
-    {
-      pingTimeout.Init(CEC_ADAPTER_PING_TIMEOUT);
-      PingAdapter();
-    }
-
-    if (!IsStopped())
-    {
-      Sleep(5);
-      WriteNextCommand();
-    }
+    SetAckMask(0);
+    if (m_commands->GetFirmwareVersion() >= 2)
+      SetControlledMode(false);
   }
 
-  /* stop the message processor */
-  m_messageProcessor->StopThread();
-  delete m_messageProcessor;
-
-  /* notify all threads that are waiting on messages to be sent */
-  CCECAdapterMessage *msg(NULL);
-  while (m_outBuffer.Pop(msg))
-    msg->event.Broadcast();
-
-  /* set the ackmask to 0 before closing the connection */
-  SetAckMaskInternal(0, true);
-
-  if (m_iFirmwareVersion >= 2)
-    SetControlledMode(false);
+  /* stop and delete the ping thread */
+  if (m_pingThread)
+    m_pingThread->StopThread(0);
+  delete m_pingThread;
+  m_pingThread = NULL;
 
-  if (m_port)
-  {
-    delete m_port;
-    m_port = NULL;
-  }
+  /* stop the reader thread */
+  StopThread(0);
 
-  return NULL;
+  /* close and delete the com port connection */
+  delete m_port;
+  m_port = NULL;
 }
 
 cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, uint8_t iMaxTries, uint8_t iLineTimeout /* = 3 */, uint8_t iRetryLineTimeout /* = 3 */)
@@ -280,22 +184,16 @@ cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &
   if (!IsRunning())
     return retVal;
 
-  CCECAdapterMessage *output = new CCECAdapterMessage(data);
+  CCECAdapterMessage *output = new CCECAdapterMessage(data, iMaxTries, iLineTimeout, iRetryLineTimeout);
 
-  /* set the number of retries */
-  if (data.opcode == CEC_OPCODE_NONE) //TODO
-    output->maxTries = 1;
-  else if (data.initiator != CECDEVICE_BROADCAST)
-    output->maxTries = iMaxTries;
-
-  output->lineTimeout = iLineTimeout;
-  output->retryTimeout = iRetryLineTimeout;
-  output->tries = 0;
+  /* mark as waiting for an ack from the destination */
+  MarkAsWaiting(data.destination);
 
+  /* send the message */
   bool bRetry(true);
   while (bRetry && ++output->tries < output->maxTries)
   {
-    bRetry = (!Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
+    bRetry = (!m_adapterMessageQueue->Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
     if (bRetry)
       Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
   }
@@ -305,727 +203,325 @@ cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &
   return retVal;
 }
 
-bool CUSBCECAdapterCommunication::Write(CCECAdapterMessage *data)
-{
-  data->state = ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT;
-  m_outBuffer.Push(data);
-  data->event.Wait(5000);
-
-  if ((data->expectControllerAck && data->state != ADAPTER_MESSAGE_STATE_SENT_ACKED) ||
-      (!data->expectControllerAck && data->state != ADAPTER_MESSAGE_STATE_SENT))
-  {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "command was not %s", data->state == ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED ? "acked" : "sent");
-    return false;
-  }
-
-  return true;
-}
-
-bool CUSBCECAdapterCommunication::Read(cec_command &command, uint32_t iTimeout)
+void *CUSBCECAdapterCommunication::Process(void)
 {
-  if (!IsRunning())
-    return false;
-
   CCECAdapterMessage msg;
-  if (Read(msg, iTimeout))
-  {
-    if (ParseMessage(msg))
-    {
-      command = m_currentframe;
-      m_currentframe.Clear();
-      return true;
-    }
-  }
-  return false;
-}
-
-bool CUSBCECAdapterCommunication::Read(CCECAdapterMessage &msg, uint32_t iTimeout)
-{
-  CLockObject lock(m_mutex);
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread started");
 
-  msg.Clear();
-  CCECAdapterMessage *buf(NULL);
-
-  if (!m_inBuffer.Pop(buf))
+  while (!IsStopped())
   {
-    if (iTimeout == 0 || !m_rcvCondition.Wait(m_mutex, m_bHasData, iTimeout))
-      return false;
-    m_inBuffer.Pop(buf);
-    m_bHasData = !m_inBuffer.IsEmpty();
-  }
+    /* read from the serial port */
+    if (!ReadFromDevice(50, 5))
+      break;
 
-  if (buf)
-  {
-    msg.packet = buf->packet;
-    msg.state = ADAPTER_MESSAGE_STATE_INCOMING;
-    delete buf;
-    return true;
+    /* TODO sleep 5 ms so other threads can get a lock */
+    Sleep(5);
   }
-  return false;
-}
 
-CStdString CUSBCECAdapterCommunication::GetError(void) const
-{
-  CStdString strError;
-  strError = m_port->GetError();
-  return strError;
-}
-
-bool CUSBCECAdapterCommunication::StartBootloader(void)
-{
-  bool bReturn(false);
-  if (!IsRunning())
-    return bReturn;
-
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "starting the bootloader");
-
-  CCECAdapterMessage params;
-  return SendCommand(MSGCODE_START_BOOTLOADER, params, false);
-}
-
-bool CUSBCECAdapterCommunication::PingAdapter(void)
-{
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "sending ping");
-
-  CCECAdapterMessage params;
-  return SendCommand(MSGCODE_PING, params);
+  m_adapterMessageQueue->Clear();
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread ended");
+  return NULL;
 }
 
-bool CUSBCECAdapterCommunication::ParseMessage(const CCECAdapterMessage &msg)
+bool CUSBCECAdapterCommunication::HandlePoll(const CCECAdapterMessage &msg)
 {
-  bool bEom(false);
   bool bIsError(msg.IsError());
+  cec_adapter_messagecode messageCode(msg.Message());
+  CLockObject lock(m_mutex);
 
-  if (msg.IsEmpty())
-    return bEom;
-
-  CLockObject adapterLock(m_mutex);
-  switch(msg.Message())
+  if (messageCode == MSGCODE_FRAME_START && msg.IsACK())
   {
-  case MSGCODE_FRAME_START:
+    m_lastPollDestination = msg.Destination();
+    if (msg.Destination() < CECDEVICE_BROADCAST)
     {
-      m_currentframe.Clear();
-      if (msg.Size() >= 2)
-      {
-        m_currentframe.initiator   = msg.Initiator();
-        m_currentframe.destination = msg.Destination();
-        m_currentframe.ack         = msg.IsACK();
-        m_currentframe.eom         = msg.IsEOM();
-      }
-      if (m_currentframe.ack == 0x1)
+      if (!m_bWaitingForAck[msg.Destination()] && !msg.IsEOM())
       {
-        m_lastInitiator = m_currentframe.initiator;
-        m_processor->HandlePoll(m_currentframe.initiator, m_currentframe.destination);
+        if (m_callback)
+          m_callback->HandlePoll(msg.Initiator(), msg.Destination());
       }
+      else
+        m_bWaitingForAck[msg.Destination()] = false;
     }
-    break;
-  case MSGCODE_RECEIVE_FAILED:
-    {
-      m_currentframe.Clear();
-      if (m_lastInitiator != CECDEVICE_UNKNOWN)
-        bIsError = m_processor->HandleReceiveFailed(m_lastInitiator);
-    }
-    break;
-  case MSGCODE_FRAME_DATA:
-    {
-      if (msg.Size() >= 2)
-      {
-        m_currentframe.PushBack(msg[1]);
-        m_currentframe.eom = msg.IsEOM();
-      }
-    }
-    break;
-  default:
-    break;
+  }
+  else if (messageCode == MSGCODE_RECEIVE_FAILED)
+  {
+    /* hack to suppress warnings when an LG is polling */
+    if (m_lastPollDestination != CECDEVICE_UNKNOWN)
+      bIsError = m_callback->HandleReceiveFailed(m_lastPollDestination);
   }
 
-  CLibCEC::AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString());
-  return msg.IsEOM();
+  return bIsError;
 }
 
-uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
+void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest)
 {
-  uint16_t iReturn(m_iFirmwareVersion);
-
-  if (iReturn == CEC_FW_VERSION_UNKNOWN)
+  /* mark as waiting for an ack from the destination */
+  if (dest < CECDEVICE_BROADCAST)
   {
     CLockObject lock(m_mutex);
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting the firmware version");
-    cec_datapacket response = GetSetting(MSGCODE_FIRMWARE_VERSION, 2);
-    if (response.size == 2)
-    {
-      m_iFirmwareVersion = (response[0] << 8 | response[1]);
-      iReturn = m_iFirmwareVersion;
-      CLibCEC::AddLog(CEC_LOG_DEBUG, "firmware version %d", m_iFirmwareVersion);
-    }
+    m_bWaitingForAck[dest] = true;
   }
-
-  return iReturn;
 }
 
-bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
+void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = 1000 */)
 {
-  m_iLineTimeout = iTimeout;
-  bool bReturn(m_iLineTimeout != iTimeout);
+  CTimeout timeout(iTimeout);
+  uint8_t buff[1024];
+  ssize_t iBytesRead(0);
+  bool bGotMsgEnd(true);
 
-  if (!bReturn)
+  while (timeout.TimeLeft() > 0 && ((iBytesRead = m_port->Read(buff, 1024, 5)) > 0 || !bGotMsgEnd))
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the line timeout to %d", iTimeout);
-    CCECAdapterMessage params;
-    params.PushEscaped(iTimeout);
-    bReturn = SendCommand(MSGCODE_TRANSMIT_IDLETIME, params);
+    bGotMsgEnd = false;
+    /* if something was received, wait for MSGEND */
+    for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
+      bGotMsgEnd = buff[iPtr] == MSGEND;
   }
-
-  return bReturn;
-}
-
-bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
-{
-  return SetAckMaskInternal(iMask, IsRunning());
 }
 
-bool CUSBCECAdapterCommunication::SetAckMaskInternal(uint16_t iMask, bool bWriteDirectly /* = false */)
+bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
 {
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting ackmask to %2x", iMask);
+  bool bReturn(true);
+  bool bChanged(false);
 
-  CCECAdapterMessage params;
-  params.PushEscaped(iMask >> 8);
-  params.PushEscaped((uint8_t)iMask);
-  return SendCommand(MSGCODE_SET_ACK_MASK, params, true, false, bWriteDirectly);
-}
+  /* only send the command if the timeout changed */
+  {
+    CLockObject lock(m_mutex);
+    bChanged = (m_iLineTimeout != iTimeout);
+    m_iLineTimeout = iTimeout;
+  }
 
-bool CUSBCECAdapterCommunication::PersistConfiguration(libcec_configuration *configuration)
-{
-  if (m_iFirmwareVersion < 2)
-    return false;
+  if (bChanged)
+    bReturn = m_commands->SetLineTimeout(iTimeout);
 
-  bool bReturn(true);
-  bReturn &= SetSettingAutoEnabled(true);
-  bReturn &= SetSettingDeviceType(CLibCEC::GetType(configuration->logicalAddresses.primary));
-  bReturn &= SetSettingDefaultLogicalAddress(configuration->logicalAddresses.primary);
-  bReturn &= SetSettingLogicalAddressMask(CLibCEC::GetMaskForType(configuration->logicalAddresses.primary));
-  bReturn &= SetSettingPhysicalAddress(configuration->iPhysicalAddress);
-  bReturn &= SetSettingCECVersion(CEC_VERSION_1_3A);
-  bReturn &= SetSettingOSDName(configuration->strDeviceName);
-  if (bReturn)
-    bReturn = WriteEEPROM();
   return bReturn;
 }
 
-bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration *configuration)
+bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
 {
-  if (m_iFirmwareVersion < 2)
-    return false;
-
-  bool bReturn(true);
-  cec_device_type type;
-  if (GetSettingDeviceType(type))
-  {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "using persisted device type setting %s", m_processor->ToString(type));
-    configuration->deviceTypes.Clear();
-    configuration->deviceTypes.Add(type);
-  }
-  else
+  CLockObject adapterLock(m_mutex);
+  if (!m_port->IsOpen())
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "no persisted device type setting");
-    bReturn = false;
+    CLibCEC::AddLog(CEC_LOG_DEBUG, "error writing command '%s' to the serial port: the connection is closed", CCECAdapterMessage::ToString(message->Message()));
+    message->state = ADAPTER_MESSAGE_STATE_ERROR;
+    return false;
   }
 
-  if (GetSettingPhysicalAddress(configuration->iPhysicalAddress))
+  /* write the message */
+  if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "using persisted physical address setting %4x", configuration->iPhysicalAddress);
+    CLibCEC::AddLog(CEC_LOG_DEBUG, "error writing command '%s' to the serial port: %s", CCECAdapterMessage::ToString(message->Message()), m_port->GetError().c_str());
+    message->state = ADAPTER_MESSAGE_STATE_ERROR;
+    return false;
   }
-  else
+
+  CLibCEC::AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
+  message->state = ADAPTER_MESSAGE_STATE_SENT;
+  return true;
+}
+
+bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
+{
+  ssize_t iBytesRead(0);
+  uint8_t buff[256];
+  if (iSize > 256)
+    iSize = 256;
+
+  /* read from the serial port */
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "no persisted physical address setting");
-    bReturn = false;
+    CLockObject lock(m_mutex);
+    if (!m_port)
+      return false;
+    iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
   }
 
-  CStdString strDeviceName;
-  if (GetSettingOSDName(strDeviceName))
+  if (iBytesRead < 0 || iBytesRead > 256)
   {
-    snprintf(configuration->strDeviceName, 13, "%s", strDeviceName.c_str());
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "using persisted device name setting %s", configuration->strDeviceName);
+    CLibCEC::AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
+    StopThread(false);
+    return false;
   }
-  else
+  else if (iBytesRead > 0)
   {
-    CLibCEC::AddLog(CEC_LOG_DEBUG, "no persisted device name setting");
-    bReturn = false;
+    /* add the data to the current frame */
+    m_adapterMessageQueue->AddData(buff, iBytesRead);
   }
 
-  // don't read the following settings:
-  // - auto enabled (always enabled)
-  // - default logical address (autodetected)
-  // - logical address mask (autodetected)
-  // - CEC version (1.3a)
-
-  // TODO to be added to the firmware:
-  // - base device (4 bits)
-  // - HDMI port number (4 bits)
-  // - TV vendor id (12 bits)
-  // - wake devices (8 bits)
-  // - standby devices (8 bits)
-  // - use TV menu language (1 bit)
-  // - activate source (1 bit)
-  // - power off screensaver (1 bit)
-  // - power off on standby (1 bit)
-  // - send inactive source (1 bit)
-  return bReturn;
-}
-
-bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
-{
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "turning controlled mode %s", controlled ? "on" : "off");
-
-  CCECAdapterMessage params;
-  params.PushEscaped(controlled ? 1 : 0);
-  return SendCommand(MSGCODE_SET_CONTROLLED, params);
+  return true;
 }
 
-bool CUSBCECAdapterCommunication::SetSettingAutoEnabled(bool enabled)
+CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry /* = false */)
 {
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "turning autonomous mode %s", enabled ? "on" : "off");
+  if (!m_port || !m_port->IsOpen() ||
+      !m_adapterMessageQueue)
+    return NULL;
 
-  CCECAdapterMessage params;
-  params.PushEscaped(enabled ? 1 : 0);
-  return SendCommand(MSGCODE_SET_AUTO_ENABLED, params);
-}
-
-bool CUSBCECAdapterCommunication::GetSettingAutoEnabled(bool &enabled)
-{
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting autonomous mode setting");
+  /* create the adapter message for this command */
+  CCECAdapterMessage *output = new CCECAdapterMessage;
+  output->PushBack(MSGSTART);
+  output->PushEscaped((uint8_t)msgCode);
+  output->Append(params);
+  output->PushBack(MSGEND);
 
-  cec_datapacket response = GetSetting(MSGCODE_GET_AUTO_ENABLED, 1);
-  if (response.size == 1)
+  /* write the command */
+  if (!m_adapterMessageQueue->Write(output))
   {
-    enabled = response[0] == 1;
-    return true;
+    // timed out
+    return output;
   }
-  return false;
-}
-
-bool CUSBCECAdapterCommunication::SetSettingDeviceType(cec_device_type type)
-{
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the device type to %1X", (uint8_t)type);
-
-  CCECAdapterMessage params;
-  params.PushEscaped((uint8_t)type);
-  return SendCommand(MSGCODE_SET_DEVICE_TYPE, params);
-}
-
-bool CUSBCECAdapterCommunication::GetSettingDeviceType(cec_device_type &value)
-{
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting device type setting");
-
-  cec_datapacket response = GetSetting(MSGCODE_GET_DEVICE_TYPE, 1);
-  if (response.size == 1)
+  else
   {
-    value = (cec_device_type)response[0];
-    return true;
+    if (!bIsRetry && output->Reply() == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED)
+    {
+      /* if the controller reported that the command was rejected, and we didn't send the command
+         to set controlled mode, then the controller probably switched to auto mode. set controlled
+         mode and retry */
+      CLibCEC::AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
+      delete output;
+      if (SetControlledMode(true))
+        return SendCommand(msgCode, params, true);
+    }
   }
-  return false;
-}
 
-bool CUSBCECAdapterCommunication::SetSettingDefaultLogicalAddress(cec_logical_address address)
-{
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the default logical address to %1X", address);
-
-  CCECAdapterMessage params;
-  params.PushEscaped((uint8_t)address);
-  return SendCommand(MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS, params);
+  return output;
 }
 
-bool CUSBCECAdapterCommunication::GetSettingDefaultLogicalAddress(cec_logical_address &address)
+bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */)
 {
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting default logical address setting");
+  bool bReturn(false);
+  CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
 
-  cec_datapacket response = GetSetting(MSGCODE_GET_DEFAULT_LOGICAL_ADDRESS, 1);
-  if (response.size == 1)
+  /* try to ping the adapter */
+  bool bPinged(false);
+  unsigned iPingTry(0);
+  while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
   {
-    address = (cec_logical_address)response[0];
-    return true;
+    CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
+    CEvent::Sleep(500);
   }
-  return false;
-}
-
-bool CUSBCECAdapterCommunication::SetSettingLogicalAddressMask(uint16_t iMask)
-{
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the logical address mask to %2X", iMask);
 
-  CCECAdapterMessage params;
-  params.PushEscaped(iMask >> 8);
-  params.PushEscaped((uint8_t)iMask);
-  return SendCommand(MSGCODE_SET_LOGICAL_ADDRESS_MASK, params);
-}
-
-bool CUSBCECAdapterCommunication::GetSettingLogicalAddressMask(uint16_t &iMask)
-{
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting logical address mask setting");
-
-  cec_datapacket response = GetSetting(MSGCODE_GET_LOGICAL_ADDRESS_MASK, 2);
-  if (response.size == 2)
+  /* try to read the firmware version */
+  if (bPinged && timeout.TimeLeft() > 0 && m_commands->RequestFirmwareVersion() >= 2)
   {
-    iMask = ((uint16_t)response[0] << 8) | ((uint16_t)response[1]);
-    return true;
+    /* try to set controlled mode for v2+ firmwares */
+    unsigned iControlledTry(0);
+    bool bControlled(false);
+    while (timeout.TimeLeft() > 0 && (bControlled = SetControlledMode(true)) == false)
+    {
+      CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
+      CEvent::Sleep(500);
+    }
+    bReturn = bControlled;
   }
-  return false;
-}
-
-bool CUSBCECAdapterCommunication::SetSettingPhysicalAddress(uint16_t iPhysicalAddress)
-{
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the physical address to %2X", iPhysicalAddress);
+  else
+    bReturn = true;
 
-  CCECAdapterMessage params;
-  params.PushEscaped(iPhysicalAddress >> 8);
-  params.PushEscaped((uint8_t)iPhysicalAddress);
-  return SendCommand(MSGCODE_SET_PHYSICAL_ADDRESS, params);
+  SetInitialised(bReturn);
+  return bReturn;
 }
 
-bool CUSBCECAdapterCommunication::GetSettingPhysicalAddress(uint16_t &iPhysicalAddress)
+bool CUSBCECAdapterCommunication::IsOpen(void)
 {
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting physical address setting");
-
-  cec_datapacket response = GetSetting(MSGCODE_GET_PHYSICAL_ADDRESS, 2);
-  if (response.size == 2)
-  {
-    iPhysicalAddress = ((uint16_t)response[0] << 8) | ((uint16_t)response[1]);
-    return true;
-  }
-  return false;
+  /* thread is not being stopped, the port is open and the thread is running */
+  return !IsStopped() && m_port->IsOpen() && IsRunning();
 }
 
-bool CUSBCECAdapterCommunication::SetSettingCECVersion(cec_version version)
+CStdString CUSBCECAdapterCommunication::GetError(void) const
 {
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the CEC version to %s", CLibCEC::GetInstance()->ToString(version));
-
-  CCECAdapterMessage params;
-  params.PushEscaped((uint8_t)version);
-  return SendCommand(MSGCODE_SET_HDMI_VERSION, params);
+  return m_port->GetError();
 }
 
-bool CUSBCECAdapterCommunication::GetSettingCECVersion(cec_version &version)
+void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo /* = true */)
 {
   CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting CEC version setting");
-
-  cec_datapacket response = GetSetting(MSGCODE_GET_HDMI_VERSION, 1);
-  if (response.size == 1)
-  {
-    version = (cec_version)response[0];
-    return true;
-  }
-  return false;
+  m_bInitialised = bSetTo;
 }
 
-bool CUSBCECAdapterCommunication::SetSettingOSDName(const char *strOSDName)
+bool CUSBCECAdapterCommunication::IsInitialised(void)
 {
   CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the OSD name to %s", strOSDName);
-
-  CCECAdapterMessage params;
-  for (size_t iPtr = 0; iPtr < strlen(strOSDName); iPtr++)
-    params.PushEscaped(strOSDName[iPtr]);
-  return SendCommand(MSGCODE_SET_OSD_NAME, params);
+  return m_bInitialised;
 }
 
-bool CUSBCECAdapterCommunication::GetSettingOSDName(CStdString &strOSDName)
+bool CUSBCECAdapterCommunication::StartBootloader(void)
 {
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting OSD name setting");
-
-  cec_datapacket response = GetSetting(MSGCODE_GET_OSD_NAME, 13);
-  if (response.size == 0)
+  if (!IsRunning())
     return false;
 
-  char buf[14];
-  for (uint8_t iPtr = 0; iPtr < response.size && iPtr < 13; iPtr++)
-    buf[iPtr] = (char)response[iPtr];
-  buf[response.size] = 0;
-
-  strOSDName.Format("%s", buf);
-  return true;
-}
-
-bool CUSBCECAdapterCommunication::WriteEEPROM(void)
-{
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "writing settings in the EEPROM");
-
-  CCECAdapterMessage params;
-  return SendCommand(MSGCODE_WRITE_EEPROM, params);
-}
-
-bool CUSBCECAdapterCommunication::IsOpen(void)
-{
-  return !IsStopped() && m_port->IsOpen() && IsRunning();
+  return m_commands->StartBootloader();
 }
 
-bool CUSBCECAdapterCommunication::WaitForAck(CCECAdapterMessage &message)
+bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
 {
-  bool bError(false);
-  bool bTransmitSucceeded(false);
-  uint8_t iPacketsLeft(message.isTransmission ? message.Size() / 4 : 1);
-
-  int64_t iNow = GetTimeMs();
-  int64_t iTargetTime = iNow + (message.transmit_timeout <= 5 ? CEC_DEFAULT_TRANSMIT_WAIT : message.transmit_timeout);
-
-  while (!bTransmitSucceeded && !bError && iNow < iTargetTime)
-  {
-    ReadFromDevice(50);
-    CCECAdapterMessage msg;
-    if (!Read(msg, 0))
-    {
-      iNow = GetTimeMs();
-      continue;
-    }
-
-    if (msg.Message() == MSGCODE_FRAME_START && msg.IsACK())
-    {
-      m_processor->HandlePoll(msg.Initiator(), msg.Destination());
-      m_lastInitiator = msg.Initiator();
-      iNow = GetTimeMs();
-      continue;
-    }
-
-    if (msg.Message() == MSGCODE_RECEIVE_FAILED &&
-        m_lastInitiator != CECDEVICE_UNKNOWN &&
-        m_processor->HandleReceiveFailed(m_lastInitiator))
-    {
-      iNow = GetTimeMs();
-      continue;
-    }
-
-    bError = msg.IsError();
-    if (bError)
-    {
-      message.reply = msg.Message();
-      CLibCEC::AddLog(CEC_LOG_DEBUG, msg.ToString());
-    }
-    else
-    {
-      switch(msg.Message())
-      {
-      case MSGCODE_COMMAND_ACCEPTED:
-        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());
-        bTransmitSucceeded = (iPacketsLeft == 0);
-        bError = !bTransmitSucceeded;
-        message.reply = MSGCODE_TRANSMIT_SUCCEEDED;
-        break;
-      default:
-        // ignore other data while waiting
-        break;
-      }
-
-      iNow = GetTimeMs();
-    }
-  }
-
-  message.state = bTransmitSucceeded && !bError ?
-      ADAPTER_MESSAGE_STATE_SENT_ACKED :
-      ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
-
-  return bTransmitSucceeded && !bError;
+  return m_commands->SetAckMask(iMask);
 }
 
-void CUSBCECAdapterCommunication::AddData(uint8_t *data, size_t iLen)
+bool CUSBCECAdapterCommunication::PingAdapter(void)
 {
-  CLockObject lock(m_mutex);
-  for (size_t iPtr = 0; iPtr < iLen; iPtr++)
-  {
-    if (!m_bGotStart)
-    {
-      if (data[iPtr] == MSGSTART)
-        m_bGotStart = true;
-    }
-    else if (data[iPtr] == MSGSTART) //we found a msgstart before msgend, this is not right, remove
-    {
-      if (m_currentAdapterMessage.Size() > 0)
-        CLibCEC::AddLog(CEC_LOG_WARNING, "received MSGSTART before MSGEND, removing previous buffer contents");
-      m_currentAdapterMessage.Clear();
-      m_bGotStart = true;
-    }
-    else if (data[iPtr] == MSGEND)
-    {
-      CCECAdapterMessage *newMessage = new CCECAdapterMessage;
-      newMessage->packet = m_currentAdapterMessage.packet;
-      m_inBuffer.Push(newMessage);
-      m_currentAdapterMessage.Clear();
-      m_bGotStart = false;
-      m_bNextIsEscaped = false;
-      m_bHasData = true;
-      m_rcvCondition.Broadcast();
-    }
-    else if (m_bNextIsEscaped)
-    {
-      m_currentAdapterMessage.PushBack(data[iPtr] + (uint8_t)ESCOFFSET);
-      m_bNextIsEscaped = false;
-    }
-    else if (data[iPtr] == MSGESC)
-    {
-      m_bNextIsEscaped = true;
-    }
-    else
-    {
-      m_currentAdapterMessage.PushBack(data[iPtr]);
-    }
-  }
+  return m_commands->PingAdapter();
 }
 
-bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
+uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
 {
-  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(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)
-  {
-    AddData(buff, iBytesRead);
-  }
-
-  return iBytesRead > 0;
+  return m_commands->GetFirmwareVersion();
 }
 
-void CUSBCECAdapterCommunication::SendMessageToAdapter(CCECAdapterMessage *msg)
+bool CUSBCECAdapterCommunication::PersistConfiguration(libcec_configuration *configuration)
 {
-  CLockObject adapterLock(m_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->isTransmission && (msg->Size() < 2 || msg->At(1) != MSGCODE_TRANSMIT_IDLETIME))
-  {
-    if (msg->tries == 1)
-      SetLineTimeout(msg->lineTimeout);
-    else
-      SetLineTimeout(msg->retryTimeout);
-  }
-
-  if (m_port->Write(msg->packet.data, msg->Size()) != (ssize_t) msg->Size())
-  {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "error writing to serial port: %s", m_port->GetError().c_str());
-    msg->state = ADAPTER_MESSAGE_STATE_ERROR;
-  }
-  else
-  {
-    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->event.Signal();
+  return m_commands->PersistConfiguration(configuration);
 }
 
-void CUSBCECAdapterCommunication::WriteNextCommand(void)
+bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration *configuration)
 {
-  CCECAdapterMessage *msg(NULL);
-  if (m_outBuffer.Pop(msg))
-    SendMessageToAdapter(msg);
+  return m_commands->GetConfiguration(configuration);
 }
 
 CStdString CUSBCECAdapterCommunication::GetPortName(void)
 {
-  CStdString strName;
-  strName = m_port->GetName();
-  return strName;
+  return m_port->GetName();
 }
 
-bool CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bExpectAck /* = true */, bool bIsTransmission /* = false */, bool bSendDirectly /* = true */)
+bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
 {
-  CLockObject lock(m_mutex);
-
-  CCECAdapterMessage *output = new CCECAdapterMessage;
-
-  output->PushBack(MSGSTART);
-  output->PushEscaped(msgCode);
-  output->Append(params);
-  output->PushBack(MSGEND);
-  output->isTransmission = bIsTransmission;
-  output->expectControllerAck = bExpectAck;
-
-  if (bSendDirectly)
-    SendMessageToAdapter(output);
-  else
-    Write(output);
-
-  bool bWriteOk = output->state == (output->expectControllerAck ? ADAPTER_MESSAGE_STATE_SENT_ACKED : ADAPTER_MESSAGE_STATE_SENT);
-  if (!bWriteOk)
-  {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "'%s' failed", CCECAdapterMessage::ToString(msgCode));
-    delete output;
-    return false;
-  }
-
-  delete output;
-  return true;
+  return m_commands->SetControlledMode(controlled);
 }
 
-cec_datapacket CUSBCECAdapterCommunication::GetSetting(cec_adapter_messagecode msgCode, uint8_t iResponseLength)
+void *CAdapterPingThread::Process(void)
 {
-  cec_datapacket retVal;
-  retVal.Clear();
-
-  CCECAdapterMessage params;
-  if (!SendCommand(msgCode, params, false))
-  {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "%s failed", CCECAdapterMessage::ToString(msgCode));
-    return retVal;
-  }
-
-  Sleep(250); // TODO ReadFromDevice() isn't waiting for the timeout to pass on win32
-  ReadFromDevice(CEC_DEFAULT_TRANSMIT_WAIT, iResponseLength + 3 /* start + msgcode + iResponseLength + end */);
-  CCECAdapterMessage input;
-  if (Read(input, 0))
+  while (!IsStopped())
   {
-    if (input.Message() != msgCode)
-      CLibCEC::AddLog(CEC_LOG_ERROR, "invalid response to %s received (%s)", CCECAdapterMessage::ToString(msgCode), CCECAdapterMessage::ToString(input.Message()));
-    else
+    if (m_timeout.TimeLeft() == 0)
     {
-      for (uint8_t iPtr = 1; iPtr < input.Size(); iPtr++)
-        retVal.PushBack(input[iPtr]);
+      /* reinit the timeout */
+      m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
+
+      /* send a ping to the adapter */
+      bool bPinged(false);
+      int iFailedCounter(0);
+      while (!bPinged && iFailedCounter < 3)
+      {
+        if (!m_com->PingAdapter())
+        {
+          /* sleep 1 second and retry */
+          Sleep(1000);
+          ++iFailedCounter;
+        }
+        else
+        {
+          bPinged = true;
+        }
+      }
+
+      if (iFailedCounter == 3)
+      {
+        /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
+        CLibCEC::AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
+        m_com->StopThread(false);
+        break;
+      }
     }
-  }
-  else
-  {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "no response to %s received", CCECAdapterMessage::ToString(msgCode));
-  }
 
-  return retVal;
+    Sleep(500);
+  }
+  return NULL;
 }