]> sigrok.org Git - libsigrok.git/blame - hardware/common/serial.c
serial: revamp internal API
[libsigrok.git] / hardware / common / serial.c
CommitLineData
a1bb33af
UH
1/*
2 * This file is part of the sigrok project.
3 *
c73d2ea4 4 * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
b19f4622 5 * Copyright (C) 2010-2012 Uwe Hermann <uwe@hermann-uwe.de>
a1bb33af
UH
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
54b38f64 21#include <string.h>
d02a535e
BV
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <fcntl.h>
25#include <unistd.h>
926b866c 26#ifdef _WIN32
a9f54bcd 27#include <windows.h>
926b866c
UH
28#else
29#include <glob.h>
d02a535e 30#include <termios.h>
926b866c 31#endif
d02a535e 32#include <stdlib.h>
b19f4622 33#include <errno.h>
a1bb33af 34#include <glib.h>
45c59c8b
BV
35#include "libsigrok.h"
36#include "libsigrok-internal.h"
a1bb33af 37
b19f4622
UH
38/* Message logging helpers with driver-specific prefix string. */
39#define DRIVER_LOG_DOMAIN "serial: "
40#define sr_log(l, s, args...) sr_log(l, DRIVER_LOG_DOMAIN s, ## args)
41#define sr_spew(s, args...) sr_spew(DRIVER_LOG_DOMAIN s, ## args)
42#define sr_dbg(s, args...) sr_dbg(DRIVER_LOG_DOMAIN s, ## args)
43#define sr_info(s, args...) sr_info(DRIVER_LOG_DOMAIN s, ## args)
44#define sr_warn(s, args...) sr_warn(DRIVER_LOG_DOMAIN s, ## args)
45#define sr_err(s, args...) sr_err(DRIVER_LOG_DOMAIN s, ## args)
46
926b866c
UH
47// FIXME: Must be moved, or rather passed as function argument.
48#ifdef _WIN32
a0ecd83b 49static HANDLE hdl;
926b866c
UH
50#endif
51
b19f4622
UH
52/**
53 * Open the specified serial port.
54 *
299bdb24 55 * @param serial Previously initialized serial port structure.
b19f4622 56 * @param flags Flags to use when opening the serial port.
299bdb24 57 * TODO: Abstract 'flags', currently they're OS-specific!
b19f4622 58 *
299bdb24
BV
59 * If the serial structure contains a serialcomm string, it will be
60 * passed to serial_set_paramstr() after the port is opened.
61 *
62 * @return SR_OK on success, SR_ERR on failure.
b19f4622 63 */
299bdb24 64SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags)
d02a535e 65{
b19f4622 66
299bdb24
BV
67 if (!serial) {
68 sr_dbg("Invalid serial port.");
69 return SR_ERR;
70 }
71
72 sr_spew("Opening serial port '%s' (flags %d).", serial->port, flags);
b19f4622 73
926b866c 74#ifdef _WIN32
299bdb24 75 hdl = CreateFile(serial->port, GENERIC_READ | GENERIC_WRITE, 0, 0,
926b866c
UH
76 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
77 if (hdl == INVALID_HANDLE_VALUE) {
299bdb24
BV
78 sr_err("Error opening serial port '%s'.", serial->port);
79 return SR_ERR;
926b866c 80 }
926b866c 81#else
299bdb24
BV
82 if ((serial->fd = open(serial->port, flags)) < 0) {
83 sr_err("Error opening serial port '%s': %s.", serial->port,
b19f4622 84 strerror(errno));
299bdb24
BV
85 return SR_ERR;
86 } else
87 sr_spew("Opened serial port '%s' (fd %d).", serial->port, serial->fd);
926b866c 88#endif
299bdb24
BV
89
90 if (serial->serialcomm)
91 return serial_set_paramstr(serial, serial->serialcomm);
92 else
93 return SR_OK;
d02a535e
BV
94}
95
b19f4622
UH
96/**
97 * Close the specified serial port.
98 *
299bdb24 99 * @param serial Previously initialized serial port structure.
b19f4622 100 *
299bdb24 101 * @return SR_OK on success, SR_ERR on failure.
2119ab03 102 */
299bdb24 103SR_PRIV int serial_close(struct sr_serial_dev_inst *serial)
d02a535e 104{
299bdb24
BV
105 int ret;
106
107 if (!serial) {
108 sr_dbg("Invalid serial port.");
109 return SR_ERR;
110 }
111
112 if (serial->fd == -1) {
113 sr_dbg("Cannot close unopened serial port %s (fd %d).",
114 serial->port, serial->fd);
115 return SR_ERR;
116 }
117
118 sr_spew("Closing serial port %s (fd %d).", serial->port, serial->fd);
119 ret = SR_OK;
b19f4622 120
926b866c 121#ifdef _WIN32
2119ab03 122 /* Returns non-zero upon success, 0 upon failure. */
299bdb24
BV
123 if (CloseHandle(hdl) == 0)
124 ret = SR_ERR;
926b866c 125#else
2119ab03 126 /* Returns 0 upon success, -1 upon failure. */
299bdb24
BV
127 if (close(serial->fd) < 0) {
128 sr_err("Error closing serial port: %s (fd %d).", strerror(errno),
129 serial->fd);
130 ret = SR_ERR;
b19f4622 131 }
299bdb24
BV
132#endif
133
134 serial->fd = -1;
b19f4622
UH
135
136 return ret;
d02a535e
BV
137}
138
b19f4622 139/**
299bdb24 140 * Flush serial port buffers.
b19f4622 141 *
299bdb24 142 * @param serial Previously initialized serial port structure.
b19f4622 143 *
299bdb24 144 * @return SR_OK on success, SR_ERR on failure.
1fdb75e1 145 */
299bdb24 146SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial)
06d64eb8 147{
299bdb24
BV
148 int ret;
149
150 if (!serial) {
151 sr_dbg("Invalid serial port.");
152 return SR_ERR;
153 }
154
155 if (serial->fd == -1) {
156 sr_dbg("Cannot flush unopened serial port %s (fd %d).",
157 serial->port, serial->fd);
158 return SR_ERR;
159 }
160
161 sr_spew("Flushing serial port %s (fd %d).", serial->port, serial->fd);
162 ret = SR_OK;
b19f4622 163
1fdb75e1
UH
164#ifdef _WIN32
165 /* Returns non-zero upon success, 0 upon failure. */
299bdb24
BV
166 if (PurgeComm(hdl, PURGE_RXCLEAR | PURGE_TXCLEAR) == 0) ) {
167 sr_err("Error flushing serial port: %s.", strerror(errno));
168 ret = SR_ERR;
169 }
1fdb75e1
UH
170#else
171 /* Returns 0 upon success, -1 upon failure. */
299bdb24 172 if (tcflush(serial->fd, TCIOFLUSH) < 0) {
b19f4622 173 sr_err("Error flushing serial port: %s.", strerror(errno));
299bdb24
BV
174 ret = SR_ERR;
175 }
b19f4622
UH
176
177 return ret;
1fdb75e1 178#endif
06d64eb8
BV
179}
180
b19f4622 181/**
2119ab03 182 * Write a number of bytes to the specified serial port.
b19f4622 183 *
299bdb24 184 * @param serial Previously initialized serial port structure.
b19f4622
UH
185 * @param buf Buffer containing the bytes to write.
186 * @param count Number of bytes to write.
187 *
188 * @return The number of bytes written, or -1 upon failure.
2119ab03 189 */
299bdb24
BV
190SR_PRIV int serial_write(struct sr_serial_dev_inst *serial,
191 const void *buf, size_t count)
2119ab03 192{
299bdb24
BV
193 ssize_t ret;
194
195 if (!serial) {
196 sr_dbg("Invalid serial port.");
197 return -1;
198 }
199
200 if (serial->fd == -1) {
201 sr_dbg("Cannot use unopened serial port %s (fd %d).",
202 serial->port, serial->fd);
203 return -1;
204 }
205
2119ab03
UH
206#ifdef _WIN32
207 DWORD tmp = 0;
208
209 /* FIXME */
210 /* Returns non-zero upon success, 0 upon failure. */
211 WriteFile(hdl, buf, count, &tmp, NULL);
212#else
213 /* Returns the number of bytes written, or -1 upon failure. */
299bdb24 214 ret = write(serial->fd, buf, count);
b19f4622 215 if (ret < 0)
299bdb24 216 sr_err("Write error: %s.", strerror(errno));
302c4b5a 217 else
299bdb24
BV
218 sr_spew("Wrote %d/%d bytes (fd %d).", ret, count, serial->fd);
219#endif
b19f4622
UH
220
221 return ret;
2119ab03
UH
222}
223
b19f4622 224/**
2119ab03 225 * Read a number of bytes from the specified serial port.
b19f4622 226 *
299bdb24 227 * @param serial Previously initialized serial port structure.
b19f4622
UH
228 * @param buf Buffer where to store the bytes that are read.
229 * @param count The number of bytes to read.
230 *
231 * @return The number of bytes read, or -1 upon failure.
2119ab03 232 */
299bdb24
BV
233SR_PRIV int serial_read(struct sr_serial_dev_inst *serial, void *buf,
234 size_t count)
2119ab03 235{
299bdb24
BV
236 ssize_t ret;
237
238 if (!serial) {
239 sr_dbg("Invalid serial port.");
240 return -1;
241 }
242
243 if (serial->fd == -1) {
244 sr_dbg("Cannot use unopened serial port %s (fd %d).",
245 serial->port, serial->fd);
246 return -1;
247 }
248
2119ab03
UH
249#ifdef _WIN32
250 DWORD tmp = 0;
251
252 /* FIXME */
253 /* Returns non-zero upon success, 0 upon failure. */
254 return ReadFile(hdl, buf, count, &tmp, NULL);
255#else
256 /* Returns the number of bytes read, or -1 upon failure. */
299bdb24
BV
257 ret = read(serial->fd, buf, count);
258 if (ret < 0)
b19f4622
UH
259 /*
260 * Should be sr_err(), but that would yield lots of
261 * "Resource temporarily unavailable" messages.
262 */
299bdb24
BV
263 sr_spew("Read error: %s (fd %d).", strerror(errno), serial->fd);
264 else
265 sr_spew("Read %d/%d bytes (fd %d).", ret, count, serial->fd);
266#endif
b19f4622
UH
267
268 return ret;
2119ab03
UH
269}
270
b19f4622
UH
271/**
272 * Set serial parameters for the specified serial port.
273 *
299bdb24 274 * @param serial Previously initialized serial port structure.
b19f4622
UH
275 * @param baudrate The baudrate to set.
276 * @param bits The number of data bits to use.
277 * @param parity The parity setting to use (0 = none, 1 = even, 2 = odd).
278 * @param stopbits The number of stop bits to use (1 or 2).
279 * @param flowcontrol The flow control settings to use (0 = none, 1 = RTS/CTS,
280 * 2 = XON/XOFF).
2119ab03 281 *
b19f4622 282 * @return SR_OK upon success, SR_ERR upon failure.
1ff7712c 283 */
299bdb24
BV
284SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, int baudrate,
285 int bits, int parity, int stopbits, int flowcontrol)
d02a535e 286{
299bdb24
BV
287
288 if (!serial) {
289 sr_dbg("Invalid serial port.");
290 return SR_ERR;
291 }
292
293 if (serial->fd == -1) {
294 sr_dbg("Cannot configure unopened serial port %s (fd %d).",
295 serial->port, serial->fd);
296 return SR_ERR;
297 }
298
299 sr_spew("Setting serial parameters on port %s (fd %d).", serial->port,
300 serial->fd);
b19f4622 301
926b866c
UH
302#ifdef _WIN32
303 DCB dcb;
304
299bdb24 305 if (!GetCommState(hdl, &dcb))
e46b8fb1 306 return SR_ERR;
926b866c 307
0abee507 308 switch (baudrate) {
926b866c
UH
309 case 115200:
310 dcb.BaudRate = CBR_115200;
311 break;
312 case 57600:
313 dcb.BaudRate = CBR_57600;
314 break;
315 case 38400:
316 dcb.BaudRate = CBR_38400;
317 break;
318 case 19200:
319 dcb.BaudRate = CBR_19200;
320 break;
321 case 9600:
322 dcb.BaudRate = CBR_9600;
323 break;
e8e9dcdd
AG
324 case 4800:
325 dcb.BaudRate = CBR_4800;
326 break;
0f708301
AG
327 case 2400:
328 dcb.BaudRate = CBR_2400;
329 break;
926b866c 330 default:
299bdb24 331 sr_err("Unsupported baudrate %d.", baudrate);
b19f4622 332 return SR_ERR;
926b866c
UH
333 }
334 dcb.ByteSize = bits;
335 dcb.Parity = NOPARITY; /* TODO: Don't hardcode. */
336 dcb.StopBits = ONESTOPBIT; /* TODO: Don't hardcode. */
337
299bdb24 338 if (!SetCommState(hdl, &dcb))
e46b8fb1 339 return SR_ERR;
926b866c 340#else
d02a535e 341 struct termios term;
1ff7712c 342 speed_t baud;
d02a535e 343
299bdb24
BV
344 if (tcgetattr(serial->fd, &term) < 0) {
345 sr_err("tcgetattr() error on port %s (fd %d): %s.",
346 serial->port, serial->fd, strerror(errno));
6a6e23ab 347 return SR_ERR;
b19f4622 348 }
6a6e23ab 349
0abee507 350 switch (baudrate) {
b19f4622
UH
351 case 50:
352 baud = B50;
353 break;
354 case 75:
355 baud = B75;
356 break;
357 case 110:
358 baud = B110;
359 break;
360 case 134:
361 baud = B134;
362 break;
363 case 150:
364 baud = B150;
365 break;
366 case 200:
367 baud = B200;
368 break;
369 case 300:
370 baud = B300;
371 break;
372 case 600:
373 baud = B600;
374 break;
375 case 1200:
376 baud = B1200;
377 break;
378 case 1800:
379 baud = B1800;
380 break;
0f708301
AG
381 case 2400:
382 baud = B2400;
383 break;
e8e9dcdd
AG
384 case 4800:
385 baud = B4800;
386 break;
1ff7712c
DR
387 case 9600:
388 baud = B9600;
389 break;
b19f4622
UH
390 case 19200:
391 baud = B19200;
392 break;
1ff7712c
DR
393 case 38400:
394 baud = B38400;
395 break;
396 case 57600:
397 baud = B57600;
398 break;
399 case 115200:
400 baud = B115200;
401 break;
b19f4622
UH
402 case 230400:
403 baud = B230400;
404 break;
9a751023 405#ifndef __APPLE__
1ff7712c
DR
406 case 460800:
407 baud = B460800;
408 break;
9a751023 409#endif
1ff7712c 410 default:
299bdb24 411 sr_err("Unsupported baudrate %d.", baudrate);
e46b8fb1 412 return SR_ERR;
1ff7712c 413 }
b19f4622 414
299bdb24 415 sr_spew("Configuring output baudrate to %d (%d).", baudrate, baud);
b19f4622 416 if (cfsetospeed(&term, baud) < 0) {
5df7b201 417 sr_err("cfsetospeed() error: %s.", strerror(errno));
e46b8fb1 418 return SR_ERR;
b19f4622
UH
419 }
420
299bdb24 421 sr_spew("Configuring input baudrate to %d (%d).", baudrate, baud);
b19f4622 422 if (cfsetispeed(&term, baud) < 0) {
5df7b201 423 sr_err("cfsetispeed() error: %s.", strerror(errno));
e46b8fb1 424 return SR_ERR;
b19f4622 425 }
1ff7712c 426
299bdb24 427 sr_spew("Configuring %d data bits.", bits);
d02a535e 428 term.c_cflag &= ~CSIZE;
1ff7712c
DR
429 switch (bits) {
430 case 8:
431 term.c_cflag |= CS8;
432 break;
433 case 7:
434 term.c_cflag |= CS7;
435 break;
436 default:
299bdb24 437 sr_err("Unsupported data bits number %d.", bits);
e46b8fb1 438 return SR_ERR;
1ff7712c
DR
439 }
440
299bdb24 441 sr_spew("Configuring %d stop bits.", stopbits);
d02a535e 442 term.c_cflag &= ~CSTOPB;
1ff7712c
DR
443 switch (stopbits) {
444 case 1:
299bdb24 445 term.c_cflag &= ~CSTOPB;
1ff7712c
DR
446 break;
447 case 2:
448 term.c_cflag |= CSTOPB;
d7c776b9 449 break;
1ff7712c 450 default:
299bdb24 451 sr_err("Unsupported stopbits number %d.", stopbits);
e46b8fb1 452 return SR_ERR;
1ff7712c
DR
453 }
454
f38b9763
BV
455 term.c_iflag &= ~(IXON | IXOFF);
456 term.c_cflag &= ~CRTSCTS;
1ff7712c 457 switch (flowcontrol) {
f38b9763
BV
458 case 0:
459 /* No flow control. */
299bdb24 460 sr_spew("Configuring no flow control.");
1ff7712c
DR
461 break;
462 case 1:
299bdb24 463 sr_spew("Configuring RTS/CTS flow control.");
1ff7712c 464 term.c_cflag |= CRTSCTS;
d7c776b9 465 break;
f38b9763 466 case 2:
299bdb24 467 sr_spew("Configuring XON/XOFF flow control.");
f38b9763
BV
468 term.c_iflag |= IXON | IXOFF;
469 break;
1ff7712c 470 default:
299bdb24 471 sr_err("Unsupported flow control setting %d.", flowcontrol);
e46b8fb1 472 return SR_ERR;
1ff7712c
DR
473 }
474
ac4a2ea4
DR
475 term.c_iflag &= ~IGNPAR;
476 term.c_cflag &= ~(PARODD | PARENB);
1ff7712c 477 switch (parity) {
f8c1fcda 478 case SERIAL_PARITY_NONE:
299bdb24 479 sr_spew("Configuring no parity.");
1ff7712c
DR
480 term.c_iflag |= IGNPAR;
481 break;
f8c1fcda 482 case SERIAL_PARITY_EVEN:
299bdb24 483 sr_spew("Configuring even parity.");
ac4a2ea4 484 term.c_cflag |= PARENB;
1ff7712c 485 break;
f8c1fcda 486 case SERIAL_PARITY_ODD:
299bdb24 487 sr_spew("Configuring odd parity.");
ac4a2ea4 488 term.c_cflag |= PARENB | PARODD;
1ff7712c
DR
489 break;
490 default:
299bdb24 491 sr_err("Unsupported parity setting %d.", parity);
e46b8fb1 492 return SR_ERR;
1ff7712c
DR
493 }
494
299bdb24 495 /* Do not translate carriage return to newline on input. */
fb9d3bf9 496 term.c_iflag &= ~(ICRNL);
b19f4622
UH
497
498 /* Disable canonical mode, and don't echo input characters. */
5c51e098
BV
499 term.c_lflag &= ~(ICANON | ECHO);
500
b19f4622 501 /* Write the configured settings. */
299bdb24
BV
502 if (tcsetattr(serial->fd, TCSADRAIN, &term) < 0) {
503 sr_err("tcsetattr() error: %s.", strerror(errno));
e46b8fb1 504 return SR_ERR;
b19f4622 505 }
926b866c 506#endif
d02a535e 507
e46b8fb1 508 return SR_OK;
d02a535e 509}
792fc686 510
299bdb24
BV
511/**
512 * Set serial parameters for the specified serial port.
513 *
514 * @param serial Previously initialized serial port structure.
515 * @param paramstr A serial communication parameters string, in the form
516 * of <speed>/<data bits><parity><stopbits>, for example "9600/8n1" or
517 * "600/7o2".
518 *
519 * @return SR_OK upon success, SR_ERR upon failure.
520 */
792fc686 521#define SERIAL_COMM_SPEC "^(\\d+)/([78])([neo])([12])$"
299bdb24
BV
522SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial,
523 const char *paramstr)
792fc686
BV
524{
525 GRegex *reg;
526 GMatchInfo *match;
527 int speed, databits, parity, stopbits;
528 char *mstr;
529
530 speed = databits = parity = stopbits = 0;
531 reg = g_regex_new(SERIAL_COMM_SPEC, 0, 0, NULL);
532 if (g_regex_match(reg, paramstr, 0, &match)) {
533 if ((mstr = g_match_info_fetch(match, 1)))
534 speed = strtoul(mstr, NULL, 10);
535 g_free(mstr);
536 if ((mstr = g_match_info_fetch(match, 2)))
537 databits = strtoul(mstr, NULL, 10);
538 g_free(mstr);
539 if ((mstr = g_match_info_fetch(match, 3))) {
540 switch (mstr[0]) {
541 case 'n':
542 parity = SERIAL_PARITY_NONE;
543 break;
544 case 'e':
545 parity = SERIAL_PARITY_EVEN;
546 break;
547 case 'o':
548 parity = SERIAL_PARITY_ODD;
549 break;
550 }
551 }
552 g_free(mstr);
553 if ((mstr = g_match_info_fetch(match, 4)))
554 stopbits = strtoul(mstr, NULL, 10);
555 g_free(mstr);
556 }
557 g_match_info_unref(match);
558 g_regex_unref(reg);
559
560 if (speed)
299bdb24 561 return serial_set_params(serial, speed, databits, parity, stopbits, 0);
792fc686
BV
562 else
563 return SR_ERR_ARG;
564}
565
299bdb24
BV
566/**
567 * Read a line from the specified serial port.
568 *
569 * @param serial Previously initialized serial port structure.
570 * @param buf Buffer where to store the bytes that are read.
571 * @param buflen Size of the buffer.
572 * @param timeout_ms How long to wait for a line to come in.
573 *
574 * Reading stops when CR of LR is found, which is stripped from the buffer.
575 *
576 * @return SR_OK on success, SR_ERR on failure.
577 */
578SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf,
579 int *buflen, gint64 timeout_ms)
6f22a8ef 580{
b87f8504 581 gint64 start;
6f22a8ef
UH
582 int maxlen, len;
583
299bdb24
BV
584 if (!serial || serial->fd == -1) {
585 sr_dbg("Invalid serial port.");
586 return SR_ERR;
587 }
588
589 if (serial->fd == -1) {
590 sr_dbg("Cannot use unopened serial port %s (fd %d).",
591 serial->port, serial->fd);
592 return -1;
593 }
594
6f22a8ef
UH
595 timeout_ms *= 1000;
596 start = g_get_monotonic_time();
597
598 maxlen = *buflen;
599 *buflen = len = 0;
600 while(1) {
601 len = maxlen - *buflen - 1;
602 if (len < 1)
603 break;
299bdb24 604 len = serial_read(serial, *buf + *buflen, 1);
6f22a8ef
UH
605 if (len > 0) {
606 *buflen += len;
607 *(*buf + *buflen) = '\0';
318dd53c
BV
608 if (*buflen > 0 && (*(*buf + *buflen - 1) == '\r'
609 || *(*buf + *buflen - 1) == '\n')) {
610 /* Strip CR/LF and terminate. */
6f22a8ef
UH
611 *(*buf + --*buflen) = '\0';
612 break;
613 }
614 }
615 if (g_get_monotonic_time() - start > timeout_ms)
616 /* Timeout */
617 break;
618 g_usleep(2000);
619 }
318dd53c
BV
620 if (*buflen)
621 sr_dbg("Received %d: '%s'.", *buflen, *buf);
6f22a8ef
UH
622
623 return SR_OK;
624}