Commit | Line | Data |
---|---|---|
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 | 50 | using namespace std; |
b9187cc6 | 51 | using namespace CEC; |
abbca718 LOK |
52 | |
53 | CSerialPort::CSerialPort() | |
54 | { | |
b9187cc6 | 55 | m_fd = -1; |
855a3a98 | 56 | m_tostdout = false; |
abbca718 LOK |
57 | } |
58 | ||
59 | CSerialPort::~CSerialPort() | |
60 | { | |
61 | Close(); | |
62 | } | |
63 | ||
28352a04 | 64 | int8_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 | 127 | int32_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 | 201 | bool 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 | ||
291 | void 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 | 303 | bool 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 | 336 | bool CSerialPort::IsOpen() |
b9187cc6 | 337 | { |
b1f50952 | 338 | CLockObject lock(&m_mutex); |
b9187cc6 LOK |
339 | return m_fd != -1; |
340 | } |