2 * This file is part of the libCEC(R) library.
4 * libCEC(R) is Copyright (C) 2011 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/
33 #include "AdapterCommunication.h"
35 #include "AdapterMessage.h"
36 #include "../CECProcessor.h"
37 #include "../platform/sockets/serialport.h"
38 #include "../platform/util/timeutils.h"
39 #include "../LibCEC.h"
43 using namespace PLATFORM
;
45 CAdapterCommunication::CAdapterCommunication(CCECProcessor
*processor
) :
47 m_processor(processor
),
49 m_iFirmwareVersion(CEC_FW_VERSION_UNKNOWN
)
51 m_port
= new PLATFORM::CSerialPort
;
54 CAdapterCommunication::~CAdapterCommunication(void)
65 bool CAdapterCommunication::Open(const char *strPort
, uint16_t iBaudRate
/* = 38400 */, uint32_t iTimeoutMs
/* = 10000 */)
67 uint64_t iNow
= GetTimeMs();
68 uint64_t iTimeout
= iNow
+ iTimeoutMs
;
70 CLockObject
lock(m_mutex
);
74 CLibCEC::AddLog(CEC_LOG_ERROR
, "port is NULL");
80 CLibCEC::AddLog(CEC_LOG_ERROR
, "port is already open");
85 bool bConnected(false);
86 while (!bConnected
&& iNow
< iTimeout
)
88 if ((bConnected
= m_port
->Open(strPort
, iBaudRate
)) == false)
90 strError
.Format("error opening serial port '%s': %s", strPort
, m_port
->GetError().c_str());
98 CLibCEC::AddLog(CEC_LOG_ERROR
, strError
);
102 CLibCEC::AddLog(CEC_LOG_DEBUG
, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
104 //clear any input bytes
106 while (m_port
->Read(buff
, 1024, 100) > 0)
108 CLibCEC::AddLog(CEC_LOG_DEBUG
, "data received, clearing it");
114 CLibCEC::AddLog(CEC_LOG_DEBUG
, "communication thread started");
119 CLibCEC::AddLog(CEC_LOG_ERROR
, "could not create a communication thread");
125 void CAdapterCommunication::Close(void)
127 CLockObject
lock(m_mutex
);
128 m_rcvCondition
.Broadcast();
132 void *CAdapterCommunication::Process(void)
141 CCECAdapterMessage
*msg(NULL
);
142 if (m_outBuffer
.Pop(msg
))
143 msg
->condition
.Broadcast();
148 bool CAdapterCommunication::Write(CCECAdapterMessage
*data
)
152 CLockObject
lock(data
->mutex
);
153 data
->state
= ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT
;
154 m_outBuffer
.Push(data
);
155 data
->condition
.Wait(data
->mutex
);
157 if (data
->state
!= ADAPTER_MESSAGE_STATE_SENT
)
159 CLibCEC::AddLog(CEC_LOG_ERROR
, "command was not sent");
161 else if (data
->expectControllerAck
)
163 bReturn
= WaitForAck(*data
);
166 if (data
->isTransmission
)
167 data
->state
= ADAPTER_MESSAGE_STATE_SENT_ACKED
;
171 data
->state
= ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED
;
172 CLibCEC::AddLog(CEC_LOG_DEBUG
, "did not receive ack");
183 bool CAdapterCommunication::Read(CCECAdapterMessage
&msg
, uint32_t iTimeout
)
185 CLockObject
lock(m_mutex
);
188 uint64_t iNow
= GetTimeMs();
189 uint64_t iTarget
= iNow
+ iTimeout
;
190 bool bGotFullMessage(false);
191 bool bNextIsEscaped(false);
192 bool bGotStart(false);
194 while(!bGotFullMessage
&& iNow
< iTarget
)
197 if (!m_inBuffer
.Pop(buf
))
199 if (!m_rcvCondition
.Wait(m_mutex
, (uint32_t) (iTarget
- iNow
)))
209 else if (buf
== MSGSTART
) //we found a msgstart before msgend, this is not right, remove
212 CLibCEC::AddLog(CEC_LOG_WARNING
, "received MSGSTART before MSGEND, removing previous buffer contents");
219 bGotFullMessage
= true;
221 else if (bNextIsEscaped
)
223 msg
.PushBack(buf
+ (uint8_t)ESCOFFSET
);
224 bNextIsEscaped
= false;
226 else if (buf
== MSGESC
)
227 bNextIsEscaped
= true;
233 msg
.state
= ADAPTER_MESSAGE_STATE_INCOMING
;
235 return bGotFullMessage
;
238 CStdString
CAdapterCommunication::GetError(void) const
241 strError
= m_port
->GetError();
245 bool CAdapterCommunication::StartBootloader(void)
251 CLibCEC::AddLog(CEC_LOG_DEBUG
, "starting the bootloader");
252 CCECAdapterMessage
*output
= new CCECAdapterMessage
;
254 output
->PushBack(MSGSTART
);
255 output
->PushEscaped(MSGCODE_START_BOOTLOADER
);
256 output
->PushBack(MSGEND
);
257 output
->isTransmission
= false;
258 output
->expectControllerAck
= false;
260 if ((bReturn
= Write(output
)) == false)
261 CLibCEC::AddLog(CEC_LOG_ERROR
, "could not start the bootloader");
267 bool CAdapterCommunication::PingAdapter(void)
273 CLibCEC::AddLog(CEC_LOG_DEBUG
, "sending ping");
274 CCECAdapterMessage
*output
= new CCECAdapterMessage
;
276 output
->PushBack(MSGSTART
);
277 output
->PushEscaped(MSGCODE_PING
);
278 output
->PushBack(MSGEND
);
279 output
->isTransmission
= false;
281 if ((bReturn
= Write(output
)) == false)
282 CLibCEC::AddLog(CEC_LOG_ERROR
, "could not ping the adapter");
288 uint16_t CAdapterCommunication::GetFirmwareVersion(void)
290 uint16_t iReturn(m_iFirmwareVersion
);
294 if (iReturn
== CEC_FW_VERSION_UNKNOWN
)
296 CLibCEC::AddLog(CEC_LOG_DEBUG
, "requesting the firmware version");
297 CCECAdapterMessage
*output
= new CCECAdapterMessage
;
299 output
->PushBack(MSGSTART
);
300 output
->PushEscaped(MSGCODE_FIRMWARE_VERSION
);
301 output
->PushBack(MSGEND
);
302 output
->isTransmission
= false;
303 output
->expectControllerAck
= false;
305 SendMessageToAdapter(output
);
308 CCECAdapterMessage input
;
309 if (!Read(input
, CEC_DEFAULT_TRANSMIT_WAIT
) || input
.Message() != MSGCODE_FIRMWARE_VERSION
|| input
.Size() != 3)
310 CLibCEC::AddLog(CEC_LOG_ERROR
, "no or invalid firmware version (size = %d, message = %d)", input
.Size(), input
.Message());
313 m_iFirmwareVersion
= (input
[1] << 8 | input
[2]);
314 iReturn
= m_iFirmwareVersion
;
321 bool CAdapterCommunication::SetLineTimeout(uint8_t iTimeout
)
323 bool bReturn(m_iLineTimeout
!= iTimeout
);
327 CCECAdapterMessage
*output
= new CCECAdapterMessage
;
329 output
->PushBack(MSGSTART
);
330 output
->PushEscaped(MSGCODE_TRANSMIT_IDLETIME
);
331 output
->PushEscaped(iTimeout
);
332 output
->PushBack(MSGEND
);
333 output
->isTransmission
= false;
335 if ((bReturn
= Write(output
)) == false)
336 CLibCEC::AddLog(CEC_LOG_ERROR
, "could not set the idletime");
343 bool CAdapterCommunication::SetAckMask(uint16_t iMask
)
347 strLog
.Format("setting ackmask to %2x", iMask
);
348 CLibCEC::AddLog(CEC_LOG_DEBUG
, strLog
.c_str());
350 CCECAdapterMessage
*output
= new CCECAdapterMessage
;
352 output
->PushBack(MSGSTART
);
353 output
->PushEscaped(MSGCODE_SET_ACK_MASK
);
354 output
->PushEscaped(iMask
>> 8);
355 output
->PushEscaped((uint8_t)iMask
);
356 output
->PushBack(MSGEND
);
357 output
->isTransmission
= false;
359 if ((bReturn
= Write(output
)) == false)
360 CLibCEC::AddLog(CEC_LOG_ERROR
, "could not set the ackmask");
366 bool CAdapterCommunication::IsOpen(void)
368 return !IsStopped() && m_port
->IsOpen() && IsRunning();
371 bool CAdapterCommunication::WaitForAck(CCECAdapterMessage
&message
)
374 bool bTransmitSucceeded(false);
375 uint8_t iPacketsLeft(message
.Size() / 4);
377 int64_t iNow
= GetTimeMs();
378 int64_t iTargetTime
= iNow
+ message
.transmit_timeout
;
380 while (!bTransmitSucceeded
&& !bError
&& (message
.transmit_timeout
== 0 || iNow
< iTargetTime
))
382 CCECAdapterMessage msg
;
383 int32_t iWait
= (int32_t)(iTargetTime
- iNow
);
384 if (iWait
<= 5 || message
.transmit_timeout
<= 5)
385 iWait
= CEC_DEFAULT_TRANSMIT_WAIT
;
387 if (!Read(msg
, iWait
))
393 if (msg
.Message() == MSGCODE_FRAME_START
&& msg
.IsACK())
395 m_processor
->HandlePoll(msg
.Initiator(), msg
.Destination());
400 if (msg
.Message() == MSGCODE_RECEIVE_FAILED
&&
401 m_processor
->HandleReceiveFailed())
407 bError
= msg
.IsError();
410 message
.reply
= msg
.Message();
411 CLibCEC::AddLog(CEC_LOG_DEBUG
, msg
.ToString());
415 switch(msg
.Message())
417 case MSGCODE_COMMAND_ACCEPTED
:
418 CLibCEC::AddLog(CEC_LOG_DEBUG
, msg
.ToString());
419 if (iPacketsLeft
> 0)
421 if (!message
.isTransmission
&& iPacketsLeft
== 0)
422 bTransmitSucceeded
= true;
424 case MSGCODE_TRANSMIT_SUCCEEDED
:
425 CLibCEC::AddLog(CEC_LOG_DEBUG
, msg
.ToString());
426 bTransmitSucceeded
= (iPacketsLeft
== 0);
427 bError
= !bTransmitSucceeded
;
428 message
.reply
= MSGCODE_TRANSMIT_SUCCEEDED
;
431 // ignore other data while waiting
439 return bTransmitSucceeded
&& !bError
;
442 void CAdapterCommunication::AddData(uint8_t *data
, uint8_t iLen
)
444 CLockObject
lock(m_mutex
);
445 for (uint8_t iPtr
= 0; iPtr
< iLen
; iPtr
++)
446 m_inBuffer
.Push(data
[iPtr
]);
448 m_rcvCondition
.Signal();
451 bool CAdapterCommunication::ReadFromDevice(uint32_t iTimeout
)
458 CLockObject
lock(m_mutex
);
459 iBytesRead
= m_port
->Read(buff
, sizeof(buff
), iTimeout
);
460 if (iBytesRead
< 0 || iBytesRead
> 256)
463 strError
.Format("error reading from serial port: %s", m_port
->GetError().c_str());
464 CLibCEC::AddLog(CEC_LOG_ERROR
, strError
);
467 else if (iBytesRead
> 0)
468 AddData(buff
, (uint8_t) iBytesRead
);
470 return iBytesRead
> 0;
473 void CAdapterCommunication::SendMessageToAdapter(CCECAdapterMessage
*msg
)
475 CLockObject
adapterLock(m_mutex
);
476 CLockObject
lock(msg
->mutex
);
477 if (m_port
->Write(msg
->packet
.data
, msg
->Size()) != (int32_t) msg
->Size())
480 strError
.Format("error writing to serial port: %s", m_port
->GetError().c_str());
481 CLibCEC::AddLog(CEC_LOG_ERROR
, strError
);
482 msg
->state
= ADAPTER_MESSAGE_STATE_ERROR
;
486 CLibCEC::AddLog(CEC_LOG_DEBUG
, "command sent");
487 msg
->state
= ADAPTER_MESSAGE_STATE_SENT
;
489 msg
->condition
.Signal();
492 void CAdapterCommunication::WriteNextCommand(void)
494 CCECAdapterMessage
*msg(NULL
);
495 if (m_outBuffer
.Pop(msg
))
496 SendMessageToAdapter(msg
);