2 * This file is part of the libCEC(R) library.
4 * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
5 * libCEC(R) is an original work, containing original code.
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
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.
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.
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.
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
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/
34 #include "USBCECAdapterCommunication.h"
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"
50 using namespace PLATFORM
;
52 #define CEC_ADAPTER_PING_TIMEOUT 15000
53 #define CEC_ADAPTER_EEPROM_WRITE_INTERVAL 30000
54 #define CEC_ADAPTER_EEPROM_WRITE_RETRY 5000
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
61 #define CEC_FW_DATE_EXTENDED_RESPONSE 0x501a4b0c
63 #define LIB_CEC m_callback->GetLib()
65 CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCallback
*callback
, const char *strPort
, uint16_t iBaudRate
/* = CEC_SERIAL_DEFAULT_BAUDRATE */) :
66 IAdapterCommunication(callback
),
69 m_lastPollDestination(CECDEVICE_UNKNOWN
),
70 m_bInitialised(false),
72 m_eepromWriteThread(NULL
),
74 m_adapterMessageQueue(NULL
)
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);
83 CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void)
86 DELETE_AND_NULL(m_commands
);
87 DELETE_AND_NULL(m_adapterMessageQueue
);
88 DELETE_AND_NULL(m_port
);
91 void CUSBCECAdapterCommunication::ResetMessageQueue(void)
93 DELETE_AND_NULL(m_adapterMessageQueue
);
94 m_adapterMessageQueue
= new CCECAdapterMessageQueue(this);
95 m_adapterMessageQueue
->CreateThread();
98 bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs
/* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool bSkipChecks
/* = false */, bool bStartListening
/* = true */)
100 bool bConnectionOpened(false);
102 CLockObject
lock(m_mutex
);
104 /* we need the port settings here */
107 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "port is NULL");
108 return bConnectionOpened
;
111 /* return true when the port is already open */
114 LIB_CEC
->AddLog(CEC_LOG_WARNING
, "port is already open");
120 /* try to open the connection */
122 CTimeout
timeout(iTimeoutMs
);
123 while (!bConnectionOpened
&& timeout
.TimeLeft() > 0)
125 if ((bConnectionOpened
= m_port
->Open(timeout
.TimeLeft())) == false)
127 strError
.Format("error opening serial port '%s': %s", m_port
->GetName().c_str(), m_port
->GetError().c_str());
130 /* and retry every 250ms until the timeout passed */
133 /* return false when we couldn't connect */
134 if (!bConnectionOpened
)
136 LIB_CEC
->AddLog(CEC_LOG_ERROR
, strError
);
138 if (m_port
->GetErrorNumber() == EACCES
)
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
);
145 else if (m_port
->GetErrorNumber() == EBUSY
)
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
);
155 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
159 // always start by setting the ackmask to 0, to clear previous values
160 cec_logical_addresses addresses
; addresses
.Clear();
161 SetLogicalAddresses(addresses
);
165 bConnectionOpened
= false;
166 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "could not create a communication thread");
168 else if (!bSkipChecks
&& !CheckAdapter())
170 bConnectionOpened
= false;
171 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "the adapter failed to pass basic checks");
173 else if (bStartListening
)
175 /* start the eeprom write thread, that handles all eeprom writes async */
176 m_eepromWriteThread
= new CAdapterEepromWriteThread(this);
177 if (!m_eepromWriteThread
->CreateThread())
179 bConnectionOpened
= false;
180 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "could not create the eeprom write thread");
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())
189 bConnectionOpened
= true;
193 bConnectionOpened
= false;
194 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "could not create a ping thread");
199 if (!bConnectionOpened
|| !bStartListening
)
202 return bConnectionOpened
;
205 void CUSBCECAdapterCommunication::Close(void)
207 /* stop the reader thread */
210 CLockObject
lock(m_mutex
);
212 /* set the ackmask to 0 before closing the connection */
213 if (IsOpen() && m_port
->GetErrorNumber() == 0)
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);
222 m_adapterMessageQueue
->Clear();
224 /* stop and delete the write thread */
225 if (m_eepromWriteThread
)
226 m_eepromWriteThread
->Stop();
227 DELETE_AND_NULL(m_eepromWriteThread
);
229 /* stop and delete the ping thread */
230 DELETE_AND_NULL(m_pingThread
);
232 /* close and delete the com port connection */
237 cec_adapter_message_state
CUSBCECAdapterCommunication::Write(const cec_command
&data
, bool &bRetry
, uint8_t iLineTimeout
, bool bIsReply
)
239 cec_adapter_message_state
retVal(ADAPTER_MESSAGE_STATE_UNKNOWN
);
243 CCECAdapterMessage
*output
= new CCECAdapterMessage(data
, iLineTimeout
);
244 output
->bFireAndForget
= bIsReply
;
246 /* mark as waiting for an ack from the destination */
247 MarkAsWaiting(data
.destination
);
249 /* send the message */
252 retVal
= m_adapterMessageQueue
->Write(output
) ?
253 ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT
: ADAPTER_MESSAGE_STATE_ERROR
;
257 bRetry
= (!m_adapterMessageQueue
->Write(output
) || output
->NeedsRetry()) && output
->transmit_timeout
> 0;
259 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT
);
260 retVal
= output
->state
;
267 void *CUSBCECAdapterCommunication::Process(void)
269 CCECAdapterMessage msg
;
270 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "communication thread started");
274 /* read from the serial port */
275 if (!ReadFromDevice(50, 5))
277 libcec_parameter param
;
278 param
.paramData
= NULL
; param
.paramType
= CEC_PARAMETER_TYPE_UNKOWN
;
279 LIB_CEC
->Alert(CEC_ALERT_CONNECTION_LOST
, param
);
284 /* TODO sleep 5 ms so other threads can get a lock */
288 m_adapterMessageQueue
->Clear();
289 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "communication thread ended");
293 bool CUSBCECAdapterCommunication::HandlePoll(const CCECAdapterMessage
&msg
)
295 bool bIsError(msg
.IsError());
296 cec_adapter_messagecode
messageCode(msg
.Message());
297 CLockObject
lock(m_mutex
);
299 if (messageCode
== MSGCODE_FRAME_START
&& msg
.IsACK())
301 m_lastPollDestination
= msg
.Destination();
302 if (msg
.Destination() < CECDEVICE_BROADCAST
)
304 if (!m_bWaitingForAck
[msg
.Destination()] && !msg
.IsEOM())
307 m_callback
->HandlePoll(msg
.Initiator(), msg
.Destination());
310 m_bWaitingForAck
[msg
.Destination()] = false;
313 else if (messageCode
== MSGCODE_RECEIVE_FAILED
)
315 /* hack to suppress warnings when an LG is polling */
316 if (m_lastPollDestination
!= CECDEVICE_UNKNOWN
)
317 bIsError
= m_callback
->HandleReceiveFailed(m_lastPollDestination
);
323 void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest
)
325 /* mark as waiting for an ack from the destination */
326 if (dest
< CECDEVICE_BROADCAST
)
328 CLockObject
lock(m_mutex
);
329 m_bWaitingForAck
[dest
] = true;
333 void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout
/* = CEC_CLEAR_INPUT_DEFAULT_WAIT */)
335 CTimeout
timeout(iTimeout
);
337 ssize_t
iBytesRead(0);
338 bool bGotMsgEnd(true);
340 while (timeout
.TimeLeft() > 0 && ((iBytesRead
= m_port
->Read(buff
, 1024, 5)) > 0 || !bGotMsgEnd
))
343 /* if something was received, wait for MSGEND */
344 for (ssize_t iPtr
= 0; iPtr
< iBytesRead
; iPtr
++)
345 bGotMsgEnd
= buff
[iPtr
] == MSGEND
;
349 bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout
)
352 bool bChanged(false);
354 /* only send the command if the timeout changed */
356 CLockObject
lock(m_mutex
);
357 bChanged
= (m_iLineTimeout
!= iTimeout
);
358 m_iLineTimeout
= iTimeout
;
362 bReturn
= m_commands
->SetLineTimeout(iTimeout
);
367 bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage
*message
)
369 CLockObject
adapterLock(m_mutex
);
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
;
377 /* write the message */
378 if (m_port
->Write(message
->packet
.data
, message
->Size()) != (ssize_t
) message
->Size())
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
387 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "command '%s' sent", message
->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message
->Message()));
388 message
->state
= ADAPTER_MESSAGE_STATE_SENT
;
392 bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout
, size_t iSize
/* = 256 */)
394 ssize_t
iBytesRead(0);
399 /* read from the serial port */
401 CLockObject
lock(m_mutex
);
406 /* retry Read() if it was interrupted */
407 iBytesRead
= m_port
->Read(buff
, sizeof(uint8_t) * iSize
, iTimeout
);
408 } while(m_port
->GetErrorNumber() == EINTR
);
411 if (m_port
->GetErrorNumber())
413 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "error reading from serial port: %s", m_port
->GetError().c_str());
419 if (iBytesRead
< 0 || iBytesRead
> 256)
421 else if (iBytesRead
> 0)
423 /* add the data to the current frame */
424 m_adapterMessageQueue
->AddData(buff
, iBytesRead
);
430 CCECAdapterMessage
*CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode
, CCECAdapterMessage
¶ms
, bool bIsRetry
/* = false */)
432 if (!IsOpen() || !m_adapterMessageQueue
)
435 /* create the adapter message for this command */
436 CCECAdapterMessage
*output
= new CCECAdapterMessage
;
437 output
->PushBack(MSGSTART
);
438 output
->PushEscaped((uint8_t)msgCode
);
439 output
->Append(params
);
440 output
->PushBack(MSGEND
);
442 /* write the command */
443 if (!m_adapterMessageQueue
->Write(output
))
445 // this will trigger an alert in the reader thread
446 if (output
->state
== ADAPTER_MESSAGE_STATE_ERROR
)
452 if (!bIsRetry
&& output
->Reply() == MSGCODE_COMMAND_REJECTED
&& msgCode
!= MSGCODE_SET_CONTROLLED
&&
453 msgCode
!= MSGCODE_GET_BUILDDATE
/* same messagecode value had a different meaning in older fw builds */)
455 /* if the controller reported that the command was rejected, and we didn't send the command
456 to set controlled mode, then the controller probably switched to auto mode. set controlled
458 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "setting controlled mode and retrying");
460 if (SetControlledMode(true))
461 return SendCommand(msgCode
, params
, true);
468 bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs
/* = CEC_DEFAULT_CONNECT_TIMEOUT */)
471 CTimeout
timeout(iTimeoutMs
> 0 ? iTimeoutMs
: CEC_DEFAULT_TRANSMIT_WAIT
);
473 /* try to ping the adapter */
475 unsigned iPingTry(0);
476 while (timeout
.TimeLeft() > 0 && (bPinged
= PingAdapter()) == false)
478 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry
);
482 /* try to read the firmware version */
483 if (bPinged
&& timeout
.TimeLeft() > 0 && m_commands
->RequestFirmwareVersion() >= 2)
485 /* try to set controlled mode for v2+ firmwares */
486 unsigned iControlledTry(0);
487 bool bControlled(false);
488 while (timeout
.TimeLeft() > 0 && (bControlled
= SetControlledMode(true)) == false)
490 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry
);
493 bReturn
= bControlled
;
498 if (m_commands
->GetFirmwareVersion() >= 2)
500 /* try to read the build date */
501 m_commands
->RequestBuildDate();
503 /* try to read the adapter type */
504 m_commands
->RequestAdapterType();
507 SetInitialised(bReturn
);
511 bool CUSBCECAdapterCommunication::IsOpen(void)
513 /* thread is not being stopped, the port is open and the thread is running */
514 return !IsStopped() && m_port
->IsOpen() && IsRunning();
517 std::string
CUSBCECAdapterCommunication::GetError(void) const
519 return m_port
->GetError();
522 void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo
/* = true */)
524 CLockObject
lock(m_mutex
);
525 m_bInitialised
= bSetTo
;
528 bool CUSBCECAdapterCommunication::IsInitialised(void)
530 CLockObject
lock(m_mutex
);
531 return m_bInitialised
;
534 bool CUSBCECAdapterCommunication::StartBootloader(void)
536 if (m_port
->IsOpen() && m_commands
->StartBootloader())
544 bool CUSBCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses
&addresses
)
547 CLockObject
lock(m_mutex
);
548 if (m_logicalAddresses
== addresses
)
552 if (IsOpen() && m_commands
->SetAckMask(addresses
.AckMask()))
554 CLockObject
lock(m_mutex
);
555 m_logicalAddresses
= addresses
;
559 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "couldn't change the ackmask: the connection is closed");
563 cec_logical_addresses
CUSBCECAdapterCommunication::GetLogicalAddresses(void)
565 cec_logical_addresses addresses
;
566 CLockObject
lock(m_mutex
);
567 addresses
= m_logicalAddresses
;
571 bool CUSBCECAdapterCommunication::PingAdapter(void)
573 return IsOpen() ? m_commands
->PingAdapter() : false;
576 uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
578 return m_commands
? m_commands
->GetFirmwareVersion() : CEC_FW_VERSION_UNKNOWN
;
581 uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
583 uint32_t iBuildDate(0);
585 iBuildDate
= m_commands
->GetPersistedBuildDate();
586 if (iBuildDate
== 0 && IsOpen())
587 iBuildDate
= m_commands
->RequestBuildDate();
592 cec_adapter_type
CUSBCECAdapterCommunication::GetAdapterType(void)
594 cec_adapter_type
type(ADAPTERTYPE_UNKNOWN
);
596 type
= (cec_adapter_type
)m_commands
->GetPersistedAdapterType();
597 if (type
== ADAPTERTYPE_UNKNOWN
&& IsOpen())
598 type
= (cec_adapter_type
)m_commands
->RequestAdapterType();
603 bool CUSBCECAdapterCommunication::ProvidesExtendedResponse(void)
605 uint32_t iBuildDate(0);
607 iBuildDate
= m_commands
->GetPersistedBuildDate();
609 return iBuildDate
>= CEC_FW_DATE_EXTENDED_RESPONSE
;
612 bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
614 return GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE
&&
615 GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION
;
618 bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration
&configuration
)
621 m_commands
->PersistConfiguration(configuration
) && m_eepromWriteThread
->Write() :
625 bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration
&configuration
)
627 return IsOpen() ? m_commands
->GetConfiguration(configuration
) : false;
630 std::string
CUSBCECAdapterCommunication::GetPortName(void)
632 return m_port
->GetName();
635 bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled
)
637 return IsOpen() ? m_commands
->SetControlledMode(controlled
) : false;
640 uint16_t CUSBCECAdapterCommunication::GetPhysicalAddress(void)
644 // try to get the PA from ADL
645 #if defined(HAS_ADL_EDID_PARSER)
647 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - trying to get the physical address via ADL", __FUNCTION__
);
649 iPA
= adl
.GetPhysicalAddress();
650 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - ADL returned physical address %04x", __FUNCTION__
, iPA
);
654 // try to get the PA from the nvidia driver
655 #if defined(HAS_NVIDIA_EDID_PARSER)
658 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - trying to get the physical address via nvidia driver", __FUNCTION__
);
660 iPA
= nv
.GetPhysicalAddress();
661 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - nvidia driver returned physical address %04x", __FUNCTION__
, iPA
);
665 // try to get the PA from the OS
668 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - trying to get the physical address from the OS", __FUNCTION__
);
669 iPA
= CEDIDParser::GetPhysicalAddress();
670 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - OS returned physical address %04x", __FUNCTION__
, iPA
);
676 void *CAdapterPingThread::Process(void)
680 if (m_timeout
.TimeLeft() == 0)
682 /* reinit the timeout */
683 m_timeout
.Init(CEC_ADAPTER_PING_TIMEOUT
);
685 /* send a ping to the adapter */
687 int iFailedCounter(0);
688 while (!bPinged
&& iFailedCounter
< 3)
690 if (!m_com
->PingAdapter())
692 /* sleep and retry */
693 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT
);
702 if (iFailedCounter
== 3)
704 /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
705 m_com
->LIB_CEC
->AddLog(CEC_LOG_ERROR
, "failed to ping the adapter 3 times in a row. closing the connection.");
706 m_com
->StopThread(false);
716 void CAdapterEepromWriteThread::Stop(void)
720 CLockObject
lock(m_mutex
);
721 if (m_iScheduleEepromWrite
> 0)
722 m_com
->LIB_CEC
->AddLog(CEC_LOG_WARNING
, "write thread stopped while a write was queued");
723 m_condition
.Signal();
728 void *CAdapterEepromWriteThread::Process(void)
732 CLockObject
lock(m_mutex
);
733 if ((m_iScheduleEepromWrite
> 0 && m_iScheduleEepromWrite
< GetTimeMs()) ||
734 m_condition
.Wait(m_mutex
, m_bWrite
, 100))
737 if (m_com
->m_commands
->WriteEEPROM())
739 m_iLastEepromWrite
= GetTimeMs();
740 m_iScheduleEepromWrite
= 0;
744 m_iScheduleEepromWrite
= GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY
;
751 bool CAdapterEepromWriteThread::Write(void)
753 CLockObject
lock(m_mutex
);
754 if (m_iScheduleEepromWrite
== 0)
756 int64_t iNow
= GetTimeMs();
757 if (m_iLastEepromWrite
+ CEC_ADAPTER_EEPROM_WRITE_INTERVAL
> iNow
)
759 m_com
->LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "delaying eeprom write by %ld ms", m_iLastEepromWrite
+ CEC_ADAPTER_EEPROM_WRITE_INTERVAL
- iNow
);
760 m_iScheduleEepromWrite
= m_iLastEepromWrite
+ CEC_ADAPTER_EEPROM_WRITE_INTERVAL
;
765 m_condition
.Signal();