cec: created a separate reader thread and fixed the 'lock timeout' bug
[deb_libcec.git] / src / lib / Communication.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
33#include "Communication.h"
34#include "CECParser.h"
35#include "libPlatform/serialport.h"
36#include "util/StdString.h"
37
38using namespace std;
39using namespace CEC;
40
41CCommunication::CCommunication(CCECParser *parser) :
42 m_parser(parser),
43 m_inbuf(NULL),
44 m_iInbufSize(0),
45 m_iInbufUsed(0),
46 m_bStarted(false),
47 m_bStop(false)
48{
49 m_port = new CSerialPort;
50}
51
52CCommunication::~CCommunication(void)
53{
54 m_port->Close();
55 m_port = NULL;
56}
57
58bool CCommunication::Open(const char *strPort, int iBaudRate /* = 38400 */, int iTimeoutMs /* = 10000 */)
59{
60 CLockObject lock(&m_commMutex);
61 if (m_bStarted)
62 return false;
63
64 if (!m_port->Open(strPort, iBaudRate))
65 {
66 CStdString strError;
67 strError.Format("error opening serial port '%s': %s", strPort, m_port->GetError().c_str());
68 m_parser->AddLog(CEC_LOG_ERROR, strError);
69 return false;
70 }
71
72 m_parser->AddLog(CEC_LOG_DEBUG, "connection opened");
73
74 //clear any input bytes
75 uint8_t buff[1024];
76 m_port->Read(buff, sizeof(buff), 50);
77
78 CCondition::Sleep(CEC_SETTLE_DOWN_TIME);
79
80 m_bStop = false;
81 m_bStarted = true;
82 if (pthread_create(&m_thread, NULL, (void *(*) (void *))&CCommunication::ReaderThreadHandler, (void *)this) == 0)
83 {
84 m_parser->AddLog(CEC_LOG_DEBUG, "reader thread created");
85 pthread_detach(m_thread);
86 return true;
87 }
88 else
89 {
90 m_parser->AddLog(CEC_LOG_DEBUG, "could not create a reader thread");
91 }
92
93 return false;
94}
95
96void *CCommunication::ReaderThreadHandler(CCommunication *comm)
97{
98 if (comm)
99 comm->ReaderProcess();
100
101 return NULL;
102}
103
104void CCommunication::Close(void)
105{
106 m_bStop = true;
107 pthread_join(m_thread, NULL);
108 m_port->Close();
109}
110
111void *CCommunication::ReaderProcess(void)
112{
113 while (!m_bStop)
114 {
115 if (!ReadFromDevice(250))
116 {
117 m_bStarted = false;
118 break;
119 }
120
121 CCondition::Sleep(50);
122 }
123
124 m_parser->AddLog(CEC_LOG_DEBUG, "reader thread terminated");
125
126 CLockObject lock(&m_commMutex);
127 m_bStarted = false;
128 return NULL;
129}
130
131bool CCommunication::ReadFromDevice(int iTimeout)
132{
133 uint8_t buff[1024];
134 CLockObject lock(&m_commMutex);
135 int iBytesRead = m_port->Read(buff, sizeof(buff), iTimeout);
136 lock.Leave();
137 if (iBytesRead < 0)
138 {
139 CStdString strError;
140 strError.Format("error reading from serial port: %s", m_port->GetError().c_str());
141 m_parser->AddLog(CEC_LOG_ERROR, strError);
142 return false;
143 }
144 else if (iBytesRead > 0)
145 AddData(buff, iBytesRead);
146
147 return true;
148}
149
150void CCommunication::AddData(uint8_t *data, int iLen)
151{
152 CLockObject lock(&m_bufferMutex);
153 if (iLen + m_iInbufUsed > m_iInbufSize)
154 {
155 m_iInbufSize = iLen + m_iInbufUsed;
156 m_inbuf = (uint8_t*)realloc(m_inbuf, m_iInbufSize);
157 }
158
159 memcpy(m_inbuf + m_iInbufUsed, data, iLen);
160 m_iInbufUsed += iLen;
161 lock.Leave();
162 m_condition.Signal();
163}
164
165bool CCommunication::Write(const cec_frame &data)
166{
167 CLockObject lock(&m_commMutex);
168
169 if (m_port->Write(data) != data.size())
170 {
171 CStdString strError;
172 strError.Format("error writing to serial port: %s", m_port->GetError().c_str());
173 m_parser->AddLog(CEC_LOG_ERROR, strError);
174 return false;
175 }
176
177 m_parser->AddLog(CEC_LOG_DEBUG, "command sent");
178 return true;
179}
180
181bool CCommunication::Read(cec_frame &msg, int iTimeout)
182{
183 CLockObject lock(&m_bufferMutex);
184
185 while (m_iInbufUsed < 1)
186 m_condition.Wait(&m_bufferMutex, iTimeout);
187
188 if (m_iInbufUsed < 1)
189 return false;
190
191 //search for first start of message
192 int startpos = -1;
193 for (int i = 0; i < m_iInbufUsed; i++)
194 {
195 if (m_inbuf[i] == MSGSTART)
196 {
197 startpos = i;
198 break;
199 }
200 }
201
202 if (startpos == -1)
203 return false;
204
205 //move anything from the first start of message to the beginning of the buffer
206 if (startpos > 0)
207 {
208 memmove(m_inbuf, m_inbuf + startpos, m_iInbufUsed - startpos);
209 m_iInbufUsed -= startpos;
210 }
211
212 if (m_iInbufUsed < 2)
213 return false;
214
215 //look for end of message
216 startpos = -1;
217 int endpos = -1;
218 for (int i = 1; i < m_iInbufUsed; i++)
219 {
220 if (m_inbuf[i] == MSGEND)
221 {
222 endpos = i;
223 break;
224 }
225 else if (m_inbuf[i] == MSGSTART)
226 {
227 startpos = i;
228 break;
229 }
230 }
231
232 if (startpos > 0) //we found a msgstart before msgend, this is not right, remove
233 {
234 m_parser->AddLog(CEC_LOG_ERROR, "received MSGSTART before MSGEND");
235 memmove(m_inbuf, m_inbuf + startpos, m_iInbufUsed - startpos);
236 m_iInbufUsed -= startpos;
237 return false;
238 }
239
240 if (endpos > 0) //found a MSGEND
241 {
242 msg.clear();
243 bool isesc = false;
244 for (int i = 1; i < endpos; i++)
245 {
246 if (isesc)
247 {
248 msg.push_back(m_inbuf[i] + (uint8_t)ESCOFFSET);
249 isesc = false;
250 }
251 else if (m_inbuf[i] == MSGESC)
252 {
253 isesc = true;
254 }
255 else
256 {
257 msg.push_back(m_inbuf[i]);
258 }
259 }
260
261 if (endpos + 1 < m_iInbufUsed)
262 memmove(m_inbuf, m_inbuf + endpos + 1, m_iInbufUsed - endpos - 1);
263
264 m_iInbufUsed -= endpos + 1;
265
266 return true;
267 }
268
269 return false;
270}
271
272std::string CCommunication::GetError(void) const
273{
274 return m_port->GetError();
275}