Merge branch 'development'
[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 UNUSED(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
245 /* mark as waiting for an ack from the destination */
246 MarkAsWaiting(data.destination);
247
248 /* send the message */
249 bRetry = (!m_adapterMessageQueue->Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
250 if (bRetry)
251 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
252 retVal = output->state;
253
254 delete output;
255 return retVal;
256 }
257
258 void *CUSBCECAdapterCommunication::Process(void)
259 {
260 CCECAdapterMessage msg;
261 LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread started");
262
263 while (!IsStopped())
264 {
265 /* read from the serial port */
266 if (!ReadFromDevice(50, 5))
267 {
268 libcec_parameter param;
269 param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN;
270 LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param);
271
272 break;
273 }
274
275 /* TODO sleep 5 ms so other threads can get a lock */
276 Sleep(5);
277 }
278
279 m_adapterMessageQueue->Clear();
280 LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread ended");
281 return NULL;
282 }
283
284 bool CUSBCECAdapterCommunication::HandlePoll(const CCECAdapterMessage &msg)
285 {
286 bool bIsError(msg.IsError());
287 cec_adapter_messagecode messageCode(msg.Message());
288 CLockObject lock(m_mutex);
289
290 if (messageCode == MSGCODE_FRAME_START && msg.IsACK())
291 {
292 m_lastPollDestination = msg.Destination();
293 if (msg.Destination() < CECDEVICE_BROADCAST)
294 {
295 if (!m_bWaitingForAck[msg.Destination()] && !msg.IsEOM())
296 {
297 if (m_callback)
298 m_callback->HandlePoll(msg.Initiator(), msg.Destination());
299 }
300 else
301 m_bWaitingForAck[msg.Destination()] = false;
302 }
303 }
304 else if (messageCode == MSGCODE_RECEIVE_FAILED)
305 {
306 /* hack to suppress warnings when an LG is polling */
307 if (m_lastPollDestination != CECDEVICE_UNKNOWN)
308 bIsError = m_callback->HandleReceiveFailed(m_lastPollDestination);
309 }
310
311 return bIsError;
312 }
313
314 void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest)
315 {
316 /* mark as waiting for an ack from the destination */
317 if (dest < CECDEVICE_BROADCAST)
318 {
319 CLockObject lock(m_mutex);
320 m_bWaitingForAck[dest] = true;
321 }
322 }
323
324 void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = CEC_CLEAR_INPUT_DEFAULT_WAIT */)
325 {
326 CTimeout timeout(iTimeout);
327 uint8_t buff[1024];
328 ssize_t iBytesRead(0);
329 bool bGotMsgEnd(true);
330
331 while (timeout.TimeLeft() > 0 && ((iBytesRead = m_port->Read(buff, 1024, 5)) > 0 || !bGotMsgEnd))
332 {
333 bGotMsgEnd = false;
334 /* if something was received, wait for MSGEND */
335 for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
336 bGotMsgEnd = buff[iPtr] == MSGEND;
337 }
338 }
339
340 bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
341 {
342 bool bReturn(true);
343 bool bChanged(false);
344
345 /* only send the command if the timeout changed */
346 {
347 CLockObject lock(m_mutex);
348 bChanged = (m_iLineTimeout != iTimeout);
349 m_iLineTimeout = iTimeout;
350 }
351
352 if (bChanged)
353 bReturn = m_commands->SetLineTimeout(iTimeout);
354
355 return bReturn;
356 }
357
358 bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
359 {
360 CLockObject adapterLock(m_mutex);
361 if (!IsOpen())
362 {
363 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());
364 message->state = ADAPTER_MESSAGE_STATE_ERROR;
365 return false;
366 }
367
368 /* write the message */
369 if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
370 {
371 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());
372 message->state = ADAPTER_MESSAGE_STATE_ERROR;
373 // this will trigger an alert in the reader thread
374 m_port->Close();
375 return false;
376 }
377
378 LIB_CEC->AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
379 message->state = ADAPTER_MESSAGE_STATE_SENT;
380 return true;
381 }
382
383 bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
384 {
385 ssize_t iBytesRead(0);
386 uint8_t buff[256];
387 if (iSize > 256)
388 iSize = 256;
389
390 /* read from the serial port */
391 {
392 CLockObject lock(m_mutex);
393 if (!IsOpen())
394 return false;
395
396 do {
397 /* retry Read() if it was interrupted */
398 iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
399 } while(m_port->GetErrorNumber() == EINTR);
400
401
402 if (m_port->GetErrorNumber())
403 {
404 LIB_CEC->AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
405 m_port->Close();
406 return false;
407 }
408 }
409
410 if (iBytesRead < 0 || iBytesRead > 256)
411 return false;
412 else if (iBytesRead > 0)
413 {
414 /* add the data to the current frame */
415 m_adapterMessageQueue->AddData(buff, iBytesRead);
416 }
417
418 return true;
419 }
420
421 CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry /* = false */)
422 {
423 if (!IsOpen() || !m_adapterMessageQueue)
424 return NULL;
425
426 /* create the adapter message for this command */
427 CCECAdapterMessage *output = new CCECAdapterMessage;
428 output->PushBack(MSGSTART);
429 output->PushEscaped((uint8_t)msgCode);
430 output->Append(params);
431 output->PushBack(MSGEND);
432
433 /* write the command */
434 if (!m_adapterMessageQueue->Write(output))
435 {
436 // this will trigger an alert in the reader thread
437 if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
438 m_port->Close();
439 return output;
440 }
441 else
442 {
443 if (!bIsRetry && output->Reply() == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED &&
444 msgCode != MSGCODE_GET_BUILDDATE /* same messagecode value had a different meaning in older fw builds */)
445 {
446 /* if the controller reported that the command was rejected, and we didn't send the command
447 to set controlled mode, then the controller probably switched to auto mode. set controlled
448 mode and retry */
449 LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
450 delete output;
451 if (SetControlledMode(true))
452 return SendCommand(msgCode, params, true);
453 }
454 }
455
456 return output;
457 }
458
459 bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
460 {
461 bool bReturn(false);
462 CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
463
464 /* try to ping the adapter */
465 bool bPinged(false);
466 unsigned iPingTry(0);
467 while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
468 {
469 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
470 CEvent::Sleep(500);
471 }
472
473 /* try to read the firmware version */
474 if (bPinged && timeout.TimeLeft() > 0 && m_commands->RequestFirmwareVersion() >= 2)
475 {
476 /* try to set controlled mode for v2+ firmwares */
477 unsigned iControlledTry(0);
478 bool bControlled(false);
479 while (timeout.TimeLeft() > 0 && (bControlled = SetControlledMode(true)) == false)
480 {
481 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
482 CEvent::Sleep(500);
483 }
484 bReturn = bControlled;
485 }
486 else
487 bReturn = true;
488
489 if (m_commands->GetFirmwareVersion() >= 2)
490 {
491 /* try to read the build date */
492 m_commands->RequestBuildDate();
493
494 /* try to read the adapter type */
495 m_commands->RequestAdapterType();
496 }
497
498 SetInitialised(bReturn);
499 return bReturn;
500 }
501
502 bool CUSBCECAdapterCommunication::IsOpen(void)
503 {
504 /* thread is not being stopped, the port is open and the thread is running */
505 return !IsStopped() && m_port->IsOpen() && IsRunning();
506 }
507
508 std::string CUSBCECAdapterCommunication::GetError(void) const
509 {
510 return m_port->GetError();
511 }
512
513 void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo /* = true */)
514 {
515 CLockObject lock(m_mutex);
516 m_bInitialised = bSetTo;
517 }
518
519 bool CUSBCECAdapterCommunication::IsInitialised(void)
520 {
521 CLockObject lock(m_mutex);
522 return m_bInitialised;
523 }
524
525 bool CUSBCECAdapterCommunication::StartBootloader(void)
526 {
527 if (m_port->IsOpen() && m_commands->StartBootloader())
528 {
529 m_port->Close();
530 return true;
531 }
532 return false;
533 }
534
535 bool CUSBCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses)
536 {
537 {
538 CLockObject lock(m_mutex);
539 if (m_logicalAddresses == addresses)
540 return true;
541 }
542
543 if (IsOpen() && m_commands->SetAckMask(addresses.AckMask()))
544 {
545 CLockObject lock(m_mutex);
546 m_logicalAddresses = addresses;
547 return true;
548 }
549
550 LIB_CEC->AddLog(CEC_LOG_DEBUG, "couldn't change the ackmask: the connection is closed");
551 return false;
552 }
553
554 cec_logical_addresses CUSBCECAdapterCommunication::GetLogicalAddresses(void)
555 {
556 cec_logical_addresses addresses;
557 CLockObject lock(m_mutex);
558 addresses = m_logicalAddresses;
559 return addresses;
560 }
561
562 bool CUSBCECAdapterCommunication::PingAdapter(void)
563 {
564 return IsOpen() ? m_commands->PingAdapter() : false;
565 }
566
567 uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
568 {
569 return m_commands ? m_commands->GetFirmwareVersion() : CEC_FW_VERSION_UNKNOWN;
570 }
571
572 uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
573 {
574 uint32_t iBuildDate(0);
575 if (m_commands)
576 iBuildDate = m_commands->GetPersistedBuildDate();
577 if (iBuildDate == 0 && IsOpen())
578 iBuildDate = m_commands->RequestBuildDate();
579
580 return iBuildDate;
581 }
582
583 cec_adapter_type CUSBCECAdapterCommunication::GetAdapterType(void)
584 {
585 cec_adapter_type type(ADAPTERTYPE_UNKNOWN);
586 if (m_commands)
587 type = (cec_adapter_type)m_commands->GetPersistedAdapterType();
588 if (type == ADAPTERTYPE_UNKNOWN && IsOpen())
589 type = (cec_adapter_type)m_commands->RequestAdapterType();
590
591 return type;
592 }
593
594 bool CUSBCECAdapterCommunication::ProvidesExtendedResponse(void)
595 {
596 uint32_t iBuildDate(0);
597 if (m_commands)
598 iBuildDate = m_commands->GetPersistedBuildDate();
599
600 return iBuildDate >= CEC_FW_DATE_EXTENDED_RESPONSE;
601 }
602
603 bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
604 {
605 return GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE &&
606 GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION;
607 }
608
609 bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration)
610 {
611 return IsOpen() ?
612 m_commands->PersistConfiguration(configuration) && m_eepromWriteThread->Write() :
613 false;
614 }
615
616 bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration)
617 {
618 return IsOpen() ? m_commands->GetConfiguration(configuration) : false;
619 }
620
621 std::string CUSBCECAdapterCommunication::GetPortName(void)
622 {
623 return m_port->GetName();
624 }
625
626 bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
627 {
628 return IsOpen() ? m_commands->SetControlledMode(controlled) : false;
629 }
630
631 uint16_t CUSBCECAdapterCommunication::GetPhysicalAddress(void)
632 {
633 uint16_t iPA(0);
634
635 // try to get the PA from ADL
636 #if defined(HAS_ADL_EDID_PARSER)
637 {
638 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via ADL", __FUNCTION__);
639 CADLEdidParser adl;
640 iPA = adl.GetPhysicalAddress();
641 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - ADL returned physical address %04x", __FUNCTION__, iPA);
642 }
643 #endif
644
645 // try to get the PA from the nvidia driver
646 #if defined(HAS_NVIDIA_EDID_PARSER)
647 if (iPA == 0)
648 {
649 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address via nvidia driver", __FUNCTION__);
650 CNVEdidParser nv;
651 iPA = nv.GetPhysicalAddress();
652 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - nvidia driver returned physical address %04x", __FUNCTION__, iPA);
653 }
654 #endif
655
656 // try to get the PA from the OS
657 if (iPA == 0)
658 {
659 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - trying to get the physical address from the OS", __FUNCTION__);
660 iPA = CEDIDParser::GetPhysicalAddress();
661 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - OS returned physical address %04x", __FUNCTION__, iPA);
662 }
663
664 return iPA;
665 }
666
667 void *CAdapterPingThread::Process(void)
668 {
669 while (!IsStopped())
670 {
671 if (m_timeout.TimeLeft() == 0)
672 {
673 /* reinit the timeout */
674 m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
675
676 /* send a ping to the adapter */
677 bool bPinged(false);
678 int iFailedCounter(0);
679 while (!bPinged && iFailedCounter < 3)
680 {
681 if (!m_com->PingAdapter())
682 {
683 /* sleep and retry */
684 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
685 ++iFailedCounter;
686 }
687 else
688 {
689 bPinged = true;
690 }
691 }
692
693 if (iFailedCounter == 3)
694 {
695 /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
696 m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
697 m_com->StopThread(false);
698 break;
699 }
700 }
701
702 Sleep(5);
703 }
704 return NULL;
705 }
706
707 void CAdapterEepromWriteThread::Stop(void)
708 {
709 StopThread(-1);
710 {
711 CLockObject lock(m_mutex);
712 if (m_iScheduleEepromWrite > 0)
713 m_com->LIB_CEC->AddLog(CEC_LOG_WARNING, "write thread stopped while a write was queued");
714 m_condition.Signal();
715 }
716 StopThread();
717 }
718
719 void *CAdapterEepromWriteThread::Process(void)
720 {
721 while (!IsStopped())
722 {
723 CLockObject lock(m_mutex);
724 if ((m_iScheduleEepromWrite > 0 && m_iScheduleEepromWrite < GetTimeMs()) ||
725 m_condition.Wait(m_mutex, m_bWrite, 100))
726 {
727 m_bWrite = false;
728 if (m_com->m_commands->WriteEEPROM())
729 {
730 m_iLastEepromWrite = GetTimeMs();
731 m_iScheduleEepromWrite = 0;
732 }
733 else
734 {
735 m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY;
736 }
737 }
738 }
739 return NULL;
740 }
741
742 bool CAdapterEepromWriteThread::Write(void)
743 {
744 CLockObject lock(m_mutex);
745 if (m_iScheduleEepromWrite == 0)
746 {
747 int64_t iNow = GetTimeMs();
748 if (m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL > iNow)
749 {
750 m_com->LIB_CEC->AddLog(CEC_LOG_DEBUG, "delaying eeprom write by %ld ms", m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL - iNow);
751 m_iScheduleEepromWrite = m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL;
752 }
753 else
754 {
755 m_bWrite = true;
756 m_condition.Signal();
757 }
758 }
759 return true;
760 }