fall back to the primary client when CCECProcessor::HandleLogicalAddressLost() and...
[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 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
5daed059
LOK
57// firmware version 2
58#define CEC_LATEST_ADAPTER_FW_VERSION 2
3ead056c
LOK
59// firmware date Thu Aug 2 08:31:24 UTC 2012
60#define CEC_LATEST_ADAPTER_FW_DATE 0x501a4b0c
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 */
287 Sleep(5);
5dcf9f25
LOK
288 }
289
a75e3a5a 290 m_adapterMessageQueue->Clear();
004b8382 291 LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread ended");
a75e3a5a 292 return NULL;
a8f0bd18
LOK
293}
294
a75e3a5a 295bool CUSBCECAdapterCommunication::HandlePoll(const CCECAdapterMessage &msg)
a8f0bd18 296{
a75e3a5a
LOK
297 bool bIsError(msg.IsError());
298 cec_adapter_messagecode messageCode(msg.Message());
299 CLockObject lock(m_mutex);
9f68cc28 300
a75e3a5a 301 if (messageCode == MSGCODE_FRAME_START && msg.IsACK())
a8f0bd18 302 {
a75e3a5a
LOK
303 m_lastPollDestination = msg.Destination();
304 if (msg.Destination() < CECDEVICE_BROADCAST)
a8f0bd18 305 {
a75e3a5a
LOK
306 if (!m_bWaitingForAck[msg.Destination()] && !msg.IsEOM())
307 {
308 if (m_callback)
309 m_callback->HandlePoll(msg.Initiator(), msg.Destination());
310 }
311 else
312 m_bWaitingForAck[msg.Destination()] = false;
a8f0bd18 313 }
7bb4ed43 314 }
a75e3a5a 315 else if (messageCode == MSGCODE_RECEIVE_FAILED)
7bb4ed43 316 {
a75e3a5a
LOK
317 /* hack to suppress warnings when an LG is polling */
318 if (m_lastPollDestination != CECDEVICE_UNKNOWN)
319 bIsError = m_callback->HandleReceiveFailed(m_lastPollDestination);
7bb4ed43 320 }
a8f0bd18 321
a75e3a5a 322 return bIsError;
a8f0bd18 323}
2abe74eb 324
a75e3a5a 325void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest)
2abe74eb 326{
a75e3a5a
LOK
327 /* mark as waiting for an ack from the destination */
328 if (dest < CECDEVICE_BROADCAST)
7bb4ed43 329 {
a75e3a5a
LOK
330 CLockObject lock(m_mutex);
331 m_bWaitingForAck[dest] = true;
7bb4ed43 332 }
7bb4ed43
LOK
333}
334
b32ffd87 335void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = CEC_CLEAR_INPUT_DEFAULT_WAIT */)
1fc16cfd 336{
a75e3a5a
LOK
337 CTimeout timeout(iTimeout);
338 uint8_t buff[1024];
339 ssize_t iBytesRead(0);
a4d657c7 340 bool bGotMsgEnd(true);
1fc16cfd 341
a75e3a5a 342 while (timeout.TimeLeft() > 0 && ((iBytesRead = m_port->Read(buff, 1024, 5)) > 0 || !bGotMsgEnd))
1fc16cfd 343 {
a4d657c7 344 bGotMsgEnd = false;
a75e3a5a
LOK
345 /* if something was received, wait for MSGEND */
346 for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
347 bGotMsgEnd = buff[iPtr] == MSGEND;
1fc16cfd 348 }
1fc16cfd
LOK
349}
350
7bb4ed43 351bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
a171d2fd 352{
16459df9 353 bool bReturn(true);
a75e3a5a 354 bool bChanged(false);
089f0e9d 355
a75e3a5a 356 /* only send the command if the timeout changed */
089f0e9d 357 {
a75e3a5a
LOK
358 CLockObject lock(m_mutex);
359 bChanged = (m_iLineTimeout != iTimeout);
360 m_iLineTimeout = iTimeout;
089f0e9d
LOK
361 }
362
a75e3a5a
LOK
363 if (bChanged)
364 bReturn = m_commands->SetLineTimeout(iTimeout);
a171d2fd 365
a75e3a5a 366 return bReturn;
f9e01dac
LOK
367}
368
a75e3a5a 369bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
5dcf9f25 370{
a75e3a5a 371 CLockObject adapterLock(m_mutex);
0b8c7eab 372 if (!IsOpen())
a75e3a5a 373 {
004b8382 374 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
375 message->state = ADAPTER_MESSAGE_STATE_ERROR;
376 return false;
377 }
5dcf9f25 378
a75e3a5a
LOK
379 /* write the message */
380 if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
381 {
004b8382 382 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 383 message->state = ADAPTER_MESSAGE_STATE_ERROR;
65108638
LOK
384 // this will trigger an alert in the reader thread
385 m_port->Close();
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());
a8559e01
LOK
418 m_port->Close();
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
5daed059
LOK
630bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
631{
3ead056c
LOK
632 return GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE &&
633 GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION;
5daed059
LOK
634}
635
c0152c09 636bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration)
ef7696f5 637{
3ead056c
LOK
638 return IsOpen() ?
639 m_commands->PersistConfiguration(configuration) && m_eepromWriteThread->Write() :
640 false;
ef7696f5
LOK
641}
642
c0152c09 643bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration)
ef7696f5 644{
0b8c7eab 645 return IsOpen() ? m_commands->GetConfiguration(configuration) : false;
ef7696f5
LOK
646}
647
2b44051c 648std::string CUSBCECAdapterCommunication::GetPortName(void)
cba904a6 649{
a75e3a5a 650 return m_port->GetName();
c9c282a4 651}
d4db0c6f 652
a75e3a5a 653bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
d4db0c6f 654{
0b8c7eab 655 return IsOpen() ? m_commands->SetControlledMode(controlled) : false;
d4db0c6f 656}
56e53c14 657
200322e5
LOK
658uint16_t CUSBCECAdapterCommunication::GetPhysicalAddress(void)
659{
660 uint16_t iPA(0);
661
662 // try to get the PA from ADL
663#if defined(HAS_ADL_EDID_PARSER)
664 {
665 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via ADL", __FUNCTION__);
666 CADLEdidParser adl;
667 iPA = adl.GetPhysicalAddress();
668 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - ADL returned physical address %04x", __FUNCTION__, iPA);
669 }
670#endif
671
cebbfe03
LOK
672 // try to get the PA from the nvidia driver
673#if defined(HAS_NVIDIA_EDID_PARSER)
674 if (iPA == 0)
675 {
676 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via nvidia driver", __FUNCTION__);
677 CNVEdidParser nv;
678 iPA = nv.GetPhysicalAddress();
679 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - nvidia driver returned physical address %04x", __FUNCTION__, iPA);
680 }
681#endif
682
200322e5
LOK
683 // try to get the PA from the OS
684 if (iPA == 0)
685 {
686 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address from the OS", __FUNCTION__);
687 iPA = CEDIDParser::GetPhysicalAddress();
688 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - OS returned physical address %04x", __FUNCTION__, iPA);
689 }
690
691 return iPA;
692}
693
56e53c14
LOK
694void *CAdapterPingThread::Process(void)
695{
696 while (!IsStopped())
697 {
698 if (m_timeout.TimeLeft() == 0)
699 {
a75e3a5a 700 /* reinit the timeout */
56e53c14 701 m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
a75e3a5a
LOK
702
703 /* send a ping to the adapter */
704 bool bPinged(false);
705 int iFailedCounter(0);
706 while (!bPinged && iFailedCounter < 3)
707 {
708 if (!m_com->PingAdapter())
709 {
b32ffd87
LOK
710 /* sleep and retry */
711 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
a75e3a5a
LOK
712 ++iFailedCounter;
713 }
714 else
715 {
716 bPinged = true;
717 }
718 }
719
720 if (iFailedCounter == 3)
721 {
722 /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
004b8382 723 m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
a75e3a5a
LOK
724 m_com->StopThread(false);
725 break;
726 }
56e53c14
LOK
727 }
728
3ead056c
LOK
729 Sleep(5);
730 }
731 return NULL;
732}
733
734void CAdapterEepromWriteThread::Stop(void)
735{
736 StopThread(-1);
737 {
738 CLockObject lock(m_mutex);
739 if (m_iScheduleEepromWrite > 0)
740 m_com->LIB_CEC->AddLog(CEC_LOG_WARNING, "write thread stopped while a write was queued");
741 m_condition.Signal();
742 }
743 StopThread();
744}
745
746void *CAdapterEepromWriteThread::Process(void)
747{
748 while (!IsStopped())
749 {
750 CLockObject lock(m_mutex);
751 if ((m_iScheduleEepromWrite > 0 && m_iScheduleEepromWrite < GetTimeMs()) ||
752 m_condition.Wait(m_mutex, m_bWrite, 100))
753 {
754 m_bWrite = false;
755 if (m_com->m_commands->WriteEEPROM())
756 {
757 m_iLastEepromWrite = GetTimeMs();
758 m_iScheduleEepromWrite = 0;
759 }
760 else
761 {
762 m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY;
763 }
764 }
56e53c14
LOK
765 }
766 return NULL;
767}
3ead056c
LOK
768
769bool CAdapterEepromWriteThread::Write(void)
770{
771 CLockObject lock(m_mutex);
772 if (m_iScheduleEepromWrite == 0)
773 {
774 int64_t iNow = GetTimeMs();
775 if (m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL > iNow)
776 {
777 m_com->LIB_CEC->AddLog(CEC_LOG_DEBUG, "delaying eeprom write by %ld ms", m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL - iNow);
778 m_iScheduleEepromWrite = m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL;
779 }
780 else
781 {
782 m_bWrite = true;
783 m_condition.Signal();
784 }
785 }
786 return true;
787}