LibCecSharp: fixed - set the primary LA in CecLogicalAddresses
[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
59#define CEC_LATEST_ADAPTER_FW_DATE 0x4F99ACB9
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);
263 uint64_t iNow = GetTimeMs();
264 if (m_iScheduleEepromWrite > 0 && m_iScheduleEepromWrite >= iNow)
265 {
266 m_iScheduleEepromWrite = 0;
267 m_iLastEepromWrite = iNow;
268 bWriteEeprom = true;
269 }
270 }
271
272 if (bWriteEeprom)
273 {
274 LIB_CEC->AddLog(CEC_LOG_DEBUG, "updating the eeprom (scheduled)");
275 bWriteEeprom = false;
276 if (!m_commands->WriteEEPROM())
277 {
278 // failed, retry later
279 CLockObject lock(m_mutex);
280 m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY;
281 }
282 }
283
a75e3a5a
LOK
284 /* TODO sleep 5 ms so other threads can get a lock */
285 Sleep(5);
5dcf9f25
LOK
286 }
287
a75e3a5a 288 m_adapterMessageQueue->Clear();
004b8382 289 LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread ended");
a75e3a5a 290 return NULL;
a8f0bd18
LOK
291}
292
a75e3a5a 293bool CUSBCECAdapterCommunication::HandlePoll(const CCECAdapterMessage &msg)
a8f0bd18 294{
a75e3a5a
LOK
295 bool bIsError(msg.IsError());
296 cec_adapter_messagecode messageCode(msg.Message());
297 CLockObject lock(m_mutex);
9f68cc28 298
a75e3a5a 299 if (messageCode == MSGCODE_FRAME_START && msg.IsACK())
a8f0bd18 300 {
a75e3a5a
LOK
301 m_lastPollDestination = msg.Destination();
302 if (msg.Destination() < CECDEVICE_BROADCAST)
a8f0bd18 303 {
a75e3a5a
LOK
304 if (!m_bWaitingForAck[msg.Destination()] && !msg.IsEOM())
305 {
306 if (m_callback)
307 m_callback->HandlePoll(msg.Initiator(), msg.Destination());
308 }
309 else
310 m_bWaitingForAck[msg.Destination()] = false;
a8f0bd18 311 }
7bb4ed43 312 }
a75e3a5a 313 else if (messageCode == MSGCODE_RECEIVE_FAILED)
7bb4ed43 314 {
a75e3a5a
LOK
315 /* hack to suppress warnings when an LG is polling */
316 if (m_lastPollDestination != CECDEVICE_UNKNOWN)
317 bIsError = m_callback->HandleReceiveFailed(m_lastPollDestination);
7bb4ed43 318 }
a8f0bd18 319
a75e3a5a 320 return bIsError;
a8f0bd18 321}
2abe74eb 322
a75e3a5a 323void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest)
2abe74eb 324{
a75e3a5a
LOK
325 /* mark as waiting for an ack from the destination */
326 if (dest < CECDEVICE_BROADCAST)
7bb4ed43 327 {
a75e3a5a
LOK
328 CLockObject lock(m_mutex);
329 m_bWaitingForAck[dest] = true;
7bb4ed43 330 }
7bb4ed43
LOK
331}
332
b32ffd87 333void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = CEC_CLEAR_INPUT_DEFAULT_WAIT */)
1fc16cfd 334{
a75e3a5a
LOK
335 CTimeout timeout(iTimeout);
336 uint8_t buff[1024];
337 ssize_t iBytesRead(0);
a4d657c7 338 bool bGotMsgEnd(true);
1fc16cfd 339
a75e3a5a 340 while (timeout.TimeLeft() > 0 && ((iBytesRead = m_port->Read(buff, 1024, 5)) > 0 || !bGotMsgEnd))
1fc16cfd 341 {
a4d657c7 342 bGotMsgEnd = false;
a75e3a5a
LOK
343 /* if something was received, wait for MSGEND */
344 for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
345 bGotMsgEnd = buff[iPtr] == MSGEND;
1fc16cfd 346 }
1fc16cfd
LOK
347}
348
7bb4ed43 349bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
a171d2fd 350{
16459df9 351 bool bReturn(true);
a75e3a5a 352 bool bChanged(false);
089f0e9d 353
a75e3a5a 354 /* only send the command if the timeout changed */
089f0e9d 355 {
a75e3a5a
LOK
356 CLockObject lock(m_mutex);
357 bChanged = (m_iLineTimeout != iTimeout);
358 m_iLineTimeout = iTimeout;
089f0e9d
LOK
359 }
360
a75e3a5a
LOK
361 if (bChanged)
362 bReturn = m_commands->SetLineTimeout(iTimeout);
a171d2fd 363
a75e3a5a 364 return bReturn;
f9e01dac
LOK
365}
366
a75e3a5a 367bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
5dcf9f25 368{
a75e3a5a 369 CLockObject adapterLock(m_mutex);
0b8c7eab 370 if (!IsOpen())
a75e3a5a 371 {
004b8382 372 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
373 message->state = ADAPTER_MESSAGE_STATE_ERROR;
374 return false;
375 }
5dcf9f25 376
a75e3a5a
LOK
377 /* write the message */
378 if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
379 {
004b8382 380 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 381 message->state = ADAPTER_MESSAGE_STATE_ERROR;
65108638
LOK
382 // this will trigger an alert in the reader thread
383 m_port->Close();
cb4ee028 384 return false;
a75e3a5a 385 }
cb4ee028 386
004b8382 387 LIB_CEC->AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
a75e3a5a
LOK
388 message->state = ADAPTER_MESSAGE_STATE_SENT;
389 return true;
c214d197 390}
b057edad 391
a75e3a5a 392bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
12a36be9 393{
a75e3a5a
LOK
394 ssize_t iBytesRead(0);
395 uint8_t buff[256];
396 if (iSize > 256)
397 iSize = 256;
12a36be9 398
a75e3a5a 399 /* read from the serial port */
12a36be9 400 {
a75e3a5a 401 CLockObject lock(m_mutex);
0b8c7eab 402 if (!IsOpen())
a75e3a5a 403 return false;
a8559e01 404
a75e3a5a 405 iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
a8559e01
LOK
406
407 if (m_port->GetErrorNumber())
408 {
004b8382 409 LIB_CEC->AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
a8559e01
LOK
410 m_port->Close();
411 return false;
412 }
12a36be9
LOK
413 }
414
a75e3a5a 415 if (iBytesRead < 0 || iBytesRead > 256)
a75e3a5a 416 return false;
a75e3a5a 417 else if (iBytesRead > 0)
12a36be9 418 {
a75e3a5a
LOK
419 /* add the data to the current frame */
420 m_adapterMessageQueue->AddData(buff, iBytesRead);
12a36be9
LOK
421 }
422
a75e3a5a 423 return true;
b057edad
BL
424}
425
a75e3a5a 426CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry /* = false */)
c214d197 427{
814a902f 428 if (!IsOpen() || !m_adapterMessageQueue)
a75e3a5a 429 return NULL;
c214d197 430
a75e3a5a
LOK
431 /* create the adapter message for this command */
432 CCECAdapterMessage *output = new CCECAdapterMessage;
433 output->PushBack(MSGSTART);
434 output->PushEscaped((uint8_t)msgCode);
435 output->Append(params);
436 output->PushBack(MSGEND);
12a36be9 437
a75e3a5a
LOK
438 /* write the command */
439 if (!m_adapterMessageQueue->Write(output))
12a36be9 440 {
814a902f 441 // this will trigger an alert in the reader thread
0b714871 442 if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
814a902f 443 m_port->Close();
a75e3a5a 444 return output;
12a36be9 445 }
a75e3a5a 446 else
12a36be9 447 {
ee090252
LOK
448 if (!bIsRetry && output->Reply() == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED &&
449 msgCode != MSGCODE_GET_BUILDDATE /* same messagecode value had a different meaning in older fw builds */)
a75e3a5a
LOK
450 {
451 /* if the controller reported that the command was rejected, and we didn't send the command
452 to set controlled mode, then the controller probably switched to auto mode. set controlled
453 mode and retry */
004b8382 454 LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
a75e3a5a
LOK
455 delete output;
456 if (SetControlledMode(true))
457 return SendCommand(msgCode, params, true);
458 }
12a36be9 459 }
12a36be9 460
a75e3a5a 461 return output;
c214d197
LOK
462}
463
b32ffd87 464bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
12a36be9 465{
a75e3a5a
LOK
466 bool bReturn(false);
467 CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
12a36be9 468
a75e3a5a
LOK
469 /* try to ping the adapter */
470 bool bPinged(false);
471 unsigned iPingTry(0);
472 while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
12a36be9 473 {
004b8382 474 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
a75e3a5a 475 CEvent::Sleep(500);
12a36be9 476 }
c214d197 477
a75e3a5a
LOK
478 /* try to read the firmware version */
479 if (bPinged && timeout.TimeLeft() > 0 && m_commands->RequestFirmwareVersion() >= 2)
12a36be9 480 {
a75e3a5a
LOK
481 /* try to set controlled mode for v2+ firmwares */
482 unsigned iControlledTry(0);
483 bool bControlled(false);
484 while (timeout.TimeLeft() > 0 && (bControlled = SetControlledMode(true)) == false)
485 {
004b8382 486 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
a75e3a5a
LOK
487 CEvent::Sleep(500);
488 }
489 bReturn = bControlled;
12a36be9 490 }
a75e3a5a
LOK
491 else
492 bReturn = true;
c214d197 493
baabc020
LOK
494 /* try to read the build date */
495 m_commands->RequestBuildDate();
496
a75e3a5a
LOK
497 SetInitialised(bReturn);
498 return bReturn;
c214d197
LOK
499}
500
a75e3a5a 501bool CUSBCECAdapterCommunication::IsOpen(void)
12a36be9 502{
a75e3a5a
LOK
503 /* thread is not being stopped, the port is open and the thread is running */
504 return !IsStopped() && m_port->IsOpen() && IsRunning();
12a36be9
LOK
505}
506
2b44051c 507std::string CUSBCECAdapterCommunication::GetError(void) const
c214d197 508{
a75e3a5a 509 return m_port->GetError();
c214d197
LOK
510}
511
a75e3a5a 512void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo /* = true */)
12a36be9
LOK
513{
514 CLockObject lock(m_mutex);
a75e3a5a 515 m_bInitialised = bSetTo;
12a36be9
LOK
516}
517
a75e3a5a 518bool CUSBCECAdapterCommunication::IsInitialised(void)
c214d197
LOK
519{
520 CLockObject lock(m_mutex);
a75e3a5a 521 return m_bInitialised;
c214d197
LOK
522}
523
a75e3a5a 524bool CUSBCECAdapterCommunication::StartBootloader(void)
12a36be9 525{
55c75e6e
LOK
526 if (m_port->IsOpen() && m_commands->StartBootloader())
527 {
65108638 528 m_port->Close();
55c75e6e
LOK
529 return true;
530 }
531 return false;
12a36be9
LOK
532}
533
2b44051c 534bool CUSBCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses)
13fd6a66 535{
c6f01e11
LOK
536 {
537 CLockObject lock(m_mutex);
2b44051c 538 if (m_logicalAddresses == addresses)
c6f01e11
LOK
539 return true;
540 }
004b8382 541
2b44051c 542 if (IsOpen() && m_commands->SetAckMask(addresses.AckMask()))
004b8382 543 {
c6f01e11 544 CLockObject lock(m_mutex);
2b44051c 545 m_logicalAddresses = addresses;
004b8382
LOK
546 return true;
547 }
548
d78e4e6f 549 LIB_CEC->AddLog(CEC_LOG_DEBUG, "couldn't change the ackmask: the connection is closed");
004b8382
LOK
550 return false;
551}
552
2b44051c 553cec_logical_addresses CUSBCECAdapterCommunication::GetLogicalAddresses(void)
004b8382 554{
2b44051c 555 cec_logical_addresses addresses;
c6f01e11 556 CLockObject lock(m_mutex);
2b44051c
LOK
557 addresses = m_logicalAddresses;
558 return addresses;
13fd6a66 559}
ef7696f5 560
a75e3a5a 561bool CUSBCECAdapterCommunication::PingAdapter(void)
6729ac71 562{
0b8c7eab 563 return IsOpen() ? m_commands->PingAdapter() : false;
6729ac71
LOK
564}
565
a75e3a5a 566uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
ef7696f5 567{
5943b9d1 568 return m_commands ? m_commands->GetFirmwareVersion() : CEC_FW_VERSION_UNKNOWN;
ef7696f5
LOK
569}
570
b2f56d35
LOK
571uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
572{
5943b9d1 573 return IsOpen() ? m_commands->RequestBuildDate() : m_commands ? m_commands->GetPersistedBuildDate() : 0;
b2f56d35
LOK
574}
575
5daed059
LOK
576bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
577{
578 return GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION &&
579 GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE;
580}
581
c0152c09 582bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration)
ef7696f5 583{
2b44051c
LOK
584 if (IsOpen())
585 {
586 // returns true when something changed
587 if (m_commands->PersistConfiguration(configuration))
588 {
589 {
590 CLockObject lock(m_mutex);
591 uint64_t iNow = GetTimeMs();
592 if (iNow - m_iLastEepromWrite < CEC_ADAPTER_EEPROM_WRITE_INTERVAL)
593 {
594 // if there was more than 1 write within the last 30 seconds, schedule another one
595 if (m_iScheduleEepromWrite == 0)
596 m_iScheduleEepromWrite = m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL;
597 return true;
598 }
599 else
600 {
601 m_iLastEepromWrite = iNow;
602 }
603 }
604
605 if (!m_commands->WriteEEPROM())
606 {
607 // write failed, retry later
608 CLockObject lock(m_mutex);
609 m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY;
610 }
611 }
612 }
0b8c7eab 613 return IsOpen() ? m_commands->PersistConfiguration(configuration) : false;
ef7696f5
LOK
614}
615
c0152c09 616bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration)
ef7696f5 617{
0b8c7eab 618 return IsOpen() ? m_commands->GetConfiguration(configuration) : false;
ef7696f5
LOK
619}
620
2b44051c 621std::string CUSBCECAdapterCommunication::GetPortName(void)
cba904a6 622{
a75e3a5a 623 return m_port->GetName();
c9c282a4 624}
d4db0c6f 625
a75e3a5a 626bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
d4db0c6f 627{
0b8c7eab 628 return IsOpen() ? m_commands->SetControlledMode(controlled) : false;
d4db0c6f 629}
56e53c14 630
200322e5
LOK
631uint16_t CUSBCECAdapterCommunication::GetPhysicalAddress(void)
632{
633 uint16_t iPA(0);
634
635 // try to get the PA from ADL
636#if defined(HAS_ADL_EDID_PARSER)
637 {
638 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via ADL", __FUNCTION__);
639 CADLEdidParser adl;
640 iPA = adl.GetPhysicalAddress();
641 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - ADL returned physical address %04x", __FUNCTION__, iPA);
642 }
643#endif
644
cebbfe03
LOK
645 // try to get the PA from the nvidia driver
646#if defined(HAS_NVIDIA_EDID_PARSER)
647 if (iPA == 0)
648 {
649 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via nvidia driver", __FUNCTION__);
650 CNVEdidParser nv;
651 iPA = nv.GetPhysicalAddress();
652 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - nvidia driver returned physical address %04x", __FUNCTION__, iPA);
653 }
654#endif
655
200322e5
LOK
656 // try to get the PA from the OS
657 if (iPA == 0)
658 {
659 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address from the OS", __FUNCTION__);
660 iPA = CEDIDParser::GetPhysicalAddress();
661 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - OS returned physical address %04x", __FUNCTION__, iPA);
662 }
663
664 return iPA;
665}
666
56e53c14
LOK
667void *CAdapterPingThread::Process(void)
668{
669 while (!IsStopped())
670 {
671 if (m_timeout.TimeLeft() == 0)
672 {
a75e3a5a 673 /* reinit the timeout */
56e53c14 674 m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
a75e3a5a
LOK
675
676 /* send a ping to the adapter */
677 bool bPinged(false);
678 int iFailedCounter(0);
679 while (!bPinged && iFailedCounter < 3)
680 {
681 if (!m_com->PingAdapter())
682 {
b32ffd87
LOK
683 /* sleep and retry */
684 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
a75e3a5a
LOK
685 ++iFailedCounter;
686 }
687 else
688 {
689 bPinged = true;
690 }
691 }
692
693 if (iFailedCounter == 3)
694 {
695 /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
004b8382 696 m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
a75e3a5a
LOK
697 m_com->StopThread(false);
698 break;
699 }
56e53c14
LOK
700 }
701
702 Sleep(500);
703 }
704 return NULL;
705}