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