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