ec8a5c1d48e2f4ea14160ab5bd51719de318a81b
[deb_libcec.git] / src / lib / platform / posix / serialport.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 <stdio.h>
34 #include <fcntl.h>
35 #include "../serialport/serialport.h"
36 #include "../serialport/baudrate.h"
37 #include "../timeutils.h"
38
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
50 using namespace std;
51 using namespace PLATFORM;
52
53 CSerialPort::CSerialPort()
54 {
55 m_fd = -1;
56 m_tostdout = false;
57 }
58
59 CSerialPort::~CSerialPort()
60 {
61 Close();
62 }
63
64 int64_t CSerialPort::Write(uint8_t* data, uint32_t len)
65 {
66 fd_set port;
67
68 CLockObject lock(m_mutex);
69 if (m_fd == -1)
70 {
71 m_error = "port closed";
72 return -1;
73 }
74
75 int64_t byteswritten = 0;
76 struct timeval *tv;
77 //TODO
78 // struct timeval timeout, *tv;
79 // if (data->transmit_timeout <= 0)
80 // {
81 tv = NULL;
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)
91 {
92 FD_ZERO(&port);
93 FD_SET(m_fd, &port);
94 int returnv = select(m_fd + 1, NULL, &port, NULL, tv);
95 if (returnv < 0)
96 {
97 m_error = strerror(errno);
98 return -1;
99 }
100 else if (returnv == 0)
101 {
102 m_error = "timeout";
103 return -1;
104 }
105
106 returnv = write(m_fd, data + byteswritten, len - byteswritten);
107 if (returnv == -1)
108 {
109 m_error = strerror(errno);
110 return -1;
111 }
112 byteswritten += returnv;
113 }
114
115 //print what's written to stdout for debugging
116 if (m_tostdout)
117 {
118 printf("%s write:", m_name.c_str());
119 for (int i = 0; i < byteswritten; i++)
120 printf(" %02x", data[i]);
121
122 printf("\n");
123 }
124
125 return byteswritten;
126 }
127
128 int32_t CSerialPort::Read(uint8_t* data, uint32_t len, uint64_t iTimeoutMs /*= 0*/)
129 {
130 fd_set port;
131 struct timeval timeout, *tv;
132 int64_t now(0), target(0);
133 int32_t bytesread = 0;
134
135 CLockObject lock(m_mutex);
136 if (m_fd == -1)
137 {
138 m_error = "port closed";
139 return -1;
140 }
141
142 if (iTimeoutMs > 0)
143 {
144 now = GetTimeMs();
145 target = now + (int64_t) iTimeoutMs;
146 }
147
148 while (bytesread < (int32_t) len && (iTimeoutMs == 0 || target > now))
149 {
150 if (iTimeoutMs == 0)
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);
163 int32_t returnv = select(m_fd + 1, &port, NULL, NULL, tv);
164
165 if (returnv == -1)
166 {
167 m_error = strerror(errno);
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 {
178 m_error = strerror(errno);
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
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 }
197
198 return bytesread;
199 }
200
201 //setting all this stuff up is a pain in the ass
202 bool CSerialPort::Open(string name, uint32_t baudrate, uint8_t databits /* = 8 */, uint8_t stopbits /* = 1 */, uint8_t parity /* = PAR_NONE */)
203 {
204 m_name = name;
205 m_error = strerror(errno);
206 CLockObject lock(m_mutex);
207
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 {
230 m_error = strerror(errno);
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 {
282 m_error = strerror(errno);
283 return false;
284 }
285
286 //non-blocking port
287 fcntl(m_fd, F_SETFL, FNDELAY);
288
289 return true;
290 }
291
292 void CSerialPort::Close()
293 {
294 CLockObject lock(m_mutex);
295 if (m_fd != -1)
296 {
297 close(m_fd);
298 m_fd = -1;
299 m_name = "";
300 m_error = "";
301 }
302 }
303
304 bool CSerialPort::SetBaudRate(uint32_t baudrate)
305 {
306 int rate = IntToBaudrate(baudrate);
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 {
318 m_error = strerror(errno);
319 return false;
320 }
321
322 if (cfsetispeed(&m_options, rate) != 0)
323 {
324 m_error = strerror(errno);
325 return false;
326 }
327
328 if (cfsetospeed(&m_options, rate) != 0)
329 {
330 m_error = strerror(errno);
331 return false;
332 }
333
334 return true;
335 }
336
337 bool CSerialPort::IsOpen()
338 {
339 CLockObject lock(m_mutex);
340 return m_fd != -1;
341 }