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