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