don't log every frame, unless debugging was enabled in ./configure or visual studio
[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
daec0320 237cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout, bool 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);
daec0320 244 output->bFireAndForget = bIsReply;
7bb4ed43 245
a75e3a5a
LOK
246 /* mark as waiting for an ack from the destination */
247 MarkAsWaiting(data.destination);
4164923b 248
a75e3a5a 249 /* send the message */
daec0320
LOK
250 if (bIsReply)
251 {
252 retVal = m_adapterMessageQueue->Write(output) ?
253 ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT : ADAPTER_MESSAGE_STATE_ERROR;
254 }
255 else
256 {
257 bRetry = (!m_adapterMessageQueue->Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
258 if (bRetry)
259 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
260 retVal = output->state;
7bb4ed43 261
daec0320
LOK
262 delete output;
263 }
7bb4ed43
LOK
264 return retVal;
265}
266
a75e3a5a 267void *CUSBCECAdapterCommunication::Process(void)
3c53ac93 268{
a75e3a5a 269 CCECAdapterMessage msg;
004b8382 270 LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread started");
5dcf9f25 271
a75e3a5a 272 while (!IsStopped())
5dcf9f25 273 {
a75e3a5a
LOK
274 /* read from the serial port */
275 if (!ReadFromDevice(50, 5))
2fbffb25
LOK
276 {
277 libcec_parameter param;
278 param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN;
279 LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param);
280
a75e3a5a 281 break;
2fbffb25 282 }
a75e3a5a
LOK
283
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
8cd2b85a 387#ifdef CEC_DEBUGGING
004b8382 388 LIB_CEC->AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
8cd2b85a 389#endif
a75e3a5a
LOK
390 message->state = ADAPTER_MESSAGE_STATE_SENT;
391 return true;
c214d197 392}
b057edad 393
a75e3a5a 394bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
12a36be9 395{
a75e3a5a
LOK
396 ssize_t iBytesRead(0);
397 uint8_t buff[256];
398 if (iSize > 256)
399 iSize = 256;
12a36be9 400
a75e3a5a 401 /* read from the serial port */
12a36be9 402 {
a75e3a5a 403 CLockObject lock(m_mutex);
0b8c7eab 404 if (!IsOpen())
a75e3a5a 405 return false;
a8559e01 406
1e597e6c
JF
407 do {
408 /* retry Read() if it was interrupted */
409 iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
410 } while(m_port->GetErrorNumber() == EINTR);
411
a8559e01
LOK
412
413 if (m_port->GetErrorNumber())
414 {
004b8382 415 LIB_CEC->AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
a8559e01
LOK
416 m_port->Close();
417 return false;
418 }
12a36be9
LOK
419 }
420
a75e3a5a 421 if (iBytesRead < 0 || iBytesRead > 256)
a75e3a5a 422 return false;
a75e3a5a 423 else if (iBytesRead > 0)
12a36be9 424 {
a75e3a5a
LOK
425 /* add the data to the current frame */
426 m_adapterMessageQueue->AddData(buff, iBytesRead);
12a36be9
LOK
427 }
428
a75e3a5a 429 return true;
b057edad
BL
430}
431
a75e3a5a 432CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry /* = false */)
c214d197 433{
814a902f 434 if (!IsOpen() || !m_adapterMessageQueue)
a75e3a5a 435 return NULL;
c214d197 436
a75e3a5a
LOK
437 /* create the adapter message for this command */
438 CCECAdapterMessage *output = new CCECAdapterMessage;
439 output->PushBack(MSGSTART);
440 output->PushEscaped((uint8_t)msgCode);
441 output->Append(params);
442 output->PushBack(MSGEND);
12a36be9 443
a75e3a5a
LOK
444 /* write the command */
445 if (!m_adapterMessageQueue->Write(output))
12a36be9 446 {
814a902f 447 // this will trigger an alert in the reader thread
0b714871 448 if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
814a902f 449 m_port->Close();
a75e3a5a 450 return output;
12a36be9 451 }
a75e3a5a 452 else
12a36be9 453 {
ee090252
LOK
454 if (!bIsRetry && output->Reply() == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED &&
455 msgCode != MSGCODE_GET_BUILDDATE /* same messagecode value had a different meaning in older fw builds */)
a75e3a5a
LOK
456 {
457 /* if the controller reported that the command was rejected, and we didn't send the command
458 to set controlled mode, then the controller probably switched to auto mode. set controlled
459 mode and retry */
004b8382 460 LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
a75e3a5a
LOK
461 delete output;
462 if (SetControlledMode(true))
463 return SendCommand(msgCode, params, true);
464 }
12a36be9 465 }
12a36be9 466
a75e3a5a 467 return output;
c214d197
LOK
468}
469
b32ffd87 470bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
12a36be9 471{
a75e3a5a
LOK
472 bool bReturn(false);
473 CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
12a36be9 474
a75e3a5a
LOK
475 /* try to ping the adapter */
476 bool bPinged(false);
477 unsigned iPingTry(0);
478 while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
12a36be9 479 {
004b8382 480 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
a75e3a5a 481 CEvent::Sleep(500);
12a36be9 482 }
c214d197 483
a75e3a5a
LOK
484 /* try to read the firmware version */
485 if (bPinged && timeout.TimeLeft() > 0 && m_commands->RequestFirmwareVersion() >= 2)
12a36be9 486 {
a75e3a5a
LOK
487 /* try to set controlled mode for v2+ firmwares */
488 unsigned iControlledTry(0);
489 bool bControlled(false);
490 while (timeout.TimeLeft() > 0 && (bControlled = SetControlledMode(true)) == false)
491 {
004b8382 492 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
a75e3a5a
LOK
493 CEvent::Sleep(500);
494 }
495 bReturn = bControlled;
12a36be9 496 }
a75e3a5a
LOK
497 else
498 bReturn = true;
c214d197 499
2d418322
LOK
500 if (m_commands->GetFirmwareVersion() >= 2)
501 {
502 /* try to read the build date */
503 m_commands->RequestBuildDate();
504
505 /* try to read the adapter type */
506 m_commands->RequestAdapterType();
507 }
baabc020 508
a75e3a5a
LOK
509 SetInitialised(bReturn);
510 return bReturn;
c214d197
LOK
511}
512
a75e3a5a 513bool CUSBCECAdapterCommunication::IsOpen(void)
12a36be9 514{
a75e3a5a
LOK
515 /* thread is not being stopped, the port is open and the thread is running */
516 return !IsStopped() && m_port->IsOpen() && IsRunning();
12a36be9
LOK
517}
518
2b44051c 519std::string CUSBCECAdapterCommunication::GetError(void) const
c214d197 520{
a75e3a5a 521 return m_port->GetError();
c214d197
LOK
522}
523
a75e3a5a 524void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo /* = true */)
12a36be9
LOK
525{
526 CLockObject lock(m_mutex);
a75e3a5a 527 m_bInitialised = bSetTo;
12a36be9
LOK
528}
529
a75e3a5a 530bool CUSBCECAdapterCommunication::IsInitialised(void)
c214d197
LOK
531{
532 CLockObject lock(m_mutex);
a75e3a5a 533 return m_bInitialised;
c214d197
LOK
534}
535
a75e3a5a 536bool CUSBCECAdapterCommunication::StartBootloader(void)
12a36be9 537{
55c75e6e
LOK
538 if (m_port->IsOpen() && m_commands->StartBootloader())
539 {
65108638 540 m_port->Close();
55c75e6e
LOK
541 return true;
542 }
543 return false;
12a36be9
LOK
544}
545
2b44051c 546bool CUSBCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses)
13fd6a66 547{
c6f01e11
LOK
548 {
549 CLockObject lock(m_mutex);
2b44051c 550 if (m_logicalAddresses == addresses)
c6f01e11
LOK
551 return true;
552 }
004b8382 553
2b44051c 554 if (IsOpen() && m_commands->SetAckMask(addresses.AckMask()))
004b8382 555 {
c6f01e11 556 CLockObject lock(m_mutex);
2b44051c 557 m_logicalAddresses = addresses;
004b8382
LOK
558 return true;
559 }
560
d78e4e6f 561 LIB_CEC->AddLog(CEC_LOG_DEBUG, "couldn't change the ackmask: the connection is closed");
004b8382
LOK
562 return false;
563}
564
2b44051c 565cec_logical_addresses CUSBCECAdapterCommunication::GetLogicalAddresses(void)
004b8382 566{
2b44051c 567 cec_logical_addresses addresses;
c6f01e11 568 CLockObject lock(m_mutex);
2b44051c
LOK
569 addresses = m_logicalAddresses;
570 return addresses;
13fd6a66 571}
ef7696f5 572
a75e3a5a 573bool CUSBCECAdapterCommunication::PingAdapter(void)
6729ac71 574{
0b8c7eab 575 return IsOpen() ? m_commands->PingAdapter() : false;
6729ac71
LOK
576}
577
a75e3a5a 578uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
ef7696f5 579{
5943b9d1 580 return m_commands ? m_commands->GetFirmwareVersion() : CEC_FW_VERSION_UNKNOWN;
ef7696f5
LOK
581}
582
b2f56d35
LOK
583uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
584{
3ead056c
LOK
585 uint32_t iBuildDate(0);
586 if (m_commands)
587 iBuildDate = m_commands->GetPersistedBuildDate();
588 if (iBuildDate == 0 && IsOpen())
589 iBuildDate = m_commands->RequestBuildDate();
590
591 return iBuildDate;
592}
593
2d418322
LOK
594cec_adapter_type CUSBCECAdapterCommunication::GetAdapterType(void)
595{
596 cec_adapter_type type(ADAPTERTYPE_UNKNOWN);
597 if (m_commands)
598 type = (cec_adapter_type)m_commands->GetPersistedAdapterType();
599 if (type == ADAPTERTYPE_UNKNOWN && IsOpen())
600 type = (cec_adapter_type)m_commands->RequestAdapterType();
601
602 return type;
603}
604
3ead056c
LOK
605bool CUSBCECAdapterCommunication::ProvidesExtendedResponse(void)
606{
607 uint32_t iBuildDate(0);
608 if (m_commands)
609 iBuildDate = m_commands->GetPersistedBuildDate();
610
611 return iBuildDate >= CEC_FW_DATE_EXTENDED_RESPONSE;
b2f56d35
LOK
612}
613
5daed059
LOK
614bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
615{
3ead056c
LOK
616 return GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE &&
617 GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION;
5daed059
LOK
618}
619
c0152c09 620bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration)
ef7696f5 621{
3ead056c
LOK
622 return IsOpen() ?
623 m_commands->PersistConfiguration(configuration) && m_eepromWriteThread->Write() :
624 false;
ef7696f5
LOK
625}
626
c0152c09 627bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration)
ef7696f5 628{
0b8c7eab 629 return IsOpen() ? m_commands->GetConfiguration(configuration) : false;
ef7696f5
LOK
630}
631
2b44051c 632std::string CUSBCECAdapterCommunication::GetPortName(void)
cba904a6 633{
a75e3a5a 634 return m_port->GetName();
c9c282a4 635}
d4db0c6f 636
a75e3a5a 637bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
d4db0c6f 638{
0b8c7eab 639 return IsOpen() ? m_commands->SetControlledMode(controlled) : false;
d4db0c6f 640}
56e53c14 641
200322e5
LOK
642uint16_t CUSBCECAdapterCommunication::GetPhysicalAddress(void)
643{
644 uint16_t iPA(0);
645
646 // try to get the PA from ADL
647#if defined(HAS_ADL_EDID_PARSER)
648 {
649 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via ADL", __FUNCTION__);
650 CADLEdidParser adl;
651 iPA = adl.GetPhysicalAddress();
652 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - ADL returned physical address %04x", __FUNCTION__, iPA);
653 }
654#endif
655
cebbfe03
LOK
656 // try to get the PA from the nvidia driver
657#if defined(HAS_NVIDIA_EDID_PARSER)
658 if (iPA == 0)
659 {
660 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via nvidia driver", __FUNCTION__);
661 CNVEdidParser nv;
662 iPA = nv.GetPhysicalAddress();
663 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - nvidia driver returned physical address %04x", __FUNCTION__, iPA);
664 }
665#endif
666
200322e5
LOK
667 // try to get the PA from the OS
668 if (iPA == 0)
669 {
670 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address from the OS", __FUNCTION__);
671 iPA = CEDIDParser::GetPhysicalAddress();
672 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - OS returned physical address %04x", __FUNCTION__, iPA);
673 }
674
675 return iPA;
676}
677
56e53c14
LOK
678void *CAdapterPingThread::Process(void)
679{
680 while (!IsStopped())
681 {
682 if (m_timeout.TimeLeft() == 0)
683 {
a75e3a5a 684 /* reinit the timeout */
56e53c14 685 m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
a75e3a5a
LOK
686
687 /* send a ping to the adapter */
688 bool bPinged(false);
689 int iFailedCounter(0);
690 while (!bPinged && iFailedCounter < 3)
691 {
692 if (!m_com->PingAdapter())
693 {
b32ffd87
LOK
694 /* sleep and retry */
695 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
a75e3a5a
LOK
696 ++iFailedCounter;
697 }
698 else
699 {
700 bPinged = true;
701 }
702 }
703
704 if (iFailedCounter == 3)
705 {
706 /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
004b8382 707 m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
a75e3a5a
LOK
708 m_com->StopThread(false);
709 break;
710 }
56e53c14
LOK
711 }
712
3ead056c
LOK
713 Sleep(5);
714 }
715 return NULL;
716}
717
718void CAdapterEepromWriteThread::Stop(void)
719{
720 StopThread(-1);
721 {
722 CLockObject lock(m_mutex);
723 if (m_iScheduleEepromWrite > 0)
724 m_com->LIB_CEC->AddLog(CEC_LOG_WARNING, "write thread stopped while a write was queued");
725 m_condition.Signal();
726 }
727 StopThread();
728}
729
730void *CAdapterEepromWriteThread::Process(void)
731{
732 while (!IsStopped())
733 {
734 CLockObject lock(m_mutex);
735 if ((m_iScheduleEepromWrite > 0 && m_iScheduleEepromWrite < GetTimeMs()) ||
736 m_condition.Wait(m_mutex, m_bWrite, 100))
737 {
738 m_bWrite = false;
739 if (m_com->m_commands->WriteEEPROM())
740 {
741 m_iLastEepromWrite = GetTimeMs();
742 m_iScheduleEepromWrite = 0;
743 }
744 else
745 {
746 m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY;
747 }
748 }
56e53c14
LOK
749 }
750 return NULL;
751}
3ead056c
LOK
752
753bool CAdapterEepromWriteThread::Write(void)
754{
755 CLockObject lock(m_mutex);
756 if (m_iScheduleEepromWrite == 0)
757 {
758 int64_t iNow = GetTimeMs();
759 if (m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL > iNow)
760 {
761 m_com->LIB_CEC->AddLog(CEC_LOG_DEBUG, "delaying eeprom write by %ld ms", m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL - iNow);
762 m_iScheduleEepromWrite = m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL;
763 }
764 else
765 {
766 m_bWrite = true;
767 m_condition.Signal();
768 }
769 }
770 return true;
771}