Release 2.2.0
[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 {
f7773e40 307 CLockObject waitingLock(m_waitingMutex);
a75e3a5a
LOK
308 if (!m_bWaitingForAck[msg.Destination()] && !msg.IsEOM())
309 {
310 if (m_callback)
311 m_callback->HandlePoll(msg.Initiator(), msg.Destination());
312 }
313 else
314 m_bWaitingForAck[msg.Destination()] = false;
a8f0bd18 315 }
7bb4ed43 316 }
a75e3a5a 317 else if (messageCode == MSGCODE_RECEIVE_FAILED)
7bb4ed43 318 {
a75e3a5a
LOK
319 /* hack to suppress warnings when an LG is polling */
320 if (m_lastPollDestination != CECDEVICE_UNKNOWN)
321 bIsError = m_callback->HandleReceiveFailed(m_lastPollDestination);
7bb4ed43 322 }
a8f0bd18 323
a75e3a5a 324 return bIsError;
a8f0bd18 325}
2abe74eb 326
a75e3a5a 327void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest)
2abe74eb 328{
a75e3a5a
LOK
329 /* mark as waiting for an ack from the destination */
330 if (dest < CECDEVICE_BROADCAST)
7bb4ed43 331 {
f7773e40 332 CLockObject waitingLock(m_waitingMutex);
a75e3a5a 333 m_bWaitingForAck[dest] = true;
7bb4ed43 334 }
7bb4ed43
LOK
335}
336
b32ffd87 337void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = CEC_CLEAR_INPUT_DEFAULT_WAIT */)
1fc16cfd 338{
a75e3a5a
LOK
339 CTimeout timeout(iTimeout);
340 uint8_t buff[1024];
341 ssize_t iBytesRead(0);
a4d657c7 342 bool bGotMsgEnd(true);
1fc16cfd 343
a75e3a5a 344 while (timeout.TimeLeft() > 0 && ((iBytesRead = m_port->Read(buff, 1024, 5)) > 0 || !bGotMsgEnd))
1fc16cfd 345 {
a4d657c7 346 bGotMsgEnd = false;
a75e3a5a
LOK
347 /* if something was received, wait for MSGEND */
348 for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
349 bGotMsgEnd = buff[iPtr] == MSGEND;
1fc16cfd 350 }
1fc16cfd
LOK
351}
352
7bb4ed43 353bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
a171d2fd 354{
16459df9 355 bool bReturn(true);
a75e3a5a 356 bool bChanged(false);
089f0e9d 357
a75e3a5a 358 /* only send the command if the timeout changed */
089f0e9d 359 {
a75e3a5a
LOK
360 CLockObject lock(m_mutex);
361 bChanged = (m_iLineTimeout != iTimeout);
362 m_iLineTimeout = iTimeout;
089f0e9d
LOK
363 }
364
a75e3a5a
LOK
365 if (bChanged)
366 bReturn = m_commands->SetLineTimeout(iTimeout);
a171d2fd 367
a75e3a5a 368 return bReturn;
f9e01dac
LOK
369}
370
a75e3a5a 371bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
5dcf9f25 372{
a75e3a5a 373 CLockObject adapterLock(m_mutex);
0b8c7eab 374 if (!IsOpen())
a75e3a5a 375 {
004b8382 376 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
377 message->state = ADAPTER_MESSAGE_STATE_ERROR;
378 return false;
379 }
5dcf9f25 380
a75e3a5a
LOK
381 /* write the message */
382 if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
383 {
004b8382 384 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 385 message->state = ADAPTER_MESSAGE_STATE_ERROR;
f7539eaf 386 // let the higher level close the port
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());
f7539eaf 419 // let the higher level close the port
a8559e01
LOK
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);
9be2206f 713 while (!bPinged && iFailedCounter < 3 && !IsStopped())
a75e3a5a
LOK
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
9be2206f 727 if (iFailedCounter == 3 && !IsStopped())
a75e3a5a
LOK
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 731 m_com->StopThread(false);
f7539eaf
LOK
732
733 libcec_parameter param;
734 param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN;
735 m_com->LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param);
736
a75e3a5a
LOK
737 break;
738 }
56e53c14
LOK
739 }
740
3ead056c
LOK
741 Sleep(5);
742 }
743 return NULL;
744}
745
746void CAdapterEepromWriteThread::Stop(void)
747{
748 StopThread(-1);
749 {
750 CLockObject lock(m_mutex);
751 if (m_iScheduleEepromWrite > 0)
752 m_com->LIB_CEC->AddLog(CEC_LOG_WARNING, "write thread stopped while a write was queued");
aaff5cee 753 m_bWrite = true;
3ead056c
LOK
754 m_condition.Signal();
755 }
756 StopThread();
757}
758
759void *CAdapterEepromWriteThread::Process(void)
760{
761 while (!IsStopped())
762 {
763 CLockObject lock(m_mutex);
764 if ((m_iScheduleEepromWrite > 0 && m_iScheduleEepromWrite < GetTimeMs()) ||
765 m_condition.Wait(m_mutex, m_bWrite, 100))
766 {
aaff5cee
LOK
767 if (IsStopped())
768 break;
3ead056c
LOK
769 m_bWrite = false;
770 if (m_com->m_commands->WriteEEPROM())
771 {
772 m_iLastEepromWrite = GetTimeMs();
773 m_iScheduleEepromWrite = 0;
774 }
775 else
776 {
777 m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY;
778 }
779 }
56e53c14
LOK
780 }
781 return NULL;
782}
3ead056c
LOK
783
784bool CAdapterEepromWriteThread::Write(void)
785{
786 CLockObject lock(m_mutex);
787 if (m_iScheduleEepromWrite == 0)
788 {
789 int64_t iNow = GetTimeMs();
790 if (m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL > iNow)
791 {
792 m_com->LIB_CEC->AddLog(CEC_LOG_DEBUG, "delaying eeprom write by %ld ms", m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL - iNow);
793 m_iScheduleEepromWrite = m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL;
794 }
795 else
796 {
797 m_bWrite = true;
798 m_condition.Signal();
799 }
800 }
801 return true;
802}