cec: fixed compilation warnings
[deb_libcec.git] / src / lib / adapter / USBCECAdapterCommunication.cpp
... / ...
CommitLineData
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 "USBCECAdapterCommunication.h"
34#include "../platform/sockets/serialport.h"
35#include "../platform/util/timeutils.h"
36#include "../LibCEC.h"
37#include "../CECProcessor.h"
38
39using namespace std;
40using namespace CEC;
41using namespace PLATFORM;
42
43CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(CCECProcessor *processor, const char *strPort, uint16_t iBaudRate /* = 38400 */) :
44 m_port(NULL),
45 m_processor(processor),
46 m_iLineTimeout(0),
47 m_iFirmwareVersion(CEC_FW_VERSION_UNKNOWN),
48 m_lastInitiator(CECDEVICE_UNKNOWN),
49 m_bNextIsEscaped(false),
50 m_bGotStart(false)
51{
52 m_port = new PLATFORM::CSerialPort(strPort, iBaudRate);
53}
54
55CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void)
56{
57 Close();
58}
59
60bool CUSBCECAdapterCommunication::Open(IAdapterCommunicationCallback *cb, uint32_t iTimeoutMs /* = 10000 */)
61{
62 uint64_t iNow = GetTimeMs();
63 uint64_t iTimeout = iNow + iTimeoutMs;
64
65 CLockObject lock(m_mutex);
66
67 if (!m_port)
68 {
69 CLibCEC::AddLog(CEC_LOG_ERROR, "port is NULL");
70 return false;
71 }
72
73 if (IsOpen())
74 {
75 CLibCEC::AddLog(CEC_LOG_ERROR, "port is already open");
76 return true;
77 }
78
79 m_callback = cb;
80 CStdString strError;
81 bool bConnected(false);
82 while (!bConnected && iNow < iTimeout)
83 {
84 if ((bConnected = m_port->Open(iTimeout)) == false)
85 {
86 strError.Format("error opening serial port '%s': %s", m_port->GetName().c_str(), m_port->GetError().c_str());
87 Sleep(250);
88 iNow = GetTimeMs();
89 }
90 }
91
92 if (!bConnected)
93 {
94 CLibCEC::AddLog(CEC_LOG_ERROR, strError);
95 return false;
96 }
97
98 CLibCEC::AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
99
100 //clear any input bytes
101 uint8_t buff[1024];
102 while (m_port->Read(buff, 1024, 100) > 0)
103 {
104 CLibCEC::AddLog(CEC_LOG_DEBUG, "data received, clearing it");
105 Sleep(250);
106 }
107
108 if (CreateThread())
109 {
110 CLibCEC::AddLog(CEC_LOG_DEBUG, "communication thread started");
111 return true;
112 }
113 else
114 {
115 CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a communication thread");
116 }
117
118 return false;
119}
120
121void CUSBCECAdapterCommunication::Close(void)
122{
123 CLockObject lock(m_mutex);
124 m_rcvCondition.Broadcast();
125 StopThread();
126}
127
128void *CUSBCECAdapterCommunication::Process(void)
129{
130 cec_command command;
131 while (!IsStopped())
132 {
133 ReadFromDevice(50);
134
135 /* push the next command to the callback method if there is one */
136 if (m_callback && Read(command, 0))
137 m_callback->OnCommandReceived(command);
138
139 Sleep(5);
140 WriteNextCommand();
141 }
142
143 CCECAdapterMessage *msg(NULL);
144 if (m_outBuffer.Pop(msg))
145 msg->condition.Broadcast();
146
147 if (m_port)
148 {
149 delete m_port;
150 m_port = NULL;
151 }
152
153 return NULL;
154}
155
156cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, uint8_t iMaxTries, uint8_t iLineTimeout /* = 3 */, uint8_t iRetryLineTimeout /* = 3 */)
157{
158 cec_adapter_message_state retVal(ADAPTER_MESSAGE_STATE_UNKNOWN);
159
160 CCECAdapterMessage *output = new CCECAdapterMessage(data);
161
162 /* set the number of retries */
163 if (data.opcode == CEC_OPCODE_NONE) //TODO
164 output->maxTries = 1;
165 else if (data.initiator != CECDEVICE_BROADCAST)
166 output->maxTries = iMaxTries;
167
168 output->lineTimeout = iLineTimeout;
169 output->retryTimeout = iRetryLineTimeout;
170 output->tries = 0;
171
172 bool bRetry(true);
173 while (bRetry && ++output->tries < output->maxTries)
174 {
175 bRetry = (!Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
176 if (bRetry)
177 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
178 }
179 retVal = output->state;
180
181 delete output;
182 return retVal;
183}
184
185bool CUSBCECAdapterCommunication::Write(CCECAdapterMessage *data)
186{
187 CLockObject lock(data->mutex);
188 data->state = ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT;
189 m_outBuffer.Push(data);
190 data->condition.Wait(data->mutex);
191
192 if ((data->expectControllerAck && data->state != ADAPTER_MESSAGE_STATE_SENT_ACKED) ||
193 (!data->expectControllerAck && data->state != ADAPTER_MESSAGE_STATE_SENT))
194 {
195 CLibCEC::AddLog(CEC_LOG_DEBUG, "command was not %s", data->state == ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED ? "acked" : "sent");
196 return false;
197 }
198
199 return true;
200}
201
202bool CUSBCECAdapterCommunication::Read(cec_command &command, uint32_t iTimeout)
203{
204 CCECAdapterMessage msg;
205 if (Read(msg, iTimeout))
206 {
207 if (ParseMessage(msg))
208 {
209 command = m_currentframe;
210 m_currentframe.Clear();
211 return true;
212 }
213 }
214 return false;
215}
216
217bool CUSBCECAdapterCommunication::Read(CCECAdapterMessage &msg, uint32_t iTimeout)
218{
219 CLockObject lock(m_mutex);
220
221 msg.Clear();
222 CCECAdapterMessage *buf(NULL);
223
224 if (!m_inBuffer.Pop(buf))
225 {
226 if (iTimeout == 0 || !m_rcvCondition.Wait(m_mutex, iTimeout))
227 return false;
228 m_inBuffer.Pop(buf);
229 }
230
231 if (buf)
232 {
233 msg.packet = buf->packet;
234 msg.state = msg.state = ADAPTER_MESSAGE_STATE_INCOMING;
235 delete buf;
236 return true;
237 }
238 return false;
239}
240
241CStdString CUSBCECAdapterCommunication::GetError(void) const
242{
243 CStdString strError;
244 strError = m_port->GetError();
245 return strError;
246}
247
248bool CUSBCECAdapterCommunication::StartBootloader(void)
249{
250 bool bReturn(false);
251 if (!IsRunning())
252 return bReturn;
253
254 CLibCEC::AddLog(CEC_LOG_DEBUG, "starting the bootloader");
255 CCECAdapterMessage *output = new CCECAdapterMessage;
256
257 output->PushBack(MSGSTART);
258 output->PushEscaped(MSGCODE_START_BOOTLOADER);
259 output->PushBack(MSGEND);
260 output->isTransmission = false;
261 output->expectControllerAck = false;
262
263 if ((bReturn = Write(output)) == false)
264 CLibCEC::AddLog(CEC_LOG_ERROR, "could not start the bootloader");
265 delete output;
266
267 return bReturn;
268}
269
270bool CUSBCECAdapterCommunication::PingAdapter(void)
271{
272 bool bReturn(false);
273 if (!IsRunning())
274 return bReturn;
275
276 CLibCEC::AddLog(CEC_LOG_DEBUG, "sending ping");
277 CCECAdapterMessage *output = new CCECAdapterMessage;
278
279 output->PushBack(MSGSTART);
280 output->PushEscaped(MSGCODE_PING);
281 output->PushBack(MSGEND);
282 output->isTransmission = false;
283
284 if ((bReturn = Write(output)) == false)
285 CLibCEC::AddLog(CEC_LOG_ERROR, "could not ping the adapter");
286 delete output;
287
288 return bReturn;
289}
290
291bool CUSBCECAdapterCommunication::ParseMessage(const CCECAdapterMessage &msg)
292{
293 bool bEom(false);
294 bool bIsError(msg.IsError());
295
296 if (msg.IsEmpty())
297 return bEom;
298
299 switch(msg.Message())
300 {
301 case MSGCODE_FRAME_START:
302 {
303 m_currentframe.Clear();
304 if (msg.Size() >= 2)
305 {
306 m_currentframe.initiator = msg.Initiator();
307 m_currentframe.destination = msg.Destination();
308 m_currentframe.ack = msg.IsACK();
309 m_currentframe.eom = msg.IsEOM();
310 }
311 if (m_currentframe.ack == 0x1)
312 {
313 m_lastInitiator = m_currentframe.initiator;
314 m_processor->HandlePoll(m_currentframe.initiator, m_currentframe.destination);
315 }
316 }
317 break;
318 case MSGCODE_RECEIVE_FAILED:
319 {
320 m_currentframe.Clear();
321 if (m_lastInitiator != CECDEVICE_UNKNOWN)
322 bIsError = m_processor->HandleReceiveFailed(m_lastInitiator);
323 }
324 break;
325 case MSGCODE_FRAME_DATA:
326 {
327 if (msg.Size() >= 2)
328 {
329 m_currentframe.PushBack(msg[1]);
330 m_currentframe.eom = msg.IsEOM();
331 }
332 bEom = msg.IsEOM();
333 }
334 break;
335 default:
336 break;
337 }
338
339 CLibCEC::AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString());
340 return bEom;
341}
342
343uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
344{
345 uint16_t iReturn(m_iFirmwareVersion);
346 if (!IsRunning())
347 return iReturn;
348
349 if (iReturn == CEC_FW_VERSION_UNKNOWN)
350 {
351 CLibCEC::AddLog(CEC_LOG_DEBUG, "requesting the firmware version");
352 CCECAdapterMessage *output = new CCECAdapterMessage;
353
354 output->PushBack(MSGSTART);
355 output->PushEscaped(MSGCODE_FIRMWARE_VERSION);
356 output->PushBack(MSGEND);
357 output->isTransmission = false;
358 output->expectControllerAck = false;
359
360 bool bWriteOk = Write(output);
361 delete output;
362 if (!bWriteOk)
363 {
364 CLibCEC::AddLog(CEC_LOG_ERROR, "could not request the firmware version");
365 }
366 else
367 {
368 ReadFromDevice(CEC_DEFAULT_TRANSMIT_WAIT, 5 /* start + msgcode + 2 bytes for fw version + end */);
369 CCECAdapterMessage input;
370 if (!Read(input, 0) || input.Message() != MSGCODE_FIRMWARE_VERSION || input.Size() != 3)
371 CLibCEC::AddLog(CEC_LOG_ERROR, "no or invalid firmware version (size = %d, message = %d)", input.Size(), input.Message());
372 else
373 {
374 m_iFirmwareVersion = (input[1] << 8 | input[2]);
375 iReturn = m_iFirmwareVersion;
376 }
377 }
378 }
379
380 return iReturn;
381}
382
383bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
384{
385 m_iLineTimeout = iTimeout;
386 return true;
387 //TODO
388// bool bReturn(m_iLineTimeout != iTimeout);
389//
390// if (!bReturn)
391// {
392// CCECAdapterMessage *output = new CCECAdapterMessage;
393//
394// output->PushBack(MSGSTART);
395// output->PushEscaped(MSGCODE_TRANSMIT_IDLETIME);
396// output->PushEscaped(iTimeout);
397// output->PushBack(MSGEND);
398// output->isTransmission = false;
399//
400// if ((bReturn = Write(output)) == false)
401// CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the idletime");
402// delete output;
403// }
404//
405// return bReturn;
406}
407
408bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
409{
410 bool bReturn(false);
411 CLibCEC::AddLog(CEC_LOG_DEBUG, "setting ackmask to %2x", iMask);
412
413 CCECAdapterMessage *output = new CCECAdapterMessage;
414
415 output->PushBack(MSGSTART);
416 output->PushEscaped(MSGCODE_SET_ACK_MASK);
417 output->PushEscaped(iMask >> 8);
418 output->PushEscaped((uint8_t)iMask);
419 output->PushBack(MSGEND);
420 output->isTransmission = false;
421
422 if ((bReturn = Write(output)) == false)
423 CLibCEC::AddLog(CEC_LOG_ERROR, "could not set the ackmask");
424 delete output;
425
426 return bReturn;
427}
428
429
430bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
431{
432 bool bReturn(false);
433 CLibCEC::AddLog(CEC_LOG_DEBUG, "turning controlled mode %s", controlled ? "on" : "off");
434
435 CCECAdapterMessage *output = new CCECAdapterMessage;
436
437 output->PushBack(MSGSTART);
438 output->PushEscaped(MSGCODE_SET_CONTROLLED);
439 output->PushEscaped(controlled);
440 output->PushBack(MSGEND);
441 output->isTransmission = false;
442
443 if ((bReturn = Write(output)) == false)
444 CLibCEC::AddLog(CEC_LOG_ERROR, "could not set controlled mode");
445 delete output;
446
447 return bReturn;
448}
449
450bool CUSBCECAdapterCommunication::IsOpen(void)
451{
452 return !IsStopped() && m_port->IsOpen() && IsRunning();
453}
454
455bool CUSBCECAdapterCommunication::WaitForAck(CCECAdapterMessage &message)
456{
457 bool bError(false);
458 bool bTransmitSucceeded(false);
459 uint8_t iPacketsLeft(message.Size() / 4);
460
461 int64_t iNow = GetTimeMs();
462 int64_t iTargetTime = iNow + (message.transmit_timeout <= 5 ? CEC_DEFAULT_TRANSMIT_WAIT : message.transmit_timeout);
463
464 while (!bTransmitSucceeded && !bError && iNow < iTargetTime)
465 {
466 ReadFromDevice(50);
467 CCECAdapterMessage msg;
468 if (!Read(msg, 0))
469 {
470 iNow = GetTimeMs();
471 continue;
472 }
473
474 if (msg.Message() == MSGCODE_FRAME_START && msg.IsACK())
475 {
476 m_processor->HandlePoll(msg.Initiator(), msg.Destination());
477 m_lastInitiator = msg.Initiator();
478 iNow = GetTimeMs();
479 continue;
480 }
481
482 if (msg.Message() == MSGCODE_RECEIVE_FAILED &&
483 m_lastInitiator != CECDEVICE_UNKNOWN &&
484 m_processor->HandleReceiveFailed(m_lastInitiator))
485 {
486 iNow = GetTimeMs();
487 continue;
488 }
489
490 bError = msg.IsError();
491 if (bError)
492 {
493 message.reply = msg.Message();
494 CLibCEC::AddLog(CEC_LOG_DEBUG, msg.ToString());
495 }
496 else
497 {
498 switch(msg.Message())
499 {
500 case MSGCODE_COMMAND_ACCEPTED:
501 CLibCEC::AddLog(CEC_LOG_DEBUG, msg.ToString());
502 if (iPacketsLeft > 0)
503 iPacketsLeft--;
504 if (!message.isTransmission && iPacketsLeft == 0)
505 bTransmitSucceeded = true;
506 break;
507 case MSGCODE_TRANSMIT_SUCCEEDED:
508 CLibCEC::AddLog(CEC_LOG_DEBUG, msg.ToString());
509 bTransmitSucceeded = (iPacketsLeft == 0);
510 bError = !bTransmitSucceeded;
511 message.reply = MSGCODE_TRANSMIT_SUCCEEDED;
512 break;
513 default:
514 // ignore other data while waiting
515 break;
516 }
517
518 iNow = GetTimeMs();
519 }
520 }
521
522 message.state = bTransmitSucceeded && !bError ?
523 ADAPTER_MESSAGE_STATE_SENT_ACKED :
524 ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
525
526 return bTransmitSucceeded && !bError;
527}
528
529void CUSBCECAdapterCommunication::AddData(uint8_t *data, size_t iLen)
530{
531 CLockObject lock(m_mutex);
532 for (size_t iPtr = 0; iPtr < iLen; iPtr++)
533 {
534 if (!m_bGotStart)
535 {
536 if (data[iPtr] == MSGSTART)
537 m_bGotStart = true;
538 }
539 else if (data[iPtr] == MSGSTART) //we found a msgstart before msgend, this is not right, remove
540 {
541 if (m_currentAdapterMessage.Size() > 0)
542 CLibCEC::AddLog(CEC_LOG_WARNING, "received MSGSTART before MSGEND, removing previous buffer contents");
543 m_currentAdapterMessage.Clear();
544 m_bGotStart = true;
545 }
546 else if (data[iPtr] == MSGEND)
547 {
548 CCECAdapterMessage *newMessage = new CCECAdapterMessage;
549 newMessage->packet = m_currentAdapterMessage.packet;
550 m_inBuffer.Push(newMessage);
551 m_currentAdapterMessage.Clear();
552 m_bGotStart = false;
553 m_bNextIsEscaped = false;
554 m_rcvCondition.Signal();
555 }
556 else if (m_bNextIsEscaped)
557 {
558 m_currentAdapterMessage.PushBack(data[iPtr] + (uint8_t)ESCOFFSET);
559 m_bNextIsEscaped = false;
560 }
561 else if (data[iPtr] == MSGESC)
562 {
563 m_bNextIsEscaped = true;
564 }
565 else
566 {
567 m_currentAdapterMessage.PushBack(data[iPtr]);
568 }
569 }
570}
571
572bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
573{
574 ssize_t iBytesRead;
575 uint8_t buff[256];
576 if (!m_port)
577 return false;
578 if (iSize > 256)
579 iSize = 256;
580
581 CLockObject lock(m_mutex);
582 iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
583 if (iBytesRead < 0 || iBytesRead > 256)
584 {
585 CLibCEC::AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
586 return false;
587 }
588 else if (iBytesRead > 0)
589 {
590 AddData(buff, iBytesRead);
591 }
592
593 return iBytesRead > 0;
594}
595
596void CUSBCECAdapterCommunication::SendMessageToAdapter(CCECAdapterMessage *msg)
597{
598 CLockObject adapterLock(m_mutex);
599 CLockObject lock(msg->mutex);
600 if (msg->tries == 1)
601 SetLineTimeout(msg->lineTimeout);
602 else
603 SetLineTimeout(msg->retryTimeout);
604
605 if (m_port->Write(msg->packet.data, msg->Size()) != (ssize_t) msg->Size())
606 {
607 CLibCEC::AddLog(CEC_LOG_ERROR, "error writing to serial port: %s", m_port->GetError().c_str());
608 msg->state = ADAPTER_MESSAGE_STATE_ERROR;
609 }
610 else
611 {
612 CLibCEC::AddLog(CEC_LOG_DEBUG, "command sent");
613 msg->state = ADAPTER_MESSAGE_STATE_SENT;
614
615 if (msg->expectControllerAck)
616 {
617 if (!WaitForAck(*msg))
618 CLibCEC::AddLog(CEC_LOG_DEBUG, "did not receive ack");
619 }
620 }
621 msg->condition.Signal();
622}
623
624void CUSBCECAdapterCommunication::WriteNextCommand(void)
625{
626 CCECAdapterMessage *msg(NULL);
627 if (m_outBuffer.Pop(msg))
628 SendMessageToAdapter(msg);
629}