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