ae1a4e451c288fd4820cc3527efaf4084d67f20b
[deb_libcec.git] / src / lib / AdapterCommunication.cpp
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
33 #include "AdapterCommunication.h"
34
35 #include "LibCEC.h"
36 #include "platform/serialport.h"
37 #include "util/StdString.h"
38
39 using namespace std;
40 using namespace CEC;
41
42 CAdapterCommunication::CAdapterCommunication(CLibCEC *controller) :
43 m_port(NULL),
44 m_controller(controller),
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
54 CAdapterCommunication::~CAdapterCommunication(void)
55 {
56 Close();
57
58 if (m_port)
59 {
60 delete m_port;
61 m_port = NULL;
62 }
63 }
64
65 bool CAdapterCommunication::Open(const char *strPort, uint16_t iBaudRate /* = 38400 */, uint64_t iTimeoutMs /* = 10000 */)
66 {
67 CLockObject lock(&m_commMutex);
68 if (m_bStarted || !m_port)
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());
75 m_controller->AddLog(CEC_LOG_ERROR, strError);
76 return false;
77 }
78
79 m_controller->AddLog(CEC_LOG_DEBUG, "connection opened");
80
81 //clear any input bytes
82 uint8_t buff[1024];
83 m_port->Read(buff, sizeof(buff), 50);
84
85 Sleep(CEC_SETTLE_DOWN_TIME);
86
87 if (CreateThread())
88 {
89 m_controller->AddLog(CEC_LOG_DEBUG, "reader thread created");
90 m_bStarted = true;
91 return true;
92 }
93 else
94 {
95 m_controller->AddLog(CEC_LOG_DEBUG, "could not create a reader thread");
96 }
97
98 return false;
99 }
100
101 void CAdapterCommunication::Close(void)
102 {
103 CLockObject lock(&m_commMutex);
104 if (m_port)
105 m_port->Close();
106
107 StopThread();
108 }
109
110 void *CAdapterCommunication::Process(void)
111 {
112 m_controller->AddLog(CEC_LOG_DEBUG, "communication thread started");
113
114 while (!m_bStop)
115 {
116 CLockObject lock(&m_commMutex);
117 if (!ReadFromDevice(250))
118 {
119 m_bStarted = false;
120 break;
121 }
122
123 if (!m_bStop)
124 {
125 lock.Leave();
126 Sleep(50);
127 }
128 }
129
130 m_bStarted = false;
131 return NULL;
132 }
133
134 bool CAdapterCommunication::ReadFromDevice(uint64_t iTimeout)
135 {
136 uint8_t buff[1024];
137 if (!m_port)
138 return false;
139
140 int32_t iBytesRead = m_port->Read(buff, sizeof(buff), iTimeout);
141 if (iBytesRead < 0 || iBytesRead > 256)
142 {
143 CStdString strError;
144 strError.Format("error reading from serial port: %s", m_port->GetError().c_str());
145 m_controller->AddLog(CEC_LOG_ERROR, strError);
146 return false;
147 }
148 else if (iBytesRead > 0)
149 AddData(buff, (uint8_t) iBytesRead);
150
151 return true;
152 }
153
154 void CAdapterCommunication::AddData(uint8_t *data, uint8_t iLen)
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
169 bool CAdapterCommunication::Write(const cec_frame &data)
170 {
171 CLockObject lock(&m_commMutex);
172
173 if (m_port->Write(data) != (int) data.size())
174 {
175 CStdString strError;
176 strError.Format("error writing to serial port: %s", m_port->GetError().c_str());
177 m_controller->AddLog(CEC_LOG_ERROR, strError);
178 return false;
179 }
180
181 m_controller->AddLog(CEC_LOG_DEBUG, "command sent");
182
183 Sleep((int) data.size() * 24 /*data*/ + 5 /*start bit (4.5 ms)*/ + 50 /* to be on the safe side */);
184
185 return true;
186 }
187
188 bool CAdapterCommunication::Read(cec_frame &msg, uint64_t iTimeout)
189 {
190 CLockObject lock(&m_bufferMutex);
191
192 if (m_iInbufUsed < 1)
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 {
241 m_controller->AddLog(CEC_LOG_ERROR, "received MSGSTART before MSGEND");
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
279 std::string CAdapterCommunication::GetError(void) const
280 {
281 return m_port->GetError();
282 }
283
284 bool 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
304 void 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
317 bool 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
343 bool 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 }