Imported Upstream version 2.2.0
[deb_libcec.git] / src / lib / adapter / Pulse-Eight / USBCECAdapterCommunication.cpp
CommitLineData
cbbe90dd
JB
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
49using namespace std;
50using namespace CEC;
51using 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
67CUSBCECAdapterCommunication::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
85CUSBCECAdapterCommunication::~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
93void CUSBCECAdapterCommunication::ResetMessageQueue(void)
94{
95 DELETE_AND_NULL(m_adapterMessageQueue);
96 m_adapterMessageQueue = new CCECAdapterMessageQueue(this);
97 m_adapterMessageQueue->CreateThread();
98}
99
100bool 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
207void 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
239cec_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
269void *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
296bool 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 CLockObject waitingLock(m_waitingMutex);
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;
315 }
316 }
317 else if (messageCode == MSGCODE_RECEIVE_FAILED)
318 {
319 /* hack to suppress warnings when an LG is polling */
320 if (m_lastPollDestination != CECDEVICE_UNKNOWN)
321 bIsError = m_callback->HandleReceiveFailed(m_lastPollDestination);
322 }
323
324 return bIsError;
325}
326
327void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest)
328{
329 /* mark as waiting for an ack from the destination */
330 if (dest < CECDEVICE_BROADCAST)
331 {
332 CLockObject waitingLock(m_waitingMutex);
333 m_bWaitingForAck[dest] = true;
334 }
335}
336
337void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = CEC_CLEAR_INPUT_DEFAULT_WAIT */)
338{
339 CTimeout timeout(iTimeout);
340 uint8_t buff[1024];
341 ssize_t iBytesRead(0);
342 bool bGotMsgEnd(true);
343
344 while (timeout.TimeLeft() > 0 && ((iBytesRead = m_port->Read(buff, 1024, 5)) > 0 || !bGotMsgEnd))
345 {
346 bGotMsgEnd = false;
347 /* if something was received, wait for MSGEND */
348 for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
349 bGotMsgEnd = buff[iPtr] == MSGEND;
350 }
351}
352
353bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
354{
355 bool bReturn(true);
356 bool bChanged(false);
357
358 /* only send the command if the timeout changed */
359 {
360 CLockObject lock(m_mutex);
361 bChanged = (m_iLineTimeout != iTimeout);
362 m_iLineTimeout = iTimeout;
363 }
364
365 if (bChanged)
366 bReturn = m_commands->SetLineTimeout(iTimeout);
367
368 return bReturn;
369}
370
371bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
372{
373 CLockObject adapterLock(m_mutex);
374 if (!IsOpen())
375 {
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());
377 message->state = ADAPTER_MESSAGE_STATE_ERROR;
378 return false;
379 }
380
381 /* write the message */
382 if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
383 {
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());
385 message->state = ADAPTER_MESSAGE_STATE_ERROR;
386 // let the higher level close the port
387 return false;
388 }
389
390#ifdef CEC_DEBUGGING
391 LIB_CEC->AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
392#endif
393 message->state = ADAPTER_MESSAGE_STATE_SENT;
394 return true;
395}
396
397bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
398{
399 ssize_t iBytesRead(0);
400 uint8_t buff[256];
401 if (iSize > 256)
402 iSize = 256;
403
404 /* read from the serial port */
405 {
406 CLockObject lock(m_mutex);
407 if (!IsOpen())
408 return false;
409
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
415
416 if (m_port->GetErrorNumber())
417 {
418 LIB_CEC->AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
419 // let the higher level close the port
420 return false;
421 }
422 }
423
424 if (iBytesRead < 0 || iBytesRead > 256)
425 return false;
426 else if (iBytesRead > 0)
427 {
428 /* add the data to the current frame */
429 m_adapterMessageQueue->AddData(buff, iBytesRead);
430 }
431
432 return true;
433}
434
435CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry /* = false */)
436{
437 if (!IsOpen() || !m_adapterMessageQueue)
438 return NULL;
439
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);
446
447 /* write the command */
448 if (!m_adapterMessageQueue->Write(output))
449 {
450 // this will trigger an alert in the reader thread
451 if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
452 m_port->Close();
453 return output;
454 }
455 else
456 {
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 */)
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 */
463 LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
464 delete output;
465 if (SetControlledMode(true))
466 return SendCommand(msgCode, params, true);
467 }
468 }
469
470 return output;
471}
472
473bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
474{
475 bool bReturn(false);
476 CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
477
478 /* try to ping the adapter */
479 bool bPinged(false);
480 unsigned iPingTry(0);
481 while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
482 {
483 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
484 CEvent::Sleep(500);
485 }
486
487 /* try to read the firmware version */
488 if (bPinged && timeout.TimeLeft() > 0 && m_commands->RequestFirmwareVersion() >= 2)
489 {
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 {
495 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
496 CEvent::Sleep(500);
497 }
498 bReturn = bControlled;
499 }
500 else
501 bReturn = true;
502
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 }
511
512 SetInitialised(bReturn);
513 return bReturn;
514}
515
516bool CUSBCECAdapterCommunication::IsOpen(void)
517{
518 /* thread is not being stopped, the port is open and the thread is running */
519 return !IsStopped() && m_port->IsOpen() && IsRunning();
520}
521
522std::string CUSBCECAdapterCommunication::GetError(void) const
523{
524 return m_port->GetError();
525}
526
527void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo /* = true */)
528{
529 CLockObject lock(m_mutex);
530 m_bInitialised = bSetTo;
531}
532
533bool CUSBCECAdapterCommunication::IsInitialised(void)
534{
535 CLockObject lock(m_mutex);
536 return m_bInitialised;
537}
538
539bool CUSBCECAdapterCommunication::StartBootloader(void)
540{
541 if (m_port->IsOpen() && m_commands->StartBootloader())
542 {
543 m_port->Close();
544 return true;
545 }
546 return false;
547}
548
549bool CUSBCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses)
550{
551 {
552 CLockObject lock(m_mutex);
553 if (m_logicalAddresses == addresses)
554 return true;
555 }
556
557 if (IsOpen() && m_commands->SetAckMask(addresses.AckMask()))
558 {
559 CLockObject lock(m_mutex);
560 m_logicalAddresses = addresses;
561 return true;
562 }
563
564 LIB_CEC->AddLog(CEC_LOG_DEBUG, "couldn't change the ackmask: the connection is closed");
565 return false;
566}
567
568cec_logical_addresses CUSBCECAdapterCommunication::GetLogicalAddresses(void)
569{
570 cec_logical_addresses addresses;
571 CLockObject lock(m_mutex);
572 addresses = m_logicalAddresses;
573 return addresses;
574}
575
576bool CUSBCECAdapterCommunication::PingAdapter(void)
577{
578 return IsOpen() ? m_commands->PingAdapter() : false;
579}
580
581uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
582{
583 return m_commands ? m_commands->GetFirmwareVersion() : CEC_FW_VERSION_UNKNOWN;
584}
585
586uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
587{
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
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
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;
615}
616
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
631void CUSBCECAdapterCommunication::SetActiveSource(bool bSetTo, bool bClientUnregistered)
632{
633 if (m_commands)
634 m_commands->SetActiveSource(bSetTo, bClientUnregistered);
635}
636
637bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
638{
639 return GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE &&
640 GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION;
641}
642
643bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration)
644{
645 return IsOpen() ?
646 m_commands->PersistConfiguration(configuration) && m_eepromWriteThread->Write() :
647 false;
648}
649
650bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration)
651{
652 return IsOpen() ? m_commands->GetConfiguration(configuration) : false;
653}
654
655std::string CUSBCECAdapterCommunication::GetPortName(void)
656{
657 return m_port->GetName();
658}
659
660bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
661{
662 return IsOpen() ? m_commands->SetControlledMode(controlled) : false;
663}
664
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
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
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
701void *CAdapterPingThread::Process(void)
702{
703 while (!IsStopped())
704 {
705 if (m_timeout.TimeLeft() == 0)
706 {
707 /* reinit the timeout */
708 m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
709
710 /* send a ping to the adapter */
711 bool bPinged(false);
712 int iFailedCounter(0);
713 while (!bPinged && iFailedCounter < 3 && !IsStopped())
714 {
715 if (!m_com->PingAdapter())
716 {
717 /* sleep and retry */
718 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
719 ++iFailedCounter;
720 }
721 else
722 {
723 bPinged = true;
724 }
725 }
726
727 if (iFailedCounter == 3 && !IsStopped())
728 {
729 /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
730 m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
731 m_com->StopThread(false);
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
737 break;
738 }
739 }
740
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");
753 m_bWrite = true;
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 {
767 if (IsStopped())
768 break;
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 }
780 }
781 return NULL;
782}
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}