cec: set the ackmask to 0 when starting, so the earlier mask is cleared when starting...
[deb_libcec.git] / src / lib / adapter / 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 "USBCECAdapterCommunication.h"
34 #include "USBCECAdapterCommands.h"
35 #include "USBCECAdapterMessageQueue.h"
36 #include "../platform/sockets/serialport.h"
37 #include "../platform/util/timeutils.h"
38 #include "../LibCEC.h"
39 #include "../CECProcessor.h"
40
41 using namespace std;
42 using namespace CEC;
43 using namespace PLATFORM;
44
45 #define CEC_ADAPTER_PING_TIMEOUT 15000
46
47 // firmware version 2
48 #define CEC_LATEST_ADAPTER_FW_VERSION 2
49 // firmware date Thu Apr 26 20:14:49 2012 +0000
50 #define CEC_LATEST_ADAPTER_FW_DATE 0x4F99ACB9
51
52 #define LIB_CEC m_callback->GetLib()
53
54 CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCallback *callback, const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */) :
55 IAdapterCommunication(callback),
56 m_port(NULL),
57 m_iLineTimeout(0),
58 m_lastPollDestination(CECDEVICE_UNKNOWN),
59 m_bInitialised(false),
60 m_pingThread(NULL),
61 m_commands(NULL),
62 m_adapterMessageQueue(NULL),
63 m_iAckMask(0xFFFF)
64 {
65 for (unsigned int iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
66 m_bWaitingForAck[iPtr] = false;
67 m_port = new CSerialPort(strPort, iBaudRate);
68 }
69
70 CUSBCECAdapterCommunication::~CUSBCECAdapterCommunication(void)
71 {
72 Close();
73 delete m_commands;
74 delete m_adapterMessageQueue;
75 delete m_port;
76 }
77
78 bool CUSBCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool bSkipChecks /* = false */, bool bStartListening /* = true */)
79 {
80 bool bConnectionOpened(false);
81 {
82 CLockObject lock(m_mutex);
83
84 /* we need the port settings here */
85 if (!m_port)
86 {
87 LIB_CEC->AddLog(CEC_LOG_ERROR, "port is NULL");
88 return bConnectionOpened;
89 }
90
91 /* return true when the port is already open */
92 if (IsOpen())
93 {
94 LIB_CEC->AddLog(CEC_LOG_WARNING, "port is already open");
95 return true;
96 }
97
98 /* adapter commands */
99 if (!m_commands)
100 m_commands = new CUSBCECAdapterCommands(this);
101
102 if (!m_adapterMessageQueue)
103 {
104 m_adapterMessageQueue = new CCECAdapterMessageQueue(this);
105 m_adapterMessageQueue->CreateThread();
106 }
107
108 /* try to open the connection */
109 CStdString strError;
110 CTimeout timeout(iTimeoutMs);
111 while (!bConnectionOpened && timeout.TimeLeft() > 0)
112 {
113 if ((bConnectionOpened = m_port->Open(timeout.TimeLeft())) == false)
114 {
115 strError.Format("error opening serial port '%s': %s", m_port->GetName().c_str(), m_port->GetError().c_str());
116 Sleep(250);
117 }
118 /* and retry every 250ms until the timeout passed */
119 }
120
121 /* return false when we couldn't connect */
122 if (!bConnectionOpened)
123 {
124 LIB_CEC->AddLog(CEC_LOG_ERROR, strError);
125 return false;
126 }
127
128 LIB_CEC->AddLog(CEC_LOG_DEBUG, "connection opened, clearing any previous input and waiting for active transmissions to end before starting");
129 ClearInputBytes();
130 }
131
132 if (!CreateThread())
133 {
134 bConnectionOpened = false;
135 LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a communication thread");
136 }
137 else if (!bSkipChecks && !CheckAdapter())
138 {
139 bConnectionOpened = false;
140 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter failed to pass basic checks");
141 }
142 else if (bStartListening)
143 {
144 /* start a ping thread, that will ping the adapter every 15 seconds
145 if it doesn't receive any ping for 30 seconds, it'll switch to auto mode */
146 m_pingThread = new CAdapterPingThread(this, CEC_ADAPTER_PING_TIMEOUT);
147 if (m_pingThread->CreateThread())
148 {
149 bConnectionOpened = true;
150 }
151 else
152 {
153 bConnectionOpened = false;
154 LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a ping thread");
155 }
156 }
157
158 if (!bConnectionOpened || !bStartListening)
159 StopThread(0);
160
161 return bConnectionOpened;
162 }
163
164 void CUSBCECAdapterCommunication::Close(void)
165 {
166 /* stop the reader thread */
167 StopThread(0);
168
169 CLockObject lock(m_mutex);
170
171 /* set the ackmask to 0 before closing the connection */
172 if (IsOpen() && m_port->GetErrorNumber() == 0)
173 {
174 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - closing the connection", __FUNCTION__);
175 SetAckMask(0);
176 if (m_commands->GetFirmwareVersion() >= 2)
177 SetControlledMode(false);
178 }
179
180 m_adapterMessageQueue->Clear();
181
182 /* stop and delete the ping thread */
183 if (m_pingThread)
184 m_pingThread->StopThread(0);
185 delete m_pingThread;
186 m_pingThread = NULL;
187
188 /* close and delete the com port connection */
189 if (m_port)
190 m_port->Close();
191 }
192
193 cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout)
194 {
195 cec_adapter_message_state retVal(ADAPTER_MESSAGE_STATE_UNKNOWN);
196 if (!IsRunning())
197 return retVal;
198
199 CCECAdapterMessage *output = new CCECAdapterMessage(data, iLineTimeout);
200
201 /* mark as waiting for an ack from the destination */
202 MarkAsWaiting(data.destination);
203
204 /* send the message */
205 bRetry = (!m_adapterMessageQueue->Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
206 if (bRetry)
207 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
208 retVal = output->state;
209
210 delete output;
211 return retVal;
212 }
213
214 void *CUSBCECAdapterCommunication::Process(void)
215 {
216 CCECAdapterMessage msg;
217 LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread started");
218
219 while (!IsStopped())
220 {
221 /* read from the serial port */
222 if (!ReadFromDevice(50, 5))
223 {
224 libcec_parameter param;
225 param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN;
226 LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param);
227
228 break;
229 }
230
231 /* TODO sleep 5 ms so other threads can get a lock */
232 Sleep(5);
233 }
234
235 m_adapterMessageQueue->Clear();
236 LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread ended");
237 return NULL;
238 }
239
240 bool CUSBCECAdapterCommunication::HandlePoll(const CCECAdapterMessage &msg)
241 {
242 bool bIsError(msg.IsError());
243 cec_adapter_messagecode messageCode(msg.Message());
244 CLockObject lock(m_mutex);
245
246 if (messageCode == MSGCODE_FRAME_START && msg.IsACK())
247 {
248 m_lastPollDestination = msg.Destination();
249 if (msg.Destination() < CECDEVICE_BROADCAST)
250 {
251 if (!m_bWaitingForAck[msg.Destination()] && !msg.IsEOM())
252 {
253 if (m_callback)
254 m_callback->HandlePoll(msg.Initiator(), msg.Destination());
255 }
256 else
257 m_bWaitingForAck[msg.Destination()] = false;
258 }
259 }
260 else if (messageCode == MSGCODE_RECEIVE_FAILED)
261 {
262 /* hack to suppress warnings when an LG is polling */
263 if (m_lastPollDestination != CECDEVICE_UNKNOWN)
264 bIsError = m_callback->HandleReceiveFailed(m_lastPollDestination);
265 }
266
267 return bIsError;
268 }
269
270 void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest)
271 {
272 /* mark as waiting for an ack from the destination */
273 if (dest < CECDEVICE_BROADCAST)
274 {
275 CLockObject lock(m_mutex);
276 m_bWaitingForAck[dest] = true;
277 }
278 }
279
280 void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = CEC_CLEAR_INPUT_DEFAULT_WAIT */)
281 {
282 CTimeout timeout(iTimeout);
283 uint8_t buff[1024];
284 ssize_t iBytesRead(0);
285 bool bGotMsgEnd(true);
286
287 while (timeout.TimeLeft() > 0 && ((iBytesRead = m_port->Read(buff, 1024, 5)) > 0 || !bGotMsgEnd))
288 {
289 bGotMsgEnd = false;
290 /* if something was received, wait for MSGEND */
291 for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
292 bGotMsgEnd = buff[iPtr] == MSGEND;
293 }
294 }
295
296 bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
297 {
298 bool bReturn(true);
299 bool bChanged(false);
300
301 /* only send the command if the timeout changed */
302 {
303 CLockObject lock(m_mutex);
304 bChanged = (m_iLineTimeout != iTimeout);
305 m_iLineTimeout = iTimeout;
306 }
307
308 if (bChanged)
309 bReturn = m_commands->SetLineTimeout(iTimeout);
310
311 return bReturn;
312 }
313
314 bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
315 {
316 CLockObject adapterLock(m_mutex);
317 if (!IsOpen())
318 {
319 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());
320 message->state = ADAPTER_MESSAGE_STATE_ERROR;
321 return false;
322 }
323
324 /* write the message */
325 if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
326 {
327 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());
328 message->state = ADAPTER_MESSAGE_STATE_ERROR;
329 Close();
330 return false;
331 }
332
333 LIB_CEC->AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
334 message->state = ADAPTER_MESSAGE_STATE_SENT;
335 return true;
336 }
337
338 bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
339 {
340 ssize_t iBytesRead(0);
341 uint8_t buff[256];
342 if (iSize > 256)
343 iSize = 256;
344
345 /* read from the serial port */
346 {
347 CLockObject lock(m_mutex);
348 if (!IsOpen())
349 return false;
350
351 iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
352
353 if (m_port->GetErrorNumber())
354 {
355 LIB_CEC->AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
356 m_port->Close();
357 return false;
358 }
359 }
360
361 if (iBytesRead < 0 || iBytesRead > 256)
362 return false;
363 else if (iBytesRead > 0)
364 {
365 /* add the data to the current frame */
366 m_adapterMessageQueue->AddData(buff, iBytesRead);
367 }
368
369 return true;
370 }
371
372 CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry /* = false */)
373 {
374 if (!m_port->IsOpen() || !m_adapterMessageQueue)
375 return NULL;
376
377 /* create the adapter message for this command */
378 CCECAdapterMessage *output = new CCECAdapterMessage;
379 output->PushBack(MSGSTART);
380 output->PushEscaped((uint8_t)msgCode);
381 output->Append(params);
382 output->PushBack(MSGEND);
383
384 /* write the command */
385 if (!m_adapterMessageQueue->Write(output))
386 {
387 if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
388 {
389 libcec_parameter param;
390 param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN;
391 LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param);
392
393 Close();
394 }
395 return output;
396 }
397 else
398 {
399 if (!bIsRetry && output->Reply() == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED &&
400 msgCode != MSGCODE_GET_BUILDDATE /* same messagecode value had a different meaning in older fw builds */)
401 {
402 /* if the controller reported that the command was rejected, and we didn't send the command
403 to set controlled mode, then the controller probably switched to auto mode. set controlled
404 mode and retry */
405 LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
406 delete output;
407 if (SetControlledMode(true))
408 return SendCommand(msgCode, params, true);
409 }
410 }
411
412 return output;
413 }
414
415 bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
416 {
417 bool bReturn(false);
418 CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
419
420 /* try to ping the adapter */
421 bool bPinged(false);
422 unsigned iPingTry(0);
423 while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
424 {
425 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
426 CEvent::Sleep(500);
427 }
428
429 /* try to read the firmware version */
430 if (bPinged && timeout.TimeLeft() > 0 && m_commands->RequestFirmwareVersion() >= 2)
431 {
432 /* try to set controlled mode for v2+ firmwares */
433 unsigned iControlledTry(0);
434 bool bControlled(false);
435 while (timeout.TimeLeft() > 0 && (bControlled = SetControlledMode(true)) == false)
436 {
437 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
438 CEvent::Sleep(500);
439 }
440 bReturn = bControlled;
441 }
442 else
443 bReturn = true;
444
445 /* try to read the build date */
446 m_commands->RequestBuildDate();
447
448 SetInitialised(bReturn);
449 return bReturn;
450 }
451
452 bool CUSBCECAdapterCommunication::IsOpen(void)
453 {
454 /* thread is not being stopped, the port is open and the thread is running */
455 return !IsStopped() && m_port->IsOpen() && IsRunning();
456 }
457
458 CStdString CUSBCECAdapterCommunication::GetError(void) const
459 {
460 return m_port->GetError();
461 }
462
463 void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo /* = true */)
464 {
465 CLockObject lock(m_mutex);
466 m_bInitialised = bSetTo;
467 }
468
469 bool CUSBCECAdapterCommunication::IsInitialised(void)
470 {
471 CLockObject lock(m_mutex);
472 return m_bInitialised;
473 }
474
475 bool CUSBCECAdapterCommunication::StartBootloader(void)
476 {
477 if (m_port->IsOpen() && m_commands->StartBootloader())
478 {
479 Close();
480 return true;
481 }
482 return false;
483 }
484
485 bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
486 {
487 if (m_iAckMask == iMask)
488 return true;
489
490 if (IsOpen() && m_commands->SetAckMask(iMask))
491 {
492 m_iAckMask = iMask;
493 return true;
494 }
495
496 LIB_CEC->AddLog(CEC_LOG_ERROR, "couldn't change the ackmask: the connection is closed");
497 return false;
498 }
499
500 uint16_t CUSBCECAdapterCommunication::GetAckMask(void)
501 {
502 return m_iAckMask;
503 }
504
505 bool CUSBCECAdapterCommunication::PingAdapter(void)
506 {
507 return IsOpen() ? m_commands->PingAdapter() : false;
508 }
509
510 uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
511 {
512 return IsOpen() ? m_commands->GetFirmwareVersion() : CEC_FW_VERSION_UNKNOWN;
513 }
514
515 uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
516 {
517 return IsOpen() ? m_commands->RequestBuildDate() : 0;
518 }
519
520 bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
521 {
522 return GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION &&
523 GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE;
524 }
525
526 bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration)
527 {
528 return IsOpen() ? m_commands->PersistConfiguration(configuration) : false;
529 }
530
531 bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration)
532 {
533 return IsOpen() ? m_commands->GetConfiguration(configuration) : false;
534 }
535
536 CStdString CUSBCECAdapterCommunication::GetPortName(void)
537 {
538 return m_port->GetName();
539 }
540
541 bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
542 {
543 return IsOpen() ? m_commands->SetControlledMode(controlled) : false;
544 }
545
546 void *CAdapterPingThread::Process(void)
547 {
548 while (!IsStopped())
549 {
550 if (m_timeout.TimeLeft() == 0)
551 {
552 /* reinit the timeout */
553 m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
554
555 /* send a ping to the adapter */
556 bool bPinged(false);
557 int iFailedCounter(0);
558 while (!bPinged && iFailedCounter < 3)
559 {
560 if (!m_com->PingAdapter())
561 {
562 /* sleep and retry */
563 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
564 ++iFailedCounter;
565 }
566 else
567 {
568 bPinged = true;
569 }
570 }
571
572 if (iFailedCounter == 3)
573 {
574 /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
575 m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
576 m_com->StopThread(false);
577 break;
578 }
579 }
580
581 Sleep(500);
582 }
583 return NULL;
584 }