changed 'couldn't change the ackmask: the connection is closed' into a debug message
[deb_libcec.git] / src / lib / adapter / USBCECAdapterCommunication.cpp
index 4f477b90d6cfa9df43e29958d6f98bea92d43c4f..b3f0f7da7ba3209806b402c2e6e839d4c0b2648f 100644 (file)
  */
 
 #include "USBCECAdapterCommunication.h"
+#include "USBCECAdapterCommands.h"
+#include "USBCECAdapterMessageQueue.h"
 #include "../platform/sockets/serialport.h"
 #include "../platform/util/timeutils.h"
+#include "../platform/util/util.h"
+#include "../platform/util/edid.h"
+#include "../platform/adl/adl-edid.h"
+#include "../platform/nvidia/nv-edid.h"
 #include "../LibCEC.h"
 #include "../CECProcessor.h"
 
@@ -42,944 +48,593 @@ 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;
-}
+// firmware version 2
+#define CEC_LATEST_ADAPTER_FW_VERSION 2
+// firmware date Thu Apr 26 20:14:49 2012 +0000
+#define CEC_LATEST_ADAPTER_FW_DATE    0x4F99ACB9
 
-void CUSBCECAdapterProcessor::AddCommand(cec_command command)
-{
-  m_inBuffer.Push(command);
-}
+#define LIB_CEC m_callback->GetLib()
 
-CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(CCECProcessor *processor, const char *strPort, uint16_t iBaudRate /* = 38400 */) :
+CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */) :
+    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),
+    m_iAckMask(0xFFFF)
 {
-  m_port = new PLATFORM::CSerialPort(strPort, iBaudRate);
+  for (unsigned int iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
+    m_bWaitingForAck[iPtr] = false;
+  m_port = new CSerialPort(strPort, iBaudRate);
+  m_commands = new CUSBCECAdapterCommands(this);
 }
 
 CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void)
 {
   Close();
+  DELETE_AND_NULL(m_commands);
+  DELETE_AND_NULL(m_adapterMessageQueue);
+  DELETE_AND_NULL(m_port);
 }
 
-bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = 10000 */)
+void CUSBCECAdapterCommunication::ResetMessageQueue(void)
 {
-  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;
+  DELETE_AND_NULL(m_adapterMessageQueue);
+  m_adapterMessageQueue = new CCECAdapterMessageQueue(this);
+  m_adapterMessageQueue->CreateThread();
 }
 
-bool CUSBCECAdapterCommunication::Open(IAdapterCommunicationCallback *cb, uint32_t iTimeoutMs /* = 10000 */, bool bSkipChecks /* = false */)
+bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool bSkipChecks /* = false */, bool bStartListening /* = true */)
 {
-  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;
+      LIB_CEC->AddLog(CEC_LOG_ERROR, "port is NULL");
+      return bConnectionOpened;
     }
 
+    /* return true when the port is already open */
     if (IsOpen())
     {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "port is already open");
+      LIB_CEC->AddLog(CEC_LOG_WARNING, "port is already open");
       return true;
     }
 
-    m_callback = cb;
+    ResetMessageQueue();
+
+    /* 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;
-    }
+      LIB_CEC->AddLog(CEC_LOG_ERROR, strError);
 
-    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)
+      if (m_port->GetErrorNumber() == EACCES)
       {
-        CLibCEC::AddLog(CEC_LOG_DEBUG, "data received, clearing it");
-        Sleep(250);
+        libcec_parameter param;
+        param.paramType = CEC_PARAMETER_TYPE_STRING;
+        param.paramData = (void*)"No permission to open the device";
+        LIB_CEC->Alert(CEC_ALERT_PERMISSION_ERROR, param);
       }
+      else if (m_port->GetErrorNumber() == EBUSY)
+      {
+        libcec_parameter param;
+        param.paramType = CEC_PARAMETER_TYPE_STRING;
+        param.paramData = (void*)"The serial port is busy. Only one program can access the device directly.";
+        LIB_CEC->Alert(CEC_ALERT_PORT_BUSY, param);
+      }
+      return false;
     }
+
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
+    ClearInputBytes();
   }
 
-  if (!bSkipChecks && !CheckAdapter())
+  // always start by setting the ackmask to 0, to clear previous values
+  SetAckMask(0);
+
+  if (!CreateThread())
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
-    return false;
+    bConnectionOpened = false;
+    LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a communication thread");
   }
-  else
+  else if (!bSkipChecks && !CheckAdapter())
   {
-    if (CreateThread())
+    bConnectionOpened = false;
+    LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
+  }
+  else if (bStartListening)
+  {
+    /* 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;
+      LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a ping thread");
     }
   }
 
-  return false;
+  if (!bConnectionOpened || !bStartListening)
+    StopThread(0);
+
+  return bConnectionOpened;
 }
 
 void CUSBCECAdapterCommunication::Close(void)
 {
-  StopThread();
-}
+  /* stop the reader thread */
+  StopThread(0);
 
-void *CUSBCECAdapterCommunication::Process(void)
-{
-  m_messageProcessor = new CUSBCECAdapterProcessor(m_callback);
-  m_messageProcessor->CreateThread();
+  CLockObject lock(m_mutex);
 
-  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 (IsOpen() && m_port->GetErrorNumber() == 0)
   {
-    {
-      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();
-    }
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - closing the connection", __FUNCTION__);
+    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);
+  m_adapterMessageQueue->Clear();
 
-  if (m_iFirmwareVersion >= 2)
-    SetControlledMode(false);
+  /* stop and delete the ping thread */
+  DELETE_AND_NULL(m_pingThread);
 
+  /* close and delete the com port connection */
   if (m_port)
-  {
-    delete m_port;
-    m_port = NULL;
-  }
-
-  return NULL;
+    m_port->Close();
 }
 
-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 CUSBCECAdapterCommunication::Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout)
 {
   cec_adapter_message_state retVal(ADAPTER_MESSAGE_STATE_UNKNOWN);
   if (!IsRunning())
     return retVal;
 
-  CCECAdapterMessage *output = new CCECAdapterMessage(data);
+  CCECAdapterMessage *output = new CCECAdapterMessage(data, iLineTimeout);
 
-  /* set the number of retries */
-  if (data.opcode == CEC_OPCODE_NONE) //TODO
-    output->maxTries = 1;
-  else if (data.initiator != CECDEVICE_BROADCAST)
-    output->maxTries = iMaxTries;
+  /* mark as waiting for an ack from the destination */
+  MarkAsWaiting(data.destination);
 
-  output->lineTimeout = iLineTimeout;
-  output->retryTimeout = iRetryLineTimeout;
-  output->tries = 0;
-
-  bool bRetry(true);
-  while (bRetry && ++output->tries < output->maxTries)
-  {
-    bRetry = (!Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
-    if (bRetry)
-      Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
-  }
+  /* send the message */
+  bRetry = (!m_adapterMessageQueue->Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
+  if (bRetry)
+    Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
   retVal = output->state;
 
   delete output;
   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;
-}
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread started");
 
-bool CUSBCECAdapterCommunication::Read(CCECAdapterMessage &msg, uint32_t iTimeout)
-{
-  CLockObject lock(m_mutex);
-
-  msg.Clear();
-  CCECAdapterMessage *buf(NULL);
-
-  if (!m_inBuffer.Pop(buf))
-  {
-    if (iTimeout == 0 || !m_rcvCondition.Wait(m_mutex, m_bHasData, iTimeout))
-      return false;
-    m_inBuffer.Pop(buf);
-    m_bHasData = !m_inBuffer.IsEmpty();
-  }
-
-  if (buf)
+  while (!IsStopped())
   {
-    msg.packet = buf->packet;
-    msg.state = ADAPTER_MESSAGE_STATE_INCOMING;
-    delete buf;
-    return true;
-  }
-  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 *output = new CCECAdapterMessage;
-
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_START_BOOTLOADER);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
-  output->expectControllerAck = false;
-
-  if ((bReturn = Write(output)) == false)
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not start the bootloader");
-  delete output;
-
-  return bReturn;
-}
-
-bool CUSBCECAdapterCommunication::PingAdapter(void)
-{
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "sending ping");
-
-  CCECAdapterMessage *output = new CCECAdapterMessage;
+    /* read from the serial port */
+    if (!ReadFromDevice(50, 5))
+    {
+      libcec_parameter param;
+      param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN;
+      LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param);
 
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_PING);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
+      break;
+    }
 
-  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;
+    /* TODO sleep 5 ms so other threads can get a lock */
+    Sleep(5);
   }
 
-  return true;
+  m_adapterMessageQueue->Clear();
+  LIB_CEC->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)
+      if (!m_bWaitingForAck[msg.Destination()] && !msg.IsEOM())
       {
-        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)
-      {
-        m_lastInitiator = m_currentframe.initiator;
-        m_processor->HandlePoll(m_currentframe.initiator, m_currentframe.destination);
-      }
-    }
-    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();
+        if (m_callback)
+          m_callback->HandlePoll(msg.Initiator(), msg.Destination());
       }
+      else
+        m_bWaitingForAck[msg.Destination()] = false;
     }
-    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");
-    CCECAdapterMessage *output = new CCECAdapterMessage;
-
-    output->PushBack(MSGSTART);
-    output->PushEscaped(MSGCODE_FIRMWARE_VERSION);
-    output->PushBack(MSGEND);
-    output->isTransmission = false;
-    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, 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
-    {
-      CLibCEC::AddLog(CEC_LOG_ERROR, "no firmware version received");
-    }
+    m_bWaitingForAck[dest] = true;
   }
-
-  return iReturn;
 }
 
-bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
+void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = CEC_CLEAR_INPUT_DEFAULT_WAIT */)
 {
-  m_iLineTimeout = iTimeout;
-  return true;
-  //TODO
-//  bool bReturn(m_iLineTimeout != iTimeout);
-//
-//  if (!bReturn)
-//  {
-//    CCECAdapterMessage *output = new CCECAdapterMessage;
-//
-//    output->PushBack(MSGSTART);
-//    output->PushEscaped(MSGCODE_TRANSMIT_IDLETIME);
-//    output->PushEscaped(iTimeout);
-//    output->PushBack(MSGEND);
-//    output->isTransmission = false;
-//
-//    if ((bReturn = Write(output)) == false)
-//      CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the idletime");
-//    delete output;
-//  }
-//
-//  return bReturn;
-}
+  CTimeout timeout(iTimeout);
+  uint8_t buff[1024];
+  ssize_t iBytesRead(0);
+  bool bGotMsgEnd(true);
 
-bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
-{
-  return SetAckMaskInternal(iMask, false);
+  while (timeout.TimeLeft() > 0 && ((iBytesRead = m_port->Read(buff, 1024, 5)) > 0 || !bGotMsgEnd))
+  {
+    bGotMsgEnd = false;
+    /* if something was received, wait for MSGEND */
+    for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
+      bGotMsgEnd = buff[iPtr] == MSGEND;
+  }
 }
 
-bool CUSBCECAdapterCommunication::SetAckMaskInternal(uint16_t iMask, bool bWriteDirectly /* = false */)
+bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
 {
-  bool bReturn(false);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting ackmask to %2x", iMask);
-
-  CCECAdapterMessage *output = new CCECAdapterMessage;
+  bool bReturn(true);
+  bool bChanged(false);
 
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_SET_ACK_MASK);
-  output->PushEscaped(iMask >> 8);
-  output->PushEscaped((uint8_t)iMask);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
+  /* only send the command if the timeout changed */
+  {
+    CLockObject lock(m_mutex);
+    bChanged = (m_iLineTimeout != iTimeout);
+    m_iLineTimeout = iTimeout;
+  }
 
-  if (bWriteDirectly)
-    SendMessageToAdapter(output);
-  else if ((bReturn = Write(output)) == false)
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the ackmask");
-  delete output;
+  if (bChanged)
+    bReturn = m_commands->SetLineTimeout(iTimeout);
 
   return bReturn;
 }
 
-bool CUSBCECAdapterCommunication::PersistConfiguration(libcec_configuration *configuration)
+bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
 {
-  if (m_iFirmwareVersion < 2)
+  CLockObject adapterLock(m_mutex);
+  if (!IsOpen())
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "error writing command '%s' to serial port '%s': the connection is closed", CCECAdapterMessage::ToString(message->Message()), m_port->GetName().c_str());
+    message->state = ADAPTER_MESSAGE_STATE_ERROR;
     return false;
+  }
 
-  bool bReturn(true);
-  bReturn &= SetAutoEnabled(true);
-  bReturn &= SetDefaultLogicalAddress(configuration->logicalAddresses.primary);
-  bReturn &= SetLogicalAddressMask(configuration->logicalAddresses.AckMask());
-  bReturn &= SetPhysicalAddress(configuration->iPhysicalAddress);
-  bReturn &= SetCECVersion(CEC_VERSION_1_3A);
-  bReturn &= SetOSDName(configuration->strDeviceName);
-  if (bReturn)
-    bReturn = WriteEEPROM();
-  return bReturn;
-}
-
-bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
-{
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "turning controlled mode %s", controlled ? "on" : "off");
-
-  CCECAdapterMessage *output = new CCECAdapterMessage;
-
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_SET_CONTROLLED);
-  output->PushEscaped(controlled);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
-
-  SendMessageToAdapter(output);
-  bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
-  delete output;
-  if (!bWriteOk)
+  /* write the message */
+  if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not set controlled mode");
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "error writing command '%s' to serial port '%s': %s", CCECAdapterMessage::ToString(message->Message()), m_port->GetName().c_str(), m_port->GetError().c_str());
+    message->state = ADAPTER_MESSAGE_STATE_ERROR;
+    // this will trigger an alert in the reader thread
+    m_port->Close();
     return false;
   }
 
+  LIB_CEC->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::SetAutoEnabled(bool enabled)
+bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
 {
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "turning autonomous mode %s", enabled ? "on" : "off");
+  ssize_t iBytesRead(0);
+  uint8_t buff[256];
+  if (iSize > 256)
+    iSize = 256;
 
-  CCECAdapterMessage *output = new CCECAdapterMessage;
+  /* read from the serial port */
+  {
+    CLockObject lock(m_mutex);
+    if (!IsOpen())
+      return false;
 
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_SET_AUTO_ENABLED);
-  output->PushEscaped(enabled);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
+    iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
 
-  SendMessageToAdapter(output);
-  bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
-  delete output;
-  if (!bWriteOk)
-  {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not set autonomous mode");
+    if (m_port->GetErrorNumber())
+    {
+      LIB_CEC->AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
+      m_port->Close();
+      return false;
+    }
+  }
+
+  if (iBytesRead < 0 || iBytesRead > 256)
     return false;
+  else if (iBytesRead > 0)
+  {
+    /* add the data to the current frame */
+    m_adapterMessageQueue->AddData(buff, iBytesRead);
   }
 
   return true;
 }
 
-bool CUSBCECAdapterCommunication::SetDefaultLogicalAddress(cec_logical_address address)
+CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry /* = false */)
 {
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the default logical address to %1X", address);
+  if (!IsOpen() || !m_adapterMessageQueue)
+    return NULL;
 
+  /* create the adapter message for this command */
   CCECAdapterMessage *output = new CCECAdapterMessage;
-
   output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS);
-  output->PushEscaped((uint8_t) address);
+  output->PushEscaped((uint8_t)msgCode);
+  output->Append(params);
   output->PushBack(MSGEND);
-  output->isTransmission = false;
 
-  SendMessageToAdapter(output);
-  bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
-  delete output;
-  if (!bWriteOk)
+  /* write the command */
+  if (!m_adapterMessageQueue->Write(output))
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the default logical address");
-    return false;
+    // this will trigger an alert in the reader thread
+    if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
+      m_port->Close();
+    return output;
+  }
+  else
+  {
+    if (!bIsRetry && output->Reply() == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED &&
+        msgCode != MSGCODE_GET_BUILDDATE /* same messagecode value had a different meaning in older fw builds */)
+    {
+      /* 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 */
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
+      delete output;
+      if (SetControlledMode(true))
+        return SendCommand(msgCode, params, true);
+    }
   }
 
-  return true;
+  return output;
 }
 
-bool CUSBCECAdapterCommunication::SetLogicalAddressMask(uint16_t iMask)
+bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
 {
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the logical address mask to %2X", iMask);
-
-  CCECAdapterMessage *output = new CCECAdapterMessage;
+  bool bReturn(false);
+  CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
 
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_SET_LOGICAL_ADDRESS_MASK);
-  output->PushEscaped(iMask >> 8);
-  output->PushEscaped((uint8_t)iMask);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
+  /* try to ping the adapter */
+  bool bPinged(false);
+  unsigned iPingTry(0);
+  while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
+  {
+    LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
+    CEvent::Sleep(500);
+  }
 
-  SendMessageToAdapter(output);
-  bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
-  delete output;
-  if (!bWriteOk)
+  /* try to read the firmware version */
+  if (bPinged && timeout.TimeLeft() > 0 && m_commands->RequestFirmwareVersion() >= 2)
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the logical address mask");
-    return false;
+    /* try to set controlled mode for v2+ firmwares */
+    unsigned iControlledTry(0);
+    bool bControlled(false);
+    while (timeout.TimeLeft() > 0 && (bControlled = SetControlledMode(true)) == false)
+    {
+      LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
+      CEvent::Sleep(500);
+    }
+    bReturn = bControlled;
   }
+  else
+    bReturn = true;
 
-  return true;
+  /* try to read the build date */
+  m_commands->RequestBuildDate();
+
+  SetInitialised(bReturn);
+  return bReturn;
 }
 
-bool CUSBCECAdapterCommunication::SetPhysicalAddress(uint16_t iPhysicalAddress)
+bool CUSBCECAdapterCommunication::IsOpen(void)
 {
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the physical address to %2X", iPhysicalAddress);
-
-  CCECAdapterMessage *output = new CCECAdapterMessage;
-
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_SET_PHYSICAL_ADDRESS);
-  output->PushEscaped(iPhysicalAddress >> 8);
-  output->PushEscaped((uint8_t)iPhysicalAddress);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
-
-  SendMessageToAdapter(output);
-  bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
-  delete output;
-  if (!bWriteOk)
-  {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the physical address");
-    return false;
-  }
+  /* thread is not being stopped, the port is open and the thread is running */
+  return !IsStopped() && m_port->IsOpen() && IsRunning();
+}
 
-  return true;
+CStdString CUSBCECAdapterCommunication::GetError(void) const
+{
+  return m_port->GetError();
 }
 
-bool CUSBCECAdapterCommunication::SetCECVersion(cec_version version)
+void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo /* = true */)
 {
   CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the CEC version to %s", CLibCEC::GetInstance()->ToString(version));
-
-  CCECAdapterMessage *output = new CCECAdapterMessage;
-
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_SET_HDMI_VERSION);
-  output->PushEscaped((uint8_t)version);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
-
-  SendMessageToAdapter(output);
-  bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
-  delete output;
-  if (!bWriteOk)
-  {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the CEC version");
-    return false;
-  }
-
-  return true;
+  m_bInitialised = bSetTo;
 }
 
-bool CUSBCECAdapterCommunication::SetOSDName(const char *strOSDName)
+bool CUSBCECAdapterCommunication::IsInitialised(void)
 {
   CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "setting the OSD name to %s", strOSDName);
-
-  CCECAdapterMessage *output = new CCECAdapterMessage;
-
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_SET_OSD_NAME);
-  for (size_t iPtr = 0; iPtr < strlen(strOSDName); iPtr++)
-    output->PushEscaped(strOSDName[iPtr]);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
+  return m_bInitialised;
+}
 
-  SendMessageToAdapter(output);
-  bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
-  delete output;
-  if (!bWriteOk)
+bool CUSBCECAdapterCommunication::StartBootloader(void)
+{
+  if (m_port->IsOpen() && m_commands->StartBootloader())
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the OSD name");
-    return false;
+    m_port->Close();
+    return true;
   }
-
-  return true;
+  return false;
 }
 
-bool CUSBCECAdapterCommunication::WriteEEPROM(void)
+bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
 {
-  CLockObject lock(m_mutex);
-  CLibCEC::AddLog(CEC_LOG_DEBUG, "writing settings in the EEPROM");
-
-  CCECAdapterMessage *output = new CCECAdapterMessage;
-
-  output->PushBack(MSGSTART);
-  output->PushEscaped(MSGCODE_WRITE_EEPROM);
-  output->PushBack(MSGEND);
-  output->isTransmission = false;
+  {
+    CLockObject lock(m_mutex);
+    if (m_iAckMask == iMask)
+      return true;
+  }
 
-  SendMessageToAdapter(output);
-  bool bWriteOk = output->state == ADAPTER_MESSAGE_STATE_SENT_ACKED;
-  delete output;
-  if (!bWriteOk)
+  if (IsOpen() && m_commands->SetAckMask(iMask))
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "could not write the settings in the EEPROM");
-    return false;
+    CLockObject lock(m_mutex);
+    m_iAckMask = iMask;
+    return true;
   }
 
-  return true;
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "couldn't change the ackmask: the connection is closed");
+  return false;
 }
 
-bool CUSBCECAdapterCommunication::IsOpen(void)
+uint16_t CUSBCECAdapterCommunication::GetAckMask(void)
 {
-  return !IsStopped() && m_port->IsOpen() && IsRunning();
+  CLockObject lock(m_mutex);
+  return m_iAckMask;
 }
 
-bool CUSBCECAdapterCommunication::WaitForAck(CCECAdapterMessage &message)
+bool CUSBCECAdapterCommunication::PingAdapter(void)
 {
-  bool bError(false);
-  bool bTransmitSucceeded(false);
-  uint8_t iPacketsLeft(message.Size() / 4);
-
-  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;
-    }
+  return IsOpen() ? m_commands->PingAdapter() : false;
+}
 
-    if (msg.Message() == MSGCODE_FRAME_START && msg.IsACK())
-    {
-      m_processor->HandlePoll(msg.Initiator(), msg.Destination());
-      m_lastInitiator = msg.Initiator();
-      iNow = GetTimeMs();
-      continue;
-    }
+uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
+{
+  return m_commands ? m_commands->GetFirmwareVersion() : CEC_FW_VERSION_UNKNOWN;
+}
 
-    if (msg.Message() == MSGCODE_RECEIVE_FAILED &&
-        m_lastInitiator != CECDEVICE_UNKNOWN &&
-        m_processor->HandleReceiveFailed(m_lastInitiator))
-    {
-      iNow = GetTimeMs();
-      continue;
-    }
+uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
+{
+  return IsOpen() ? m_commands->RequestBuildDate() : m_commands ? m_commands->GetPersistedBuildDate() : 0;
+}
 
-    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;
-      }
+bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
+{
+  return GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION &&
+      GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE;
+}
 
-      iNow = GetTimeMs();
-    }
-  }
+bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration)
+{
+  return IsOpen() ? m_commands->PersistConfiguration(configuration) : false;
+}
 
-  message.state = bTransmitSucceeded && !bError ?
-      ADAPTER_MESSAGE_STATE_SENT_ACKED :
-      ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
+bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration)
+{
+  return IsOpen() ? m_commands->GetConfiguration(configuration) : false;
+}
 
-  return bTransmitSucceeded && !bError;
+CStdString CUSBCECAdapterCommunication::GetPortName(void)
+{
+  return m_port->GetName();
 }
 
-void CUSBCECAdapterCommunication::AddData(uint8_t *data, size_t iLen)
+bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
 {
-  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 IsOpen() ? m_commands->SetControlledMode(controlled) : false;
 }
 
-bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
+uint16_t CUSBCECAdapterCommunication::GetPhysicalAddress(void)
 {
-  ssize_t iBytesRead;
-  uint8_t buff[256];
-  if (!m_port)
-    return false;
-  if (iSize > 256)
-    iSize = 256;
+  uint16_t iPA(0);
 
-  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)
+  // try to get the PA from ADL
+#if defined(HAS_ADL_EDID_PARSER)
   {
-    AddData(buff, iBytesRead);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via ADL", __FUNCTION__);
+    CADLEdidParser adl;
+    iPA = adl.GetPhysicalAddress();
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - ADL returned physical address %04x", __FUNCTION__, iPA);
   }
+#endif
 
-  return iBytesRead > 0;
-}
-
-void CUSBCECAdapterCommunication::SendMessageToAdapter(CCECAdapterMessage *msg)
-{
-  CLockObject adapterLock(m_mutex);
-  if (!m_port->IsOpen())
+  // try to get the PA from the nvidia driver
+#if defined(HAS_NVIDIA_EDID_PARSER)
+  if (iPA == 0)
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "error writing to serial port: the connection is closed");
-    msg->state = ADAPTER_MESSAGE_STATE_ERROR;
-    return;
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via nvidia driver", __FUNCTION__);
+    CNVEdidParser nv;
+    iPA = nv.GetPhysicalAddress();
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - nvidia driver returned physical address %04x", __FUNCTION__, iPA);
   }
+#endif
 
-  if (msg->tries == 1)
-    SetLineTimeout(msg->lineTimeout);
-  else
-    SetLineTimeout(msg->retryTimeout);
-
-  if (m_port->Write(msg->packet.data, msg->Size()) != (ssize_t) msg->Size())
+  // try to get the PA from the OS
+  if (iPA == 0)
   {
-    CLibCEC::AddLog(CEC_LOG_ERROR, "error writing to serial port: %s", m_port->GetError().c_str());
-    msg->state = ADAPTER_MESSAGE_STATE_ERROR;
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address from the OS", __FUNCTION__);
+    iPA = CEDIDParser::GetPhysicalAddress();
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - OS returned physical address %04x", __FUNCTION__, iPA);
   }
-  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 iPA;
 }
 
-void CUSBCECAdapterCommunication::WriteNextCommand(void)
+void *CAdapterPingThread::Process(void)
 {
-  CCECAdapterMessage *msg(NULL);
-  if (m_outBuffer.Pop(msg))
-    SendMessageToAdapter(msg);
-}
+  while (!IsStopped())
+  {
+    if (m_timeout.TimeLeft() == 0)
+    {
+      /* reinit the timeout */
+      m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
 
-CStdString CUSBCECAdapterCommunication::GetPortName(void)
-{
-  CStdString strName;
-  strName = m_port->GetName();
-  return strName;
+      /* send a ping to the adapter */
+      bool bPinged(false);
+      int iFailedCounter(0);
+      while (!bPinged && iFailedCounter < 3)
+      {
+        if (!m_com->PingAdapter())
+        {
+          /* sleep and retry */
+          Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
+          ++iFailedCounter;
+        }
+        else
+        {
+          bPinged = true;
+        }
+      }
+
+      if (iFailedCounter == 3)
+      {
+        /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
+        m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
+        m_com->StopThread(false);
+        break;
+      }
+    }
+
+    Sleep(500);
+  }
+  return NULL;
 }