added protection against standby without a notification from XBMC and clock changes...
[deb_libcec.git] / src / lib / adapter / Pulse-Eight / USBCECAdapterCommunication.cpp
1 /*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved.
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
33 #include "env.h"
34 #include "USBCECAdapterCommunication.h"
35
36 #include "USBCECAdapterCommands.h"
37 #include "USBCECAdapterMessageQueue.h"
38 #include "USBCECAdapterMessage.h"
39 #include "USBCECAdapterDetection.h"
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"
48
49 using namespace std;
50 using namespace CEC;
51 using namespace PLATFORM;
52
53 #define CEC_ADAPTER_PING_TIMEOUT 15000
54 #define CEC_ADAPTER_EEPROM_WRITE_INTERVAL 30000
55 #define CEC_ADAPTER_EEPROM_WRITE_RETRY 5000
56
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
61
62 #define CEC_FW_DATE_EXTENDED_RESPONSE 0x501a4b0c
63 #define CEC_FW_DATE_DESCRIPTOR2 0x5045dbf5
64
65 #define LIB_CEC m_callback->GetLib()
66
67 CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */) :
68 IAdapterCommunication(callback),
69 m_port(NULL),
70 m_iLineTimeout(0),
71 m_lastPollDestination(CECDEVICE_UNKNOWN),
72 m_bInitialised(false),
73 m_pingThread(NULL),
74 m_eepromWriteThread(NULL),
75 m_commands(NULL),
76 m_adapterMessageQueue(NULL)
77 {
78 m_logicalAddresses.Clear();
79 for (unsigned int iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
80 m_bWaitingForAck[iPtr] = false;
81 m_port = new CSerialPort(strPort, iBaudRate);
82 m_commands = new CUSBCECAdapterCommands(this);
83 }
84
85 CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void)
86 {
87 Close();
88 DELETE_AND_NULL(m_commands);
89 DELETE_AND_NULL(m_adapterMessageQueue);
90 DELETE_AND_NULL(m_port);
91 }
92
93 void CUSBCECAdapterCommunication::ResetMessageQueue(void)
94 {
95 DELETE_AND_NULL(m_adapterMessageQueue);
96 m_adapterMessageQueue = new CCECAdapterMessageQueue(this);
97 m_adapterMessageQueue->CreateThread();
98 }
99
100 bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool bSkipChecks /* = false */, bool bStartListening /* = true */)
101 {
102 bool bConnectionOpened(false);
103 {
104 CLockObject lock(m_mutex);
105
106 /* we need the port settings here */
107 if (!m_port)
108 {
109 LIB_CEC->AddLog(CEC_LOG_ERROR, "port is NULL");
110 return bConnectionOpened;
111 }
112
113 /* return true when the port is already open */
114 if (IsOpen())
115 {
116 LIB_CEC->AddLog(CEC_LOG_WARNING, "port is already open");
117 return true;
118 }
119
120 ResetMessageQueue();
121
122 /* try to open the connection */
123 CStdString strError;
124 CTimeout timeout(iTimeoutMs);
125 while (!bConnectionOpened && timeout.TimeLeft() > 0)
126 {
127 if ((bConnectionOpened = m_port->Open(timeout.TimeLeft())) == false)
128 {
129 strError.Format("error opening serial port '%s': %s", m_port->GetName().c_str(), m_port->GetError().c_str());
130 Sleep(250);
131 }
132 /* and retry every 250ms until the timeout passed */
133 }
134
135 /* return false when we couldn't connect */
136 if (!bConnectionOpened)
137 {
138 LIB_CEC->AddLog(CEC_LOG_ERROR, strError);
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 }
154 return false;
155 }
156
157 LIB_CEC->AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
158 ClearInputBytes();
159 }
160
161 // always start by setting the ackmask to 0, to clear previous values
162 cec_logical_addresses addresses; addresses.Clear();
163 SetLogicalAddresses(addresses);
164
165 if (!CreateThread())
166 {
167 bConnectionOpened = false;
168 LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a communication thread");
169 }
170 else if (!bSkipChecks && !CheckAdapter())
171 {
172 bConnectionOpened = false;
173 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
174 }
175 else if (bStartListening)
176 {
177 /* start the eeprom write thread, that handles all eeprom writes async */
178 m_eepromWriteThread = new CAdapterEepromWriteThread(this);
179 if (!m_eepromWriteThread->CreateThread())
180 {
181 bConnectionOpened = false;
182 LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create the eeprom write thread");
183 }
184 else
185 {
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 }
198 }
199 }
200
201 if (!bConnectionOpened || !bStartListening)
202 StopThread(0);
203
204 return bConnectionOpened;
205 }
206
207 void CUSBCECAdapterCommunication::Close(void)
208 {
209 /* stop the reader thread */
210 StopThread(0);
211
212 CLockObject lock(m_mutex);
213
214 /* set the ackmask to 0 before closing the connection */
215 if (IsOpen() && m_port->GetErrorNumber() == 0)
216 {
217 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - closing the connection", __FUNCTION__);
218 cec_logical_addresses addresses; addresses.Clear();
219 SetLogicalAddresses(addresses);
220 if (m_commands->GetFirmwareVersion() >= 2)
221 SetControlledMode(false);
222 }
223
224 m_adapterMessageQueue->Clear();
225
226 /* stop and delete the write thread */
227 if (m_eepromWriteThread)
228 m_eepromWriteThread->Stop();
229 DELETE_AND_NULL(m_eepromWriteThread);
230
231 /* stop and delete the ping thread */
232 DELETE_AND_NULL(m_pingThread);
233
234 /* close and delete the com port connection */
235 if (m_port)
236 m_port->Close();
237 }
238
239 cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout, bool bIsReply)
240 {
241 cec_adapter_message_state retVal(ADAPTER_MESSAGE_STATE_UNKNOWN);
242 if (!IsRunning())
243 return retVal;
244
245 CCECAdapterMessage *output = new CCECAdapterMessage(data, iLineTimeout);
246 output->bFireAndForget = bIsReply;
247
248 /* mark as waiting for an ack from the destination */
249 MarkAsWaiting(data.destination);
250
251 /* send the message */
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;
263
264 delete output;
265 }
266 return retVal;
267 }
268
269 void *CUSBCECAdapterCommunication::Process(void)
270 {
271 CCECAdapterMessage msg;
272 LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread started");
273
274 while (!IsStopped())
275 {
276 /* read from the serial port */
277 if (!ReadFromDevice(50, 5))
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
283 break;
284 }
285
286 /* TODO sleep 5 ms so other threads can get a lock */
287 if (!IsStopped())
288 Sleep(5);
289 }
290
291 m_adapterMessageQueue->Clear();
292 LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread ended");
293 return NULL;
294 }
295
296 bool CUSBCECAdapterCommunication::HandlePoll(const CCECAdapterMessage &msg)
297 {
298 bool bIsError(msg.IsError());
299 cec_adapter_messagecode messageCode(msg.Message());
300 CLockObject lock(m_mutex);
301
302 if (messageCode == MSGCODE_FRAME_START && msg.IsACK())
303 {
304 m_lastPollDestination = msg.Destination();
305 if (msg.Destination() < CECDEVICE_BROADCAST)
306 {
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;
314 }
315 }
316 else if (messageCode == MSGCODE_RECEIVE_FAILED)
317 {
318 /* hack to suppress warnings when an LG is polling */
319 if (m_lastPollDestination != CECDEVICE_UNKNOWN)
320 bIsError = m_callback->HandleReceiveFailed(m_lastPollDestination);
321 }
322
323 return bIsError;
324 }
325
326 void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest)
327 {
328 /* mark as waiting for an ack from the destination */
329 if (dest < CECDEVICE_BROADCAST)
330 {
331 CLockObject lock(m_mutex);
332 m_bWaitingForAck[dest] = true;
333 }
334 }
335
336 void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = CEC_CLEAR_INPUT_DEFAULT_WAIT */)
337 {
338 CTimeout timeout(iTimeout);
339 uint8_t buff[1024];
340 ssize_t iBytesRead(0);
341 bool bGotMsgEnd(true);
342
343 while (timeout.TimeLeft() > 0 && ((iBytesRead = m_port->Read(buff, 1024, 5)) > 0 || !bGotMsgEnd))
344 {
345 bGotMsgEnd = false;
346 /* if something was received, wait for MSGEND */
347 for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
348 bGotMsgEnd = buff[iPtr] == MSGEND;
349 }
350 }
351
352 bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
353 {
354 bool bReturn(true);
355 bool bChanged(false);
356
357 /* only send the command if the timeout changed */
358 {
359 CLockObject lock(m_mutex);
360 bChanged = (m_iLineTimeout != iTimeout);
361 m_iLineTimeout = iTimeout;
362 }
363
364 if (bChanged)
365 bReturn = m_commands->SetLineTimeout(iTimeout);
366
367 return bReturn;
368 }
369
370 bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
371 {
372 CLockObject adapterLock(m_mutex);
373 if (!IsOpen())
374 {
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());
376 message->state = ADAPTER_MESSAGE_STATE_ERROR;
377 return false;
378 }
379
380 /* write the message */
381 if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
382 {
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());
384 message->state = ADAPTER_MESSAGE_STATE_ERROR;
385 // let the higher level close the port
386 return false;
387 }
388
389 #ifdef CEC_DEBUGGING
390 LIB_CEC->AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
391 #endif
392 message->state = ADAPTER_MESSAGE_STATE_SENT;
393 return true;
394 }
395
396 bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
397 {
398 ssize_t iBytesRead(0);
399 uint8_t buff[256];
400 if (iSize > 256)
401 iSize = 256;
402
403 /* read from the serial port */
404 {
405 CLockObject lock(m_mutex);
406 if (!IsOpen())
407 return false;
408
409 do {
410 /* retry Read() if it was interrupted */
411 iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
412 } while(m_port->GetErrorNumber() == EINTR);
413
414
415 if (m_port->GetErrorNumber())
416 {
417 LIB_CEC->AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
418 // let the higher level close the port
419 return false;
420 }
421 }
422
423 if (iBytesRead < 0 || iBytesRead > 256)
424 return false;
425 else if (iBytesRead > 0)
426 {
427 /* add the data to the current frame */
428 m_adapterMessageQueue->AddData(buff, iBytesRead);
429 }
430
431 return true;
432 }
433
434 CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry /* = false */)
435 {
436 if (!IsOpen() || !m_adapterMessageQueue)
437 return NULL;
438
439 /* create the adapter message for this command */
440 CCECAdapterMessage *output = new CCECAdapterMessage;
441 output->PushBack(MSGSTART);
442 output->PushEscaped((uint8_t)msgCode);
443 output->Append(params);
444 output->PushBack(MSGEND);
445
446 /* write the command */
447 if (!m_adapterMessageQueue->Write(output))
448 {
449 // this will trigger an alert in the reader thread
450 if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
451 m_port->Close();
452 return output;
453 }
454 else
455 {
456 if (!bIsRetry && output->Reply() == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED &&
457 msgCode != MSGCODE_GET_BUILDDATE /* same messagecode value had a different meaning in older fw builds */)
458 {
459 /* if the controller reported that the command was rejected, and we didn't send the command
460 to set controlled mode, then the controller probably switched to auto mode. set controlled
461 mode and retry */
462 LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
463 delete output;
464 if (SetControlledMode(true))
465 return SendCommand(msgCode, params, true);
466 }
467 }
468
469 return output;
470 }
471
472 bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
473 {
474 bool bReturn(false);
475 CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
476
477 /* try to ping the adapter */
478 bool bPinged(false);
479 unsigned iPingTry(0);
480 while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
481 {
482 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
483 CEvent::Sleep(500);
484 }
485
486 /* try to read the firmware version */
487 if (bPinged && timeout.TimeLeft() > 0 && m_commands->RequestFirmwareVersion() >= 2)
488 {
489 /* try to set controlled mode for v2+ firmwares */
490 unsigned iControlledTry(0);
491 bool bControlled(false);
492 while (timeout.TimeLeft() > 0 && (bControlled = SetControlledMode(true)) == false)
493 {
494 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
495 CEvent::Sleep(500);
496 }
497 bReturn = bControlled;
498 }
499 else
500 bReturn = true;
501
502 if (m_commands->GetFirmwareVersion() >= 2)
503 {
504 /* try to read the build date */
505 m_commands->RequestBuildDate();
506
507 /* try to read the adapter type */
508 m_commands->RequestAdapterType();
509 }
510
511 SetInitialised(bReturn);
512 return bReturn;
513 }
514
515 bool CUSBCECAdapterCommunication::IsOpen(void)
516 {
517 /* thread is not being stopped, the port is open and the thread is running */
518 return !IsStopped() && m_port->IsOpen() && IsRunning();
519 }
520
521 std::string CUSBCECAdapterCommunication::GetError(void) const
522 {
523 return m_port->GetError();
524 }
525
526 void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo /* = true */)
527 {
528 CLockObject lock(m_mutex);
529 m_bInitialised = bSetTo;
530 }
531
532 bool CUSBCECAdapterCommunication::IsInitialised(void)
533 {
534 CLockObject lock(m_mutex);
535 return m_bInitialised;
536 }
537
538 bool CUSBCECAdapterCommunication::StartBootloader(void)
539 {
540 if (m_port->IsOpen() && m_commands->StartBootloader())
541 {
542 m_port->Close();
543 return true;
544 }
545 return false;
546 }
547
548 bool CUSBCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses)
549 {
550 {
551 CLockObject lock(m_mutex);
552 if (m_logicalAddresses == addresses)
553 return true;
554 }
555
556 if (IsOpen() && m_commands->SetAckMask(addresses.AckMask()))
557 {
558 CLockObject lock(m_mutex);
559 m_logicalAddresses = addresses;
560 return true;
561 }
562
563 LIB_CEC->AddLog(CEC_LOG_DEBUG, "couldn't change the ackmask: the connection is closed");
564 return false;
565 }
566
567 cec_logical_addresses CUSBCECAdapterCommunication::GetLogicalAddresses(void)
568 {
569 cec_logical_addresses addresses;
570 CLockObject lock(m_mutex);
571 addresses = m_logicalAddresses;
572 return addresses;
573 }
574
575 bool CUSBCECAdapterCommunication::PingAdapter(void)
576 {
577 return IsOpen() ? m_commands->PingAdapter() : false;
578 }
579
580 uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
581 {
582 return m_commands ? m_commands->GetFirmwareVersion() : CEC_FW_VERSION_UNKNOWN;
583 }
584
585 uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
586 {
587 uint32_t iBuildDate(0);
588 if (m_commands)
589 iBuildDate = m_commands->GetPersistedBuildDate();
590 if (iBuildDate == 0 && IsOpen())
591 iBuildDate = m_commands->RequestBuildDate();
592
593 return iBuildDate;
594 }
595
596 cec_adapter_type CUSBCECAdapterCommunication::GetAdapterType(void)
597 {
598 cec_adapter_type type(ADAPTERTYPE_UNKNOWN);
599 if (m_commands)
600 type = (cec_adapter_type)m_commands->GetPersistedAdapterType();
601 if (type == ADAPTERTYPE_UNKNOWN && IsOpen())
602 type = (cec_adapter_type)m_commands->RequestAdapterType();
603
604 return type;
605 }
606
607 bool CUSBCECAdapterCommunication::ProvidesExtendedResponse(void)
608 {
609 uint32_t iBuildDate(0);
610 if (m_commands)
611 iBuildDate = m_commands->GetPersistedBuildDate();
612
613 return iBuildDate >= CEC_FW_DATE_EXTENDED_RESPONSE;
614 }
615
616 uint16_t CUSBCECAdapterCommunication::GetAdapterVendorId(void) const
617 {
618 return CEC_VID;
619 }
620
621 uint16_t CUSBCECAdapterCommunication::GetAdapterProductId(void) const
622 {
623 uint32_t iBuildDate(0);
624 if (m_commands)
625 iBuildDate = m_commands->GetPersistedBuildDate();
626
627 return iBuildDate >= CEC_FW_DATE_DESCRIPTOR2 ? CEC_PID2 : CEC_PID;
628 }
629
630 void CUSBCECAdapterCommunication::SetActiveSource(bool bSetTo, bool bClientUnregistered)
631 {
632 if (m_commands)
633 m_commands->SetActiveSource(bSetTo, bClientUnregistered);
634 }
635
636 bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
637 {
638 return GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE &&
639 GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION;
640 }
641
642 bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration)
643 {
644 return IsOpen() ?
645 m_commands->PersistConfiguration(configuration) && m_eepromWriteThread->Write() :
646 false;
647 }
648
649 bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration)
650 {
651 return IsOpen() ? m_commands->GetConfiguration(configuration) : false;
652 }
653
654 std::string CUSBCECAdapterCommunication::GetPortName(void)
655 {
656 return m_port->GetName();
657 }
658
659 bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
660 {
661 return IsOpen() ? m_commands->SetControlledMode(controlled) : false;
662 }
663
664 uint16_t CUSBCECAdapterCommunication::GetPhysicalAddress(void)
665 {
666 uint16_t iPA(0);
667
668 // try to get the PA from ADL
669 #if defined(HAS_ADL_EDID_PARSER)
670 {
671 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via ADL", __FUNCTION__);
672 CADLEdidParser adl;
673 iPA = adl.GetPhysicalAddress();
674 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - ADL returned physical address %04x", __FUNCTION__, iPA);
675 }
676 #endif
677
678 // try to get the PA from the nvidia driver
679 #if defined(HAS_NVIDIA_EDID_PARSER)
680 if (iPA == 0)
681 {
682 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via nvidia driver", __FUNCTION__);
683 CNVEdidParser nv;
684 iPA = nv.GetPhysicalAddress();
685 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - nvidia driver returned physical address %04x", __FUNCTION__, iPA);
686 }
687 #endif
688
689 // try to get the PA from the OS
690 if (iPA == 0)
691 {
692 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address from the OS", __FUNCTION__);
693 iPA = CEDIDParser::GetPhysicalAddress();
694 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - OS returned physical address %04x", __FUNCTION__, iPA);
695 }
696
697 return iPA;
698 }
699
700 void *CAdapterPingThread::Process(void)
701 {
702 while (!IsStopped())
703 {
704 if (m_timeout.TimeLeft() == 0)
705 {
706 /* reinit the timeout */
707 m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
708
709 /* send a ping to the adapter */
710 bool bPinged(false);
711 int iFailedCounter(0);
712 while (!bPinged && iFailedCounter < 3)
713 {
714 if (!m_com->PingAdapter())
715 {
716 /* sleep and retry */
717 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
718 ++iFailedCounter;
719 }
720 else
721 {
722 bPinged = true;
723 }
724 }
725
726 if (iFailedCounter == 3)
727 {
728 /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
729 m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
730 m_com->StopThread(false);
731
732 libcec_parameter param;
733 param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN;
734 m_com->LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param);
735
736 break;
737 }
738 }
739
740 Sleep(5);
741 }
742 return NULL;
743 }
744
745 void CAdapterEepromWriteThread::Stop(void)
746 {
747 StopThread(-1);
748 {
749 CLockObject lock(m_mutex);
750 if (m_iScheduleEepromWrite > 0)
751 m_com->LIB_CEC->AddLog(CEC_LOG_WARNING, "write thread stopped while a write was queued");
752 m_bWrite = true;
753 m_condition.Signal();
754 }
755 StopThread();
756 }
757
758 void *CAdapterEepromWriteThread::Process(void)
759 {
760 while (!IsStopped())
761 {
762 CLockObject lock(m_mutex);
763 if ((m_iScheduleEepromWrite > 0 && m_iScheduleEepromWrite < GetTimeMs()) ||
764 m_condition.Wait(m_mutex, m_bWrite, 100))
765 {
766 if (IsStopped())
767 break;
768 m_bWrite = false;
769 if (m_com->m_commands->WriteEEPROM())
770 {
771 m_iLastEepromWrite = GetTimeMs();
772 m_iScheduleEepromWrite = 0;
773 }
774 else
775 {
776 m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY;
777 }
778 }
779 }
780 return NULL;
781 }
782
783 bool CAdapterEepromWriteThread::Write(void)
784 {
785 CLockObject lock(m_mutex);
786 if (m_iScheduleEepromWrite == 0)
787 {
788 int64_t iNow = GetTimeMs();
789 if (m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL > iNow)
790 {
791 m_com->LIB_CEC->AddLog(CEC_LOG_DEBUG, "delaying eeprom write by %ld ms", m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL - iNow);
792 m_iScheduleEepromWrite = m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL;
793 }
794 else
795 {
796 m_bWrite = true;
797 m_condition.Signal();
798 }
799 }
800 return true;
801 }