cec: fixed - delayed source switch time was reset, resulting in an attempt every...
[deb_libcec.git] / src / lib / adapter / Pulse-Eight / USBCECAdapterCommunication.cpp
CommitLineData
a8f0bd18
LOK
1/*
2 * This file is part of the libCEC(R) library.
3 *
b492c10e 4 * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
a8f0bd18
LOK
5 * libCEC(R) is an original work, containing original code.
6 *
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
8 *
9 * This program is dual-licensed; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 *
23 *
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
26 *
27 * For more information contact:
28 * Pulse-Eight Licensing <license@pulse-eight.com>
29 * http://www.pulse-eight.com/
30 * http://www.pulse-eight.net/
31 */
32
2b44051c 33#include "env.h"
7bb4ed43 34#include "USBCECAdapterCommunication.h"
2b44051c 35
a75e3a5a
LOK
36#include "USBCECAdapterCommands.h"
37#include "USBCECAdapterMessageQueue.h"
2b44051c
LOK
38#include "USBCECAdapterMessage.h"
39#include "lib/platform/sockets/serialport.h"
40#include "lib/platform/util/timeutils.h"
41#include "lib/platform/util/util.h"
42#include "lib/platform/util/edid.h"
43#include "lib/platform/adl/adl-edid.h"
44#include "lib/platform/nvidia/nv-edid.h"
45#include "lib/LibCEC.h"
46#include "lib/CECProcessor.h"
a8f0bd18
LOK
47
48using namespace std;
49using namespace CEC;
f00ff009 50using namespace PLATFORM;
a8f0bd18 51
2b44051c
LOK
52#define CEC_ADAPTER_PING_TIMEOUT 15000
53#define CEC_ADAPTER_EEPROM_WRITE_INTERVAL 30000
54#define CEC_ADAPTER_EEPROM_WRITE_RETRY 5000
ae54110f 55
5daed059
LOK
56// firmware version 2
57#define CEC_LATEST_ADAPTER_FW_VERSION 2
3ead056c
LOK
58// firmware date Thu Aug 2 08:31:24 UTC 2012
59#define CEC_LATEST_ADAPTER_FW_DATE 0x501a4b0c
60
61#define CEC_FW_DATE_EXTENDED_RESPONSE 0x501a4b0c
5daed059 62
004b8382
LOK
63#define LIB_CEC m_callback->GetLib()
64
b32ffd87 65CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */) :
a75e3a5a 66 IAdapterCommunication(callback),
12027dbe 67 m_port(NULL),
1fc16cfd 68 m_iLineTimeout(0),
a75e3a5a 69 m_lastPollDestination(CECDEVICE_UNKNOWN),
56e53c14 70 m_bInitialised(false),
a75e3a5a 71 m_pingThread(NULL),
3ead056c 72 m_eepromWriteThread(NULL),
a75e3a5a 73 m_commands(NULL),
3ead056c 74 m_adapterMessageQueue(NULL)
a8f0bd18 75{
2b44051c 76 m_logicalAddresses.Clear();
d2d1660c 77 for (unsigned int iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
4164923b 78 m_bWaitingForAck[iPtr] = false;
089f0e9d 79 m_port = new CSerialPort(strPort, iBaudRate);
c6f01e11 80 m_commands = new CUSBCECAdapterCommands(this);
a8f0bd18
LOK
81}
82
a75e3a5a 83CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void)
a8f0bd18 84{
a75e3a5a 85 Close();
c9d15485
LOK
86 DELETE_AND_NULL(m_commands);
87 DELETE_AND_NULL(m_adapterMessageQueue);
88 DELETE_AND_NULL(m_port);
efed01e1 89}
a8f0bd18 90
c6f01e11
LOK
91void CUSBCECAdapterCommunication::ResetMessageQueue(void)
92{
93 DELETE_AND_NULL(m_adapterMessageQueue);
94 m_adapterMessageQueue = new CCECAdapterMessageQueue(this);
95 m_adapterMessageQueue->CreateThread();
96}
97
b32ffd87 98bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool bSkipChecks /* = false */, bool bStartListening /* = true */)
efed01e1 99{
a75e3a5a 100 bool bConnectionOpened(false);
2c780401 101 {
efed01e1
LOK
102 CLockObject lock(m_mutex);
103
a75e3a5a 104 /* we need the port settings here */
efed01e1
LOK
105 if (!m_port)
106 {
004b8382 107 LIB_CEC->AddLog(CEC_LOG_ERROR, "port is NULL");
a75e3a5a 108 return bConnectionOpened;
efed01e1
LOK
109 }
110
a75e3a5a 111 /* return true when the port is already open */
efed01e1
LOK
112 if (IsOpen())
113 {
004b8382 114 LIB_CEC->AddLog(CEC_LOG_WARNING, "port is already open");
efed01e1
LOK
115 return true;
116 }
117
c6f01e11 118 ResetMessageQueue();
a75e3a5a
LOK
119
120 /* try to open the connection */
efed01e1 121 CStdString strError;
a75e3a5a
LOK
122 CTimeout timeout(iTimeoutMs);
123 while (!bConnectionOpened && timeout.TimeLeft() > 0)
efed01e1 124 {
a75e3a5a 125 if ((bConnectionOpened = m_port->Open(timeout.TimeLeft())) == false)
efed01e1
LOK
126 {
127 strError.Format("error opening serial port '%s': %s", m_port->GetName().c_str(), m_port->GetError().c_str());
128 Sleep(250);
efed01e1 129 }
a75e3a5a 130 /* and retry every 250ms until the timeout passed */
efed01e1
LOK
131 }
132
a75e3a5a
LOK
133 /* return false when we couldn't connect */
134 if (!bConnectionOpened)
efed01e1 135 {
004b8382 136 LIB_CEC->AddLog(CEC_LOG_ERROR, strError);
7068f331
LOK
137
138 if (m_port->GetErrorNumber() == EACCES)
139 {
140 libcec_parameter param;
141 param.paramType = CEC_PARAMETER_TYPE_STRING;
142 param.paramData = (void*)"No permission to open the device";
143 LIB_CEC->Alert(CEC_ALERT_PERMISSION_ERROR, param);
144 }
145 else if (m_port->GetErrorNumber() == EBUSY)
146 {
147 libcec_parameter param;
148 param.paramType = CEC_PARAMETER_TYPE_STRING;
149 param.paramData = (void*)"The serial port is busy. Only one program can access the device directly.";
150 LIB_CEC->Alert(CEC_ALERT_PORT_BUSY, param);
151 }
efed01e1
LOK
152 return false;
153 }
154
004b8382 155 LIB_CEC->AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
a75e3a5a 156 ClearInputBytes();
2c780401 157 }
a8f0bd18 158
c6f01e11 159 // always start by setting the ackmask to 0, to clear previous values
2b44051c
LOK
160 cec_logical_addresses addresses; addresses.Clear();
161 SetLogicalAddresses(addresses);
c6f01e11 162
a75e3a5a 163 if (!CreateThread())
a8f0bd18 164 {
a75e3a5a 165 bConnectionOpened = false;
004b8382 166 LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a communication thread");
a75e3a5a
LOK
167 }
168 else if (!bSkipChecks && !CheckAdapter())
169 {
170 bConnectionOpened = false;
004b8382 171 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
befa3a23 172 }
f80cd208 173 else if (bStartListening)
befa3a23 174 {
3ead056c
LOK
175 /* start the eeprom write thread, that handles all eeprom writes async */
176 m_eepromWriteThread = new CAdapterEepromWriteThread(this);
177 if (!m_eepromWriteThread->CreateThread())
efed01e1 178 {
3ead056c
LOK
179 bConnectionOpened = false;
180 LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create the eeprom write thread");
efed01e1
LOK
181 }
182 else
183 {
3ead056c
LOK
184 /* start a ping thread, that will ping the adapter every 15 seconds
185 if it doesn't receive any ping for 30 seconds, it'll switch to auto mode */
186 m_pingThread = new CAdapterPingThread(this, CEC_ADAPTER_PING_TIMEOUT);
187 if (m_pingThread->CreateThread())
188 {
189 bConnectionOpened = true;
190 }
191 else
192 {
193 bConnectionOpened = false;
194 LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a ping thread");
195 }
efed01e1 196 }
a8f0bd18 197 }
a75e3a5a
LOK
198
199 if (!bConnectionOpened || !bStartListening)
200 StopThread(0);
a8f0bd18 201
a75e3a5a 202 return bConnectionOpened;
a8f0bd18
LOK
203}
204
7bb4ed43 205void CUSBCECAdapterCommunication::Close(void)
a8f0bd18 206{
0b714871
LOK
207 /* stop the reader thread */
208 StopThread(0);
209
210 CLockObject lock(m_mutex);
211
a75e3a5a 212 /* set the ackmask to 0 before closing the connection */
0b8c7eab 213 if (IsOpen() && m_port->GetErrorNumber() == 0)
a75e3a5a 214 {
004b8382 215 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - closing the connection", __FUNCTION__);
2b44051c
LOK
216 cec_logical_addresses addresses; addresses.Clear();
217 SetLogicalAddresses(addresses);
a75e3a5a
LOK
218 if (m_commands->GetFirmwareVersion() >= 2)
219 SetControlledMode(false);
220 }
221
a8559e01
LOK
222 m_adapterMessageQueue->Clear();
223
3ead056c
LOK
224 /* stop and delete the write thread */
225 m_eepromWriteThread->Stop();
226 DELETE_AND_NULL(m_eepromWriteThread);
227
a75e3a5a 228 /* stop and delete the ping thread */
c9d15485 229 DELETE_AND_NULL(m_pingThread);
a8f0bd18 230
a75e3a5a 231 /* close and delete the com port connection */
e4a53f8e
LOK
232 if (m_port)
233 m_port->Close();
a8f0bd18
LOK
234}
235
2b44051c 236cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout, bool UNUSED(bIsReply))
7bb4ed43
LOK
237{
238 cec_adapter_message_state retVal(ADAPTER_MESSAGE_STATE_UNKNOWN);
9f68cc28
LOK
239 if (!IsRunning())
240 return retVal;
7bb4ed43 241
33dd87a9 242 CCECAdapterMessage *output = new CCECAdapterMessage(data, iLineTimeout);
7bb4ed43 243
a75e3a5a
LOK
244 /* mark as waiting for an ack from the destination */
245 MarkAsWaiting(data.destination);
4164923b 246
a75e3a5a 247 /* send the message */
33dd87a9 248 bRetry = (!m_adapterMessageQueue->Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
a8559e01 249 if (bRetry)
33dd87a9 250 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
7bb4ed43
LOK
251 retVal = output->state;
252
253 delete output;
254 return retVal;
255}
256
a75e3a5a 257void *CUSBCECAdapterCommunication::Process(void)
3c53ac93 258{
a75e3a5a 259 CCECAdapterMessage msg;
004b8382 260 LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread started");
5dcf9f25 261
a75e3a5a 262 while (!IsStopped())
5dcf9f25 263 {
a75e3a5a
LOK
264 /* read from the serial port */
265 if (!ReadFromDevice(50, 5))
2fbffb25
LOK
266 {
267 libcec_parameter param;
268 param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN;
269 LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param);
270
a75e3a5a 271 break;
2fbffb25 272 }
a75e3a5a
LOK
273
274 /* TODO sleep 5 ms so other threads can get a lock */
275 Sleep(5);
5dcf9f25
LOK
276 }
277
a75e3a5a 278 m_adapterMessageQueue->Clear();
004b8382 279 LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread ended");
a75e3a5a 280 return NULL;
a8f0bd18
LOK
281}
282
a75e3a5a 283bool CUSBCECAdapterCommunication::HandlePoll(const CCECAdapterMessage &msg)
a8f0bd18 284{
a75e3a5a
LOK
285 bool bIsError(msg.IsError());
286 cec_adapter_messagecode messageCode(msg.Message());
287 CLockObject lock(m_mutex);
9f68cc28 288
a75e3a5a 289 if (messageCode == MSGCODE_FRAME_START && msg.IsACK())
a8f0bd18 290 {
a75e3a5a
LOK
291 m_lastPollDestination = msg.Destination();
292 if (msg.Destination() < CECDEVICE_BROADCAST)
a8f0bd18 293 {
a75e3a5a
LOK
294 if (!m_bWaitingForAck[msg.Destination()] && !msg.IsEOM())
295 {
296 if (m_callback)
297 m_callback->HandlePoll(msg.Initiator(), msg.Destination());
298 }
299 else
300 m_bWaitingForAck[msg.Destination()] = false;
a8f0bd18 301 }
7bb4ed43 302 }
a75e3a5a 303 else if (messageCode == MSGCODE_RECEIVE_FAILED)
7bb4ed43 304 {
a75e3a5a
LOK
305 /* hack to suppress warnings when an LG is polling */
306 if (m_lastPollDestination != CECDEVICE_UNKNOWN)
307 bIsError = m_callback->HandleReceiveFailed(m_lastPollDestination);
7bb4ed43 308 }
a8f0bd18 309
a75e3a5a 310 return bIsError;
a8f0bd18 311}
2abe74eb 312
a75e3a5a 313void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest)
2abe74eb 314{
a75e3a5a
LOK
315 /* mark as waiting for an ack from the destination */
316 if (dest < CECDEVICE_BROADCAST)
7bb4ed43 317 {
a75e3a5a
LOK
318 CLockObject lock(m_mutex);
319 m_bWaitingForAck[dest] = true;
7bb4ed43 320 }
7bb4ed43
LOK
321}
322
b32ffd87 323void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = CEC_CLEAR_INPUT_DEFAULT_WAIT */)
1fc16cfd 324{
a75e3a5a
LOK
325 CTimeout timeout(iTimeout);
326 uint8_t buff[1024];
327 ssize_t iBytesRead(0);
a4d657c7 328 bool bGotMsgEnd(true);
1fc16cfd 329
a75e3a5a 330 while (timeout.TimeLeft() > 0 && ((iBytesRead = m_port->Read(buff, 1024, 5)) > 0 || !bGotMsgEnd))
1fc16cfd 331 {
a4d657c7 332 bGotMsgEnd = false;
a75e3a5a
LOK
333 /* if something was received, wait for MSGEND */
334 for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
335 bGotMsgEnd = buff[iPtr] == MSGEND;
1fc16cfd 336 }
1fc16cfd
LOK
337}
338
7bb4ed43 339bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
a171d2fd 340{
16459df9 341 bool bReturn(true);
a75e3a5a 342 bool bChanged(false);
089f0e9d 343
a75e3a5a 344 /* only send the command if the timeout changed */
089f0e9d 345 {
a75e3a5a
LOK
346 CLockObject lock(m_mutex);
347 bChanged = (m_iLineTimeout != iTimeout);
348 m_iLineTimeout = iTimeout;
089f0e9d
LOK
349 }
350
a75e3a5a
LOK
351 if (bChanged)
352 bReturn = m_commands->SetLineTimeout(iTimeout);
a171d2fd 353
a75e3a5a 354 return bReturn;
f9e01dac
LOK
355}
356
a75e3a5a 357bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
5dcf9f25 358{
a75e3a5a 359 CLockObject adapterLock(m_mutex);
0b8c7eab 360 if (!IsOpen())
a75e3a5a 361 {
004b8382 362 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());
a75e3a5a
LOK
363 message->state = ADAPTER_MESSAGE_STATE_ERROR;
364 return false;
365 }
5dcf9f25 366
a75e3a5a
LOK
367 /* write the message */
368 if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
369 {
004b8382 370 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());
a75e3a5a 371 message->state = ADAPTER_MESSAGE_STATE_ERROR;
65108638
LOK
372 // this will trigger an alert in the reader thread
373 m_port->Close();
cb4ee028 374 return false;
a75e3a5a 375 }
cb4ee028 376
004b8382 377 LIB_CEC->AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
a75e3a5a
LOK
378 message->state = ADAPTER_MESSAGE_STATE_SENT;
379 return true;
c214d197 380}
b057edad 381
a75e3a5a 382bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
12a36be9 383{
a75e3a5a
LOK
384 ssize_t iBytesRead(0);
385 uint8_t buff[256];
386 if (iSize > 256)
387 iSize = 256;
12a36be9 388
a75e3a5a 389 /* read from the serial port */
12a36be9 390 {
a75e3a5a 391 CLockObject lock(m_mutex);
0b8c7eab 392 if (!IsOpen())
a75e3a5a 393 return false;
a8559e01 394
a75e3a5a 395 iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
a8559e01
LOK
396
397 if (m_port->GetErrorNumber())
398 {
004b8382 399 LIB_CEC->AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
a8559e01
LOK
400 m_port->Close();
401 return false;
402 }
12a36be9
LOK
403 }
404
a75e3a5a 405 if (iBytesRead < 0 || iBytesRead > 256)
a75e3a5a 406 return false;
a75e3a5a 407 else if (iBytesRead > 0)
12a36be9 408 {
a75e3a5a
LOK
409 /* add the data to the current frame */
410 m_adapterMessageQueue->AddData(buff, iBytesRead);
12a36be9
LOK
411 }
412
a75e3a5a 413 return true;
b057edad
BL
414}
415
a75e3a5a 416CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry /* = false */)
c214d197 417{
814a902f 418 if (!IsOpen() || !m_adapterMessageQueue)
a75e3a5a 419 return NULL;
c214d197 420
a75e3a5a
LOK
421 /* create the adapter message for this command */
422 CCECAdapterMessage *output = new CCECAdapterMessage;
423 output->PushBack(MSGSTART);
424 output->PushEscaped((uint8_t)msgCode);
425 output->Append(params);
426 output->PushBack(MSGEND);
12a36be9 427
a75e3a5a
LOK
428 /* write the command */
429 if (!m_adapterMessageQueue->Write(output))
12a36be9 430 {
814a902f 431 // this will trigger an alert in the reader thread
0b714871 432 if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
814a902f 433 m_port->Close();
a75e3a5a 434 return output;
12a36be9 435 }
a75e3a5a 436 else
12a36be9 437 {
ee090252
LOK
438 if (!bIsRetry && output->Reply() == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED &&
439 msgCode != MSGCODE_GET_BUILDDATE /* same messagecode value had a different meaning in older fw builds */)
a75e3a5a
LOK
440 {
441 /* if the controller reported that the command was rejected, and we didn't send the command
442 to set controlled mode, then the controller probably switched to auto mode. set controlled
443 mode and retry */
004b8382 444 LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
a75e3a5a
LOK
445 delete output;
446 if (SetControlledMode(true))
447 return SendCommand(msgCode, params, true);
448 }
12a36be9 449 }
12a36be9 450
a75e3a5a 451 return output;
c214d197
LOK
452}
453
b32ffd87 454bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
12a36be9 455{
a75e3a5a
LOK
456 bool bReturn(false);
457 CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
12a36be9 458
a75e3a5a
LOK
459 /* try to ping the adapter */
460 bool bPinged(false);
461 unsigned iPingTry(0);
462 while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
12a36be9 463 {
004b8382 464 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
a75e3a5a 465 CEvent::Sleep(500);
12a36be9 466 }
c214d197 467
a75e3a5a
LOK
468 /* try to read the firmware version */
469 if (bPinged && timeout.TimeLeft() > 0 && m_commands->RequestFirmwareVersion() >= 2)
12a36be9 470 {
a75e3a5a
LOK
471 /* try to set controlled mode for v2+ firmwares */
472 unsigned iControlledTry(0);
473 bool bControlled(false);
474 while (timeout.TimeLeft() > 0 && (bControlled = SetControlledMode(true)) == false)
475 {
004b8382 476 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
a75e3a5a
LOK
477 CEvent::Sleep(500);
478 }
479 bReturn = bControlled;
12a36be9 480 }
a75e3a5a
LOK
481 else
482 bReturn = true;
c214d197 483
baabc020
LOK
484 /* try to read the build date */
485 m_commands->RequestBuildDate();
486
a75e3a5a
LOK
487 SetInitialised(bReturn);
488 return bReturn;
c214d197
LOK
489}
490
a75e3a5a 491bool CUSBCECAdapterCommunication::IsOpen(void)
12a36be9 492{
a75e3a5a
LOK
493 /* thread is not being stopped, the port is open and the thread is running */
494 return !IsStopped() && m_port->IsOpen() && IsRunning();
12a36be9
LOK
495}
496
2b44051c 497std::string CUSBCECAdapterCommunication::GetError(void) const
c214d197 498{
a75e3a5a 499 return m_port->GetError();
c214d197
LOK
500}
501
a75e3a5a 502void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo /* = true */)
12a36be9
LOK
503{
504 CLockObject lock(m_mutex);
a75e3a5a 505 m_bInitialised = bSetTo;
12a36be9
LOK
506}
507
a75e3a5a 508bool CUSBCECAdapterCommunication::IsInitialised(void)
c214d197
LOK
509{
510 CLockObject lock(m_mutex);
a75e3a5a 511 return m_bInitialised;
c214d197
LOK
512}
513
a75e3a5a 514bool CUSBCECAdapterCommunication::StartBootloader(void)
12a36be9 515{
55c75e6e
LOK
516 if (m_port->IsOpen() && m_commands->StartBootloader())
517 {
65108638 518 m_port->Close();
55c75e6e
LOK
519 return true;
520 }
521 return false;
12a36be9
LOK
522}
523
2b44051c 524bool CUSBCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses)
13fd6a66 525{
c6f01e11
LOK
526 {
527 CLockObject lock(m_mutex);
2b44051c 528 if (m_logicalAddresses == addresses)
c6f01e11
LOK
529 return true;
530 }
004b8382 531
2b44051c 532 if (IsOpen() && m_commands->SetAckMask(addresses.AckMask()))
004b8382 533 {
c6f01e11 534 CLockObject lock(m_mutex);
2b44051c 535 m_logicalAddresses = addresses;
004b8382
LOK
536 return true;
537 }
538
d78e4e6f 539 LIB_CEC->AddLog(CEC_LOG_DEBUG, "couldn't change the ackmask: the connection is closed");
004b8382
LOK
540 return false;
541}
542
2b44051c 543cec_logical_addresses CUSBCECAdapterCommunication::GetLogicalAddresses(void)
004b8382 544{
2b44051c 545 cec_logical_addresses addresses;
c6f01e11 546 CLockObject lock(m_mutex);
2b44051c
LOK
547 addresses = m_logicalAddresses;
548 return addresses;
13fd6a66 549}
ef7696f5 550
a75e3a5a 551bool CUSBCECAdapterCommunication::PingAdapter(void)
6729ac71 552{
0b8c7eab 553 return IsOpen() ? m_commands->PingAdapter() : false;
6729ac71
LOK
554}
555
a75e3a5a 556uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
ef7696f5 557{
5943b9d1 558 return m_commands ? m_commands->GetFirmwareVersion() : CEC_FW_VERSION_UNKNOWN;
ef7696f5
LOK
559}
560
b2f56d35
LOK
561uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
562{
3ead056c
LOK
563 uint32_t iBuildDate(0);
564 if (m_commands)
565 iBuildDate = m_commands->GetPersistedBuildDate();
566 if (iBuildDate == 0 && IsOpen())
567 iBuildDate = m_commands->RequestBuildDate();
568
569 return iBuildDate;
570}
571
572bool CUSBCECAdapterCommunication::ProvidesExtendedResponse(void)
573{
574 uint32_t iBuildDate(0);
575 if (m_commands)
576 iBuildDate = m_commands->GetPersistedBuildDate();
577
578 return iBuildDate >= CEC_FW_DATE_EXTENDED_RESPONSE;
b2f56d35
LOK
579}
580
5daed059
LOK
581bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
582{
3ead056c
LOK
583 return GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE &&
584 GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION;
5daed059
LOK
585}
586
c0152c09 587bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration)
ef7696f5 588{
3ead056c
LOK
589 return IsOpen() ?
590 m_commands->PersistConfiguration(configuration) && m_eepromWriteThread->Write() :
591 false;
ef7696f5
LOK
592}
593
c0152c09 594bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration)
ef7696f5 595{
0b8c7eab 596 return IsOpen() ? m_commands->GetConfiguration(configuration) : false;
ef7696f5
LOK
597}
598
2b44051c 599std::string CUSBCECAdapterCommunication::GetPortName(void)
cba904a6 600{
a75e3a5a 601 return m_port->GetName();
c9c282a4 602}
d4db0c6f 603
a75e3a5a 604bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
d4db0c6f 605{
0b8c7eab 606 return IsOpen() ? m_commands->SetControlledMode(controlled) : false;
d4db0c6f 607}
56e53c14 608
200322e5
LOK
609uint16_t CUSBCECAdapterCommunication::GetPhysicalAddress(void)
610{
611 uint16_t iPA(0);
612
613 // try to get the PA from ADL
614#if defined(HAS_ADL_EDID_PARSER)
615 {
616 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via ADL", __FUNCTION__);
617 CADLEdidParser adl;
618 iPA = adl.GetPhysicalAddress();
619 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - ADL returned physical address %04x", __FUNCTION__, iPA);
620 }
621#endif
622
cebbfe03
LOK
623 // try to get the PA from the nvidia driver
624#if defined(HAS_NVIDIA_EDID_PARSER)
625 if (iPA == 0)
626 {
627 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via nvidia driver", __FUNCTION__);
628 CNVEdidParser nv;
629 iPA = nv.GetPhysicalAddress();
630 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - nvidia driver returned physical address %04x", __FUNCTION__, iPA);
631 }
632#endif
633
200322e5
LOK
634 // try to get the PA from the OS
635 if (iPA == 0)
636 {
637 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address from the OS", __FUNCTION__);
638 iPA = CEDIDParser::GetPhysicalAddress();
639 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - OS returned physical address %04x", __FUNCTION__, iPA);
640 }
641
642 return iPA;
643}
644
56e53c14
LOK
645void *CAdapterPingThread::Process(void)
646{
647 while (!IsStopped())
648 {
649 if (m_timeout.TimeLeft() == 0)
650 {
a75e3a5a 651 /* reinit the timeout */
56e53c14 652 m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
a75e3a5a
LOK
653
654 /* send a ping to the adapter */
655 bool bPinged(false);
656 int iFailedCounter(0);
657 while (!bPinged && iFailedCounter < 3)
658 {
659 if (!m_com->PingAdapter())
660 {
b32ffd87
LOK
661 /* sleep and retry */
662 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
a75e3a5a
LOK
663 ++iFailedCounter;
664 }
665 else
666 {
667 bPinged = true;
668 }
669 }
670
671 if (iFailedCounter == 3)
672 {
673 /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
004b8382 674 m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
a75e3a5a
LOK
675 m_com->StopThread(false);
676 break;
677 }
56e53c14
LOK
678 }
679
3ead056c
LOK
680 Sleep(5);
681 }
682 return NULL;
683}
684
685void CAdapterEepromWriteThread::Stop(void)
686{
687 StopThread(-1);
688 {
689 CLockObject lock(m_mutex);
690 if (m_iScheduleEepromWrite > 0)
691 m_com->LIB_CEC->AddLog(CEC_LOG_WARNING, "write thread stopped while a write was queued");
692 m_condition.Signal();
693 }
694 StopThread();
695}
696
697void *CAdapterEepromWriteThread::Process(void)
698{
699 while (!IsStopped())
700 {
701 CLockObject lock(m_mutex);
702 if ((m_iScheduleEepromWrite > 0 && m_iScheduleEepromWrite < GetTimeMs()) ||
703 m_condition.Wait(m_mutex, m_bWrite, 100))
704 {
705 m_bWrite = false;
706 if (m_com->m_commands->WriteEEPROM())
707 {
708 m_iLastEepromWrite = GetTimeMs();
709 m_iScheduleEepromWrite = 0;
710 }
711 else
712 {
713 m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY;
714 }
715 }
56e53c14
LOK
716 }
717 return NULL;
718}
3ead056c
LOK
719
720bool CAdapterEepromWriteThread::Write(void)
721{
722 CLockObject lock(m_mutex);
723 if (m_iScheduleEepromWrite == 0)
724 {
725 int64_t iNow = GetTimeMs();
726 if (m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL > iNow)
727 {
728 m_com->LIB_CEC->AddLog(CEC_LOG_DEBUG, "delaying eeprom write by %ld ms", m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL - iNow);
729 m_iScheduleEepromWrite = m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL;
730 }
731 else
732 {
733 m_bWrite = true;
734 m_condition.Signal();
735 }
736 }
737 return true;
738}