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