libcec v0.5 (WIP)
[deb_libcec.git] / src / lib / AdapterCommunication.cpp
CommitLineData
a8f0bd18
LOK
1/*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011 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
828682d3 33#include "AdapterCommunication.h"
2abe74eb
LOK
34
35#include "LibCEC.h"
b9187cc6 36#include "platform/serialport.h"
a8f0bd18
LOK
37#include "util/StdString.h"
38
39using namespace std;
40using namespace CEC;
41
2abe74eb 42CAdapterCommunication::CAdapterCommunication(CLibCEC *controller) :
12027dbe 43 m_port(NULL),
2abe74eb 44 m_controller(controller),
a8f0bd18
LOK
45 m_inbuf(NULL),
46 m_iInbufSize(0),
47 m_iInbufUsed(0),
48 m_bStarted(false),
49 m_bStop(false)
50{
51 m_port = new CSerialPort;
52}
53
828682d3 54CAdapterCommunication::~CAdapterCommunication(void)
a8f0bd18 55{
12027dbe
LOK
56 Close();
57
58 if (m_port)
59 {
60 delete m_port;
61 m_port = NULL;
62 }
25701fa6
LOK
63
64 if (m_inbuf)
65 free(m_inbuf);
a8f0bd18
LOK
66}
67
25701fa6 68bool CAdapterCommunication::Open(const char *strPort, uint16_t iBaudRate /* = 38400 */, uint32_t iTimeoutMs /* = 10000 */)
a8f0bd18 69{
12027dbe 70 if (m_bStarted || !m_port)
a8f0bd18
LOK
71 return false;
72
73 if (!m_port->Open(strPort, iBaudRate))
74 {
75 CStdString strError;
76 strError.Format("error opening serial port '%s': %s", strPort, m_port->GetError().c_str());
2abe74eb 77 m_controller->AddLog(CEC_LOG_ERROR, strError);
a8f0bd18
LOK
78 return false;
79 }
80
2abe74eb 81 m_controller->AddLog(CEC_LOG_DEBUG, "connection opened");
a8f0bd18
LOK
82
83 //clear any input bytes
84 uint8_t buff[1024];
85 m_port->Read(buff, sizeof(buff), 50);
86
12027dbe 87 Sleep(CEC_SETTLE_DOWN_TIME);
828682d3
LOK
88
89 if (CreateThread())
a8f0bd18 90 {
2abe74eb 91 m_controller->AddLog(CEC_LOG_DEBUG, "reader thread created");
12027dbe 92 m_bStarted = true;
a8f0bd18
LOK
93 return true;
94 }
95 else
96 {
2abe74eb 97 m_controller->AddLog(CEC_LOG_DEBUG, "could not create a reader thread");
a8f0bd18
LOK
98 }
99
100 return false;
101}
102
828682d3 103void CAdapterCommunication::Close(void)
a8f0bd18 104{
25701fa6
LOK
105 m_bStop = true;
106 m_rcvCondition.Broadcast();
12027dbe 107
828682d3 108 StopThread();
25701fa6
LOK
109
110 if (m_port)
111 m_port->Close();
a8f0bd18
LOK
112}
113
828682d3 114void *CAdapterCommunication::Process(void)
a8f0bd18 115{
12027dbe
LOK
116 m_controller->AddLog(CEC_LOG_DEBUG, "communication thread started");
117
a8f0bd18
LOK
118 while (!m_bStop)
119 {
25701fa6 120 if (!ReadFromDevice(1000))
a8f0bd18
LOK
121 {
122 m_bStarted = false;
123 break;
124 }
125
f7e6ba70 126 if (!m_bStop)
12027dbe 127 Sleep(50);
a8f0bd18
LOK
128 }
129
a8f0bd18
LOK
130 m_bStarted = false;
131 return NULL;
132}
133
25701fa6 134bool CAdapterCommunication::ReadFromDevice(uint32_t iTimeout)
a8f0bd18 135{
25701fa6 136 int32_t iBytesRead;
b6c82769 137
a8f0bd18 138 {
25701fa6
LOK
139 CLockObject lock(&m_mutex);
140
141 uint8_t buff[1024];
142 if (!m_port)
143 return false;
144
145 iBytesRead = m_port->Read(buff, sizeof(buff), iTimeout);
146 if (iBytesRead < 0 || iBytesRead > 256)
147 {
148 CStdString strError;
149 strError.Format("error reading from serial port: %s", m_port->GetError().c_str());
150 m_controller->AddLog(CEC_LOG_ERROR, strError);
151 return false;
152 }
153 else if (iBytesRead > 0)
154 AddData(buff, (uint8_t) iBytesRead);
a8f0bd18 155 }
25701fa6
LOK
156
157 if (iBytesRead > 0)
158 m_rcvCondition.Signal();
a8f0bd18
LOK
159
160 return true;
161}
162
8bca69de 163void CAdapterCommunication::AddData(uint8_t *data, uint8_t iLen)
a8f0bd18 164{
25701fa6 165 if (m_iInbufUsed + iLen > m_iInbufSize)
a8f0bd18 166 {
25701fa6 167 m_iInbufSize = m_iInbufUsed + iLen;
a8f0bd18
LOK
168 m_inbuf = (uint8_t*)realloc(m_inbuf, m_iInbufSize);
169 }
170
171 memcpy(m_inbuf + m_iInbufUsed, data, iLen);
172 m_iInbufUsed += iLen;
a8f0bd18
LOK
173}
174
828682d3 175bool CAdapterCommunication::Write(const cec_frame &data)
a8f0bd18 176{
a8f0bd18 177 {
25701fa6
LOK
178 CLockObject lock(&m_mutex);
179 if (m_port->Write(data) != (int32_t) data.size)
180 {
181 CStdString strError;
182 strError.Format("error writing to serial port: %s", m_port->GetError().c_str());
183 m_controller->AddLog(CEC_LOG_ERROR, strError);
184 return false;
185 }
a8f0bd18 186
25701fa6 187 m_controller->AddLog(CEC_LOG_DEBUG, "command sent");
2abe74eb 188
25701fa6
LOK
189 CCondition::Sleep((uint32_t) data.size * (uint32_t)24 /*data*/ + (uint32_t)5 /*start bit (4.5 ms)*/ + (uint32_t)50 /* to be on the safe side */);
190 }
2abe74eb 191
a8f0bd18
LOK
192 return true;
193}
194
25701fa6 195bool CAdapterCommunication::Read(cec_frame &msg, uint32_t iTimeout)
a8f0bd18 196{
25701fa6 197 CLockObject lock(&m_mutex);
a8f0bd18 198
60fa4578 199 if (m_iInbufUsed < 1)
25701fa6 200 m_rcvCondition.Wait(&m_mutex, iTimeout);
a8f0bd18 201
25701fa6 202 if (m_iInbufUsed < 1 || m_bStop)
a8f0bd18
LOK
203 return false;
204
205 //search for first start of message
25701fa6
LOK
206 int16_t startpos = -1;
207 for (int16_t iPtr = 0; iPtr < m_iInbufUsed; iPtr++)
a8f0bd18 208 {
25701fa6 209 if (m_inbuf[iPtr] == MSGSTART)
a8f0bd18 210 {
25701fa6 211 startpos = iPtr;
a8f0bd18
LOK
212 break;
213 }
214 }
215
216 if (startpos == -1)
217 return false;
218
219 //move anything from the first start of message to the beginning of the buffer
220 if (startpos > 0)
221 {
222 memmove(m_inbuf, m_inbuf + startpos, m_iInbufUsed - startpos);
223 m_iInbufUsed -= startpos;
224 }
225
226 if (m_iInbufUsed < 2)
227 return false;
228
229 //look for end of message
230 startpos = -1;
25701fa6
LOK
231 int16_t endpos = -1;
232 for (int16_t iPtr = 1; iPtr < m_iInbufUsed; iPtr++)
a8f0bd18 233 {
25701fa6 234 if (m_inbuf[iPtr] == MSGEND)
a8f0bd18 235 {
25701fa6 236 endpos = iPtr;
a8f0bd18
LOK
237 break;
238 }
25701fa6 239 else if (m_inbuf[iPtr] == MSGSTART)
a8f0bd18 240 {
25701fa6 241 startpos = iPtr;
a8f0bd18
LOK
242 break;
243 }
244 }
245
246 if (startpos > 0) //we found a msgstart before msgend, this is not right, remove
247 {
2abe74eb 248 m_controller->AddLog(CEC_LOG_ERROR, "received MSGSTART before MSGEND");
a8f0bd18
LOK
249 memmove(m_inbuf, m_inbuf + startpos, m_iInbufUsed - startpos);
250 m_iInbufUsed -= startpos;
251 return false;
252 }
253
254 if (endpos > 0) //found a MSGEND
255 {
256 msg.clear();
257 bool isesc = false;
25701fa6 258 for (int16_t iPtr = 1; iPtr < endpos; iPtr++)
a8f0bd18
LOK
259 {
260 if (isesc)
261 {
25701fa6 262 msg.push_back(m_inbuf[iPtr] + (uint8_t)ESCOFFSET);
a8f0bd18
LOK
263 isesc = false;
264 }
25701fa6 265 else if (m_inbuf[iPtr] == MSGESC)
a8f0bd18
LOK
266 {
267 isesc = true;
268 }
269 else
270 {
25701fa6 271 msg.push_back(m_inbuf[iPtr]);
a8f0bd18
LOK
272 }
273 }
274
275 if (endpos + 1 < m_iInbufUsed)
276 memmove(m_inbuf, m_inbuf + endpos + 1, m_iInbufUsed - endpos - 1);
277
278 m_iInbufUsed -= endpos + 1;
279
280 return true;
281 }
282
283 return false;
284}
285
828682d3 286std::string CAdapterCommunication::GetError(void) const
a8f0bd18
LOK
287{
288 return m_port->GetError();
289}
2abe74eb
LOK
290
291bool CAdapterCommunication::StartBootloader(void)
292{
293 if (!IsRunning())
294 return false;
295
296 m_controller->AddLog(CEC_LOG_DEBUG, "starting the bootloader");
297 cec_frame output;
25701fa6
LOK
298 output.clear();
299
2abe74eb
LOK
300 output.push_back(MSGSTART);
301 PushEscaped(output, MSGCODE_START_BOOTLOADER);
302 output.push_back(MSGEND);
303
304 if (!Write(output))
305 {
306 m_controller->AddLog(CEC_LOG_ERROR, "could not start the bootloader");
307 return false;
308 }
309 m_controller->AddLog(CEC_LOG_DEBUG, "bootloader start command transmitted");
310 return true;
311}
312
313void CAdapterCommunication::PushEscaped(cec_frame &vec, uint8_t byte)
314{
315 if (byte >= MSGESC && byte != MSGSTART)
316 {
317 vec.push_back(MSGESC);
318 vec.push_back(byte - ESCOFFSET);
319 }
320 else
321 {
322 vec.push_back(byte);
323 }
324}
325
326bool CAdapterCommunication::SetAckMask(uint16_t iMask)
327{
328 if (!IsRunning())
329 return false;
330
331 CStdString strLog;
332 strLog.Format("setting ackmask to %2x", iMask);
333 m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
334
335 cec_frame output;
25701fa6 336 output.clear();
2abe74eb
LOK
337
338 output.push_back(MSGSTART);
339 PushEscaped(output, MSGCODE_SET_ACK_MASK);
340 PushEscaped(output, iMask >> 8);
341 PushEscaped(output, (uint8_t)iMask);
342 output.push_back(MSGEND);
343
344 if (!Write(output))
345 {
346 m_controller->AddLog(CEC_LOG_ERROR, "could not set the ackmask");
347 return false;
348 }
349
350 return true;
351}
352
353bool CAdapterCommunication::PingAdapter(void)
354{
355 if (!IsRunning())
356 return false;
357
358 m_controller->AddLog(CEC_LOG_DEBUG, "sending ping");
359 cec_frame output;
25701fa6
LOK
360 output.clear();
361
2abe74eb
LOK
362 output.push_back(MSGSTART);
363 PushEscaped(output, MSGCODE_PING);
364 output.push_back(MSGEND);
365
366 if (!Write(output))
367 {
368 m_controller->AddLog(CEC_LOG_ERROR, "could not send ping command");
369 return false;
370 }
371
372 m_controller->AddLog(CEC_LOG_DEBUG, "ping tranmitted");
373
374 // TODO check for pong
375 return true;
376}