]> sigrok.org Git - libserialport.git/blob - serialport.c
Check for termiox support at runtime.
[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-2012 Uwe Hermann <uwe@hermann-uwe.de>
6  * Copyright (C) 2013 Martin Ling <martin-libserialport@earth.li>
7  * Copyright (C) 2013 Matthias Heidbrink <m-sigrok@heidbrink.biz>
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as
11  * published by the Free Software Foundation, either version 3 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #ifdef _WIN32
31 #include <windows.h>
32 #include <tchar.h>
33 #include <stdio.h>
34 #else
35 #include <termios.h>
36 #include <sys/ioctl.h>
37 #endif
38 #ifdef __APPLE__
39 #include <IOKit/IOKitLib.h>
40 #include <IOKit/serial/IOSerialKeys.h>
41 #include <IOKit/serial/ioss.h>
42 #include <sys/syslimits.h>
43 #endif
44 #ifdef __linux__
45 #include "libudev.h"
46 #include "linux/serial.h"
47 #include "linux_termios.h"
48 #if defined(TCGETX) && defined(TCSETX) && defined(HAVE_TERMIOX)
49 #define USE_TERMIOX
50 #endif
51 #endif
52
53 #include "libserialport.h"
54
55 struct port_data {
56 #ifdef _WIN32
57         DCB dcb;
58 #else
59         struct termios term;
60         int controlbits;
61         int termiox_supported;
62         int flow;
63 #endif
64 };
65
66 /* Standard baud rates. */
67 #ifdef _WIN32
68 #define BAUD_TYPE DWORD
69 #define BAUD(n) {CBR_##n, n}
70 #else
71 #define BAUD_TYPE speed_t
72 #define BAUD(n) {B##n, n}
73 #endif
74
75 struct std_baudrate {
76         BAUD_TYPE index;
77         int value;
78 };
79
80 const struct std_baudrate std_baudrates[] = {
81 #ifdef _WIN32
82         /*
83          * The baudrates 50/75/134/150/200/1800/230400/460800 do not seem to
84          * have documented CBR_* macros.
85          */
86         BAUD(110), BAUD(300), BAUD(600), BAUD(1200), BAUD(2400), BAUD(4800),
87         BAUD(9600), BAUD(14400), BAUD(19200), BAUD(38400), BAUD(57600),
88         BAUD(115200), BAUD(128000), BAUD(256000),
89 #else
90         BAUD(50), BAUD(75), BAUD(110), BAUD(134), BAUD(150), BAUD(200),
91         BAUD(300), BAUD(600), BAUD(1200), BAUD(1800), BAUD(2400), BAUD(4800),
92         BAUD(9600), BAUD(19200), BAUD(38400), BAUD(57600), BAUD(115200),
93         BAUD(230400),
94 #if !defined(__APPLE__) && !defined(__OpenBSD__)
95         BAUD(460800),
96 #endif
97 #endif
98 };
99
100 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
101 #define NUM_STD_BAUDRATES ARRAY_SIZE(std_baudrates)
102
103 #define TRY(x) do { int ret = x; if (ret != SP_OK) return ret; } while (0)
104
105 /* Helper functions. */
106 static enum sp_return validate_port(struct sp_port *port);
107 static struct sp_port **list_append(struct sp_port **list, const char *portname);
108 static enum sp_return get_config(struct sp_port *port, struct port_data *data,
109         struct sp_port_config *config);
110 static enum sp_return set_config(struct sp_port *port, struct port_data *data,
111         const struct sp_port_config *config);
112
113 enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_ptr)
114 {
115         struct sp_port *port;
116         int len;
117
118         if (!port_ptr)
119                 return SP_ERR_ARG;
120
121         *port_ptr = NULL;
122
123         if (!portname)
124                 return SP_ERR_ARG;
125
126         if (!(port = malloc(sizeof(struct sp_port))))
127                 return SP_ERR_MEM;
128
129         len = strlen(portname) + 1;
130
131         if (!(port->name = malloc(len))) {
132                 free(port);
133                 return SP_ERR_MEM;
134         }
135
136         memcpy(port->name, portname, len);
137
138 #ifdef _WIN32
139         port->hdl = INVALID_HANDLE_VALUE;
140 #else
141         port->fd = -1;
142 #endif
143
144         *port_ptr = port;
145
146         return SP_OK;
147 }
148
149 enum sp_return sp_copy_port(const struct sp_port *port, struct sp_port **copy_ptr)
150 {
151         if (!copy_ptr)
152                 return SP_ERR_ARG;
153
154         *copy_ptr = NULL;
155
156         if (!port || !port->name)
157                 return SP_ERR_ARG;
158
159         return sp_get_port_by_name(port->name, copy_ptr);
160 }
161
162 void sp_free_port(struct sp_port *port)
163 {
164         if (!port)
165                 return;
166
167         if (port->name)
168                 free(port->name);
169
170         free(port);
171 }
172
173 static struct sp_port **list_append(struct sp_port **list, const char *portname)
174 {
175         void *tmp;
176         unsigned int count;
177
178         for (count = 0; list[count]; count++);
179         if (!(tmp = realloc(list, sizeof(struct sp_port *) * (count + 2))))
180                 goto fail;
181         list = tmp;
182         if (sp_get_port_by_name(portname, &list[count]) != SP_OK)
183                 goto fail;
184         list[count + 1] = NULL;
185         return list;
186
187 fail:
188         sp_free_port_list(list);
189         return NULL;
190 }
191
192 enum sp_return sp_list_ports(struct sp_port ***list_ptr)
193 {
194         struct sp_port **list;
195         int ret = SP_OK;
196
197         if (!(list = malloc(sizeof(struct sp_port **))))
198                 return SP_ERR_MEM;
199
200         list[0] = NULL;
201
202 #ifdef _WIN32
203         HKEY key;
204         TCHAR *value, *data;
205         DWORD max_value_len, max_data_size, max_data_len;
206         DWORD value_len, data_size, data_len;
207         DWORD type, index = 0;
208         char *name;
209         int name_len;
210
211         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"),
212                         0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS) {
213                 ret = SP_ERR_FAIL;
214                 goto out_done;
215         }
216         if (RegQueryInfoKey(key, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
217                                 &max_value_len, &max_data_size, NULL, NULL) != ERROR_SUCCESS) {
218                 ret = SP_ERR_FAIL;
219                 goto out_close;
220         }
221         max_data_len = max_data_size / sizeof(TCHAR);
222         if (!(value = malloc((max_value_len + 1) * sizeof(TCHAR)))) {
223                 ret = SP_ERR_MEM;
224                 goto out_close;
225         }
226         if (!(data = malloc((max_data_len + 1) * sizeof(TCHAR)))) {
227                 ret = SP_ERR_MEM;
228                 goto out_free_value;
229         }
230         while (
231                 value_len = max_value_len + 1,
232                 data_size = max_data_size,
233                 RegEnumValue(key, index, value, &value_len,
234                         NULL, &type, (LPBYTE)data, &data_size) == ERROR_SUCCESS)
235         {
236                 data_len = data_size / sizeof(TCHAR);
237                 data[data_len] = '\0';
238 #ifdef UNICODE
239                 name_len = WideCharToMultiByte(CP_ACP, 0, data, -1, NULL, 0, NULL, NULL)
240 #else
241                 name_len = data_len + 1;
242 #endif
243                 if (!(name = malloc(name_len))) {
244                         ret = SP_ERR_MEM;
245                         goto out;
246                 }
247 #ifdef UNICODE
248                 WideCharToMultiByte(CP_ACP, 0, data, -1, name, name_len, NULL, NULL);
249 #else
250                 strcpy(name, data);
251 #endif
252                 if (type == REG_SZ && !(list = list_append(list, name))) {
253                         ret = SP_ERR_MEM;
254                         goto out;
255                 }
256                 index++;
257         }
258 out:
259         free(data);
260 out_free_value:
261         free(value);
262 out_close:
263         RegCloseKey(key);
264 out_done:
265 #endif
266 #ifdef __APPLE__
267         mach_port_t master;
268         CFMutableDictionaryRef classes;
269         io_iterator_t iter;
270         char *path;
271         io_object_t port;
272         CFTypeRef cf_path;
273         Boolean result;
274
275         if (IOMasterPort(MACH_PORT_NULL, &master) != KERN_SUCCESS) {
276                 ret = SP_ERR_FAIL;
277                 goto out_done;
278         }
279
280         if (!(classes = IOServiceMatching(kIOSerialBSDServiceValue))) {
281                 ret = SP_ERR_FAIL;
282                 goto out_done;
283         }
284
285         CFDictionarySetValue(classes,
286                         CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));
287
288         if (IOServiceGetMatchingServices(master, classes, &iter) != KERN_SUCCESS) {
289                 ret = SP_ERR_FAIL;
290                 goto out_done;
291         }
292
293         if (!(path = malloc(PATH_MAX))) {
294                 ret = SP_ERR_MEM;
295                 goto out_release;
296         }
297
298         while ((port = IOIteratorNext(iter))) {
299                 cf_path = IORegistryEntryCreateCFProperty(port,
300                                 CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
301                 if (cf_path) {
302                         result = CFStringGetCString(cf_path,
303                                         path, PATH_MAX, kCFStringEncodingASCII);
304                         CFRelease(cf_path);
305                         if (result && !(list = list_append(list, path))) {
306                                 ret = SP_ERR_MEM;
307                                 IOObjectRelease(port);
308                                 goto out;
309                         }
310                 }
311                 IOObjectRelease(port);
312         }
313 out:
314         free(path);
315 out_release:
316         IOObjectRelease(iter);
317 out_done:
318 #endif
319 #ifdef __linux__
320         struct udev *ud;
321         struct udev_enumerate *ud_enumerate;
322         struct udev_list_entry *ud_list;
323         struct udev_list_entry *ud_entry;
324         const char *path;
325         struct udev_device *ud_dev, *ud_parent;
326         const char *name;
327         const char *driver;
328         int fd, ioctl_result;
329         struct serial_struct serial_info;
330
331         ud = udev_new();
332         ud_enumerate = udev_enumerate_new(ud);
333         udev_enumerate_add_match_subsystem(ud_enumerate, "tty");
334         udev_enumerate_scan_devices(ud_enumerate);
335         ud_list = udev_enumerate_get_list_entry(ud_enumerate);
336         udev_list_entry_foreach(ud_entry, ud_list) {
337                 path = udev_list_entry_get_name(ud_entry);
338                 ud_dev = udev_device_new_from_syspath(ud, path);
339                 /* If there is no parent device, this is a virtual tty. */
340                 ud_parent = udev_device_get_parent(ud_dev);
341                 if (ud_parent == NULL) {
342                         udev_device_unref(ud_dev);
343                         continue;
344                 }
345                 name = udev_device_get_devnode(ud_dev);
346                 /* The serial8250 driver has a hardcoded number of ports.
347                  * The only way to tell which actually exist on a given system
348                  * is to try to open them and make an ioctl call. */
349                 driver = udev_device_get_driver(ud_parent);
350                 if (driver && !strcmp(driver, "serial8250")) {
351                         if ((fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0)
352                                 goto skip;
353                         ioctl_result = ioctl(fd, TIOCGSERIAL, &serial_info);
354                         close(fd);
355                         if (ioctl_result != 0)
356                                 goto skip;
357                         if (serial_info.type == PORT_UNKNOWN)
358                                 goto skip;
359                 }
360                 list = list_append(list, name);
361 skip:
362                 udev_device_unref(ud_dev);
363                 if (!list) {
364                         ret = SP_ERR_MEM;
365                         goto out;
366                 }
367         }
368 out:
369         udev_enumerate_unref(ud_enumerate);
370         udev_unref(ud);
371 #endif
372
373         if (ret == SP_OK) {
374                 *list_ptr = list;
375         } else {
376                 if (list)
377                         sp_free_port_list(list);
378                 *list_ptr = NULL;
379         }
380
381         return ret;
382 }
383
384 void sp_free_port_list(struct sp_port **list)
385 {
386         unsigned int i;
387
388         for (i = 0; list[i]; i++)
389                 sp_free_port(list[i]);
390         free(list);
391 }
392
393 static enum sp_return validate_port(struct sp_port *port)
394 {
395         if (port == NULL)
396                 return 0;
397 #ifdef _WIN32
398         if (port->hdl == INVALID_HANDLE_VALUE)
399                 return 0;
400 #else
401         if (port->fd < 0)
402                 return 0;
403 #endif
404         return 1;
405 }
406
407 #define CHECK_PORT() do { if (!validate_port(port)) return SP_ERR_ARG; } while (0)
408
409 enum sp_return sp_open(struct sp_port *port, enum sp_mode flags)
410 {
411         if (!port)
412                 return SP_ERR_ARG;
413
414 #ifdef _WIN32
415         DWORD desired_access = 0, flags_and_attributes = 0;
416         char *escaped_port_name;
417
418         /* Prefix port name with '\\.\' to work with ports above COM9. */
419         if (!(escaped_port_name = malloc(strlen(port->name + 5))))
420                 return SP_ERR_MEM;
421         sprintf(escaped_port_name, "\\\\.\\%s", port->name);
422
423         /* Map 'flags' to the OS-specific settings. */
424         flags_and_attributes = FILE_ATTRIBUTE_NORMAL;
425         if (flags & SP_MODE_READ)
426                 desired_access |= GENERIC_READ;
427         if (flags & SP_MODE_WRITE)
428                 desired_access |= GENERIC_WRITE;
429         if (flags & SP_MODE_NONBLOCK)
430                 flags_and_attributes |= FILE_FLAG_OVERLAPPED;
431
432         port->hdl = CreateFile(escaped_port_name, desired_access, 0, 0,
433                          OPEN_EXISTING, flags_and_attributes, 0);
434
435         free(escaped_port_name);
436
437         if (port->hdl == INVALID_HANDLE_VALUE)
438                 return SP_ERR_FAIL;
439 #else
440         int flags_local = 0;
441         struct port_data data;
442         struct sp_port_config config;
443         int ret;
444
445         /* Map 'flags' to the OS-specific settings. */
446         if (flags & (SP_MODE_READ | SP_MODE_WRITE))
447                 flags_local |= O_RDWR;
448         else if (flags & SP_MODE_READ)
449                 flags_local |= O_RDONLY;
450         else if (flags & SP_MODE_WRITE)
451                 flags_local |= O_WRONLY;
452         if (flags & SP_MODE_NONBLOCK)
453                 flags_local |= O_NONBLOCK;
454
455         if ((port->fd = open(port->name, flags_local)) < 0)
456                 return SP_ERR_FAIL;
457
458         ret = get_config(port, &data, &config);
459
460         if (ret < 0) {
461                 sp_close(port);
462                 return ret;
463         }
464
465         /* Turn off all serial port cooking. */
466         data.term.c_iflag &= ~(ISTRIP | INLCR | ICRNL);
467         data.term.c_oflag &= ~(ONLCR | OCRNL | ONOCR);
468 #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
469         data.term.c_oflag &= ~OFILL;
470 #endif
471         /* Disable canonical mode, and don't echo input characters. */
472         data.term.c_lflag &= ~(ICANON | ECHO);
473
474         /* Ignore modem status lines; enable receiver */
475         data.term.c_cflag |= (CLOCAL | CREAD);
476
477         ret = set_config(port, &data, &config);
478
479         if (ret < 0) {
480                 sp_close(port);
481                 return ret;
482         }
483 #endif
484
485         return SP_OK;
486 }
487
488 enum sp_return sp_close(struct sp_port *port)
489 {
490         CHECK_PORT();
491
492 #ifdef _WIN32
493         /* Returns non-zero upon success, 0 upon failure. */
494         if (CloseHandle(port->hdl) == 0)
495                 return SP_ERR_FAIL;
496         port->hdl = INVALID_HANDLE_VALUE;
497 #else
498         /* Returns 0 upon success, -1 upon failure. */
499         if (close(port->fd) == -1)
500                 return SP_ERR_FAIL;
501         port->fd = -1;
502 #endif
503
504         return SP_OK;
505 }
506
507 enum sp_return sp_flush(struct sp_port *port, enum sp_buffer buffers)
508 {
509         CHECK_PORT();
510
511 #ifdef _WIN32
512         DWORD flags = 0;
513         if (buffers & SP_BUF_INPUT)
514                 flags |= PURGE_RXCLEAR;
515         if (buffers & SP_BUF_OUTPUT)
516                 flags |= PURGE_TXCLEAR;
517
518         /* Returns non-zero upon success, 0 upon failure. */
519         if (PurgeComm(port->hdl, flags) == 0)
520                 return SP_ERR_FAIL;
521 #else
522         int flags = 0;
523         if (buffers & SP_BUF_BOTH)
524                 flags = TCIOFLUSH;
525         else if (buffers & SP_BUF_INPUT)
526                 flags = TCIFLUSH;
527         else if (buffers & SP_BUF_OUTPUT)
528                 flags = TCOFLUSH;
529
530         /* Returns 0 upon success, -1 upon failure. */
531         if (tcflush(port->fd, flags) < 0)
532                 return SP_ERR_FAIL;
533 #endif
534         return SP_OK;
535 }
536
537 enum sp_return sp_drain(struct sp_port *port)
538 {
539         CHECK_PORT();
540
541 #ifdef _WIN32
542         /* Returns non-zero upon success, 0 upon failure. */
543         if (FlushFileBuffers(port->hdl) == 0)
544                 return SP_ERR_FAIL;
545 #else
546         /* Returns 0 upon success, -1 upon failure. */
547         if (tcdrain(port->fd) < 0)
548                 return SP_ERR_FAIL;
549 #endif
550
551         return SP_OK;
552 }
553
554 enum sp_return sp_write(struct sp_port *port, const void *buf, size_t count)
555 {
556         CHECK_PORT();
557
558         if (!buf)
559                 return SP_ERR_ARG;
560
561 #ifdef _WIN32
562         DWORD written = 0;
563
564         /* Returns non-zero upon success, 0 upon failure. */
565         if (WriteFile(port->hdl, buf, count, &written, NULL) == 0)
566                 return SP_ERR_FAIL;
567         return written;
568 #else
569         /* Returns the number of bytes written, or -1 upon failure. */
570         ssize_t written = write(port->fd, buf, count);
571
572         if (written < 0)
573                 return SP_ERR_FAIL;
574         else
575                 return written;
576 #endif
577 }
578
579 enum sp_return sp_read(struct sp_port *port, void *buf, size_t count)
580 {
581         CHECK_PORT();
582
583         if (!buf)
584                 return SP_ERR_ARG;
585
586 #ifdef _WIN32
587         DWORD bytes_read = 0;
588
589         /* Returns non-zero upon success, 0 upon failure. */
590         if (ReadFile(port->hdl, buf, count, &bytes_read, NULL) == 0)
591                 return SP_ERR_FAIL;
592         return bytes_read;
593 #else
594         ssize_t bytes_read;
595
596         /* Returns the number of bytes read, or -1 upon failure. */
597         if ((bytes_read = read(port->fd, buf, count)) < 0)
598                 return SP_ERR_FAIL;
599         return bytes_read;
600 #endif
601 }
602
603 #ifdef __linux__
604 static enum sp_return get_baudrate(int fd, int *baudrate)
605 {
606         void *data;
607
608         if (!(data = malloc(get_termios_size())))
609                 return SP_ERR_MEM;
610
611         if (ioctl(fd, get_termios_get_ioctl(), data) < 0) {
612                 free(data);
613                 return SP_ERR_FAIL;
614         }
615
616         *baudrate = get_termios_speed(data);
617
618         free(data);
619
620         return SP_OK;
621 }
622
623 static enum sp_return set_baudrate(int fd, int baudrate)
624 {
625         void *data;
626
627         if (!(data = malloc(get_termios_size())))
628                 return SP_ERR_MEM;
629
630         if (ioctl(fd, get_termios_get_ioctl(), data) < 0) {
631                 free(data);
632                 return SP_ERR_FAIL;
633         }
634
635         set_termios_speed(data, baudrate);
636
637         if (ioctl(fd, get_termios_set_ioctl(), data) < 0) {
638                 free(data);
639                 return SP_ERR_FAIL;
640         }
641
642         free(data);
643
644         return SP_OK;
645 }
646
647 #ifdef USE_TERMIOX
648 static enum sp_return get_flow(int fd, int *flow)
649 {
650         void *data;
651
652         if (!(data = malloc(get_termiox_size())))
653                 return SP_ERR_MEM;
654
655         if (ioctl(fd, TCGETX, data) < 0) {
656                 free(data);
657                 return SP_ERR_FAIL;
658         }
659
660         *flow = get_termiox_flow(data);
661
662         free(data);
663
664         return SP_OK;
665 }
666
667 static enum sp_return set_flow(int fd, int flow)
668 {
669         void *data;
670
671         if (!(data = malloc(get_termiox_size())))
672                 return SP_ERR_MEM;
673
674         if (ioctl(fd, TCGETX, data) < 0) {
675                 free(data);
676                 return SP_ERR_FAIL;
677         }
678
679         set_termiox_flow(data, flow);
680
681         if (ioctl(fd, TCSETX, data) < 0) {
682                 free(data);
683                 return SP_ERR_FAIL;
684         }
685
686         free(data);
687
688         return SP_OK;
689 }
690 #endif /* USE_TERMIOX */
691 #endif /* __linux__ */
692
693 static enum sp_return get_config(struct sp_port *port, struct port_data *data,
694         struct sp_port_config *config)
695 {
696         unsigned int i;
697
698 #ifdef _WIN32
699         if (!GetCommState(port->hdl, &data->dcb))
700                 return SP_ERR_FAIL;
701
702         for (i = 0; i < NUM_STD_BAUDRATES; i++) {
703                 if (data->dcb.BaudRate == std_baudrates[i].index) {
704                         config->baudrate = std_baudrates[i].value;
705                         break;
706                 }
707         }
708
709         if (i == NUM_STD_BAUDRATES)
710                 /* BaudRate field can be either an index or a custom baud rate. */
711                 config->baudrate = data->dcb.BaudRate;
712
713         config->bits = data->dcb.ByteSize;
714
715         if (data->dcb.fParity)
716                 switch (data->dcb.Parity) {
717                 case NOPARITY:
718                         config->parity = SP_PARITY_NONE;
719                         break;
720                 case EVENPARITY:
721                         config->parity = SP_PARITY_EVEN;
722                         break;
723                 case ODDPARITY:
724                         config->parity = SP_PARITY_ODD;
725                         break;
726                 default:
727                         config->parity = -1;
728                 }
729         else
730                 config->parity = SP_PARITY_NONE;
731
732         switch (data->dcb.StopBits) {
733         case ONESTOPBIT:
734                 config->stopbits = 1;
735                 break;
736         case TWOSTOPBITS:
737                 config->stopbits = 2;
738                 break;
739         default:
740                 config->stopbits = -1;
741         }
742
743         switch (data->dcb.fRtsControl) {
744         case RTS_CONTROL_DISABLE:
745                 config->rts = SP_RTS_OFF;
746                 break;
747         case RTS_CONTROL_ENABLE:
748                 config->rts = SP_RTS_ON;
749                 break;
750         case RTS_CONTROL_HANDSHAKE:
751                 config->rts = SP_RTS_FLOW_CONTROL;
752                 break;
753         default:
754                 config->rts = -1;
755         }
756
757         config->cts = data->dcb.fOutxCtsFlow ? SP_CTS_FLOW_CONTROL : SP_CTS_IGNORE;
758
759         switch (data->dcb.fDtrControl) {
760         case DTR_CONTROL_DISABLE:
761                 config->dtr = SP_DTR_OFF;
762                 break;
763         case DTR_CONTROL_ENABLE:
764                 config->dtr = SP_DTR_ON;
765                 break;
766         case DTR_CONTROL_HANDSHAKE:
767                 config->dtr = SP_DTR_FLOW_CONTROL;
768                 break;
769         default:
770                 config->dtr = -1;
771         }
772
773         config->dsr = data->dcb.fOutxDsrFlow ? SP_DSR_FLOW_CONTROL : SP_DSR_IGNORE;
774
775         if (data->dcb.fInX) {
776                 if (data->dcb.fOutX)
777                         config->xon_xoff = SP_XONXOFF_INOUT;
778                 else
779                         config->xon_xoff = SP_XONXOFF_IN;
780         } else {
781                 if (data->dcb.fOutX)
782                         config->xon_xoff = SP_XONXOFF_OUT;
783                 else
784                         config->xon_xoff = SP_XONXOFF_DISABLED;
785         }
786
787 #else // !_WIN32
788
789         if (tcgetattr(port->fd, &data->term) < 0)
790                 return SP_ERR_FAIL;
791
792         if (ioctl(port->fd, TIOCMGET, &data->controlbits) < 0)
793                 return SP_ERR_FAIL;
794
795 #ifdef USE_TERMIOX
796         int ret = get_flow(port->fd, &data->flow);
797
798         if (ret == SP_ERR_FAIL && errno == EINVAL)
799                 data->termiox_supported = 0;
800         else if (ret < 0)
801                 return ret;
802         else
803                 data->termiox_supported = 1;
804 #else
805         data->termiox_supported = 0;
806 #endif
807
808         for (i = 0; i < NUM_STD_BAUDRATES; i++) {
809                 if (cfgetispeed(&data->term) == std_baudrates[i].index) {
810                         config->baudrate = std_baudrates[i].value;
811                         break;
812                 }
813         }
814
815         if (i == NUM_STD_BAUDRATES) {
816 #ifdef __APPLE__
817                 config->baudrate = (int)data->term.c_ispeed;
818 #elif defined(__linux__)
819                 TRY(get_baudrate(port->fd, &config->baudrate));
820 #else
821                 config->baudrate = -1;
822 #endif
823         }
824
825         switch (data->term.c_cflag & CSIZE) {
826         case CS8:
827                 config->bits = 8;
828                 break;
829         case CS7:
830                 config->bits = 7;
831                 break;
832         case CS6:
833                 config->bits = 6;
834                 break;
835         case CS5:
836                 config->bits = 5;
837                 break;
838         default:
839                 config->bits = -1;
840         }
841
842         if (!(data->term.c_cflag & PARENB) && (data->term.c_iflag & IGNPAR))
843                 config->parity = SP_PARITY_NONE;
844         else if (!(data->term.c_cflag & PARENB) || (data->term.c_iflag & IGNPAR))
845                 config->parity = -1;
846         else
847                 config->parity = (data->term.c_cflag & PARODD) ? SP_PARITY_ODD : SP_PARITY_EVEN;
848
849         config->stopbits = (data->term.c_cflag & CSTOPB) ? 2 : 1;
850
851         if (data->term.c_cflag & CRTSCTS) {
852                 config->rts = SP_RTS_FLOW_CONTROL;
853                 config->cts = SP_CTS_FLOW_CONTROL;
854         } else {
855                 if (data->termiox_supported && data->flow & RTS_FLOW)
856                         config->rts = SP_RTS_FLOW_CONTROL;
857                 else
858                         config->rts = (data->controlbits & TIOCM_RTS) ? SP_RTS_ON : SP_RTS_OFF;
859
860                 config->cts = (data->termiox_supported && data->flow & CTS_FLOW) ?
861                         SP_CTS_FLOW_CONTROL : SP_CTS_IGNORE;
862         }
863
864         if (data->termiox_supported && data->flow & DTR_FLOW)
865                 config->dtr = SP_DTR_FLOW_CONTROL;
866         else
867                 config->dtr = (data->controlbits & TIOCM_DTR) ? SP_DTR_ON : SP_DTR_OFF;
868
869         config->dsr = (data->termiox_supported && data->flow & DSR_FLOW) ?
870                 SP_DSR_FLOW_CONTROL : SP_DSR_IGNORE;
871
872         if (data->term.c_iflag & IXOFF) {
873                 if (data->term.c_iflag & IXON)
874                         config->xon_xoff = SP_XONXOFF_INOUT;
875                 else
876                         config->xon_xoff = SP_XONXOFF_IN;
877         } else {
878                 if (data->term.c_iflag & IXON)
879                         config->xon_xoff = SP_XONXOFF_OUT;
880                 else
881                         config->xon_xoff = SP_XONXOFF_DISABLED;
882         }
883 #endif
884
885         return SP_OK;
886 }
887
888 static enum sp_return set_config(struct sp_port *port, struct port_data *data,
889         const struct sp_port_config *config)
890 {
891         unsigned int i;
892 #ifdef __APPLE__
893         BAUD_TYPE baud_nonstd;
894
895         baud_nonstd = B0;
896 #endif
897 #ifdef __linux__
898         int baud_nonstd = 0;
899 #endif
900
901 #ifdef _WIN32
902         if (config->baudrate >= 0) {
903                 for (i = 0; i < NUM_STD_BAUDRATES; i++) {
904                         if (config->baudrate == std_baudrates[i].value) {
905                                 data->dcb.BaudRate = std_baudrates[i].index;
906                                 break;
907                         }
908                 }
909
910                 if (i == NUM_STD_BAUDRATES)
911                         data->dcb.BaudRate = config->baudrate;
912         }
913
914         if (config->bits >= 0)
915                 data->dcb.ByteSize = config->bits;
916
917         if (config->parity >= 0) {
918                 switch (config->parity) {
919                 /* Note: There's also SPACEPARITY, MARKPARITY (unneeded so far). */
920                 case SP_PARITY_NONE:
921                         data->dcb.Parity = NOPARITY;
922                         break;
923                 case SP_PARITY_EVEN:
924                         data->dcb.Parity = EVENPARITY;
925                         break;
926                 case SP_PARITY_ODD:
927                         data->dcb.Parity = ODDPARITY;
928                         break;
929                 default:
930                         return SP_ERR_ARG;
931                 }
932         }
933
934         if (config->stopbits >= 0) {
935                 switch (config->stopbits) {
936                 /* Note: There's also ONE5STOPBITS == 1.5 (unneeded so far). */
937                 case 1:
938                         data->dcb.StopBits = ONESTOPBIT;
939                         break;
940                 case 2:
941                         data->dcb.StopBits = TWOSTOPBITS;
942                         break;
943                 default:
944                         return SP_ERR_ARG;
945                 }
946         }
947
948         if (config->rts >= 0) {
949                 switch (config->rts) {
950                 case SP_RTS_OFF:
951                         data->dcb.fRtsControl = RTS_CONTROL_DISABLE;
952                         break;
953                 case SP_RTS_ON:
954                         data->dcb.fRtsControl = RTS_CONTROL_ENABLE;
955                         break;
956                 case SP_RTS_FLOW_CONTROL:
957                         data->dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
958                         break;
959                 default:
960                         return SP_ERR_ARG;
961                 }
962         }
963
964         if (config->cts >= 0) {
965                 switch (config->cts) {
966                 case SP_CTS_IGNORE:
967                         data->dcb.fOutxCtsFlow = FALSE;
968                         break;
969                 case SP_CTS_FLOW_CONTROL:
970                         data->dcb.fOutxCtsFlow = TRUE;
971                         break;
972                 default:
973                         return SP_ERR_ARG;
974                 }
975         }
976
977         if (config->dtr >= 0) {
978                 switch (config->dtr) {
979                 case SP_DTR_OFF:
980                         data->dcb.fDtrControl = DTR_CONTROL_DISABLE;
981                         break;
982                 case SP_DTR_ON:
983                         data->dcb.fDtrControl = DTR_CONTROL_ENABLE;
984                         break;
985                 case SP_DTR_FLOW_CONTROL:
986                         data->dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
987                         break;
988                 default:
989                         return SP_ERR_ARG;
990                 }
991         }
992
993         if (config->dsr >= 0) {
994                 switch (config->dsr) {
995                 case SP_DSR_IGNORE:
996                         data->dcb.fOutxDsrFlow = FALSE;
997                         break;
998                 case SP_DSR_FLOW_CONTROL:
999                         data->dcb.fOutxDsrFlow = TRUE;
1000                         break;
1001                 default:
1002                         return SP_ERR_ARG;
1003                 }
1004         }
1005
1006         if (config->xon_xoff >= 0) {
1007                 switch (config->xon_xoff) {
1008                 case SP_XONXOFF_DISABLED:
1009                         data->dcb.fInX = FALSE;
1010                         data->dcb.fOutX = FALSE;
1011                         break;
1012                 case SP_XONXOFF_IN:
1013                         data->dcb.fInX = TRUE;
1014                         data->dcb.fOutX = FALSE;
1015                         break;
1016                 case SP_XONXOFF_OUT:
1017                         data->dcb.fInX = FALSE;
1018                         data->dcb.fOutX = TRUE;
1019                         break;
1020                 case SP_XONXOFF_INOUT:
1021                         data->dcb.fInX = TRUE;
1022                         data->dcb.fOutX = TRUE;
1023                         break;
1024                 default:
1025                         return SP_ERR_ARG;
1026                 }
1027         }
1028
1029         if (!SetCommState(port->hdl, &data->dcb))
1030                 return SP_ERR_FAIL;
1031
1032 #else /* !_WIN32 */
1033
1034         int controlbits;
1035
1036         if (config->baudrate >= 0) {
1037                 for (i = 0; i < NUM_STD_BAUDRATES; i++) {
1038                         if (config->baudrate == std_baudrates[i].value) {
1039                                 if (cfsetospeed(&data->term, std_baudrates[i].index) < 0)
1040                                         return SP_ERR_FAIL;
1041
1042                                 if (cfsetispeed(&data->term, std_baudrates[i].index) < 0)
1043                                         return SP_ERR_FAIL;
1044                                 break;
1045                         }
1046                 }
1047
1048                 /* Non-standard baud rate */
1049                 if (i == NUM_STD_BAUDRATES) {
1050 #ifdef __APPLE__
1051                         /* Set "dummy" baud rate. */
1052                         if (cfsetspeed(&data->term, B9600) < 0)
1053                                 return SP_ERR_FAIL;
1054                         baud_nonstd = config->baudrate;
1055 #elif defined(__linux__)
1056                         baud_nonstd = 1;
1057 #else
1058                         return SP_ERR_ARG;
1059 #endif
1060                 }
1061         }
1062
1063         if (config->bits >= 0) {
1064                 data->term.c_cflag &= ~CSIZE;
1065                 switch (config->bits) {
1066                 case 8:
1067                         data->term.c_cflag |= CS8;
1068                         break;
1069                 case 7:
1070                         data->term.c_cflag |= CS7;
1071                         break;
1072                 case 6:
1073                         data->term.c_cflag |= CS6;
1074                         break;
1075                 case 5:
1076                         data->term.c_cflag |= CS5;
1077                         break;
1078                 default:
1079                         return SP_ERR_ARG;
1080                 }
1081         }
1082
1083         if (config->parity >= 0) {
1084                 data->term.c_iflag &= ~IGNPAR;
1085                 data->term.c_cflag &= ~(PARENB | PARODD);
1086                 switch (config->parity) {
1087                 case SP_PARITY_NONE:
1088                         data->term.c_iflag |= IGNPAR;
1089                         break;
1090                 case SP_PARITY_EVEN:
1091                         data->term.c_cflag |= PARENB;
1092                         break;
1093                 case SP_PARITY_ODD:
1094                         data->term.c_cflag |= PARENB | PARODD;
1095                         break;
1096                 default:
1097                         return SP_ERR_ARG;
1098                 }
1099         }
1100
1101         if (config->stopbits >= 0) {
1102                 data->term.c_cflag &= ~CSTOPB;
1103                 switch (config->stopbits) {
1104                 case 1:
1105                         data->term.c_cflag &= ~CSTOPB;
1106                         break;
1107                 case 2:
1108                         data->term.c_cflag |= CSTOPB;
1109                         break;
1110                 default:
1111                         return SP_ERR_ARG;
1112                 }
1113         }
1114
1115         if (config->rts >= 0 || config->cts >= 0) {
1116                 if (data->termiox_supported) {
1117                         data->flow &= ~(RTS_FLOW | CTS_FLOW);
1118                         switch (config->rts) {
1119                         case SP_RTS_OFF:
1120                         case SP_RTS_ON:
1121                                 controlbits = TIOCM_RTS;
1122                                 if (ioctl(port->fd, config->rts == SP_RTS_ON ? TIOCMBIS : TIOCMBIC, &controlbits) < 0)
1123                                         return SP_ERR_FAIL;
1124                                 break;
1125                         case SP_RTS_FLOW_CONTROL:
1126                                 data->flow |= RTS_FLOW;
1127                                 break;
1128                         default:
1129                                 break;
1130                         }
1131                         if (config->cts == SP_CTS_FLOW_CONTROL)
1132                                 data->flow |= CTS_FLOW;
1133
1134                         if (data->flow & (RTS_FLOW | CTS_FLOW))
1135                                 data->term.c_iflag |= CRTSCTS;
1136                         else
1137                                 data->term.c_iflag &= ~CRTSCTS;
1138                 } else {
1139                         /* Asymmetric use of RTS/CTS not supported. */
1140                         if (data->term.c_iflag & CRTSCTS) {
1141                                 /* Flow control can only be disabled for both RTS & CTS together. */
1142                                 if (config->rts >= 0 && config->rts != SP_RTS_FLOW_CONTROL) {
1143                                         if (config->cts != SP_CTS_IGNORE)
1144                                                 return SP_ERR_ARG;
1145                                 }
1146                                 if (config->cts >= 0 && config->cts != SP_CTS_FLOW_CONTROL) {
1147                                         if (config->rts <= 0 || config->rts == SP_RTS_FLOW_CONTROL)
1148                                                 return SP_ERR_ARG;
1149                                 }
1150                         } else {
1151                                 /* Flow control can only be enabled for both RTS & CTS together. */
1152                                 if (((config->rts == SP_RTS_FLOW_CONTROL) && (config->cts != SP_CTS_FLOW_CONTROL)) ||
1153                                         ((config->cts == SP_CTS_FLOW_CONTROL) && (config->rts != SP_RTS_FLOW_CONTROL)))
1154                                         return SP_ERR_ARG;
1155                         }
1156
1157                         if (config->rts >= 0) {
1158                                 if (config->rts == SP_RTS_FLOW_CONTROL) {
1159                                         data->term.c_iflag |= CRTSCTS;
1160                                 } else {
1161                                         controlbits = TIOCM_RTS;
1162                                         if (ioctl(port->fd, config->rts == SP_RTS_ON ? TIOCMBIS : TIOCMBIC,
1163                                                         &controlbits) < 0)
1164                                                 return SP_ERR_FAIL;
1165                                 }
1166                         }
1167                 }
1168         }
1169
1170         if (config->dtr >= 0 || config->dsr >= 0) {
1171                 if (data->termiox_supported) {
1172                         data->flow &= ~(DTR_FLOW | DSR_FLOW);
1173                         switch (config->dtr) {
1174                         case SP_DTR_OFF:
1175                         case SP_DTR_ON:
1176                                 controlbits = TIOCM_DTR;
1177                                 if (ioctl(port->fd, config->dtr == SP_DTR_ON ? TIOCMBIS : TIOCMBIC, &controlbits) < 0)
1178                                         return SP_ERR_FAIL;
1179                                 break;
1180                         case SP_DTR_FLOW_CONTROL:
1181                                 data->flow |= DTR_FLOW;
1182                                 break;
1183                         default:
1184                                 break;
1185                         }
1186                         if (config->dsr == SP_DSR_FLOW_CONTROL)
1187                                 data->flow |= DSR_FLOW;
1188                 } else {
1189                         /* DTR/DSR flow control not supported. */
1190                         if (config->dtr == SP_DTR_FLOW_CONTROL || config->dsr == SP_DSR_FLOW_CONTROL)
1191                                 return SP_ERR_ARG;
1192
1193                         if (config->dtr >= 0) {
1194                                 controlbits = TIOCM_DTR;
1195                                 if (ioctl(port->fd, config->dtr == SP_DTR_ON ? TIOCMBIS : TIOCMBIC,
1196                                                 &controlbits) < 0)
1197                                         return SP_ERR_FAIL;
1198                         }
1199                 }
1200         }
1201
1202         if (config->xon_xoff >= 0) {
1203                 data->term.c_iflag &= ~(IXON | IXOFF | IXANY);
1204                 switch (config->xon_xoff) {
1205                 case SP_XONXOFF_DISABLED:
1206                         break;
1207                 case SP_XONXOFF_IN:
1208                         data->term.c_iflag |= IXOFF;
1209                         break;
1210                 case SP_XONXOFF_OUT:
1211                         data->term.c_iflag |= IXON | IXANY;
1212                         break;
1213                 case SP_XONXOFF_INOUT:
1214                         data->term.c_iflag |= IXON | IXOFF | IXANY;
1215                         break;
1216                 default:
1217                         return SP_ERR_ARG;
1218                 }
1219         }
1220
1221         if (tcsetattr(port->fd, TCSADRAIN, &data->term) < 0)
1222                 return SP_ERR_FAIL;
1223
1224 #ifdef __APPLE__
1225         if (baud_nonstd != B0) {
1226                 if (ioctl(port->fd, IOSSIOSPEED, &baud_nonstd) == -1)
1227                         return SP_ERR_FAIL;
1228                 /* Set baud rates in data->term to correct, but incompatible
1229                  * with tcsetattr() value, same as delivered by tcgetattr(). */
1230                 if (cfsetspeed(&data->term, baud_nonstd) < 0)
1231                         return SP_ERR_FAIL;
1232         }
1233 #elif defined(__linux__)
1234         if (baud_nonstd)
1235                 TRY(set_baudrate(port->fd, config->baudrate));
1236 #ifdef USE_TERMIOX
1237         if (data->termiox_supported)
1238                 TRY(set_flow(port->fd, data->flow));
1239 #endif
1240 #endif
1241
1242 #endif /* !_WIN32 */
1243
1244         return SP_OK;
1245 }
1246
1247 enum sp_return sp_set_config(struct sp_port *port, const struct sp_port_config *config)
1248 {
1249         struct port_data data;
1250         struct sp_port_config prev_config;
1251
1252         CHECK_PORT();
1253
1254         if (!config)
1255                 return SP_ERR_ARG;
1256
1257         TRY(get_config(port, &data, &prev_config));
1258         TRY(set_config(port, &data, config));
1259
1260         return SP_OK;
1261 }
1262
1263 #define CREATE_SETTER(x, type) int sp_set_##x(struct sp_port *port, type x) { \
1264         struct port_data data; \
1265         struct sp_port_config config; \
1266         CHECK_PORT(); \
1267         TRY(get_config(port, &data, &config)); \
1268         config.x = x; \
1269         TRY(set_config(port, &data, &config)); \
1270         return SP_OK; \
1271 }
1272
1273 CREATE_SETTER(baudrate, int)
1274 CREATE_SETTER(bits, int)
1275 CREATE_SETTER(parity, enum sp_parity)
1276 CREATE_SETTER(stopbits, int)
1277 CREATE_SETTER(rts, enum sp_rts)
1278 CREATE_SETTER(cts, enum sp_cts)
1279 CREATE_SETTER(dtr, enum sp_dtr)
1280 CREATE_SETTER(dsr, enum sp_dsr)
1281 CREATE_SETTER(xon_xoff, enum sp_xonxoff)
1282
1283 enum sp_return sp_set_flowcontrol(struct sp_port *port, enum sp_flowcontrol flowcontrol)
1284 {
1285         struct port_data data;
1286         struct sp_port_config config;
1287
1288         CHECK_PORT();
1289
1290         TRY(get_config(port, &data, &config));
1291
1292         if (flowcontrol == SP_FLOWCONTROL_XONXOFF)
1293                 config.xon_xoff = SP_XONXOFF_INOUT;
1294         else
1295                 config.xon_xoff = SP_XONXOFF_DISABLED;
1296
1297         if (flowcontrol == SP_FLOWCONTROL_RTSCTS) {
1298                 config.rts = SP_RTS_FLOW_CONTROL;
1299                 config.cts = SP_CTS_FLOW_CONTROL;
1300         } else {
1301                 if (config.rts == SP_RTS_FLOW_CONTROL)
1302                         config.rts = SP_RTS_ON;
1303                 config.cts = SP_CTS_IGNORE;
1304         }
1305
1306         if (flowcontrol == SP_FLOWCONTROL_DTRDSR) {
1307                 config.dtr = SP_DTR_FLOW_CONTROL;
1308                 config.dsr = SP_DSR_FLOW_CONTROL;
1309         } else {
1310                 if (config.dtr == SP_DTR_FLOW_CONTROL)
1311                         config.dtr = SP_DTR_ON;
1312                 config.dsr = SP_DSR_IGNORE;
1313         }
1314
1315         TRY(set_config(port, &data, &config));
1316
1317         return SP_OK;
1318 }
1319
1320 enum sp_return sp_get_signals(struct sp_port *port, enum sp_signal *signals)
1321 {
1322         CHECK_PORT();
1323
1324         if (!signals)
1325                 return SP_ERR_ARG;
1326
1327         *signals = 0;
1328 #ifdef _WIN32
1329         DWORD bits;
1330         if (GetCommModemStatus(port->hdl, &bits) == 0)
1331                 return SP_ERR_FAIL;
1332         if (bits & MS_CTS_ON)
1333                 *signals |= SP_SIG_CTS;
1334         if (bits & MS_DSR_ON)
1335                 *signals |= SP_SIG_DSR;
1336         if (bits & MS_RING_ON)
1337                 *signals |= SP_SIG_DCD;
1338         if (bits & MS_RLSD_ON)
1339                 *signals |= SP_SIG_RI;
1340 #else
1341         int bits;
1342         if (ioctl(port->fd, TIOCMGET, &bits) < 0)
1343                 return SP_ERR_FAIL;
1344         if (bits & TIOCM_CTS)
1345                 *signals |= SP_SIG_CTS;
1346         if (bits & TIOCM_DSR)
1347                 *signals |= SP_SIG_DSR;
1348         if (bits & TIOCM_CAR)
1349                 *signals |= SP_SIG_DCD;
1350         if (bits & TIOCM_RNG)
1351                 *signals |= SP_SIG_RI;
1352 #endif
1353         return SP_OK;
1354 }
1355
1356 enum sp_return sp_start_break(struct sp_port *port)
1357 {
1358         CHECK_PORT();
1359 #ifdef _WIN32
1360         if (SetCommBreak(port->hdl) == 0)
1361                 return SP_ERR_FAIL;
1362 #else
1363         if (ioctl(port->fd, TIOCSBRK, 1) < 0)
1364                 return SP_ERR_FAIL;
1365 #endif
1366
1367         return SP_OK;
1368 }
1369
1370 enum sp_return sp_end_break(struct sp_port *port)
1371 {
1372         CHECK_PORT();
1373 #ifdef _WIN32
1374         if (ClearCommBreak(port->hdl) == 0)
1375                 return SP_ERR_FAIL;
1376 #else
1377         if (ioctl(port->fd, TIOCCBRK, 1) < 0)
1378                 return SP_ERR_FAIL;
1379 #endif
1380
1381         return SP_OK;
1382 }
1383
1384 int sp_last_error_code(void)
1385 {
1386 #ifdef _WIN32
1387         return GetLastError();
1388 #else
1389         return errno;
1390 #endif
1391 }
1392
1393 char *sp_last_error_message(void)
1394 {
1395 #ifdef _WIN32
1396         LPVOID message;
1397         DWORD error = GetLastError();
1398
1399         FormatMessage(
1400                 FORMAT_MESSAGE_ALLOCATE_BUFFER |
1401                 FORMAT_MESSAGE_FROM_SYSTEM |
1402                 FORMAT_MESSAGE_IGNORE_INSERTS,
1403                 NULL,
1404                 error,
1405                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
1406                 (LPTSTR) &message,
1407                 0, NULL );
1408
1409         return message;
1410 #else
1411         return strerror(errno);
1412 #endif
1413 }
1414
1415 void sp_free_error_message(char *message)
1416 {
1417 #ifdef _WIN32
1418         LocalFree(message);
1419 #else
1420         (void)message;
1421 #endif
1422 }