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