cec: send a feature abort again for all unhandled commands, removed statics, refactor...
[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 (IsRunning() && m_port->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 libcec_parameter param;
193 LIB_CEC->Alert(CEC_ALERT_CONNECTION_LOST, param);
194 }
195
196 cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout)
197 {
198 cec_adapter_message_state retVal(ADAPTER_MESSAGE_STATE_UNKNOWN);
199 if (!IsRunning())
200 return retVal;
201
202 CCECAdapterMessage *output = new CCECAdapterMessage(data, iLineTimeout);
203
204 /* mark as waiting for an ack from the destination */
205 MarkAsWaiting(data.destination);
206
207 /* send the message */
208 bRetry = (!m_adapterMessageQueue->Write(output) || output->NeedsRetry()) && output->transmit_timeout > 0;
209 if (bRetry)
210 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
211 retVal = output->state;
212
213 delete output;
214 return retVal;
215 }
216
217 void *CUSBCECAdapterCommunication::Process(void)
218 {
219 CCECAdapterMessage msg;
220 LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread started");
221
222 while (!IsStopped())
223 {
224 /* read from the serial port */
225 if (!ReadFromDevice(50, 5))
226 break;
227
228 /* TODO sleep 5 ms so other threads can get a lock */
229 Sleep(5);
230 }
231
232 m_adapterMessageQueue->Clear();
233 LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread ended");
234 return NULL;
235 }
236
237 bool CUSBCECAdapterCommunication::HandlePoll(const CCECAdapterMessage &msg)
238 {
239 bool bIsError(msg.IsError());
240 cec_adapter_messagecode messageCode(msg.Message());
241 CLockObject lock(m_mutex);
242
243 if (messageCode == MSGCODE_FRAME_START && msg.IsACK())
244 {
245 m_lastPollDestination = msg.Destination();
246 if (msg.Destination() < CECDEVICE_BROADCAST)
247 {
248 if (!m_bWaitingForAck[msg.Destination()] && !msg.IsEOM())
249 {
250 if (m_callback)
251 m_callback->HandlePoll(msg.Initiator(), msg.Destination());
252 }
253 else
254 m_bWaitingForAck[msg.Destination()] = false;
255 }
256 }
257 else if (messageCode == MSGCODE_RECEIVE_FAILED)
258 {
259 /* hack to suppress warnings when an LG is polling */
260 if (m_lastPollDestination != CECDEVICE_UNKNOWN)
261 bIsError = m_callback->HandleReceiveFailed(m_lastPollDestination);
262 }
263
264 return bIsError;
265 }
266
267 void CUSBCECAdapterCommunication::MarkAsWaiting(const cec_logical_address dest)
268 {
269 /* mark as waiting for an ack from the destination */
270 if (dest < CECDEVICE_BROADCAST)
271 {
272 CLockObject lock(m_mutex);
273 m_bWaitingForAck[dest] = true;
274 }
275 }
276
277 void CUSBCECAdapterCommunication::ClearInputBytes(uint32_t iTimeout /* = CEC_CLEAR_INPUT_DEFAULT_WAIT */)
278 {
279 CTimeout timeout(iTimeout);
280 uint8_t buff[1024];
281 ssize_t iBytesRead(0);
282 bool bGotMsgEnd(true);
283
284 while (timeout.TimeLeft() > 0 && ((iBytesRead = m_port->Read(buff, 1024, 5)) > 0 || !bGotMsgEnd))
285 {
286 bGotMsgEnd = false;
287 /* if something was received, wait for MSGEND */
288 for (ssize_t iPtr = 0; iPtr < iBytesRead; iPtr++)
289 bGotMsgEnd = buff[iPtr] == MSGEND;
290 }
291 }
292
293 bool CUSBCECAdapterCommunication::SetLineTimeout(uint8_t iTimeout)
294 {
295 bool bReturn(true);
296 bool bChanged(false);
297
298 /* only send the command if the timeout changed */
299 {
300 CLockObject lock(m_mutex);
301 bChanged = (m_iLineTimeout != iTimeout);
302 m_iLineTimeout = iTimeout;
303 }
304
305 if (bChanged)
306 bReturn = m_commands->SetLineTimeout(iTimeout);
307
308 return bReturn;
309 }
310
311 bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message)
312 {
313 CLockObject adapterLock(m_mutex);
314 if (!m_port->IsOpen())
315 {
316 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());
317 message->state = ADAPTER_MESSAGE_STATE_ERROR;
318 return false;
319 }
320
321 /* write the message */
322 if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size())
323 {
324 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());
325 message->state = ADAPTER_MESSAGE_STATE_ERROR;
326 Close();
327 return false;
328 }
329
330 LIB_CEC->AddLog(CEC_LOG_DEBUG, "command '%s' sent", message->IsTranmission() ? "CEC transmission" : CCECAdapterMessage::ToString(message->Message()));
331 message->state = ADAPTER_MESSAGE_STATE_SENT;
332 return true;
333 }
334
335 bool CUSBCECAdapterCommunication::ReadFromDevice(uint32_t iTimeout, size_t iSize /* = 256 */)
336 {
337 ssize_t iBytesRead(0);
338 uint8_t buff[256];
339 if (iSize > 256)
340 iSize = 256;
341
342 /* read from the serial port */
343 {
344 CLockObject lock(m_mutex);
345 if (!m_port || !m_port->IsOpen())
346 return false;
347
348 iBytesRead = m_port->Read(buff, sizeof(uint8_t) * iSize, iTimeout);
349
350 if (m_port->GetErrorNumber())
351 {
352 LIB_CEC->AddLog(CEC_LOG_ERROR, "error reading from serial port: %s", m_port->GetError().c_str());
353 m_port->Close();
354 return false;
355 }
356 }
357
358 if (iBytesRead < 0 || iBytesRead > 256)
359 return false;
360 else if (iBytesRead > 0)
361 {
362 /* add the data to the current frame */
363 m_adapterMessageQueue->AddData(buff, iBytesRead);
364 }
365
366 return true;
367 }
368
369 CCECAdapterMessage *CUSBCECAdapterCommunication::SendCommand(cec_adapter_messagecode msgCode, CCECAdapterMessage &params, bool bIsRetry /* = false */)
370 {
371 if (!m_port || !m_port->IsOpen() ||
372 !m_adapterMessageQueue)
373 return NULL;
374
375 /* create the adapter message for this command */
376 CCECAdapterMessage *output = new CCECAdapterMessage;
377 output->PushBack(MSGSTART);
378 output->PushEscaped((uint8_t)msgCode);
379 output->Append(params);
380 output->PushBack(MSGEND);
381
382 /* write the command */
383 if (!m_adapterMessageQueue->Write(output))
384 {
385 if (output->state == ADAPTER_MESSAGE_STATE_ERROR)
386 Close();
387 return output;
388 }
389 else
390 {
391 if (!bIsRetry && output->Reply() == MSGCODE_COMMAND_REJECTED && msgCode != MSGCODE_SET_CONTROLLED &&
392 msgCode != MSGCODE_GET_BUILDDATE /* same messagecode value had a different meaning in older fw builds */)
393 {
394 /* if the controller reported that the command was rejected, and we didn't send the command
395 to set controlled mode, then the controller probably switched to auto mode. set controlled
396 mode and retry */
397 LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting controlled mode and retrying");
398 delete output;
399 if (SetControlledMode(true))
400 return SendCommand(msgCode, params, true);
401 }
402 }
403
404 return output;
405 }
406
407 bool CUSBCECAdapterCommunication::CheckAdapter(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
408 {
409 bool bReturn(false);
410 CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
411
412 /* try to ping the adapter */
413 bool bPinged(false);
414 unsigned iPingTry(0);
415 while (timeout.TimeLeft() > 0 && (bPinged = PingAdapter()) == false)
416 {
417 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to a ping (try %d)", ++iPingTry);
418 CEvent::Sleep(500);
419 }
420
421 /* try to read the firmware version */
422 if (bPinged && timeout.TimeLeft() > 0 && m_commands->RequestFirmwareVersion() >= 2)
423 {
424 /* try to set controlled mode for v2+ firmwares */
425 unsigned iControlledTry(0);
426 bool bControlled(false);
427 while (timeout.TimeLeft() > 0 && (bControlled = SetControlledMode(true)) == false)
428 {
429 LIB_CEC->AddLog(CEC_LOG_ERROR, "the adapter did not respond correctly to setting controlled mode (try %d)", ++iControlledTry);
430 CEvent::Sleep(500);
431 }
432 bReturn = bControlled;
433 }
434 else
435 bReturn = true;
436
437 /* try to read the build date */
438 m_commands->RequestBuildDate();
439
440 SetInitialised(bReturn);
441 return bReturn;
442 }
443
444 bool CUSBCECAdapterCommunication::IsOpen(void)
445 {
446 /* thread is not being stopped, the port is open and the thread is running */
447 return !IsStopped() && m_port->IsOpen() && IsRunning();
448 }
449
450 CStdString CUSBCECAdapterCommunication::GetError(void) const
451 {
452 return m_port->GetError();
453 }
454
455 void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo /* = true */)
456 {
457 CLockObject lock(m_mutex);
458 m_bInitialised = bSetTo;
459 }
460
461 bool CUSBCECAdapterCommunication::IsInitialised(void)
462 {
463 CLockObject lock(m_mutex);
464 return m_bInitialised;
465 }
466
467 bool CUSBCECAdapterCommunication::StartBootloader(void)
468 {
469 if (m_port->IsOpen() && m_commands->StartBootloader())
470 {
471 Close();
472 return true;
473 }
474 return false;
475 }
476
477 bool CUSBCECAdapterCommunication::SetAckMask(uint16_t iMask)
478 {
479 if (m_iAckMask == iMask)
480 return true;
481
482 if (m_port && m_port->IsOpen() && m_commands->SetAckMask(iMask))
483 {
484 m_iAckMask = iMask;
485 return true;
486 }
487
488 return false;
489 }
490
491 uint16_t CUSBCECAdapterCommunication::GetAckMask(void)
492 {
493 return m_iAckMask;
494 }
495
496 bool CUSBCECAdapterCommunication::PingAdapter(void)
497 {
498 return m_port->IsOpen() ? m_commands->PingAdapter() : false;
499 }
500
501 uint16_t CUSBCECAdapterCommunication::GetFirmwareVersion(void)
502 {
503 return m_commands->GetFirmwareVersion();
504 }
505
506 uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
507 {
508 return m_commands->RequestBuildDate();
509 }
510
511 bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
512 {
513 return GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION &&
514 GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE;
515 }
516
517 bool CUSBCECAdapterCommunication::PersistConfiguration(libcec_configuration *configuration)
518 {
519 return m_port->IsOpen() ? m_commands->PersistConfiguration(configuration) : false;
520 }
521
522 bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration *configuration)
523 {
524 return m_port->IsOpen() ? m_commands->GetConfiguration(configuration) : false;
525 }
526
527 CStdString CUSBCECAdapterCommunication::GetPortName(void)
528 {
529 return m_port->GetName();
530 }
531
532 bool CUSBCECAdapterCommunication::SetControlledMode(bool controlled)
533 {
534 return m_port->IsOpen() ? m_commands->SetControlledMode(controlled) : false;
535 }
536
537 void *CAdapterPingThread::Process(void)
538 {
539 while (!IsStopped())
540 {
541 if (m_timeout.TimeLeft() == 0)
542 {
543 /* reinit the timeout */
544 m_timeout.Init(CEC_ADAPTER_PING_TIMEOUT);
545
546 /* send a ping to the adapter */
547 bool bPinged(false);
548 int iFailedCounter(0);
549 while (!bPinged && iFailedCounter < 3)
550 {
551 if (!m_com->PingAdapter())
552 {
553 /* sleep and retry */
554 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
555 ++iFailedCounter;
556 }
557 else
558 {
559 bPinged = true;
560 }
561 }
562
563 if (iFailedCounter == 3)
564 {
565 /* failed to ping the adapter 3 times in a row. something must be wrong with the connection */
566 m_com->LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to ping the adapter 3 times in a row. closing the connection.");
567 m_com->StopThread(false);
568 break;
569 }
570 }
571
572 Sleep(500);
573 }
574 return NULL;
575 }