cec: stick some interfaces before the pthread stuff, so we can add other implementati...
[deb_libcec.git] / src / lib / platform / posix / serialport.cpp
CommitLineData
abbca718 1/*
f7febb0e
LOK
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
abbca718 12 * (at your option) any later version.
f7febb0e
LOK
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/
abbca718
LOK
31 */
32
abbca718 33#include <stdio.h>
abbca718 34#include <fcntl.h>
abbca718
LOK
35#include "../serialport.h"
36#include "../baudrate.h"
b9187cc6 37#include "../timeutils.h"
abbca718 38
6df2c52f 39#if defined(__APPLE__)
40#ifndef XCASE
41#define XCASE 0
42#endif
43#ifndef OLCUC
44#define OLCUC 0
45#endif
46#ifndef IUCLC
47#define IUCLC 0
48#endif
49#endif
abbca718 50using namespace std;
b9187cc6 51using namespace CEC;
abbca718
LOK
52
53CSerialPort::CSerialPort()
54{
b9187cc6 55 m_fd = -1;
855a3a98 56 m_tostdout = false;
abbca718
LOK
57}
58
59CSerialPort::~CSerialPort()
60{
61 Close();
62}
63
28352a04 64int8_t CSerialPort::Write(CCECAdapterMessage *data)
abbca718
LOK
65{
66 fd_set port;
b1f50952
LOK
67
68 CLockObject lock(&m_mutex);
abbca718
LOK
69 if (m_fd == -1)
70 {
71 m_error = "port closed";
72 return -1;
73 }
74
7eb13cca 75 int32_t byteswritten = 0;
abbca718 76
2c56dd51
LOK
77 struct timeval timeout, *tv;
78 if (data->transmit_timeout <= 0)
79 {
80 tv = NULL;
81 }
82 else
83 {
84 timeout.tv_sec = (long int)data->transmit_timeout / (long int)1000.;
85 timeout.tv_usec = (long int)data->transmit_timeout % (long int)1000.;
86 tv = &timeout;
87 }
88
5a1e87c8 89 while (byteswritten < (int32_t) data->size())
abbca718
LOK
90 {
91 FD_ZERO(&port);
92 FD_SET(m_fd, &port);
2c56dd51
LOK
93 int returnv = select(m_fd + 1, NULL, &port, NULL, tv);
94 if (returnv < 0)
abbca718 95 {
b9187cc6 96 m_error = strerror(errno);
abbca718
LOK
97 return -1;
98 }
2c56dd51
LOK
99 else if (returnv == 0)
100 {
101 m_error = "timeout";
102 return -1;
103 }
abbca718 104
5a1e87c8 105 returnv = write(m_fd, data->packet.data + byteswritten, data->size() - byteswritten);
abbca718
LOK
106 if (returnv == -1)
107 {
b9187cc6 108 m_error = strerror(errno);
abbca718
LOK
109 return -1;
110 }
111 byteswritten += returnv;
112 }
113
114 //print what's written to stdout for debugging
855a3a98
LOK
115 if (m_tostdout)
116 {
117 printf("%s write:", m_name.c_str());
118 for (int i = 0; i < byteswritten; i++)
119 printf(" %02x", data->at(i));
120
121 printf("\n");
122 }
abbca718
LOK
123
124 return byteswritten;
125}
126
7eb13cca 127int32_t CSerialPort::Read(uint8_t* data, uint32_t len, uint64_t iTimeoutMs /*= 0*/)
abbca718
LOK
128{
129 fd_set port;
130 struct timeval timeout, *tv;
7eb13cca
LOK
131 int64_t now(0), target(0);
132 int32_t bytesread = 0;
abbca718 133
b1f50952 134 CLockObject lock(&m_mutex);
abbca718
LOK
135 if (m_fd == -1)
136 {
137 m_error = "port closed";
138 return -1;
139 }
140
7eb13cca 141 if (iTimeoutMs > 0)
abbca718
LOK
142 {
143 now = GetTimeMs();
144 target = now + (int64_t) iTimeoutMs;
145 }
146
7eb13cca 147 while (bytesread < (int32_t) len && (iTimeoutMs == 0 || target > now))
abbca718 148 {
7eb13cca 149 if (iTimeoutMs == 0)
abbca718
LOK
150 {
151 tv = NULL;
152 }
153 else
154 {
155 timeout.tv_sec = ((long int)target - (long int)now) / (long int)1000.;
156 timeout.tv_usec = ((long int)target - (long int)now) % (long int)1000.;
157 tv = &timeout;
158 }
159
160 FD_ZERO(&port);
161 FD_SET(m_fd, &port);
7eb13cca 162 int32_t returnv = select(m_fd + 1, &port, NULL, NULL, tv);
abbca718
LOK
163
164 if (returnv == -1)
165 {
b9187cc6 166 m_error = strerror(errno);
abbca718
LOK
167 return -1;
168 }
169 else if (returnv == 0)
170 {
171 break; //nothing to read
172 }
173
174 returnv = read(m_fd, data + bytesread, len - bytesread);
175 if (returnv == -1)
176 {
b9187cc6 177 m_error = strerror(errno);
abbca718
LOK
178 return -1;
179 }
180
181 bytesread += returnv;
182
183 if (iTimeoutMs > 0)
184 now = GetTimeMs();
185 }
186
187 //print what's read to stdout for debugging
855a3a98
LOK
188 if (m_tostdout && bytesread > 0)
189 {
190 printf("%s read:", m_name.c_str());
191 for (int i = 0; i < bytesread; i++)
192 printf(" %02x", data[i]);
193
194 printf("\n");
195 }
abbca718
LOK
196
197 return bytesread;
198}
199
200//setting all this stuff up is a pain in the ass
7eb13cca 201bool CSerialPort::Open(string name, uint32_t baudrate, uint8_t databits /* = 8 */, uint8_t stopbits /* = 1 */, uint8_t parity /* = PAR_NONE */)
abbca718
LOK
202{
203 m_name = name;
b9187cc6 204 m_error = strerror(errno);
b1f50952
LOK
205 CLockObject lock(&m_mutex);
206
abbca718
LOK
207 if (databits < 5 || databits > 8)
208 {
209 m_error = "Databits has to be between 5 and 8";
210 return false;
211 }
212
213 if (stopbits != 1 && stopbits != 2)
214 {
215 m_error = "Stopbits has to be 1 or 2";
216 return false;
217 }
218
219 if (parity != PAR_NONE && parity != PAR_EVEN && parity != PAR_ODD)
220 {
221 m_error = "Parity has to be none, even or odd";
222 return false;
223 }
224
225 m_fd = open(name.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
226
227 if (m_fd == -1)
228 {
b9187cc6 229 m_error = strerror(errno);
abbca718
LOK
230 return false;
231 }
232
233 fcntl(m_fd, F_SETFL, 0);
234
235 if (!SetBaudRate(baudrate))
236 {
237 return false;
238 }
239
240 m_options.c_cflag |= (CLOCAL | CREAD);
241 m_options.c_cflag &= ~HUPCL;
242
243 m_options.c_cflag &= ~CSIZE;
244 if (databits == 5) m_options.c_cflag |= CS5;
245 if (databits == 6) m_options.c_cflag |= CS6;
246 if (databits == 7) m_options.c_cflag |= CS7;
247 if (databits == 8) m_options.c_cflag |= CS8;
248
249 m_options.c_cflag &= ~PARENB;
250 if (parity == PAR_EVEN || parity == PAR_ODD)
251 m_options.c_cflag |= PARENB;
252 if (parity == PAR_ODD)
253 m_options.c_cflag |= PARODD;
254
255#ifdef CRTSCTS
256 m_options.c_cflag &= ~CRTSCTS;
257#elif defined(CNEW_RTSCTS)
258 m_options.c_cflag &= ~CNEW_RTSCTS;
259#endif
260
261 if (stopbits == 1) m_options.c_cflag &= ~CSTOPB;
262 else m_options.c_cflag |= CSTOPB;
263
264 //I guessed a little here
265 m_options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | XCASE | ECHOK | ECHONL | ECHOCTL | ECHOPRT | ECHOKE | TOSTOP);
266
267 if (parity == PAR_NONE)
268 {
269 m_options.c_iflag &= ~INPCK;
270 }
271 else
272 {
273 m_options.c_iflag |= INPCK | ISTRIP;
274 }
275
276 m_options.c_iflag &= ~(IXON | IXOFF | IXANY | BRKINT | INLCR | IGNCR | ICRNL | IUCLC | IMAXBEL);
277 m_options.c_oflag &= ~(OPOST | ONLCR | OCRNL);
278
279 if (tcsetattr(m_fd, TCSANOW, &m_options) != 0)
280 {
b9187cc6 281 m_error = strerror(errno);
abbca718
LOK
282 return false;
283 }
284
285 //non-blocking port
286 fcntl(m_fd, F_SETFL, FNDELAY);
287
288 return true;
289}
290
291void CSerialPort::Close()
292{
b1f50952 293 CLockObject lock(&m_mutex);
abbca718
LOK
294 if (m_fd != -1)
295 {
296 close(m_fd);
297 m_fd = -1;
298 m_name = "";
299 m_error = "";
300 }
301}
302
7eb13cca 303bool CSerialPort::SetBaudRate(uint32_t baudrate)
abbca718 304{
b9187cc6 305 int rate = IntToBaudrate(baudrate);
abbca718
LOK
306 if (rate == -1)
307 {
308 char buff[255];
309 sprintf(buff, "%i is not a valid baudrate", baudrate);
310 m_error = buff;
311 return false;
312 }
313
314 //get the current port attributes
315 if (tcgetattr(m_fd, &m_options) != 0)
316 {
b9187cc6 317 m_error = strerror(errno);
abbca718
LOK
318 return false;
319 }
320
321 if (cfsetispeed(&m_options, rate) != 0)
322 {
b9187cc6 323 m_error = strerror(errno);
abbca718
LOK
324 return false;
325 }
326
327 if (cfsetospeed(&m_options, rate) != 0)
328 {
b9187cc6 329 m_error = strerror(errno);
abbca718
LOK
330 return false;
331 }
332
333 return true;
334}
b9187cc6 335
b1f50952 336bool CSerialPort::IsOpen()
b9187cc6 337{
b1f50952 338 CLockObject lock(&m_mutex);
b9187cc6
LOK
339 return m_fd != -1;
340}