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