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