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