]> sigrok.org Git - libserialport.git/blob - serialport.c
Support timing helpers on Windows.
[libserialport.git] / serialport.c
1 /*
2  * This file is part of the libserialport project.
3  *
4  * Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
5  * Copyright (C) 2010-2015 Uwe Hermann <uwe@hermann-uwe.de>
6  * Copyright (C) 2013-2015 Martin Ling <martin-libserialport@earth.li>
7  * Copyright (C) 2013 Matthias Heidbrink <m-sigrok@heidbrink.biz>
8  * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU Lesser General Public License as
12  * published by the Free Software Foundation, either version 3 of the
13  * License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24 #include <config.h>
25 #include "libserialport.h"
26 #include "libserialport_internal.h"
27
28 static const struct std_baudrate std_baudrates[] = {
29 #ifdef _WIN32
30         /*
31          * The baudrates 50/75/134/150/200/1800/230400/460800 do not seem to
32          * have documented CBR_* macros.
33          */
34         BAUD(110), BAUD(300), BAUD(600), BAUD(1200), BAUD(2400), BAUD(4800),
35         BAUD(9600), BAUD(14400), BAUD(19200), BAUD(38400), BAUD(57600),
36         BAUD(115200), BAUD(128000), BAUD(256000),
37 #else
38         BAUD(50), BAUD(75), BAUD(110), BAUD(134), BAUD(150), BAUD(200),
39         BAUD(300), BAUD(600), BAUD(1200), BAUD(1800), BAUD(2400), BAUD(4800),
40         BAUD(9600), BAUD(19200), BAUD(38400), BAUD(57600), BAUD(115200),
41         BAUD(230400),
42 #if !defined(__APPLE__) && !defined(__OpenBSD__)
43         BAUD(460800),
44 #endif
45 #endif
46 };
47
48 #define NUM_STD_BAUDRATES ARRAY_SIZE(std_baudrates)
49
50 void (*sp_debug_handler)(const char *format, ...) = sp_default_debug_handler;
51
52 static enum sp_return get_config(struct sp_port *port, struct port_data *data,
53         struct sp_port_config *config);
54
55 static enum sp_return set_config(struct sp_port *port, struct port_data *data,
56         const struct sp_port_config *config);
57
58 /* Timing abstraction */
59
60 struct time {
61 #ifdef _WIN32
62         int64_t ticks;
63 #else
64         struct timeval tv;
65 #endif
66 };
67
68 struct timeout {
69         unsigned int ms, limit_ms;
70         struct time start, now, end, delta, delta_max;
71         struct timeval delta_tv;
72         bool overflow;
73 };
74
75 static void time_get(struct time *time)
76 {
77 #ifdef _WIN32
78         LARGE_INTEGER count;
79         QueryPerformanceCounter(&count);
80         time->ticks = count.QuadPart;
81 #elif defined(HAVE_CLOCK_GETTIME)
82         struct timespec ts;
83         if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
84                 clock_gettime(CLOCK_REALTIME, &ts);
85         time->tv.tv_sec = ts.tv_sec;
86         time->tv.tv_usec = ts.tv_nsec / 1000;
87 #elif defined(__APPLE__)
88         mach_timebase_info_data_t info;
89         mach_timebase_info(&info);
90         uint64_t ticks = mach_absolute_time();
91         uint64_t ns = (ticks * info.numer) / info.denom;
92         time->tv.tv_sec = ns / 1000000000;
93         time->tv.tv_usec = (ns % 1000000000) / 1000;
94 #else
95         gettimeofday(&time->tv, NULL);
96 #endif
97 }
98
99 static void time_set_ms(struct time *time, unsigned int ms)
100 {
101 #ifdef _WIN32
102         LARGE_INTEGER frequency;
103         QueryPerformanceFrequency(&frequency);
104         time->ticks = ms * (frequency.QuadPart / 1000);
105 #else
106         time->tv.tv_sec = ms / 1000;
107         time->tv.tv_usec = (ms % 1000) * 1000;
108 #endif
109 }
110
111 static void time_add(const struct time *a,
112                 const struct time *b, struct time *result)
113 {
114 #ifdef _WIN32
115         result->ticks = a->ticks + b->ticks;
116 #else
117         timeradd(&a->tv, &b->tv, &result->tv);
118 #endif
119 }
120
121 static void time_sub(const struct time *a,
122                 const struct time *b, struct time *result)
123 {
124 #ifdef _WIN32
125         result->ticks = a->ticks - b->ticks;
126 #else
127         timersub(&a->tv, &b->tv, &result->tv);
128 #endif
129 }
130
131 static bool time_greater(const struct time *a, const struct time *b)
132 {
133 #ifdef _WIN32
134         return (a->ticks > b->ticks);
135 #else
136         return timercmp(&a->tv, &b->tv, >);
137 #endif
138 }
139
140 static void time_as_timeval(const struct time *time, struct timeval *tv)
141 {
142 #ifdef _WIN32
143         LARGE_INTEGER frequency;
144         QueryPerformanceFrequency(&frequency);
145         tv->tv_sec = time->ticks / frequency.QuadPart;
146         tv->tv_usec = (time->ticks % frequency.QuadPart) /
147                 (frequency.QuadPart / 1000000);
148 #else
149         *tv = time->tv;
150 #endif
151 }
152
153 static unsigned int time_as_ms(const struct time *time)
154 {
155 #ifdef _WIN32
156         LARGE_INTEGER frequency;
157         QueryPerformanceFrequency(&frequency);
158         return time->ticks / (frequency.QuadPart / 1000);
159 #else
160         return time->tv.tv_sec * 1000 + time->tv.tv_usec / 1000;
161 #endif
162 }
163
164 static void timeout_start(struct timeout *timeout, unsigned int timeout_ms)
165 {
166         timeout->ms = timeout_ms;
167
168         /* Get time at start of operation. */
169         time_get(&timeout->start);
170         /* Define duration of timeout. */
171         time_set_ms(&timeout->delta, timeout_ms);
172         /* Calculate time at which we should give up. */
173         time_add(&timeout->start, &timeout->delta, &timeout->end);
174         /* Disable limit unless timeout_limit() called. */
175         timeout->limit_ms = 0;
176 }
177
178 static void timeout_limit(struct timeout *timeout, unsigned int limit_ms)
179 {
180         timeout->limit_ms = limit_ms;
181         timeout->overflow = (timeout->ms > timeout->limit_ms);
182         time_set_ms(&timeout->delta_max, timeout->limit_ms);
183 }
184
185 static bool timeout_check(struct timeout *timeout)
186 {
187         if (timeout->ms == 0)
188                 return false;
189
190         time_get(&timeout->now);
191         time_sub(&timeout->end, &timeout->now, &timeout->delta);
192         if (timeout->limit_ms)
193                 if ((timeout->overflow = time_greater(&timeout->delta, &timeout->delta_max)))
194                         timeout->delta = timeout->delta_max;
195
196         return time_greater(&timeout->now, &timeout->end);
197 }
198
199 #ifndef _WIN32
200 static struct timeval *timeout_timeval(struct timeout *timeout)
201 {
202         if (timeout->ms == 0)
203                 return NULL;
204
205         time_as_timeval(&timeout->delta, &timeout->delta_tv);
206
207         return &timeout->delta_tv;
208 }
209 #endif
210
211 static unsigned int timeout_remaining_ms(struct timeout *timeout)
212 {
213         if (timeout->ms == 0)
214                 return -1;
215         else if (timeout->limit_ms && timeout->overflow)
216                 return timeout->limit_ms;
217         else
218                 return time_as_ms(&timeout->delta);
219 }
220
221 SP_API enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_ptr)
222 {
223         struct sp_port *port;
224 #ifndef NO_PORT_METADATA
225         enum sp_return ret;
226 #endif
227         int len;
228
229         TRACE("%s, %p", portname, port_ptr);
230
231         if (!port_ptr)
232                 RETURN_ERROR(SP_ERR_ARG, "Null result pointer");
233
234         *port_ptr = NULL;
235
236         if (!portname)
237                 RETURN_ERROR(SP_ERR_ARG, "Null port name");
238
239         DEBUG_FMT("Building structure for port %s", portname);
240
241 #if !defined(_WIN32) && defined(HAVE_REALPATH)
242         /*
243          * get_port_details() below tries to be too smart and figure out
244          * some transport properties from the port name which breaks with
245          * symlinks. Therefore we canonicalize the portname first.
246          */
247         char pathbuf[PATH_MAX + 1];
248         char *res = realpath(portname, pathbuf);
249         if (!res)
250                 RETURN_ERROR(SP_ERR_ARG, "Could not retrieve realpath behind port name");
251
252         portname = pathbuf;
253 #endif
254
255         if (!(port = malloc(sizeof(struct sp_port))))
256                 RETURN_ERROR(SP_ERR_MEM, "Port structure malloc failed");
257
258         len = strlen(portname) + 1;
259
260         if (!(port->name = malloc(len))) {
261                 free(port);
262                 RETURN_ERROR(SP_ERR_MEM, "Port name malloc failed");
263         }
264
265         memcpy(port->name, portname, len);
266
267 #ifdef _WIN32
268         port->usb_path = NULL;
269         port->hdl = INVALID_HANDLE_VALUE;
270         port->write_buf = NULL;
271         port->write_buf_size = 0;
272 #else
273         port->fd = -1;
274 #endif
275
276         port->description = NULL;
277         port->transport = SP_TRANSPORT_NATIVE;
278         port->usb_bus = -1;
279         port->usb_address = -1;
280         port->usb_vid = -1;
281         port->usb_pid = -1;
282         port->usb_manufacturer = NULL;
283         port->usb_product = NULL;
284         port->usb_serial = NULL;
285         port->bluetooth_address = NULL;
286
287 #ifndef NO_PORT_METADATA
288         if ((ret = get_port_details(port)) != SP_OK) {
289                 sp_free_port(port);
290                 return ret;
291         }
292 #endif
293
294         *port_ptr = port;
295
296         RETURN_OK();
297 }
298
299 SP_API char *sp_get_port_name(const struct sp_port *port)
300 {
301         TRACE("%p", port);
302
303         if (!port)
304                 return NULL;
305
306         RETURN_STRING(port->name);
307 }
308
309 SP_API char *sp_get_port_description(const struct sp_port *port)
310 {
311         TRACE("%p", port);
312
313         if (!port || !port->description)
314                 return NULL;
315
316         RETURN_STRING(port->description);
317 }
318
319 SP_API enum sp_transport sp_get_port_transport(const struct sp_port *port)
320 {
321         TRACE("%p", port);
322
323         if (!port)
324                 RETURN_ERROR(SP_ERR_ARG, "Null port");
325
326         RETURN_INT(port->transport);
327 }
328
329 SP_API enum sp_return sp_get_port_usb_bus_address(const struct sp_port *port,
330                                                   int *usb_bus,int *usb_address)
331 {
332         TRACE("%p", port);
333
334         if (!port)
335                 RETURN_ERROR(SP_ERR_ARG, "Null port");
336         if (port->transport != SP_TRANSPORT_USB)
337                 RETURN_ERROR(SP_ERR_ARG, "Port does not use USB transport");
338         if (port->usb_bus < 0 || port->usb_address < 0)
339                 RETURN_ERROR(SP_ERR_SUPP, "Bus and address values are not available");
340
341         if (usb_bus)
342                 *usb_bus = port->usb_bus;
343         if (usb_address)
344                 *usb_address = port->usb_address;
345
346         RETURN_OK();
347 }
348
349 SP_API enum sp_return sp_get_port_usb_vid_pid(const struct sp_port *port,
350                                               int *usb_vid, int *usb_pid)
351 {
352         TRACE("%p", port);
353
354         if (!port)
355                 RETURN_ERROR(SP_ERR_ARG, "Null port");
356         if (port->transport != SP_TRANSPORT_USB)
357                 RETURN_ERROR(SP_ERR_ARG, "Port does not use USB transport");
358         if (port->usb_vid < 0 || port->usb_pid < 0)
359                 RETURN_ERROR(SP_ERR_SUPP, "VID:PID values are not available");
360
361         if (usb_vid)
362                 *usb_vid = port->usb_vid;
363         if (usb_pid)
364                 *usb_pid = port->usb_pid;
365
366         RETURN_OK();
367 }
368
369 SP_API char *sp_get_port_usb_manufacturer(const struct sp_port *port)
370 {
371         TRACE("%p", port);
372
373         if (!port || port->transport != SP_TRANSPORT_USB || !port->usb_manufacturer)
374                 return NULL;
375
376         RETURN_STRING(port->usb_manufacturer);
377 }
378
379 SP_API char *sp_get_port_usb_product(const struct sp_port *port)
380 {
381         TRACE("%p", port);
382
383         if (!port || port->transport != SP_TRANSPORT_USB || !port->usb_product)
384                 return NULL;
385
386         RETURN_STRING(port->usb_product);
387 }
388
389 SP_API char *sp_get_port_usb_serial(const struct sp_port *port)
390 {
391         TRACE("%p", port);
392
393         if (!port || port->transport != SP_TRANSPORT_USB || !port->usb_serial)
394                 return NULL;
395
396         RETURN_STRING(port->usb_serial);
397 }
398
399 SP_API char *sp_get_port_bluetooth_address(const struct sp_port *port)
400 {
401         TRACE("%p", port);
402
403         if (!port || port->transport != SP_TRANSPORT_BLUETOOTH
404             || !port->bluetooth_address)
405                 return NULL;
406
407         RETURN_STRING(port->bluetooth_address);
408 }
409
410 SP_API enum sp_return sp_get_port_handle(const struct sp_port *port,
411                                          void *result_ptr)
412 {
413         TRACE("%p, %p", port, result_ptr);
414
415         if (!port)
416                 RETURN_ERROR(SP_ERR_ARG, "Null port");
417         if (!result_ptr)
418                 RETURN_ERROR(SP_ERR_ARG, "Null result pointer");
419
420 #ifdef _WIN32
421         HANDLE *handle_ptr = result_ptr;
422         *handle_ptr = port->hdl;
423 #else
424         int *fd_ptr = result_ptr;
425         *fd_ptr = port->fd;
426 #endif
427
428         RETURN_OK();
429 }
430
431 SP_API enum sp_return sp_copy_port(const struct sp_port *port,
432                                    struct sp_port **copy_ptr)
433 {
434         TRACE("%p, %p", port, copy_ptr);
435
436         if (!copy_ptr)
437                 RETURN_ERROR(SP_ERR_ARG, "Null result pointer");
438
439         *copy_ptr = NULL;
440
441         if (!port)
442                 RETURN_ERROR(SP_ERR_ARG, "Null port");
443
444         if (!port->name)
445                 RETURN_ERROR(SP_ERR_ARG, "Null port name");
446
447         DEBUG("Copying port structure");
448
449         RETURN_INT(sp_get_port_by_name(port->name, copy_ptr));
450 }
451
452 SP_API void sp_free_port(struct sp_port *port)
453 {
454         TRACE("%p", port);
455
456         if (!port) {
457                 DEBUG("Null port");
458                 RETURN();
459         }
460
461         DEBUG("Freeing port structure");
462
463         if (port->name)
464                 free(port->name);
465         if (port->description)
466                 free(port->description);
467         if (port->usb_manufacturer)
468                 free(port->usb_manufacturer);
469         if (port->usb_product)
470                 free(port->usb_product);
471         if (port->usb_serial)
472                 free(port->usb_serial);
473         if (port->bluetooth_address)
474                 free(port->bluetooth_address);
475 #ifdef _WIN32
476         if (port->usb_path)
477                 free(port->usb_path);
478         if (port->write_buf)
479                 free(port->write_buf);
480 #endif
481
482         free(port);
483
484         RETURN();
485 }
486
487 SP_PRIV struct sp_port **list_append(struct sp_port **list,
488                                      const char *portname)
489 {
490         void *tmp;
491         unsigned int count;
492
493         for (count = 0; list[count]; count++)
494                 ;
495         if (!(tmp = realloc(list, sizeof(struct sp_port *) * (count + 2))))
496                 goto fail;
497         list = tmp;
498         if (sp_get_port_by_name(portname, &list[count]) != SP_OK)
499                 goto fail;
500         list[count + 1] = NULL;
501         return list;
502
503 fail:
504         sp_free_port_list(list);
505         return NULL;
506 }
507
508 SP_API enum sp_return sp_list_ports(struct sp_port ***list_ptr)
509 {
510 #ifndef NO_ENUMERATION
511         struct sp_port **list;
512         int ret;
513 #endif
514
515         TRACE("%p", list_ptr);
516
517         if (!list_ptr)
518                 RETURN_ERROR(SP_ERR_ARG, "Null result pointer");
519
520         *list_ptr = NULL;
521
522 #ifdef NO_ENUMERATION
523         RETURN_ERROR(SP_ERR_SUPP, "Enumeration not supported on this platform");
524 #else
525         DEBUG("Enumerating ports");
526
527         if (!(list = malloc(sizeof(struct sp_port *))))
528                 RETURN_ERROR(SP_ERR_MEM, "Port list malloc failed");
529
530         list[0] = NULL;
531
532         ret = list_ports(&list);
533
534         if (ret == SP_OK) {
535                 *list_ptr = list;
536         } else {
537                 sp_free_port_list(list);
538                 *list_ptr = NULL;
539         }
540
541         RETURN_CODEVAL(ret);
542 #endif
543 }
544
545 SP_API void sp_free_port_list(struct sp_port **list)
546 {
547         unsigned int i;
548
549         TRACE("%p", list);
550
551         if (!list) {
552                 DEBUG("Null list");
553                 RETURN();
554         }
555
556         DEBUG("Freeing port list");
557
558         for (i = 0; list[i]; i++)
559                 sp_free_port(list[i]);
560         free(list);
561
562         RETURN();
563 }
564
565 #define CHECK_PORT() do { \
566         if (!port) \
567                 RETURN_ERROR(SP_ERR_ARG, "Null port"); \
568         if (!port->name) \
569                 RETURN_ERROR(SP_ERR_ARG, "Null port name"); \
570 } while (0)
571 #ifdef _WIN32
572 #define CHECK_PORT_HANDLE() do { \
573         if (port->hdl == INVALID_HANDLE_VALUE) \
574                 RETURN_ERROR(SP_ERR_ARG, "Port not open"); \
575 } while (0)
576 #else
577 #define CHECK_PORT_HANDLE() do { \
578         if (port->fd < 0) \
579                 RETURN_ERROR(SP_ERR_ARG, "Port not open"); \
580 } while (0)
581 #endif
582 #define CHECK_OPEN_PORT() do { \
583         CHECK_PORT(); \
584         CHECK_PORT_HANDLE(); \
585 } while (0)
586
587 #ifdef WIN32
588 /** To be called after port receive buffer is emptied. */
589 static enum sp_return restart_wait(struct sp_port *port)
590 {
591         DWORD wait_result;
592
593         if (port->wait_running) {
594                 /* Check status of running wait operation. */
595                 if (GetOverlappedResult(port->hdl, &port->wait_ovl,
596                                 &wait_result, FALSE)) {
597                         DEBUG("Previous wait completed");
598                         port->wait_running = FALSE;
599                 } else if (GetLastError() == ERROR_IO_INCOMPLETE) {
600                         DEBUG("Previous wait still running");
601                         RETURN_OK();
602                 } else {
603                         RETURN_FAIL("GetOverlappedResult() failed");
604                 }
605         }
606
607         if (!port->wait_running) {
608                 /* Start new wait operation. */
609                 if (WaitCommEvent(port->hdl, &port->events,
610                                 &port->wait_ovl)) {
611                         DEBUG("New wait returned, events already pending");
612                 } else if (GetLastError() == ERROR_IO_PENDING) {
613                         DEBUG("New wait running in background");
614                         port->wait_running = TRUE;
615                 } else {
616                         RETURN_FAIL("WaitCommEvent() failed");
617                 }
618         }
619
620         RETURN_OK();
621 }
622 #endif
623
624 SP_API enum sp_return sp_open(struct sp_port *port, enum sp_mode flags)
625 {
626         struct port_data data;
627         struct sp_port_config config;
628         enum sp_return ret;
629
630         TRACE("%p, 0x%x", port, flags);
631
632         CHECK_PORT();
633
634         if (flags > SP_MODE_READ_WRITE)
635                 RETURN_ERROR(SP_ERR_ARG, "Invalid flags");
636
637         DEBUG_FMT("Opening port %s", port->name);
638
639 #ifdef _WIN32
640         DWORD desired_access = 0, flags_and_attributes = 0, errors;
641         char *escaped_port_name;
642         COMSTAT status;
643
644         /* Prefix port name with '\\.\' to work with ports above COM9. */
645         if (!(escaped_port_name = malloc(strlen(port->name) + 5)))
646                 RETURN_ERROR(SP_ERR_MEM, "Escaped port name malloc failed");
647         sprintf(escaped_port_name, "\\\\.\\%s", port->name);
648
649         /* Map 'flags' to the OS-specific settings. */
650         flags_and_attributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED;
651         if (flags & SP_MODE_READ)
652                 desired_access |= GENERIC_READ;
653         if (flags & SP_MODE_WRITE)
654                 desired_access |= GENERIC_WRITE;
655
656         port->hdl = CreateFile(escaped_port_name, desired_access, 0, 0,
657                          OPEN_EXISTING, flags_and_attributes, 0);
658
659         free(escaped_port_name);
660
661         if (port->hdl == INVALID_HANDLE_VALUE)
662                 RETURN_FAIL("Port CreateFile() failed");
663
664         /* All timeouts initially disabled. */
665         port->timeouts.ReadIntervalTimeout = 0;
666         port->timeouts.ReadTotalTimeoutMultiplier = 0;
667         port->timeouts.ReadTotalTimeoutConstant = 0;
668         port->timeouts.WriteTotalTimeoutMultiplier = 0;
669         port->timeouts.WriteTotalTimeoutConstant = 0;
670
671         if (SetCommTimeouts(port->hdl, &port->timeouts) == 0) {
672                 sp_close(port);
673                 RETURN_FAIL("SetCommTimeouts() failed");
674         }
675
676         /* Prepare OVERLAPPED structures. */
677 #define INIT_OVERLAPPED(ovl) do { \
678         memset(&port->ovl, 0, sizeof(port->ovl)); \
679         port->ovl.hEvent = INVALID_HANDLE_VALUE; \
680         if ((port->ovl.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL)) \
681                         == INVALID_HANDLE_VALUE) { \
682                 sp_close(port); \
683                 RETURN_FAIL(#ovl "CreateEvent() failed"); \
684         } \
685 } while (0)
686
687         INIT_OVERLAPPED(read_ovl);
688         INIT_OVERLAPPED(write_ovl);
689         INIT_OVERLAPPED(wait_ovl);
690
691         /* Set event mask for RX and error events. */
692         if (SetCommMask(port->hdl, EV_RXCHAR | EV_ERR) == 0) {
693                 sp_close(port);
694                 RETURN_FAIL("SetCommMask() failed");
695         }
696
697         port->writing = FALSE;
698         port->wait_running = FALSE;
699
700         ret = restart_wait(port);
701
702         if (ret < 0) {
703                 sp_close(port);
704                 RETURN_CODEVAL(ret);
705         }
706 #else
707         int flags_local = O_NONBLOCK | O_NOCTTY | O_CLOEXEC;
708
709         /* Map 'flags' to the OS-specific settings. */
710         if ((flags & SP_MODE_READ_WRITE) == SP_MODE_READ_WRITE)
711                 flags_local |= O_RDWR;
712         else if (flags & SP_MODE_READ)
713                 flags_local |= O_RDONLY;
714         else if (flags & SP_MODE_WRITE)
715                 flags_local |= O_WRONLY;
716
717         if ((port->fd = open(port->name, flags_local)) < 0)
718                 RETURN_FAIL("open() failed");
719 #endif
720
721         ret = get_config(port, &data, &config);
722
723         if (ret < 0) {
724                 sp_close(port);
725                 RETURN_CODEVAL(ret);
726         }
727
728         /* Set sane port settings. */
729 #ifdef _WIN32
730         data.dcb.fBinary = TRUE;
731         data.dcb.fDsrSensitivity = FALSE;
732         data.dcb.fErrorChar = FALSE;
733         data.dcb.fNull = FALSE;
734         data.dcb.fAbortOnError = FALSE;
735 #else
736         /* Turn off all fancy termios tricks, give us a raw channel. */
737         data.term.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IMAXBEL);
738 #ifdef IUCLC
739         data.term.c_iflag &= ~IUCLC;
740 #endif
741         data.term.c_oflag &= ~(OPOST | ONLCR | OCRNL | ONOCR | ONLRET);
742 #ifdef OLCUC
743         data.term.c_oflag &= ~OLCUC;
744 #endif
745 #ifdef NLDLY
746         data.term.c_oflag &= ~NLDLY;
747 #endif
748 #ifdef CRDLY
749         data.term.c_oflag &= ~CRDLY;
750 #endif
751 #ifdef TABDLY
752         data.term.c_oflag &= ~TABDLY;
753 #endif
754 #ifdef BSDLY
755         data.term.c_oflag &= ~BSDLY;
756 #endif
757 #ifdef VTDLY
758         data.term.c_oflag &= ~VTDLY;
759 #endif
760 #ifdef FFDLY
761         data.term.c_oflag &= ~FFDLY;
762 #endif
763 #ifdef OFILL
764         data.term.c_oflag &= ~OFILL;
765 #endif
766         data.term.c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN);
767         data.term.c_cc[VMIN] = 0;
768         data.term.c_cc[VTIME] = 0;
769
770         /* Ignore modem status lines; enable receiver; leave control lines alone on close. */
771         data.term.c_cflag |= (CLOCAL | CREAD | HUPCL);
772 #endif
773
774 #ifdef _WIN32
775         if (ClearCommError(port->hdl, &errors, &status) == 0)
776                 RETURN_FAIL("ClearCommError() failed");
777 #endif
778
779         ret = set_config(port, &data, &config);
780
781         if (ret < 0) {
782                 sp_close(port);
783                 RETURN_CODEVAL(ret);
784         }
785
786         RETURN_OK();
787 }
788
789 SP_API enum sp_return sp_close(struct sp_port *port)
790 {
791         TRACE("%p", port);
792
793         CHECK_OPEN_PORT();
794
795         DEBUG_FMT("Closing port %s", port->name);
796
797 #ifdef _WIN32
798         /* Returns non-zero upon success, 0 upon failure. */
799         if (CloseHandle(port->hdl) == 0)
800                 RETURN_FAIL("Port CloseHandle() failed");
801         port->hdl = INVALID_HANDLE_VALUE;
802
803         /* Close event handles for overlapped structures. */
804 #define CLOSE_OVERLAPPED(ovl) do { \
805         if (port->ovl.hEvent != INVALID_HANDLE_VALUE && \
806                 CloseHandle(port->ovl.hEvent) == 0) \
807                 RETURN_FAIL(# ovl "event CloseHandle() failed"); \
808 } while (0)
809         CLOSE_OVERLAPPED(read_ovl);
810         CLOSE_OVERLAPPED(write_ovl);
811         CLOSE_OVERLAPPED(wait_ovl);
812
813         if (port->write_buf) {
814                 free(port->write_buf);
815                 port->write_buf = NULL;
816         }
817 #else
818         /* Returns 0 upon success, -1 upon failure. */
819         if (close(port->fd) == -1)
820                 RETURN_FAIL("close() failed");
821         port->fd = -1;
822 #endif
823
824         RETURN_OK();
825 }
826
827 SP_API enum sp_return sp_flush(struct sp_port *port, enum sp_buffer buffers)
828 {
829         TRACE("%p, 0x%x", port, buffers);
830
831         CHECK_OPEN_PORT();
832
833         if (buffers > SP_BUF_BOTH)
834                 RETURN_ERROR(SP_ERR_ARG, "Invalid buffer selection");
835
836         const char *buffer_names[] = {"no", "input", "output", "both"};
837
838         DEBUG_FMT("Flushing %s buffers on port %s",
839                 buffer_names[buffers], port->name);
840
841 #ifdef _WIN32
842         DWORD flags = 0;
843         if (buffers & SP_BUF_INPUT)
844                 flags |= PURGE_RXCLEAR;
845         if (buffers & SP_BUF_OUTPUT)
846                 flags |= PURGE_TXCLEAR;
847
848         /* Returns non-zero upon success, 0 upon failure. */
849         if (PurgeComm(port->hdl, flags) == 0)
850                 RETURN_FAIL("PurgeComm() failed");
851
852         if (buffers & SP_BUF_INPUT)
853                 TRY(restart_wait(port));
854 #else
855         int flags = 0;
856         if (buffers == SP_BUF_BOTH)
857                 flags = TCIOFLUSH;
858         else if (buffers == SP_BUF_INPUT)
859                 flags = TCIFLUSH;
860         else if (buffers == SP_BUF_OUTPUT)
861                 flags = TCOFLUSH;
862
863         /* Returns 0 upon success, -1 upon failure. */
864         if (tcflush(port->fd, flags) < 0)
865                 RETURN_FAIL("tcflush() failed");
866 #endif
867         RETURN_OK();
868 }
869
870 SP_API enum sp_return sp_drain(struct sp_port *port)
871 {
872         TRACE("%p", port);
873
874         CHECK_OPEN_PORT();
875
876         DEBUG_FMT("Draining port %s", port->name);
877
878 #ifdef _WIN32
879         /* Returns non-zero upon success, 0 upon failure. */
880         if (FlushFileBuffers(port->hdl) == 0)
881                 RETURN_FAIL("FlushFileBuffers() failed");
882         RETURN_OK();
883 #else
884         int result;
885         while (1) {
886 #if defined(__ANDROID__) && (__ANDROID_API__ < 21)
887                 /* Android only has tcdrain from platform 21 onwards.
888                  * On previous API versions, use the ioctl directly. */
889                 int arg = 1;
890                 result = ioctl(port->fd, TCSBRK, &arg);
891 #else
892                 result = tcdrain(port->fd);
893 #endif
894                 if (result < 0) {
895                         if (errno == EINTR) {
896                                 DEBUG("tcdrain() was interrupted");
897                                 continue;
898                         } else {
899                                 RETURN_FAIL("tcdrain() failed");
900                         }
901                 } else {
902                         RETURN_OK();
903                 }
904         }
905 #endif
906 }
907
908 #ifdef _WIN32
909 static enum sp_return await_write_completion(struct sp_port *port)
910 {
911         TRACE("%p", port);
912         DWORD bytes_written;
913         BOOL result;
914
915         /* Wait for previous non-blocking write to complete, if any. */
916         if (port->writing) {
917                 DEBUG("Waiting for previous write to complete");
918                 result = GetOverlappedResult(port->hdl, &port->write_ovl, &bytes_written, TRUE);
919                 port->writing = 0;
920                 if (!result)
921                         RETURN_FAIL("Previous write failed to complete");
922                 DEBUG("Previous write completed");
923         }
924
925         RETURN_OK();
926 }
927 #endif
928
929 SP_API enum sp_return sp_blocking_write(struct sp_port *port, const void *buf,
930                                         size_t count, unsigned int timeout_ms)
931 {
932         TRACE("%p, %p, %d, %d", port, buf, count, timeout_ms);
933
934         CHECK_OPEN_PORT();
935
936         if (!buf)
937                 RETURN_ERROR(SP_ERR_ARG, "Null buffer");
938
939         if (timeout_ms)
940                 DEBUG_FMT("Writing %d bytes to port %s, timeout %d ms",
941                         count, port->name, timeout_ms);
942         else
943                 DEBUG_FMT("Writing %d bytes to port %s, no timeout",
944                         count, port->name);
945
946         if (count == 0)
947                 RETURN_INT(0);
948
949 #ifdef _WIN32
950         DWORD bytes_written = 0;
951
952         TRY(await_write_completion(port));
953
954         /* Set timeout. */
955         if (port->timeouts.WriteTotalTimeoutConstant != timeout_ms) {
956                 port->timeouts.WriteTotalTimeoutConstant = timeout_ms;
957                 if (SetCommTimeouts(port->hdl, &port->timeouts) == 0)
958                         RETURN_FAIL("SetCommTimeouts() failed");
959         }
960
961         /* Reduce count if it exceeds the WriteFile limit. */
962         if (count > WRITEFILE_MAX_SIZE)
963                 count = WRITEFILE_MAX_SIZE;
964
965         /* Start write. */
966         if (WriteFile(port->hdl, buf, count, NULL, &port->write_ovl)) {
967                 DEBUG("Write completed immediately");
968                 RETURN_INT(count);
969         } else if (GetLastError() == ERROR_IO_PENDING) {
970                 DEBUG("Waiting for write to complete");
971                 if (GetOverlappedResult(port->hdl, &port->write_ovl, &bytes_written, TRUE) == 0) {
972                         if (GetLastError() == ERROR_SEM_TIMEOUT) {
973                                 DEBUG("Write timed out");
974                                 RETURN_INT(0);
975                         } else {
976                                 RETURN_FAIL("GetOverlappedResult() failed");
977                         }
978                 }
979                 DEBUG_FMT("Write completed, %d/%d bytes written", bytes_written, count);
980                 RETURN_INT(bytes_written);
981         } else {
982                 RETURN_FAIL("WriteFile() failed");
983         }
984 #else
985         size_t bytes_written = 0;
986         unsigned char *ptr = (unsigned char *) buf;
987         struct timeout timeout;
988         int started = 0;
989         fd_set fds;
990         int result;
991
992         timeout_start(&timeout, timeout_ms);
993
994         FD_ZERO(&fds);
995         FD_SET(port->fd, &fds);
996
997         /* Loop until we have written the requested number of bytes. */
998         while (bytes_written < count) {
999                 /*
1000                  * Check timeout only if we have run select() at least once,
1001                  * to avoid any issues if a short timeout is reached before
1002                  * select() is even run.
1003                  */
1004                 if (started && timeout_check(&timeout))
1005                         break;
1006
1007                 result = select(port->fd + 1, NULL, &fds, NULL, timeout_timeval(&timeout));
1008                 started = 1;
1009                 if (result < 0) {
1010                         if (errno == EINTR) {
1011                                 DEBUG("select() call was interrupted, repeating");
1012                                 continue;
1013                         } else {
1014                                 RETURN_FAIL("select() failed");
1015                         }
1016                 } else if (result == 0) {
1017                         /* Timeout has expired. */
1018                         break;
1019                 }
1020
1021                 /* Do write. */
1022                 result = write(port->fd, ptr, count - bytes_written);
1023
1024                 if (result < 0) {
1025                         if (errno == EAGAIN)
1026                                 /* This shouldn't happen because we did a select() first, but handle anyway. */
1027                                 continue;
1028                         else
1029                                 /* This is an actual failure. */
1030                                 RETURN_FAIL("write() failed");
1031                 }
1032
1033                 bytes_written += result;
1034                 ptr += result;
1035         }
1036
1037         if (bytes_written < count)
1038                 DEBUG("Write timed out");
1039
1040         RETURN_INT(bytes_written);
1041 #endif
1042 }
1043
1044 SP_API enum sp_return sp_nonblocking_write(struct sp_port *port,
1045                                            const void *buf, size_t count)
1046 {
1047         TRACE("%p, %p, %d", port, buf, count);
1048
1049         CHECK_OPEN_PORT();
1050
1051         if (!buf)
1052                 RETURN_ERROR(SP_ERR_ARG, "Null buffer");
1053
1054         DEBUG_FMT("Writing up to %d bytes to port %s", count, port->name);
1055
1056         if (count == 0)
1057                 RETURN_INT(0);
1058
1059 #ifdef _WIN32
1060         DWORD buf_bytes;
1061
1062         /* Check whether previous write is complete. */
1063         if (port->writing) {
1064                 if (HasOverlappedIoCompleted(&port->write_ovl)) {
1065                         DEBUG("Previous write completed");
1066                         port->writing = 0;
1067                 } else {
1068                         DEBUG("Previous write not complete");
1069                         /* Can't take a new write until the previous one finishes. */
1070                         RETURN_INT(0);
1071                 }
1072         }
1073
1074         /* Set timeout. */
1075         if (port->timeouts.WriteTotalTimeoutConstant != 0) {
1076                 port->timeouts.WriteTotalTimeoutConstant = 0;
1077                 if (SetCommTimeouts(port->hdl, &port->timeouts) == 0)
1078                         RETURN_FAIL("SetCommTimeouts() failed");
1079         }
1080
1081         /* Reduce count if it exceeds the WriteFile limit. */
1082         if (count > WRITEFILE_MAX_SIZE)
1083                 count = WRITEFILE_MAX_SIZE;
1084
1085         /* Copy data to our write buffer. */
1086         buf_bytes = min(port->write_buf_size, count);
1087         memcpy(port->write_buf, buf, buf_bytes);
1088
1089         /* Start asynchronous write. */
1090         if (WriteFile(port->hdl, port->write_buf, buf_bytes, NULL, &port->write_ovl) == 0) {
1091                 if (GetLastError() == ERROR_IO_PENDING) {
1092                         if ((port->writing = !HasOverlappedIoCompleted(&port->write_ovl)))
1093                                 DEBUG("Asynchronous write completed immediately");
1094                         else
1095                                 DEBUG("Asynchronous write running");
1096                 } else {
1097                         /* Actual failure of some kind. */
1098                         RETURN_FAIL("WriteFile() failed");
1099                 }
1100         }
1101
1102         DEBUG("All bytes written immediately");
1103
1104         RETURN_INT(buf_bytes);
1105 #else
1106         /* Returns the number of bytes written, or -1 upon failure. */
1107         ssize_t written = write(port->fd, buf, count);
1108
1109         if (written < 0) {
1110                 if (errno == EAGAIN)
1111                         // Buffer is full, no bytes written.
1112                         RETURN_INT(0);
1113                 else
1114                         RETURN_FAIL("write() failed");
1115         } else {
1116                 RETURN_INT(written);
1117         }
1118 #endif
1119 }
1120
1121 #ifdef _WIN32
1122 /* Restart wait operation if buffer was emptied. */
1123 static enum sp_return restart_wait_if_needed(struct sp_port *port, unsigned int bytes_read)
1124 {
1125         DWORD errors;
1126         COMSTAT comstat;
1127
1128         if (bytes_read == 0)
1129                 RETURN_OK();
1130
1131         if (ClearCommError(port->hdl, &errors, &comstat) == 0)
1132                 RETURN_FAIL("ClearCommError() failed");
1133
1134         if (comstat.cbInQue == 0)
1135                 TRY(restart_wait(port));
1136
1137         RETURN_OK();
1138 }
1139 #endif
1140
1141 SP_API enum sp_return sp_blocking_read(struct sp_port *port, void *buf,
1142                                        size_t count, unsigned int timeout_ms)
1143 {
1144         TRACE("%p, %p, %d, %d", port, buf, count, timeout_ms);
1145
1146         CHECK_OPEN_PORT();
1147
1148         if (!buf)
1149                 RETURN_ERROR(SP_ERR_ARG, "Null buffer");
1150
1151         if (timeout_ms)
1152                 DEBUG_FMT("Reading %d bytes from port %s, timeout %d ms",
1153                         count, port->name, timeout_ms);
1154         else
1155                 DEBUG_FMT("Reading %d bytes from port %s, no timeout",
1156                         count, port->name);
1157
1158         if (count == 0)
1159                 RETURN_INT(0);
1160
1161 #ifdef _WIN32
1162         DWORD bytes_read = 0;
1163
1164         /* Set timeout. */
1165         if (port->timeouts.ReadIntervalTimeout != 0 ||
1166                         port->timeouts.ReadTotalTimeoutMultiplier != 0 ||
1167                         port->timeouts.ReadTotalTimeoutConstant != timeout_ms) {
1168                 port->timeouts.ReadIntervalTimeout = 0;
1169                 port->timeouts.ReadTotalTimeoutMultiplier = 0;
1170                 port->timeouts.ReadTotalTimeoutConstant = timeout_ms;
1171                 if (SetCommTimeouts(port->hdl, &port->timeouts) == 0)
1172                         RETURN_FAIL("SetCommTimeouts() failed");
1173         }
1174
1175         /* Start read. */
1176         if (ReadFile(port->hdl, buf, count, NULL, &port->read_ovl)) {
1177                 DEBUG("Read completed immediately");
1178                 bytes_read = count;
1179         } else if (GetLastError() == ERROR_IO_PENDING) {
1180                 DEBUG("Waiting for read to complete");
1181                 if (GetOverlappedResult(port->hdl, &port->read_ovl, &bytes_read, TRUE) == 0)
1182                         RETURN_FAIL("GetOverlappedResult() failed");
1183                 DEBUG_FMT("Read completed, %d/%d bytes read", bytes_read, count);
1184         } else {
1185                 RETURN_FAIL("ReadFile() failed");
1186         }
1187
1188         TRY(restart_wait_if_needed(port, bytes_read));
1189
1190         RETURN_INT(bytes_read);
1191
1192 #else
1193         size_t bytes_read = 0;
1194         unsigned char *ptr = (unsigned char *) buf;
1195         struct timeout timeout;
1196         int started = 0;
1197         fd_set fds;
1198         int result;
1199
1200         timeout_start(&timeout, timeout_ms);
1201
1202         FD_ZERO(&fds);
1203         FD_SET(port->fd, &fds);
1204
1205         /* Loop until we have the requested number of bytes. */
1206         while (bytes_read < count) {
1207                 /*
1208                  * Check timeout only if we have run select() at least once,
1209                  * to avoid any issues if a short timeout is reached before
1210                  * select() is even run.
1211                  */
1212                 if (started && timeout_check(&timeout))
1213                         /* Timeout has expired. */
1214                         break;
1215
1216                 result = select(port->fd + 1, &fds, NULL, NULL, timeout_timeval(&timeout));
1217                 started = 1;
1218                 if (result < 0) {
1219                         if (errno == EINTR) {
1220                                 DEBUG("select() call was interrupted, repeating");
1221                                 continue;
1222                         } else {
1223                                 RETURN_FAIL("select() failed");
1224                         }
1225                 } else if (result == 0) {
1226                         /* Timeout has expired. */
1227                         break;
1228                 }
1229
1230                 /* Do read. */
1231                 result = read(port->fd, ptr, count - bytes_read);
1232
1233                 if (result < 0) {
1234                         if (errno == EAGAIN)
1235                                 /*
1236                                  * This shouldn't happen because we did a
1237                                  * select() first, but handle anyway.
1238                                  */
1239                                 continue;
1240                         else
1241                                 /* This is an actual failure. */
1242                                 RETURN_FAIL("read() failed");
1243                 }
1244
1245                 bytes_read += result;
1246                 ptr += result;
1247         }
1248
1249         if (bytes_read < count)
1250                 DEBUG("Read timed out");
1251
1252         RETURN_INT(bytes_read);
1253 #endif
1254 }
1255
1256 SP_API enum sp_return sp_blocking_read_next(struct sp_port *port, void *buf,
1257                                             size_t count, unsigned int timeout_ms)
1258 {
1259         TRACE("%p, %p, %d, %d", port, buf, count, timeout_ms);
1260
1261         CHECK_OPEN_PORT();
1262
1263         if (!buf)
1264                 RETURN_ERROR(SP_ERR_ARG, "Null buffer");
1265
1266         if (count == 0)
1267                 RETURN_ERROR(SP_ERR_ARG, "Zero count");
1268
1269         if (timeout_ms)
1270                 DEBUG_FMT("Reading next max %d bytes from port %s, timeout %d ms",
1271                         count, port->name, timeout_ms);
1272         else
1273                 DEBUG_FMT("Reading next max %d bytes from port %s, no timeout",
1274                         count, port->name);
1275
1276 #ifdef _WIN32
1277         DWORD bytes_read = 0;
1278
1279         /* If timeout_ms == 0, set maximum timeout. */
1280         DWORD timeout_val = (timeout_ms == 0 ? MAXDWORD - 1 : timeout_ms);
1281
1282         /* Set timeout. */
1283         if (port->timeouts.ReadIntervalTimeout != MAXDWORD ||
1284                         port->timeouts.ReadTotalTimeoutMultiplier != MAXDWORD ||
1285                         port->timeouts.ReadTotalTimeoutConstant != timeout_val) {
1286                 port->timeouts.ReadIntervalTimeout = MAXDWORD;
1287                 port->timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
1288                 port->timeouts.ReadTotalTimeoutConstant = timeout_val;
1289                 if (SetCommTimeouts(port->hdl, &port->timeouts) == 0)
1290                         RETURN_FAIL("SetCommTimeouts() failed");
1291         }
1292
1293         /* Loop until we have at least one byte, or timeout is reached. */
1294         while (bytes_read == 0) {
1295                 /* Start read. */
1296                 if (ReadFile(port->hdl, buf, count, &bytes_read, &port->read_ovl)) {
1297                         DEBUG("Read completed immediately");
1298                 } else if (GetLastError() == ERROR_IO_PENDING) {
1299                         DEBUG("Waiting for read to complete");
1300                         if (GetOverlappedResult(port->hdl, &port->read_ovl, &bytes_read, TRUE) == 0)
1301                                 RETURN_FAIL("GetOverlappedResult() failed");
1302                         if (bytes_read > 0) {
1303                                 DEBUG("Read completed");
1304                         } else if (timeout_ms > 0) {
1305                                 DEBUG("Read timed out");
1306                                 break;
1307                         } else {
1308                                 DEBUG("Restarting read");
1309                         }
1310                 } else {
1311                         RETURN_FAIL("ReadFile() failed");
1312                 }
1313         }
1314
1315         TRY(restart_wait_if_needed(port, bytes_read));
1316
1317         RETURN_INT(bytes_read);
1318
1319 #else
1320         size_t bytes_read = 0;
1321         struct timeout timeout;
1322         int started = 0;
1323         fd_set fds;
1324         int result;
1325
1326         timeout_start(&timeout, timeout_ms);
1327
1328         FD_ZERO(&fds);
1329         FD_SET(port->fd, &fds);
1330
1331         /* Loop until we have at least one byte, or timeout is reached. */
1332         while (bytes_read == 0) {
1333                 /*
1334                  * Check timeout only if we have run select() at least once,
1335                  * to avoid any issues if a short timeout is reached before
1336                  * select() is even run.
1337                  */
1338                 if (started && timeout_check(&timeout))
1339                         /* Timeout has expired. */
1340                         break;
1341
1342                 result = select(port->fd + 1, &fds, NULL, NULL, timeout_timeval(&timeout));
1343                 started = 1;
1344                 if (result < 0) {
1345                         if (errno == EINTR) {
1346                                 DEBUG("select() call was interrupted, repeating");
1347                                 continue;
1348                         } else {
1349                                 RETURN_FAIL("select() failed");
1350                         }
1351                 } else if (result == 0) {
1352                         /* Timeout has expired. */
1353                         break;
1354                 }
1355
1356                 /* Do read. */
1357                 result = read(port->fd, buf, count);
1358
1359                 if (result < 0) {
1360                         if (errno == EAGAIN)
1361                                 /* This shouldn't happen because we did a select() first, but handle anyway. */
1362                                 continue;
1363                         else
1364                                 /* This is an actual failure. */
1365                                 RETURN_FAIL("read() failed");
1366                 }
1367
1368                 bytes_read = result;
1369         }
1370
1371         if (bytes_read == 0)
1372                 DEBUG("Read timed out");
1373
1374         RETURN_INT(bytes_read);
1375 #endif
1376 }
1377
1378 SP_API enum sp_return sp_nonblocking_read(struct sp_port *port, void *buf,
1379                                           size_t count)
1380 {
1381         TRACE("%p, %p, %d", port, buf, count);
1382
1383         CHECK_OPEN_PORT();
1384
1385         if (!buf)
1386                 RETURN_ERROR(SP_ERR_ARG, "Null buffer");
1387
1388         DEBUG_FMT("Reading up to %d bytes from port %s", count, port->name);
1389
1390 #ifdef _WIN32
1391         DWORD bytes_read;
1392
1393         /* Set timeout. */
1394         if (port->timeouts.ReadIntervalTimeout != MAXDWORD ||
1395                         port->timeouts.ReadTotalTimeoutMultiplier != 0 ||
1396                         port->timeouts.ReadTotalTimeoutConstant != 0) {
1397                 port->timeouts.ReadIntervalTimeout = MAXDWORD;
1398                 port->timeouts.ReadTotalTimeoutMultiplier = 0;
1399                 port->timeouts.ReadTotalTimeoutConstant = 0;
1400                 if (SetCommTimeouts(port->hdl, &port->timeouts) == 0)
1401                         RETURN_FAIL("SetCommTimeouts() failed");
1402         }
1403
1404         /* Do read. */
1405         if (ReadFile(port->hdl, buf, count, NULL, &port->read_ovl) == 0)
1406                 if (GetLastError() != ERROR_IO_PENDING)
1407                         RETURN_FAIL("ReadFile() failed");
1408
1409         /* Get number of bytes read. */
1410         if (GetOverlappedResult(port->hdl, &port->read_ovl, &bytes_read, FALSE) == 0)
1411                 RETURN_FAIL("GetOverlappedResult() failed");
1412
1413         TRY(restart_wait_if_needed(port, bytes_read));
1414
1415         RETURN_INT(bytes_read);
1416 #else
1417         ssize_t bytes_read;
1418
1419         /* Returns the number of bytes read, or -1 upon failure. */
1420         if ((bytes_read = read(port->fd, buf, count)) < 0) {
1421                 if (errno == EAGAIN)
1422                         /* No bytes available. */
1423                         bytes_read = 0;
1424                 else
1425                         /* This is an actual failure. */
1426                         RETURN_FAIL("read() failed");
1427         }
1428         RETURN_INT(bytes_read);
1429 #endif
1430 }
1431
1432 SP_API enum sp_return sp_input_waiting(struct sp_port *port)
1433 {
1434         TRACE("%p", port);
1435
1436         CHECK_OPEN_PORT();
1437
1438         DEBUG_FMT("Checking input bytes waiting on port %s", port->name);
1439
1440 #ifdef _WIN32
1441         DWORD errors;
1442         COMSTAT comstat;
1443
1444         if (ClearCommError(port->hdl, &errors, &comstat) == 0)
1445                 RETURN_FAIL("ClearCommError() failed");
1446         RETURN_INT(comstat.cbInQue);
1447 #else
1448         int bytes_waiting;
1449         if (ioctl(port->fd, TIOCINQ, &bytes_waiting) < 0)
1450                 RETURN_FAIL("TIOCINQ ioctl failed");
1451         RETURN_INT(bytes_waiting);
1452 #endif
1453 }
1454
1455 SP_API enum sp_return sp_output_waiting(struct sp_port *port)
1456 {
1457         TRACE("%p", port);
1458
1459         CHECK_OPEN_PORT();
1460
1461         DEBUG_FMT("Checking output bytes waiting on port %s", port->name);
1462
1463 #ifdef _WIN32
1464         DWORD errors;
1465         COMSTAT comstat;
1466
1467         if (ClearCommError(port->hdl, &errors, &comstat) == 0)
1468                 RETURN_FAIL("ClearCommError() failed");
1469         RETURN_INT(comstat.cbOutQue);
1470 #else
1471         int bytes_waiting;
1472         if (ioctl(port->fd, TIOCOUTQ, &bytes_waiting) < 0)
1473                 RETURN_FAIL("TIOCOUTQ ioctl failed");
1474         RETURN_INT(bytes_waiting);
1475 #endif
1476 }
1477
1478 SP_API enum sp_return sp_new_event_set(struct sp_event_set **result_ptr)
1479 {
1480         struct sp_event_set *result;
1481
1482         TRACE("%p", result_ptr);
1483
1484         if (!result_ptr)
1485                 RETURN_ERROR(SP_ERR_ARG, "Null result");
1486
1487         *result_ptr = NULL;
1488
1489         if (!(result = malloc(sizeof(struct sp_event_set))))
1490                 RETURN_ERROR(SP_ERR_MEM, "sp_event_set malloc() failed");
1491
1492         memset(result, 0, sizeof(struct sp_event_set));
1493
1494         *result_ptr = result;
1495
1496         RETURN_OK();
1497 }
1498
1499 static enum sp_return add_handle(struct sp_event_set *event_set,
1500                 event_handle handle, enum sp_event mask)
1501 {
1502         void *new_handles;
1503         enum sp_event *new_masks;
1504
1505         TRACE("%p, %d, %d", event_set, handle, mask);
1506
1507         if (!(new_handles = realloc(event_set->handles,
1508                         sizeof(event_handle) * (event_set->count + 1))))
1509                 RETURN_ERROR(SP_ERR_MEM, "Handle array realloc() failed");
1510
1511         event_set->handles = new_handles;
1512
1513         if (!(new_masks = realloc(event_set->masks,
1514                         sizeof(enum sp_event) * (event_set->count + 1))))
1515                 RETURN_ERROR(SP_ERR_MEM, "Mask array realloc() failed");
1516
1517         event_set->masks = new_masks;
1518
1519         ((event_handle *) event_set->handles)[event_set->count] = handle;
1520         event_set->masks[event_set->count] = mask;
1521
1522         event_set->count++;
1523
1524         RETURN_OK();
1525 }
1526
1527 SP_API enum sp_return sp_add_port_events(struct sp_event_set *event_set,
1528         const struct sp_port *port, enum sp_event mask)
1529 {
1530         TRACE("%p, %p, %d", event_set, port, mask);
1531
1532         if (!event_set)
1533                 RETURN_ERROR(SP_ERR_ARG, "Null event set");
1534
1535         if (!port)
1536                 RETURN_ERROR(SP_ERR_ARG, "Null port");
1537
1538         if (mask > (SP_EVENT_RX_READY | SP_EVENT_TX_READY | SP_EVENT_ERROR))
1539                 RETURN_ERROR(SP_ERR_ARG, "Invalid event mask");
1540
1541         if (!mask)
1542                 RETURN_OK();
1543
1544 #ifdef _WIN32
1545         enum sp_event handle_mask;
1546         if ((handle_mask = mask & SP_EVENT_TX_READY))
1547                 TRY(add_handle(event_set, port->write_ovl.hEvent, handle_mask));
1548         if ((handle_mask = mask & (SP_EVENT_RX_READY | SP_EVENT_ERROR)))
1549                 TRY(add_handle(event_set, port->wait_ovl.hEvent, handle_mask));
1550 #else
1551         TRY(add_handle(event_set, port->fd, mask));
1552 #endif
1553
1554         RETURN_OK();
1555 }
1556
1557 SP_API void sp_free_event_set(struct sp_event_set *event_set)
1558 {
1559         TRACE("%p", event_set);
1560
1561         if (!event_set) {
1562                 DEBUG("Null event set");
1563                 RETURN();
1564         }
1565
1566         DEBUG("Freeing event set");
1567
1568         if (event_set->handles)
1569                 free(event_set->handles);
1570         if (event_set->masks)
1571                 free(event_set->masks);
1572
1573         free(event_set);
1574
1575         RETURN();
1576 }
1577
1578 SP_API enum sp_return sp_wait(struct sp_event_set *event_set,
1579                               unsigned int timeout_ms)
1580 {
1581         TRACE("%p, %d", event_set, timeout_ms);
1582
1583         if (!event_set)
1584                 RETURN_ERROR(SP_ERR_ARG, "Null event set");
1585
1586 #ifdef _WIN32
1587         if (WaitForMultipleObjects(event_set->count, event_set->handles, FALSE,
1588                         timeout_ms ? timeout_ms : INFINITE) == WAIT_FAILED)
1589                 RETURN_FAIL("WaitForMultipleObjects() failed");
1590
1591         RETURN_OK();
1592 #else
1593         struct timeout timeout;
1594         int started = 0, result;
1595         struct pollfd *pollfds;
1596         unsigned int i;
1597
1598         if (!(pollfds = malloc(sizeof(struct pollfd) * event_set->count)))
1599                 RETURN_ERROR(SP_ERR_MEM, "pollfds malloc() failed");
1600
1601         for (i = 0; i < event_set->count; i++) {
1602                 pollfds[i].fd = ((int *)event_set->handles)[i];
1603                 pollfds[i].events = 0;
1604                 pollfds[i].revents = 0;
1605                 if (event_set->masks[i] & SP_EVENT_RX_READY)
1606                         pollfds[i].events |= POLLIN;
1607                 if (event_set->masks[i] & SP_EVENT_TX_READY)
1608                         pollfds[i].events |= POLLOUT;
1609                 if (event_set->masks[i] & SP_EVENT_ERROR)
1610                         pollfds[i].events |= POLLERR;
1611         }
1612
1613         timeout_start(&timeout, timeout_ms);
1614         timeout_limit(&timeout, INT_MAX);
1615
1616         /* Loop until an event occurs. */
1617         while (1) {
1618                 /*
1619                  * Check timeout only if we have run poll() at least once,
1620                  * to avoid any issues if a short timeout is reached before
1621                  * poll() is even run.
1622                  */
1623                 if (started && timeout_check(&timeout)) {
1624                         DEBUG("Wait timed out");
1625                         break;
1626                 }
1627
1628                 result = poll(pollfds, event_set->count, timeout_remaining_ms(&timeout));
1629                 started = 1;
1630
1631                 if (result < 0) {
1632                         if (errno == EINTR) {
1633                                 DEBUG("poll() call was interrupted, repeating");
1634                                 continue;
1635                         } else {
1636                                 free(pollfds);
1637                                 RETURN_FAIL("poll() failed");
1638                         }
1639                 } else if (result == 0) {
1640                         DEBUG("poll() timed out");
1641                         if (!timeout.overflow)
1642                                 break;
1643                 } else {
1644                         DEBUG("poll() completed");
1645                         break;
1646                 }
1647         }
1648
1649         free(pollfds);
1650         RETURN_OK();
1651 #endif
1652 }
1653
1654 #ifdef USE_TERMIOS_SPEED
1655 static enum sp_return get_baudrate(int fd, int *baudrate)
1656 {
1657         void *data;
1658
1659         TRACE("%d, %p", fd, baudrate);
1660
1661         DEBUG("Getting baud rate");
1662
1663         if (!(data = malloc(get_termios_size())))
1664                 RETURN_ERROR(SP_ERR_MEM, "termios malloc failed");
1665
1666         if (ioctl(fd, get_termios_get_ioctl(), data) < 0) {
1667                 free(data);
1668                 RETURN_FAIL("Getting termios failed");
1669         }
1670
1671         *baudrate = get_termios_speed(data);
1672
1673         free(data);
1674
1675         RETURN_OK();
1676 }
1677
1678 static enum sp_return set_baudrate(int fd, int baudrate)
1679 {
1680         void *data;
1681
1682         TRACE("%d, %d", fd, baudrate);
1683
1684         DEBUG("Getting baud rate");
1685
1686         if (!(data = malloc(get_termios_size())))
1687                 RETURN_ERROR(SP_ERR_MEM, "termios malloc failed");
1688
1689         if (ioctl(fd, get_termios_get_ioctl(), data) < 0) {
1690                 free(data);
1691                 RETURN_FAIL("Getting termios failed");
1692         }
1693
1694         DEBUG("Setting baud rate");
1695
1696         set_termios_speed(data, baudrate);
1697
1698         if (ioctl(fd, get_termios_set_ioctl(), data) < 0) {
1699                 free(data);
1700                 RETURN_FAIL("Setting termios failed");
1701         }
1702
1703         free(data);
1704
1705         RETURN_OK();
1706 }
1707 #endif /* USE_TERMIOS_SPEED */
1708
1709 #ifdef USE_TERMIOX
1710 static enum sp_return get_flow(int fd, struct port_data *data)
1711 {
1712         void *termx;
1713
1714         TRACE("%d, %p", fd, data);
1715
1716         DEBUG("Getting advanced flow control");
1717
1718         if (!(termx = malloc(get_termiox_size())))
1719                 RETURN_ERROR(SP_ERR_MEM, "termiox malloc failed");
1720
1721         if (ioctl(fd, TCGETX, termx) < 0) {
1722                 free(termx);
1723                 RETURN_FAIL("Getting termiox failed");
1724         }
1725
1726         get_termiox_flow(termx, &data->rts_flow, &data->cts_flow,
1727                         &data->dtr_flow, &data->dsr_flow);
1728
1729         free(termx);
1730
1731         RETURN_OK();
1732 }
1733
1734 static enum sp_return set_flow(int fd, struct port_data *data)
1735 {
1736         void *termx;
1737
1738         TRACE("%d, %p", fd, data);
1739
1740         DEBUG("Getting advanced flow control");
1741
1742         if (!(termx = malloc(get_termiox_size())))
1743                 RETURN_ERROR(SP_ERR_MEM, "termiox malloc failed");
1744
1745         if (ioctl(fd, TCGETX, termx) < 0) {
1746                 free(termx);
1747                 RETURN_FAIL("Getting termiox failed");
1748         }
1749
1750         DEBUG("Setting advanced flow control");
1751
1752         set_termiox_flow(termx, data->rts_flow, data->cts_flow,
1753                         data->dtr_flow, data->dsr_flow);
1754
1755         if (ioctl(fd, TCSETX, termx) < 0) {
1756                 free(termx);
1757                 RETURN_FAIL("Setting termiox failed");
1758         }
1759
1760         free(termx);
1761
1762         RETURN_OK();
1763 }
1764 #endif /* USE_TERMIOX */
1765
1766 static enum sp_return get_config(struct sp_port *port, struct port_data *data,
1767         struct sp_port_config *config)
1768 {
1769         unsigned int i;
1770
1771         TRACE("%p, %p, %p", port, data, config);
1772
1773         DEBUG_FMT("Getting configuration for port %s", port->name);
1774
1775 #ifdef _WIN32
1776         if (!GetCommState(port->hdl, &data->dcb))
1777                 RETURN_FAIL("GetCommState() failed");
1778
1779         for (i = 0; i < NUM_STD_BAUDRATES; i++) {
1780                 if (data->dcb.BaudRate == std_baudrates[i].index) {
1781                         config->baudrate = std_baudrates[i].value;
1782                         break;
1783                 }
1784         }
1785
1786         if (i == NUM_STD_BAUDRATES)
1787                 /* BaudRate field can be either an index or a custom baud rate. */
1788                 config->baudrate = data->dcb.BaudRate;
1789
1790         config->bits = data->dcb.ByteSize;
1791
1792         if (data->dcb.fParity)
1793                 switch (data->dcb.Parity) {
1794                 case NOPARITY:
1795                         config->parity = SP_PARITY_NONE;
1796                         break;
1797                 case ODDPARITY:
1798                         config->parity = SP_PARITY_ODD;
1799                         break;
1800                 case EVENPARITY:
1801                         config->parity = SP_PARITY_EVEN;
1802                         break;
1803                 case MARKPARITY:
1804                         config->parity = SP_PARITY_MARK;
1805                         break;
1806                 case SPACEPARITY:
1807                         config->parity = SP_PARITY_SPACE;
1808                         break;
1809                 default:
1810                         config->parity = -1;
1811                 }
1812         else
1813                 config->parity = SP_PARITY_NONE;
1814
1815         switch (data->dcb.StopBits) {
1816         case ONESTOPBIT:
1817                 config->stopbits = 1;
1818                 break;
1819         case TWOSTOPBITS:
1820                 config->stopbits = 2;
1821                 break;
1822         default:
1823                 config->stopbits = -1;
1824         }
1825
1826         switch (data->dcb.fRtsControl) {
1827         case RTS_CONTROL_DISABLE:
1828                 config->rts = SP_RTS_OFF;
1829                 break;
1830         case RTS_CONTROL_ENABLE:
1831                 config->rts = SP_RTS_ON;
1832                 break;
1833         case RTS_CONTROL_HANDSHAKE:
1834                 config->rts = SP_RTS_FLOW_CONTROL;
1835                 break;
1836         default:
1837                 config->rts = -1;
1838         }
1839
1840         config->cts = data->dcb.fOutxCtsFlow ? SP_CTS_FLOW_CONTROL : SP_CTS_IGNORE;
1841
1842         switch (data->dcb.fDtrControl) {
1843         case DTR_CONTROL_DISABLE:
1844                 config->dtr = SP_DTR_OFF;
1845                 break;
1846         case DTR_CONTROL_ENABLE:
1847                 config->dtr = SP_DTR_ON;
1848                 break;
1849         case DTR_CONTROL_HANDSHAKE:
1850                 config->dtr = SP_DTR_FLOW_CONTROL;
1851                 break;
1852         default:
1853                 config->dtr = -1;
1854         }
1855
1856         config->dsr = data->dcb.fOutxDsrFlow ? SP_DSR_FLOW_CONTROL : SP_DSR_IGNORE;
1857
1858         if (data->dcb.fInX) {
1859                 if (data->dcb.fOutX)
1860                         config->xon_xoff = SP_XONXOFF_INOUT;
1861                 else
1862                         config->xon_xoff = SP_XONXOFF_IN;
1863         } else {
1864                 if (data->dcb.fOutX)
1865                         config->xon_xoff = SP_XONXOFF_OUT;
1866                 else
1867                         config->xon_xoff = SP_XONXOFF_DISABLED;
1868         }
1869
1870 #else // !_WIN32
1871
1872         if (tcgetattr(port->fd, &data->term) < 0)
1873                 RETURN_FAIL("tcgetattr() failed");
1874
1875         if (ioctl(port->fd, TIOCMGET, &data->controlbits) < 0)
1876                 RETURN_FAIL("TIOCMGET ioctl failed");
1877
1878 #ifdef USE_TERMIOX
1879         int ret = get_flow(port->fd, data);
1880
1881         if (ret == SP_ERR_FAIL && errno == EINVAL)
1882                 data->termiox_supported = 0;
1883         else if (ret < 0)
1884                 RETURN_CODEVAL(ret);
1885         else
1886                 data->termiox_supported = 1;
1887 #else
1888         data->termiox_supported = 0;
1889 #endif
1890
1891         for (i = 0; i < NUM_STD_BAUDRATES; i++) {
1892                 if (cfgetispeed(&data->term) == std_baudrates[i].index) {
1893                         config->baudrate = std_baudrates[i].value;
1894                         break;
1895                 }
1896         }
1897
1898         if (i == NUM_STD_BAUDRATES) {
1899 #ifdef __APPLE__
1900                 config->baudrate = (int)data->term.c_ispeed;
1901 #elif defined(USE_TERMIOS_SPEED)
1902                 TRY(get_baudrate(port->fd, &config->baudrate));
1903 #else
1904                 config->baudrate = -1;
1905 #endif
1906         }
1907
1908         switch (data->term.c_cflag & CSIZE) {
1909         case CS8:
1910                 config->bits = 8;
1911                 break;
1912         case CS7:
1913                 config->bits = 7;
1914                 break;
1915         case CS6:
1916                 config->bits = 6;
1917                 break;
1918         case CS5:
1919                 config->bits = 5;
1920                 break;
1921         default:
1922                 config->bits = -1;
1923         }
1924
1925         if (!(data->term.c_cflag & PARENB) && (data->term.c_iflag & IGNPAR))
1926                 config->parity = SP_PARITY_NONE;
1927         else if (!(data->term.c_cflag & PARENB) || (data->term.c_iflag & IGNPAR))
1928                 config->parity = -1;
1929 #ifdef CMSPAR
1930         else if (data->term.c_cflag & CMSPAR)
1931                 config->parity = (data->term.c_cflag & PARODD) ? SP_PARITY_MARK : SP_PARITY_SPACE;
1932 #endif
1933         else
1934                 config->parity = (data->term.c_cflag & PARODD) ? SP_PARITY_ODD : SP_PARITY_EVEN;
1935
1936         config->stopbits = (data->term.c_cflag & CSTOPB) ? 2 : 1;
1937
1938         if (data->term.c_cflag & CRTSCTS) {
1939                 config->rts = SP_RTS_FLOW_CONTROL;
1940                 config->cts = SP_CTS_FLOW_CONTROL;
1941         } else {
1942                 if (data->termiox_supported && data->rts_flow)
1943                         config->rts = SP_RTS_FLOW_CONTROL;
1944                 else
1945                         config->rts = (data->controlbits & TIOCM_RTS) ? SP_RTS_ON : SP_RTS_OFF;
1946
1947                 config->cts = (data->termiox_supported && data->cts_flow) ?
1948                         SP_CTS_FLOW_CONTROL : SP_CTS_IGNORE;
1949         }
1950
1951         if (data->termiox_supported && data->dtr_flow)
1952                 config->dtr = SP_DTR_FLOW_CONTROL;
1953         else
1954                 config->dtr = (data->controlbits & TIOCM_DTR) ? SP_DTR_ON : SP_DTR_OFF;
1955
1956         config->dsr = (data->termiox_supported && data->dsr_flow) ?
1957                 SP_DSR_FLOW_CONTROL : SP_DSR_IGNORE;
1958
1959         if (data->term.c_iflag & IXOFF) {
1960                 if (data->term.c_iflag & IXON)
1961                         config->xon_xoff = SP_XONXOFF_INOUT;
1962                 else
1963                         config->xon_xoff = SP_XONXOFF_IN;
1964         } else {
1965                 if (data->term.c_iflag & IXON)
1966                         config->xon_xoff = SP_XONXOFF_OUT;
1967                 else
1968                         config->xon_xoff = SP_XONXOFF_DISABLED;
1969         }
1970 #endif
1971
1972         RETURN_OK();
1973 }
1974
1975 static enum sp_return set_config(struct sp_port *port, struct port_data *data,
1976         const struct sp_port_config *config)
1977 {
1978         unsigned int i;
1979 #ifdef __APPLE__
1980         BAUD_TYPE baud_nonstd;
1981
1982         baud_nonstd = B0;
1983 #endif
1984 #ifdef USE_TERMIOS_SPEED
1985         int baud_nonstd = 0;
1986 #endif
1987
1988         TRACE("%p, %p, %p", port, data, config);
1989
1990         DEBUG_FMT("Setting configuration for port %s", port->name);
1991
1992 #ifdef _WIN32
1993
1994         TRY(await_write_completion(port));
1995
1996         if (config->baudrate >= 0) {
1997                 for (i = 0; i < NUM_STD_BAUDRATES; i++) {
1998                         if (config->baudrate == std_baudrates[i].value) {
1999                                 data->dcb.BaudRate = std_baudrates[i].index;
2000                                 break;
2001                         }
2002                 }
2003
2004                 if (i == NUM_STD_BAUDRATES)
2005                         data->dcb.BaudRate = config->baudrate;
2006
2007                 /* Allocate write buffer for 50ms of data at baud rate. */
2008                 port->write_buf_size = max(config->baudrate / (8 * 20), 1);
2009                 port->write_buf = realloc(port->write_buf,
2010                                           port->write_buf_size);
2011
2012                 if (!port->write_buf)
2013                         RETURN_ERROR(SP_ERR_MEM, "Allocating write buffer failed");
2014         }
2015
2016         if (config->bits >= 0)
2017                 data->dcb.ByteSize = config->bits;
2018
2019         if (config->parity >= 0) {
2020                 switch (config->parity) {
2021                 case SP_PARITY_NONE:
2022                         data->dcb.Parity = NOPARITY;
2023                         break;
2024                 case SP_PARITY_ODD:
2025                         data->dcb.Parity = ODDPARITY;
2026                         break;
2027                 case SP_PARITY_EVEN:
2028                         data->dcb.Parity = EVENPARITY;
2029                         break;
2030                 case SP_PARITY_MARK:
2031                         data->dcb.Parity = MARKPARITY;
2032                         break;
2033                 case SP_PARITY_SPACE:
2034                         data->dcb.Parity = SPACEPARITY;
2035                         break;
2036                 default:
2037                         RETURN_ERROR(SP_ERR_ARG, "Invalid parity setting");
2038                 }
2039         }
2040
2041         if (config->stopbits >= 0) {
2042                 switch (config->stopbits) {
2043                 /* Note: There's also ONE5STOPBITS == 1.5 (unneeded so far). */
2044                 case 1:
2045                         data->dcb.StopBits = ONESTOPBIT;
2046                         break;
2047                 case 2:
2048                         data->dcb.StopBits = TWOSTOPBITS;
2049                         break;
2050                 default:
2051                         RETURN_ERROR(SP_ERR_ARG, "Invalid stop bit setting");
2052                 }
2053         }
2054
2055         if (config->rts >= 0) {
2056                 switch (config->rts) {
2057                 case SP_RTS_OFF:
2058                         data->dcb.fRtsControl = RTS_CONTROL_DISABLE;
2059                         break;
2060                 case SP_RTS_ON:
2061                         data->dcb.fRtsControl = RTS_CONTROL_ENABLE;
2062                         break;
2063                 case SP_RTS_FLOW_CONTROL:
2064                         data->dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
2065                         break;
2066                 default:
2067                         RETURN_ERROR(SP_ERR_ARG, "Invalid RTS setting");
2068                 }
2069         }
2070
2071         if (config->cts >= 0) {
2072                 switch (config->cts) {
2073                 case SP_CTS_IGNORE:
2074                         data->dcb.fOutxCtsFlow = FALSE;
2075                         break;
2076                 case SP_CTS_FLOW_CONTROL:
2077                         data->dcb.fOutxCtsFlow = TRUE;
2078                         break;
2079                 default:
2080                         RETURN_ERROR(SP_ERR_ARG, "Invalid CTS setting");
2081                 }
2082         }
2083
2084         if (config->dtr >= 0) {
2085                 switch (config->dtr) {
2086                 case SP_DTR_OFF:
2087                         data->dcb.fDtrControl = DTR_CONTROL_DISABLE;
2088                         break;
2089                 case SP_DTR_ON:
2090                         data->dcb.fDtrControl = DTR_CONTROL_ENABLE;
2091                         break;
2092                 case SP_DTR_FLOW_CONTROL:
2093                         data->dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
2094                         break;
2095                 default:
2096                         RETURN_ERROR(SP_ERR_ARG, "Invalid DTR setting");
2097                 }
2098         }
2099
2100         if (config->dsr >= 0) {
2101                 switch (config->dsr) {
2102                 case SP_DSR_IGNORE:
2103                         data->dcb.fOutxDsrFlow = FALSE;
2104                         break;
2105                 case SP_DSR_FLOW_CONTROL:
2106                         data->dcb.fOutxDsrFlow = TRUE;
2107                         break;
2108                 default:
2109                         RETURN_ERROR(SP_ERR_ARG, "Invalid DSR setting");
2110                 }
2111         }
2112
2113         if (config->xon_xoff >= 0) {
2114                 switch (config->xon_xoff) {
2115                 case SP_XONXOFF_DISABLED:
2116                         data->dcb.fInX = FALSE;
2117                         data->dcb.fOutX = FALSE;
2118                         break;
2119                 case SP_XONXOFF_IN:
2120                         data->dcb.fInX = TRUE;
2121                         data->dcb.fOutX = FALSE;
2122                         break;
2123                 case SP_XONXOFF_OUT:
2124                         data->dcb.fInX = FALSE;
2125                         data->dcb.fOutX = TRUE;
2126                         break;
2127                 case SP_XONXOFF_INOUT:
2128                         data->dcb.fInX = TRUE;
2129                         data->dcb.fOutX = TRUE;
2130                         break;
2131                 default:
2132                         RETURN_ERROR(SP_ERR_ARG, "Invalid XON/XOFF setting");
2133                 }
2134         }
2135
2136         if (!SetCommState(port->hdl, &data->dcb))
2137                 RETURN_FAIL("SetCommState() failed");
2138
2139 #else /* !_WIN32 */
2140
2141         int controlbits;
2142
2143         if (config->baudrate >= 0) {
2144                 for (i = 0; i < NUM_STD_BAUDRATES; i++) {
2145                         if (config->baudrate == std_baudrates[i].value) {
2146                                 if (cfsetospeed(&data->term, std_baudrates[i].index) < 0)
2147                                         RETURN_FAIL("cfsetospeed() failed");
2148
2149                                 if (cfsetispeed(&data->term, std_baudrates[i].index) < 0)
2150                                         RETURN_FAIL("cfsetispeed() failed");
2151                                 break;
2152                         }
2153                 }
2154
2155                 /* Non-standard baud rate */
2156                 if (i == NUM_STD_BAUDRATES) {
2157 #ifdef __APPLE__
2158                         /* Set "dummy" baud rate. */
2159                         if (cfsetspeed(&data->term, B9600) < 0)
2160                                 RETURN_FAIL("cfsetspeed() failed");
2161                         baud_nonstd = config->baudrate;
2162 #elif defined(USE_TERMIOS_SPEED)
2163                         baud_nonstd = 1;
2164 #else
2165                         RETURN_ERROR(SP_ERR_SUPP, "Non-standard baudrate not supported");
2166 #endif
2167                 }
2168         }
2169
2170         if (config->bits >= 0) {
2171                 data->term.c_cflag &= ~CSIZE;
2172                 switch (config->bits) {
2173                 case 8:
2174                         data->term.c_cflag |= CS8;
2175                         break;
2176                 case 7:
2177                         data->term.c_cflag |= CS7;
2178                         break;
2179                 case 6:
2180                         data->term.c_cflag |= CS6;
2181                         break;
2182                 case 5:
2183                         data->term.c_cflag |= CS5;
2184                         break;
2185                 default:
2186                         RETURN_ERROR(SP_ERR_ARG, "Invalid data bits setting");
2187                 }
2188         }
2189
2190         if (config->parity >= 0) {
2191                 data->term.c_iflag &= ~IGNPAR;
2192                 data->term.c_cflag &= ~(PARENB | PARODD);
2193 #ifdef CMSPAR
2194                 data->term.c_cflag &= ~CMSPAR;
2195 #endif
2196                 switch (config->parity) {
2197                 case SP_PARITY_NONE:
2198                         data->term.c_iflag |= IGNPAR;
2199                         break;
2200                 case SP_PARITY_EVEN:
2201                         data->term.c_cflag |= PARENB;
2202                         break;
2203                 case SP_PARITY_ODD:
2204                         data->term.c_cflag |= PARENB | PARODD;
2205                         break;
2206 #ifdef CMSPAR
2207                 case SP_PARITY_MARK:
2208                         data->term.c_cflag |= PARENB | PARODD;
2209                         data->term.c_cflag |= CMSPAR;
2210                         break;
2211                 case SP_PARITY_SPACE:
2212                         data->term.c_cflag |= PARENB;
2213                         data->term.c_cflag |= CMSPAR;
2214                         break;
2215 #else
2216                 case SP_PARITY_MARK:
2217                 case SP_PARITY_SPACE:
2218                         RETURN_ERROR(SP_ERR_SUPP, "Mark/space parity not supported");
2219 #endif
2220                 default:
2221                         RETURN_ERROR(SP_ERR_ARG, "Invalid parity setting");
2222                 }
2223         }
2224
2225         if (config->stopbits >= 0) {
2226                 data->term.c_cflag &= ~CSTOPB;
2227                 switch (config->stopbits) {
2228                 case 1:
2229                         data->term.c_cflag &= ~CSTOPB;
2230                         break;
2231                 case 2:
2232                         data->term.c_cflag |= CSTOPB;
2233                         break;
2234                 default:
2235                         RETURN_ERROR(SP_ERR_ARG, "Invalid stop bits setting");
2236                 }
2237         }
2238
2239         if (config->rts >= 0 || config->cts >= 0) {
2240                 if (data->termiox_supported) {
2241                         data->rts_flow = data->cts_flow = 0;
2242                         switch (config->rts) {
2243                         case SP_RTS_OFF:
2244                         case SP_RTS_ON:
2245                                 controlbits = TIOCM_RTS;
2246                                 if (ioctl(port->fd, config->rts == SP_RTS_ON ? TIOCMBIS : TIOCMBIC, &controlbits) < 0)
2247                                         RETURN_FAIL("Setting RTS signal level failed");
2248                                 break;
2249                         case SP_RTS_FLOW_CONTROL:
2250                                 data->rts_flow = 1;
2251                                 break;
2252                         default:
2253                                 break;
2254                         }
2255                         if (config->cts == SP_CTS_FLOW_CONTROL)
2256                                 data->cts_flow = 1;
2257
2258                         if (data->rts_flow && data->cts_flow)
2259                                 data->term.c_iflag |= CRTSCTS;
2260                         else
2261                                 data->term.c_iflag &= ~CRTSCTS;
2262                 } else {
2263                         /* Asymmetric use of RTS/CTS not supported. */
2264                         if (data->term.c_iflag & CRTSCTS) {
2265                                 /* Flow control can only be disabled for both RTS & CTS together. */
2266                                 if (config->rts >= 0 && config->rts != SP_RTS_FLOW_CONTROL) {
2267                                         if (config->cts != SP_CTS_IGNORE)
2268                                                 RETURN_ERROR(SP_ERR_SUPP, "RTS & CTS flow control must be disabled together");
2269                                 }
2270                                 if (config->cts >= 0 && config->cts != SP_CTS_FLOW_CONTROL) {
2271                                         if (config->rts <= 0 || config->rts == SP_RTS_FLOW_CONTROL)
2272                                                 RETURN_ERROR(SP_ERR_SUPP, "RTS & CTS flow control must be disabled together");
2273                                 }
2274                         } else {
2275                                 /* Flow control can only be enabled for both RTS & CTS together. */
2276                                 if (((config->rts == SP_RTS_FLOW_CONTROL) && (config->cts != SP_CTS_FLOW_CONTROL)) ||
2277                                         ((config->cts == SP_CTS_FLOW_CONTROL) && (config->rts != SP_RTS_FLOW_CONTROL)))
2278                                         RETURN_ERROR(SP_ERR_SUPP, "RTS & CTS flow control must be enabled together");
2279                         }
2280
2281                         if (config->rts >= 0) {
2282                                 if (config->rts == SP_RTS_FLOW_CONTROL) {
2283                                         data->term.c_iflag |= CRTSCTS;
2284                                 } else {
2285                                         controlbits = TIOCM_RTS;
2286                                         if (ioctl(port->fd, config->rts == SP_RTS_ON ? TIOCMBIS : TIOCMBIC,
2287                                                         &controlbits) < 0)
2288                                                 RETURN_FAIL("Setting RTS signal level failed");
2289                                 }
2290                         }
2291                 }
2292         }
2293
2294         if (config->dtr >= 0 || config->dsr >= 0) {
2295                 if (data->termiox_supported) {
2296                         data->dtr_flow = data->dsr_flow = 0;
2297                         switch (config->dtr) {
2298                         case SP_DTR_OFF:
2299                         case SP_DTR_ON:
2300                                 controlbits = TIOCM_DTR;
2301                                 if (ioctl(port->fd, config->dtr == SP_DTR_ON ? TIOCMBIS : TIOCMBIC, &controlbits) < 0)
2302                                         RETURN_FAIL("Setting DTR signal level failed");
2303                                 break;
2304                         case SP_DTR_FLOW_CONTROL:
2305                                 data->dtr_flow = 1;
2306                                 break;
2307                         default:
2308                                 break;
2309                         }
2310                         if (config->dsr == SP_DSR_FLOW_CONTROL)
2311                                 data->dsr_flow = 1;
2312                 } else {
2313                         /* DTR/DSR flow control not supported. */
2314                         if (config->dtr == SP_DTR_FLOW_CONTROL || config->dsr == SP_DSR_FLOW_CONTROL)
2315                                 RETURN_ERROR(SP_ERR_SUPP, "DTR/DSR flow control not supported");
2316
2317                         if (config->dtr >= 0) {
2318                                 controlbits = TIOCM_DTR;
2319                                 if (ioctl(port->fd, config->dtr == SP_DTR_ON ? TIOCMBIS : TIOCMBIC,
2320                                                 &controlbits) < 0)
2321                                         RETURN_FAIL("Setting DTR signal level failed");
2322                         }
2323                 }
2324         }
2325
2326         if (config->xon_xoff >= 0) {
2327                 data->term.c_iflag &= ~(IXON | IXOFF | IXANY);
2328                 switch (config->xon_xoff) {
2329                 case SP_XONXOFF_DISABLED:
2330                         break;
2331                 case SP_XONXOFF_IN:
2332                         data->term.c_iflag |= IXOFF;
2333                         break;
2334                 case SP_XONXOFF_OUT:
2335                         data->term.c_iflag |= IXON | IXANY;
2336                         break;
2337                 case SP_XONXOFF_INOUT:
2338                         data->term.c_iflag |= IXON | IXOFF | IXANY;
2339                         break;
2340                 default:
2341                         RETURN_ERROR(SP_ERR_ARG, "Invalid XON/XOFF setting");
2342                 }
2343         }
2344
2345         if (tcsetattr(port->fd, TCSANOW, &data->term) < 0)
2346                 RETURN_FAIL("tcsetattr() failed");
2347
2348 #ifdef __APPLE__
2349         if (baud_nonstd != B0) {
2350                 if (ioctl(port->fd, IOSSIOSPEED, &baud_nonstd) == -1)
2351                         RETURN_FAIL("IOSSIOSPEED ioctl failed");
2352                 /*
2353                  * Set baud rates in data->term to correct, but incompatible
2354                  * with tcsetattr() value, same as delivered by tcgetattr().
2355                  */
2356                 if (cfsetspeed(&data->term, baud_nonstd) < 0)
2357                         RETURN_FAIL("cfsetspeed() failed");
2358         }
2359 #elif defined(__linux__)
2360 #ifdef USE_TERMIOS_SPEED
2361         if (baud_nonstd)
2362                 TRY(set_baudrate(port->fd, config->baudrate));
2363 #endif
2364 #ifdef USE_TERMIOX
2365         if (data->termiox_supported)
2366                 TRY(set_flow(port->fd, data));
2367 #endif
2368 #endif
2369
2370 #endif /* !_WIN32 */
2371
2372         RETURN_OK();
2373 }
2374
2375 SP_API enum sp_return sp_new_config(struct sp_port_config **config_ptr)
2376 {
2377         struct sp_port_config *config;
2378
2379         TRACE("%p", config_ptr);
2380
2381         if (!config_ptr)
2382                 RETURN_ERROR(SP_ERR_ARG, "Null result pointer");
2383
2384         *config_ptr = NULL;
2385
2386         if (!(config = malloc(sizeof(struct sp_port_config))))
2387                 RETURN_ERROR(SP_ERR_MEM, "Config malloc failed");
2388
2389         config->baudrate = -1;
2390         config->bits = -1;
2391         config->parity = -1;
2392         config->stopbits = -1;
2393         config->rts = -1;
2394         config->cts = -1;
2395         config->dtr = -1;
2396         config->dsr = -1;
2397
2398         *config_ptr = config;
2399
2400         RETURN_OK();
2401 }
2402
2403 SP_API void sp_free_config(struct sp_port_config *config)
2404 {
2405         TRACE("%p", config);
2406
2407         if (!config)
2408                 DEBUG("Null config");
2409         else
2410                 free(config);
2411
2412         RETURN();
2413 }
2414
2415 SP_API enum sp_return sp_get_config(struct sp_port *port,
2416                                     struct sp_port_config *config)
2417 {
2418         struct port_data data;
2419
2420         TRACE("%p, %p", port, config);
2421
2422         CHECK_OPEN_PORT();
2423
2424         if (!config)
2425                 RETURN_ERROR(SP_ERR_ARG, "Null config");
2426
2427         TRY(get_config(port, &data, config));
2428
2429         RETURN_OK();
2430 }
2431
2432 SP_API enum sp_return sp_set_config(struct sp_port *port,
2433                                     const struct sp_port_config *config)
2434 {
2435         struct port_data data;
2436         struct sp_port_config prev_config;
2437
2438         TRACE("%p, %p", port, config);
2439
2440         CHECK_OPEN_PORT();
2441
2442         if (!config)
2443                 RETURN_ERROR(SP_ERR_ARG, "Null config");
2444
2445         TRY(get_config(port, &data, &prev_config));
2446         TRY(set_config(port, &data, config));
2447
2448         RETURN_OK();
2449 }
2450
2451 #define CREATE_ACCESSORS(x, type) \
2452 SP_API enum sp_return sp_set_##x(struct sp_port *port, type x) { \
2453         struct port_data data; \
2454         struct sp_port_config config; \
2455         TRACE("%p, %d", port, x); \
2456         CHECK_OPEN_PORT(); \
2457         TRY(get_config(port, &data, &config)); \
2458         config.x = x; \
2459         TRY(set_config(port, &data, &config)); \
2460         RETURN_OK(); \
2461 } \
2462 SP_API enum sp_return sp_get_config_##x(const struct sp_port_config *config, \
2463                                         type *x) { \
2464         TRACE("%p, %p", config, x); \
2465         if (!x) \
2466                 RETURN_ERROR(SP_ERR_ARG, "Null result pointer"); \
2467         if (!config) \
2468                 RETURN_ERROR(SP_ERR_ARG, "Null config"); \
2469         *x = config->x; \
2470         RETURN_OK(); \
2471 } \
2472 SP_API enum sp_return sp_set_config_##x(struct sp_port_config *config, \
2473                                         type x) { \
2474         TRACE("%p, %d", config, x); \
2475         if (!config) \
2476                 RETURN_ERROR(SP_ERR_ARG, "Null config"); \
2477         config->x = x; \
2478         RETURN_OK(); \
2479 }
2480
2481 CREATE_ACCESSORS(baudrate, int)
2482 CREATE_ACCESSORS(bits, int)
2483 CREATE_ACCESSORS(parity, enum sp_parity)
2484 CREATE_ACCESSORS(stopbits, int)
2485 CREATE_ACCESSORS(rts, enum sp_rts)
2486 CREATE_ACCESSORS(cts, enum sp_cts)
2487 CREATE_ACCESSORS(dtr, enum sp_dtr)
2488 CREATE_ACCESSORS(dsr, enum sp_dsr)
2489 CREATE_ACCESSORS(xon_xoff, enum sp_xonxoff)
2490
2491 SP_API enum sp_return sp_set_config_flowcontrol(struct sp_port_config *config,
2492                                                 enum sp_flowcontrol flowcontrol)
2493 {
2494         if (!config)
2495                 RETURN_ERROR(SP_ERR_ARG, "Null configuration");
2496
2497         if (flowcontrol > SP_FLOWCONTROL_DTRDSR)
2498                 RETURN_ERROR(SP_ERR_ARG, "Invalid flow control setting");
2499
2500         if (flowcontrol == SP_FLOWCONTROL_XONXOFF)
2501                 config->xon_xoff = SP_XONXOFF_INOUT;
2502         else
2503                 config->xon_xoff = SP_XONXOFF_DISABLED;
2504
2505         if (flowcontrol == SP_FLOWCONTROL_RTSCTS) {
2506                 config->rts = SP_RTS_FLOW_CONTROL;
2507                 config->cts = SP_CTS_FLOW_CONTROL;
2508         } else {
2509                 if (config->rts == SP_RTS_FLOW_CONTROL)
2510                         config->rts = SP_RTS_ON;
2511                 config->cts = SP_CTS_IGNORE;
2512         }
2513
2514         if (flowcontrol == SP_FLOWCONTROL_DTRDSR) {
2515                 config->dtr = SP_DTR_FLOW_CONTROL;
2516                 config->dsr = SP_DSR_FLOW_CONTROL;
2517         } else {
2518                 if (config->dtr == SP_DTR_FLOW_CONTROL)
2519                         config->dtr = SP_DTR_ON;
2520                 config->dsr = SP_DSR_IGNORE;
2521         }
2522
2523         RETURN_OK();
2524 }
2525
2526 SP_API enum sp_return sp_set_flowcontrol(struct sp_port *port,
2527                                          enum sp_flowcontrol flowcontrol)
2528 {
2529         struct port_data data;
2530         struct sp_port_config config;
2531
2532         TRACE("%p, %d", port, flowcontrol);
2533
2534         CHECK_OPEN_PORT();
2535
2536         TRY(get_config(port, &data, &config));
2537
2538         TRY(sp_set_config_flowcontrol(&config, flowcontrol));
2539
2540         TRY(set_config(port, &data, &config));
2541
2542         RETURN_OK();
2543 }
2544
2545 SP_API enum sp_return sp_get_signals(struct sp_port *port,
2546                                      enum sp_signal *signals)
2547 {
2548         TRACE("%p, %p", port, signals);
2549
2550         CHECK_OPEN_PORT();
2551
2552         if (!signals)
2553                 RETURN_ERROR(SP_ERR_ARG, "Null result pointer");
2554
2555         DEBUG_FMT("Getting control signals for port %s", port->name);
2556
2557         *signals = 0;
2558 #ifdef _WIN32
2559         DWORD bits;
2560         if (GetCommModemStatus(port->hdl, &bits) == 0)
2561                 RETURN_FAIL("GetCommModemStatus() failed");
2562         if (bits & MS_CTS_ON)
2563                 *signals |= SP_SIG_CTS;
2564         if (bits & MS_DSR_ON)
2565                 *signals |= SP_SIG_DSR;
2566         if (bits & MS_RLSD_ON)
2567                 *signals |= SP_SIG_DCD;
2568         if (bits & MS_RING_ON)
2569                 *signals |= SP_SIG_RI;
2570 #else
2571         int bits;
2572         if (ioctl(port->fd, TIOCMGET, &bits) < 0)
2573                 RETURN_FAIL("TIOCMGET ioctl failed");
2574         if (bits & TIOCM_CTS)
2575                 *signals |= SP_SIG_CTS;
2576         if (bits & TIOCM_DSR)
2577                 *signals |= SP_SIG_DSR;
2578         if (bits & TIOCM_CAR)
2579                 *signals |= SP_SIG_DCD;
2580         if (bits & TIOCM_RNG)
2581                 *signals |= SP_SIG_RI;
2582 #endif
2583         RETURN_OK();
2584 }
2585
2586 SP_API enum sp_return sp_start_break(struct sp_port *port)
2587 {
2588         TRACE("%p", port);
2589
2590         CHECK_OPEN_PORT();
2591 #ifdef _WIN32
2592         if (SetCommBreak(port->hdl) == 0)
2593                 RETURN_FAIL("SetCommBreak() failed");
2594 #else
2595         if (ioctl(port->fd, TIOCSBRK, 1) < 0)
2596                 RETURN_FAIL("TIOCSBRK ioctl failed");
2597 #endif
2598
2599         RETURN_OK();
2600 }
2601
2602 SP_API enum sp_return sp_end_break(struct sp_port *port)
2603 {
2604         TRACE("%p", port);
2605
2606         CHECK_OPEN_PORT();
2607 #ifdef _WIN32
2608         if (ClearCommBreak(port->hdl) == 0)
2609                 RETURN_FAIL("ClearCommBreak() failed");
2610 #else
2611         if (ioctl(port->fd, TIOCCBRK, 1) < 0)
2612                 RETURN_FAIL("TIOCCBRK ioctl failed");
2613 #endif
2614
2615         RETURN_OK();
2616 }
2617
2618 SP_API int sp_last_error_code(void)
2619 {
2620         TRACE_VOID();
2621 #ifdef _WIN32
2622         RETURN_INT(GetLastError());
2623 #else
2624         RETURN_INT(errno);
2625 #endif
2626 }
2627
2628 SP_API char *sp_last_error_message(void)
2629 {
2630         TRACE_VOID();
2631
2632 #ifdef _WIN32
2633         TCHAR *message;
2634         DWORD error = GetLastError();
2635
2636         DWORD length = FormatMessage(
2637                 FORMAT_MESSAGE_ALLOCATE_BUFFER |
2638                 FORMAT_MESSAGE_FROM_SYSTEM |
2639                 FORMAT_MESSAGE_IGNORE_INSERTS,
2640                 NULL,
2641                 error,
2642                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
2643                 (LPTSTR) &message,
2644                 0, NULL );
2645
2646         if (length >= 2 && message[length - 2] == '\r')
2647                 message[length - 2] = '\0';
2648
2649         RETURN_STRING(message);
2650 #else
2651         RETURN_STRING(strerror(errno));
2652 #endif
2653 }
2654
2655 SP_API void sp_free_error_message(char *message)
2656 {
2657         TRACE("%s", message);
2658
2659 #ifdef _WIN32
2660         LocalFree(message);
2661 #else
2662         (void)message;
2663 #endif
2664
2665         RETURN();
2666 }
2667
2668 SP_API void sp_set_debug_handler(void (*handler)(const char *format, ...))
2669 {
2670         TRACE("%p", handler);
2671
2672         sp_debug_handler = handler;
2673
2674         RETURN();
2675 }
2676
2677 SP_API void sp_default_debug_handler(const char *format, ...)
2678 {
2679         va_list args;
2680         va_start(args, format);
2681         if (getenv("LIBSERIALPORT_DEBUG")) {
2682                 fputs("sp: ", stderr);
2683                 vfprintf(stderr, format, args);
2684         }
2685         va_end(args);
2686 }
2687
2688 SP_API int sp_get_major_package_version(void)
2689 {
2690         return SP_PACKAGE_VERSION_MAJOR;
2691 }
2692
2693 SP_API int sp_get_minor_package_version(void)
2694 {
2695         return SP_PACKAGE_VERSION_MINOR;
2696 }
2697
2698 SP_API int sp_get_micro_package_version(void)
2699 {
2700         return SP_PACKAGE_VERSION_MICRO;
2701 }
2702
2703 SP_API const char *sp_get_package_version_string(void)
2704 {
2705         return SP_PACKAGE_VERSION_STRING;
2706 }
2707
2708 SP_API int sp_get_current_lib_version(void)
2709 {
2710         return SP_LIB_VERSION_CURRENT;
2711 }
2712
2713 SP_API int sp_get_revision_lib_version(void)
2714 {
2715         return SP_LIB_VERSION_REVISION;
2716 }
2717
2718 SP_API int sp_get_age_lib_version(void)
2719 {
2720         return SP_LIB_VERSION_AGE;
2721 }
2722
2723 SP_API const char *sp_get_lib_version_string(void)
2724 {
2725         return SP_LIB_VERSION_STRING;
2726 }
2727
2728 /** @} */