]>
Commit | Line | Data |
---|---|---|
74510d4b ML |
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 | * | |
8 | * This program is free software: you can redistribute it and/or modify | |
9 | * it under the terms of the GNU Lesser General Public License as | |
10 | * published by the Free Software Foundation, either version 3 of the | |
11 | * License, or (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU Lesser General Public License | |
19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | */ | |
21 | ||
22 | #include <string.h> | |
23 | #include <sys/types.h> | |
24 | #include <sys/stat.h> | |
25 | #include <fcntl.h> | |
26 | #include <unistd.h> | |
3b63f34d ML |
27 | #include <stdlib.h> |
28 | #include <errno.h> | |
74510d4b ML |
29 | #ifdef _WIN32 |
30 | #include <windows.h> | |
3b63f34d | 31 | #include <tchar.h> |
767c5ba8 | 32 | #include <stdio.h> |
74510d4b | 33 | #else |
74510d4b ML |
34 | #include <termios.h> |
35 | #include <sys/ioctl.h> | |
36 | #endif | |
3b63f34d | 37 | #ifdef __APPLE__ |
1ebf4347 ML |
38 | #include <IOKit/IOKitLib.h> |
39 | #include <IOKit/serial/IOSerialKeys.h> | |
40 | #include <sys/syslimits.h> | |
3b63f34d ML |
41 | #endif |
42 | #ifdef __linux__ | |
43 | #include "libudev.h" | |
4b97c9fc | 44 | #include "linux/serial.h" |
3b63f34d | 45 | #endif |
74510d4b | 46 | |
f6a1fb65 | 47 | #include "libserialport.h" |
74510d4b | 48 | |
8094e4a0 ML |
49 | struct sp_port_data { |
50 | #ifdef _WIN32 | |
51 | DCB dcb; | |
52 | #else | |
53 | struct termios term; | |
824dcb45 | 54 | int controlbits; |
8094e4a0 | 55 | int rts; |
d514a26f | 56 | int cts; |
8094e4a0 | 57 | int dtr; |
d514a26f | 58 | int dsr; |
8094e4a0 ML |
59 | #endif |
60 | }; | |
61 | ||
da2748bf ML |
62 | /* Standard baud rates. */ |
63 | #ifdef _WIN32 | |
64 | #define BAUD_TYPE DWORD | |
65 | #define BAUD(n) {CBR_##n, n} | |
66 | #else | |
67 | #define BAUD_TYPE speed_t | |
68 | #define BAUD(n) {B##n, n} | |
69 | #endif | |
70 | ||
71 | struct std_baudrate { | |
72 | BAUD_TYPE index; | |
73 | int value; | |
74 | }; | |
75 | ||
76 | const struct std_baudrate std_baudrates[] = { | |
77 | #ifdef _WIN32 | |
78 | /* | |
79 | * The baudrates 50/75/134/150/200/1800/230400/460800 do not seem to | |
80 | * have documented CBR_* macros. | |
81 | */ | |
82 | BAUD(110), BAUD(300), BAUD(600), BAUD(1200), BAUD(2400), BAUD(4800), | |
83 | BAUD(9600), BAUD(14400), BAUD(19200), BAUD(38400), BAUD(57600), | |
84 | BAUD(115200), BAUD(128000), BAUD(256000) | |
85 | #else | |
86 | BAUD(50), BAUD(75), BAUD(110), BAUD(134), BAUD(150), BAUD(200), BAUD(300), | |
87 | BAUD(600), BAUD(1200), BAUD(1800), BAUD(2400), BAUD(4800), BAUD(9600), | |
88 | BAUD(19200), BAUD(38400), BAUD(57600), BAUD(115200), BAUD(230400), | |
89 | #if !defined(__APPLE__) && !defined(__OpenBSD__) | |
90 | BAUD(460800) | |
91 | #endif | |
92 | #endif | |
93 | }; | |
94 | ||
95 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) | |
96 | #define NUM_STD_BAUDRATES ARRAY_SIZE(std_baudrates) | |
97 | ||
80186526 ML |
98 | /* Helper functions for configuring ports. */ |
99 | static int start_config(struct sp_port *port, struct sp_port_data *data); | |
100 | static int set_baudrate(struct sp_port_data *data, int baudrate); | |
101 | static int set_bits(struct sp_port_data *data, int bits); | |
102 | static int set_parity(struct sp_port_data *data, int parity); | |
103 | static int set_stopbits(struct sp_port_data *data, int stopbits); | |
104 | static int set_rts(struct sp_port_data *data, int rts); | |
105 | static int set_cts(struct sp_port_data *data, int cts); | |
106 | static int set_dtr(struct sp_port_data *data, int dtr); | |
107 | static int set_dsr(struct sp_port_data *data, int dsr); | |
108 | static int set_xon_xoff(struct sp_port_data *data, int xon_xoff); | |
109 | static int apply_config(struct sp_port *port, struct sp_port_data *data); | |
110 | ||
77f262c4 | 111 | int sp_get_port_by_name(const char *portname, struct sp_port **port_ptr) |
d54e9004 ML |
112 | { |
113 | struct sp_port *port; | |
5919c913 | 114 | int len; |
d54e9004 | 115 | |
32b5ac05 ML |
116 | if (!port_ptr) |
117 | return SP_ERR_ARG; | |
118 | ||
77f262c4 ML |
119 | *port_ptr = NULL; |
120 | ||
d4babed2 | 121 | if (!portname) |
77f262c4 | 122 | return SP_ERR_ARG; |
d4babed2 | 123 | |
d54e9004 | 124 | if (!(port = malloc(sizeof(struct sp_port)))) |
77f262c4 | 125 | return SP_ERR_MEM; |
d54e9004 | 126 | |
5919c913 ML |
127 | len = strlen(portname) + 1; |
128 | ||
d54e9004 ML |
129 | if (!(port->name = malloc(len))) |
130 | { | |
131 | free(port); | |
77f262c4 | 132 | return SP_ERR_MEM; |
d54e9004 ML |
133 | } |
134 | ||
135 | memcpy(port->name, portname, len); | |
136 | ||
77f262c4 ML |
137 | *port_ptr = port; |
138 | ||
139 | return SP_OK; | |
d54e9004 ML |
140 | } |
141 | ||
32b5ac05 ML |
142 | int sp_copy_port(const struct sp_port *port, struct sp_port **copy_ptr) |
143 | { | |
144 | if (!copy_ptr) | |
145 | return SP_ERR_ARG; | |
146 | ||
147 | *copy_ptr = NULL; | |
148 | ||
149 | if (!port || !port->name) | |
150 | return SP_ERR_ARG; | |
151 | ||
152 | return sp_get_port_by_name(port->name, copy_ptr); | |
153 | } | |
154 | ||
e3b2f7a4 ML |
155 | void sp_free_port(struct sp_port *port) |
156 | { | |
157 | if (!port) | |
158 | return; | |
159 | ||
160 | if (port->name) | |
161 | free(port->name); | |
162 | ||
163 | free(port); | |
164 | } | |
165 | ||
5919c913 | 166 | static struct sp_port **sp_list_append(struct sp_port **list, const char *portname) |
3b63f34d ML |
167 | { |
168 | void *tmp; | |
169 | unsigned int count; | |
f92f1f0c | 170 | |
3b63f34d | 171 | for (count = 0; list[count]; count++); |
d54e9004 | 172 | if (!(tmp = realloc(list, sizeof(struct sp_port *) * (count + 2)))) |
3b63f34d ML |
173 | goto fail; |
174 | list = tmp; | |
77f262c4 | 175 | if (sp_get_port_by_name(portname, &list[count]) != SP_OK) |
3b63f34d | 176 | goto fail; |
db2794ce | 177 | list[count + 1] = NULL; |
3b63f34d | 178 | return list; |
f92f1f0c | 179 | |
3b63f34d ML |
180 | fail: |
181 | sp_free_port_list(list); | |
182 | return NULL; | |
183 | } | |
184 | ||
77f262c4 | 185 | int sp_list_ports(struct sp_port ***list_ptr) |
3b63f34d | 186 | { |
d54e9004 | 187 | struct sp_port **list; |
77f262c4 | 188 | int ret = SP_OK; |
24c1a4bb | 189 | |
d54e9004 | 190 | if (!(list = malloc(sizeof(struct sp_port **)))) |
f92f1f0c | 191 | return SP_ERR_MEM; |
24c1a4bb ML |
192 | |
193 | list[0] = NULL; | |
3b63f34d ML |
194 | |
195 | #ifdef _WIN32 | |
196 | HKEY key; | |
bdfb5b8c ML |
197 | TCHAR *value, *data; |
198 | DWORD max_value_len, max_data_size, max_data_len; | |
199 | DWORD value_len, data_size, data_len; | |
3b63f34d | 200 | DWORD type, index = 0; |
8b532d9c ML |
201 | char *name; |
202 | int name_len; | |
3b63f34d ML |
203 | |
204 | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"), | |
205 | 0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS) | |
77f262c4 ML |
206 | { |
207 | ret = SP_ERR_FAIL; | |
208 | goto out_done; | |
209 | } | |
3b63f34d | 210 | if (RegQueryInfoKey(key, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
bdfb5b8c | 211 | &max_value_len, &max_data_size, NULL, NULL) != ERROR_SUCCESS) |
77f262c4 ML |
212 | { |
213 | ret = SP_ERR_FAIL; | |
3b63f34d | 214 | goto out_close; |
77f262c4 | 215 | } |
3b63f34d | 216 | max_data_len = max_data_size / sizeof(TCHAR); |
bdfb5b8c | 217 | if (!(value = malloc((max_value_len + 1) * sizeof(TCHAR)))) |
77f262c4 ML |
218 | { |
219 | ret = SP_ERR_MEM; | |
3b63f34d | 220 | goto out_close; |
77f262c4 | 221 | } |
3b63f34d | 222 | if (!(data = malloc((max_data_len + 1) * sizeof(TCHAR)))) |
77f262c4 ML |
223 | { |
224 | ret = SP_ERR_MEM; | |
bdfb5b8c | 225 | goto out_free_value; |
77f262c4 | 226 | } |
3b63f34d | 227 | while ( |
d9573bad | 228 | value_len = max_value_len + 1, |
3b63f34d | 229 | data_size = max_data_size, |
bdfb5b8c | 230 | RegEnumValue(key, index, value, &value_len, |
3b63f34d ML |
231 | NULL, &type, (LPBYTE)data, &data_size) == ERROR_SUCCESS) |
232 | { | |
233 | data_len = data_size / sizeof(TCHAR); | |
234 | data[data_len] = '\0'; | |
8b532d9c ML |
235 | #ifdef UNICODE |
236 | name_len = WideCharToMultiByte(CP_ACP, 0, data, -1, NULL, 0, NULL, NULL) | |
237 | #else | |
238 | name_len = data_len + 1; | |
239 | #endif | |
240 | if (!(name = malloc(name_len))) | |
77f262c4 ML |
241 | { |
242 | ret = SP_ERR_MEM; | |
8b532d9c | 243 | goto out; |
77f262c4 | 244 | } |
8b532d9c ML |
245 | #ifdef UNICODE |
246 | WideCharToMultiByte(CP_ACP, 0, data, -1, name, name_len, NULL, NULL); | |
247 | #else | |
248 | strcpy(name, data); | |
249 | #endif | |
77f262c4 ML |
250 | if (type == REG_SZ && !(list = sp_list_append(list, name))) |
251 | { | |
252 | ret = SP_ERR_MEM; | |
253 | goto out; | |
254 | } | |
3b63f34d ML |
255 | index++; |
256 | } | |
257 | out: | |
258 | free(data); | |
bdfb5b8c ML |
259 | out_free_value: |
260 | free(value); | |
3b63f34d ML |
261 | out_close: |
262 | RegCloseKey(key); | |
77f262c4 | 263 | out_done: |
3b63f34d ML |
264 | #endif |
265 | #ifdef __APPLE__ | |
266 | mach_port_t master; | |
267 | CFMutableDictionaryRef classes; | |
268 | io_iterator_t iter; | |
269 | char *path; | |
270 | io_object_t port; | |
271 | CFTypeRef cf_path; | |
272 | Boolean result; | |
273 | ||
274 | if (IOMasterPort(MACH_PORT_NULL, &master) != KERN_SUCCESS) | |
77f262c4 ML |
275 | { |
276 | ret = SP_ERR_FAIL; | |
277 | goto out_done; | |
278 | } | |
3b63f34d ML |
279 | |
280 | if (!(classes = IOServiceMatching(kIOSerialBSDServiceValue))) | |
77f262c4 ML |
281 | { |
282 | ret = SP_ERR_FAIL; | |
283 | goto out_done; | |
284 | } | |
3b63f34d ML |
285 | |
286 | CFDictionarySetValue(classes, | |
287 | CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes)); | |
288 | ||
0d34b451 | 289 | if (IOServiceGetMatchingServices(master, classes, &iter) != KERN_SUCCESS) |
77f262c4 ML |
290 | { |
291 | ret = SP_ERR_FAIL; | |
292 | goto out_done; | |
293 | } | |
3b63f34d ML |
294 | |
295 | if (!(path = malloc(PATH_MAX))) | |
77f262c4 ML |
296 | { |
297 | ret = SP_ERR_MEM; | |
3b63f34d | 298 | goto out_release; |
77f262c4 | 299 | } |
3b63f34d | 300 | |
1ebf4347 | 301 | while ((port = IOIteratorNext(iter))) { |
3b63f34d ML |
302 | cf_path = IORegistryEntryCreateCFProperty(port, |
303 | CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); | |
304 | if (cf_path) { | |
305 | result = CFStringGetCString(cf_path, | |
306 | path, PATH_MAX, kCFStringEncodingASCII); | |
307 | CFRelease(cf_path); | |
77f262c4 ML |
308 | if (result && !(list = sp_list_append(list, path))) |
309 | { | |
310 | ret = SP_ERR_MEM; | |
311 | IOObjectRelease(port); | |
312 | goto out; | |
313 | } | |
3b63f34d ML |
314 | } |
315 | IOObjectRelease(port); | |
316 | } | |
3b63f34d ML |
317 | out: |
318 | free(path); | |
319 | out_release: | |
320 | IOObjectRelease(iter); | |
77f262c4 | 321 | out_done: |
3b63f34d ML |
322 | #endif |
323 | #ifdef __linux__ | |
324 | struct udev *ud; | |
325 | struct udev_enumerate *ud_enumerate; | |
326 | struct udev_list_entry *ud_list; | |
327 | struct udev_list_entry *ud_entry; | |
328 | const char *path; | |
08fe0bdb | 329 | struct udev_device *ud_dev, *ud_parent; |
3b63f34d | 330 | const char *name; |
4b97c9fc ML |
331 | const char *driver; |
332 | int fd, ioctl_result; | |
333 | struct serial_struct serial_info; | |
3b63f34d ML |
334 | |
335 | ud = udev_new(); | |
336 | ud_enumerate = udev_enumerate_new(ud); | |
337 | udev_enumerate_add_match_subsystem(ud_enumerate, "tty"); | |
338 | udev_enumerate_scan_devices(ud_enumerate); | |
339 | ud_list = udev_enumerate_get_list_entry(ud_enumerate); | |
3b63f34d ML |
340 | udev_list_entry_foreach(ud_entry, ud_list) |
341 | { | |
342 | path = udev_list_entry_get_name(ud_entry); | |
343 | ud_dev = udev_device_new_from_syspath(ud, path); | |
08fe0bdb ML |
344 | /* If there is no parent device, this is a virtual tty. */ |
345 | ud_parent = udev_device_get_parent(ud_dev); | |
346 | if (ud_parent == NULL) | |
347 | { | |
348 | udev_device_unref(ud_dev); | |
349 | continue; | |
350 | } | |
3b63f34d | 351 | name = udev_device_get_devnode(ud_dev); |
4b97c9fc ML |
352 | /* The serial8250 driver has a hardcoded number of ports. |
353 | * The only way to tell which actually exist on a given system | |
354 | * is to try to open them and make an ioctl call. */ | |
355 | driver = udev_device_get_driver(ud_parent); | |
356 | if (driver && !strcmp(driver, "serial8250")) | |
357 | { | |
358 | if ((fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) | |
359 | goto skip; | |
360 | ioctl_result = ioctl(fd, TIOCGSERIAL, &serial_info); | |
361 | close(fd); | |
362 | if (ioctl_result != 0) | |
363 | goto skip; | |
364 | if (serial_info.type == PORT_UNKNOWN) | |
365 | goto skip; | |
366 | } | |
5919c913 | 367 | list = sp_list_append(list, name); |
4b97c9fc | 368 | skip: |
3b63f34d ML |
369 | udev_device_unref(ud_dev); |
370 | if (!list) | |
77f262c4 ML |
371 | { |
372 | ret = SP_ERR_MEM; | |
3b63f34d | 373 | goto out; |
77f262c4 | 374 | } |
3b63f34d ML |
375 | } |
376 | out: | |
377 | udev_enumerate_unref(ud_enumerate); | |
378 | udev_unref(ud); | |
3b63f34d | 379 | #endif |
77f262c4 ML |
380 | |
381 | if (ret == SP_OK) | |
382 | { | |
383 | *list_ptr = list; | |
384 | } | |
385 | else | |
386 | { | |
387 | if (list) | |
388 | sp_free_port_list(list); | |
389 | ||
390 | *list_ptr = NULL; | |
391 | } | |
392 | ||
393 | return ret; | |
3b63f34d ML |
394 | } |
395 | ||
d54e9004 | 396 | void sp_free_port_list(struct sp_port **list) |
3b63f34d ML |
397 | { |
398 | unsigned int i; | |
f92f1f0c | 399 | |
3b63f34d | 400 | for (i = 0; list[i]; i++) |
e3b2f7a4 | 401 | sp_free_port(list[i]); |
3b63f34d ML |
402 | free(list); |
403 | } | |
404 | ||
74510d4b ML |
405 | static int sp_validate_port(struct sp_port *port) |
406 | { | |
407 | if (port == NULL) | |
408 | return 0; | |
409 | #ifdef _WIN32 | |
410 | if (port->hdl == INVALID_HANDLE_VALUE) | |
411 | return 0; | |
412 | #else | |
413 | if (port->fd < 0) | |
414 | return 0; | |
415 | #endif | |
416 | return 1; | |
417 | } | |
418 | ||
419 | #define CHECK_PORT() do { if (!sp_validate_port(port)) return SP_ERR_ARG; } while (0) | |
420 | ||
d54e9004 | 421 | int sp_open(struct sp_port *port, int flags) |
74510d4b ML |
422 | { |
423 | if (!port) | |
424 | return SP_ERR_ARG; | |
425 | ||
74510d4b ML |
426 | #ifdef _WIN32 |
427 | DWORD desired_access = 0, flags_and_attributes = 0; | |
99945a1f ML |
428 | char *escaped_port_name; |
429 | ||
430 | /* Prefix port name with '\\.\' to work with ports above COM9. */ | |
431 | if (!(escaped_port_name = malloc(strlen(port->name + 5)))) | |
432 | return SP_ERR_MEM; | |
433 | sprintf(escaped_port_name, "\\\\.\\%s", port->name); | |
434 | ||
74510d4b ML |
435 | /* Map 'flags' to the OS-specific settings. */ |
436 | desired_access |= GENERIC_READ; | |
437 | flags_and_attributes = FILE_ATTRIBUTE_NORMAL; | |
438 | if (flags & SP_MODE_RDWR) | |
439 | desired_access |= GENERIC_WRITE; | |
440 | if (flags & SP_MODE_NONBLOCK) | |
441 | flags_and_attributes |= FILE_FLAG_OVERLAPPED; | |
442 | ||
99945a1f | 443 | port->hdl = CreateFile(escaped_port_name, desired_access, 0, 0, |
74510d4b | 444 | OPEN_EXISTING, flags_and_attributes, 0); |
99945a1f ML |
445 | |
446 | free(escaped_port_name); | |
447 | ||
74510d4b ML |
448 | if (port->hdl == INVALID_HANDLE_VALUE) |
449 | return SP_ERR_FAIL; | |
450 | #else | |
451 | int flags_local = 0; | |
9cb98459 | 452 | struct sp_port_data data; |
f92f1f0c | 453 | |
74510d4b ML |
454 | /* Map 'flags' to the OS-specific settings. */ |
455 | if (flags & SP_MODE_RDWR) | |
456 | flags_local |= O_RDWR; | |
457 | if (flags & SP_MODE_RDONLY) | |
458 | flags_local |= O_RDONLY; | |
459 | if (flags & SP_MODE_NONBLOCK) | |
460 | flags_local |= O_NONBLOCK; | |
461 | ||
462 | if ((port->fd = open(port->name, flags_local)) < 0) | |
463 | return SP_ERR_FAIL; | |
9cb98459 ML |
464 | |
465 | start_config(port, &data); | |
466 | ||
467 | /* Turn off all serial port cooking. */ | |
468 | data.term.c_iflag &= ~(ISTRIP | INLCR | ICRNL); | |
469 | data.term.c_oflag &= ~(ONLCR | OCRNL | ONOCR); | |
470 | #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) | |
471 | data.term.c_oflag &= ~OFILL; | |
472 | #endif | |
473 | /* Disable canonical mode, and don't echo input characters. */ | |
474 | data.term.c_lflag &= ~(ICANON | ECHO); | |
475 | ||
476 | /* Ignore modem status lines; enable receiver */ | |
477 | data.term.c_cflag |= (CLOCAL | CREAD); | |
478 | ||
479 | apply_config(port, &data); | |
74510d4b ML |
480 | #endif |
481 | ||
482 | return SP_OK; | |
483 | } | |
484 | ||
74510d4b ML |
485 | int sp_close(struct sp_port *port) |
486 | { | |
487 | CHECK_PORT(); | |
488 | ||
489 | #ifdef _WIN32 | |
490 | /* Returns non-zero upon success, 0 upon failure. */ | |
491 | if (CloseHandle(port->hdl) == 0) | |
492 | return SP_ERR_FAIL; | |
493 | #else | |
494 | /* Returns 0 upon success, -1 upon failure. */ | |
495 | if (close(port->fd) == -1) | |
496 | return SP_ERR_FAIL; | |
497 | #endif | |
498 | ||
499 | return SP_OK; | |
500 | } | |
501 | ||
74510d4b ML |
502 | int sp_flush(struct sp_port *port) |
503 | { | |
504 | CHECK_PORT(); | |
505 | ||
506 | #ifdef _WIN32 | |
507 | /* Returns non-zero upon success, 0 upon failure. */ | |
508 | if (PurgeComm(port->hdl, PURGE_RXCLEAR | PURGE_TXCLEAR) == 0) | |
509 | return SP_ERR_FAIL; | |
510 | #else | |
511 | /* Returns 0 upon success, -1 upon failure. */ | |
512 | if (tcflush(port->fd, TCIOFLUSH) < 0) | |
513 | return SP_ERR_FAIL; | |
514 | #endif | |
515 | return SP_OK; | |
516 | } | |
517 | ||
74510d4b ML |
518 | int sp_write(struct sp_port *port, const void *buf, size_t count) |
519 | { | |
520 | CHECK_PORT(); | |
521 | ||
522 | if (!buf) | |
523 | return SP_ERR_ARG; | |
524 | ||
525 | #ifdef _WIN32 | |
526 | DWORD written = 0; | |
f92f1f0c | 527 | |
74510d4b ML |
528 | /* Returns non-zero upon success, 0 upon failure. */ |
529 | if (WriteFile(port->hdl, buf, count, &written, NULL) == 0) | |
530 | return SP_ERR_FAIL; | |
531 | return written; | |
532 | #else | |
533 | /* Returns the number of bytes written, or -1 upon failure. */ | |
534 | ssize_t written = write(port->fd, buf, count); | |
f92f1f0c | 535 | |
74510d4b ML |
536 | if (written < 0) |
537 | return SP_ERR_FAIL; | |
538 | else | |
f92f1f0c | 539 | return written; |
74510d4b ML |
540 | #endif |
541 | } | |
542 | ||
74510d4b ML |
543 | int sp_read(struct sp_port *port, void *buf, size_t count) |
544 | { | |
545 | CHECK_PORT(); | |
546 | ||
547 | if (!buf) | |
548 | return SP_ERR_ARG; | |
549 | ||
550 | #ifdef _WIN32 | |
551 | DWORD bytes_read = 0; | |
f92f1f0c | 552 | |
74510d4b ML |
553 | /* Returns non-zero upon success, 0 upon failure. */ |
554 | if (ReadFile(port->hdl, buf, count, &bytes_read, NULL) == 0) | |
555 | return SP_ERR_FAIL; | |
556 | return bytes_read; | |
557 | #else | |
558 | ssize_t bytes_read; | |
f92f1f0c | 559 | |
74510d4b ML |
560 | /* Returns the number of bytes read, or -1 upon failure. */ |
561 | if ((bytes_read = read(port->fd, buf, count)) < 0) | |
562 | return SP_ERR_FAIL; | |
563 | return bytes_read; | |
564 | #endif | |
565 | } | |
566 | ||
8094e4a0 | 567 | static int start_config(struct sp_port *port, struct sp_port_data *data) |
74510d4b ML |
568 | { |
569 | CHECK_PORT(); | |
74510d4b | 570 | #ifdef _WIN32 |
8094e4a0 | 571 | if (!GetCommState(port->hdl, &data->dcb)) |
74510d4b | 572 | return SP_ERR_FAIL; |
8094e4a0 ML |
573 | #else |
574 | if (tcgetattr(port->fd, &data->term) < 0) | |
575 | return SP_ERR_FAIL; | |
9f90173c | 576 | |
824dcb45 | 577 | if (ioctl(port->fd, TIOCMGET, &data->controlbits) < 0) |
9f90173c ML |
578 | return SP_ERR_FAIL; |
579 | ||
580 | if (data->term.c_cflag & CRTSCTS) { | |
581 | data->rts = SP_RTS_FLOW_CONTROL; | |
582 | data->cts = SP_CTS_FLOW_CONTROL; | |
583 | } else { | |
824dcb45 | 584 | data->rts = (data->controlbits & TIOCM_RTS) ? SP_RTS_ON : SP_RTS_OFF; |
9f90173c ML |
585 | data->cts = SP_CTS_IGNORE; |
586 | } | |
587 | ||
824dcb45 | 588 | data->dtr = (data->controlbits & TIOCM_DTR) ? SP_DTR_ON : SP_DTR_OFF; |
9f90173c | 589 | data->dsr = SP_DSR_IGNORE; |
8094e4a0 ML |
590 | #endif |
591 | return SP_OK; | |
592 | } | |
74510d4b | 593 | |
8094e4a0 ML |
594 | static int set_baudrate(struct sp_port_data *data, int baudrate) |
595 | { | |
da2748bf ML |
596 | unsigned int i; |
597 | for (i = 0; i < NUM_STD_BAUDRATES; i++) { | |
598 | if (baudrate == std_baudrates[i].value) { | |
8094e4a0 | 599 | #ifdef _WIN32 |
da2748bf | 600 | data->dcb.BaudRate = std_baudrates[i].index; |
74510d4b | 601 | #else |
da2748bf ML |
602 | if (cfsetospeed(&data->term, std_baudrates[i].index) < 0) |
603 | return SP_ERR_FAIL; | |
604 | ||
605 | if (cfsetispeed(&data->term, std_baudrates[i].index) < 0) | |
606 | return SP_ERR_FAIL; | |
74510d4b | 607 | #endif |
da2748bf ML |
608 | break; |
609 | } | |
74510d4b | 610 | } |
cbf628c7 | 611 | |
da2748bf ML |
612 | if (i == NUM_STD_BAUDRATES) |
613 | return SP_ERR_ARG; | |
cbf628c7 | 614 | |
8094e4a0 ML |
615 | return SP_OK; |
616 | } | |
74510d4b | 617 | |
8094e4a0 ML |
618 | static int set_bits(struct sp_port_data *data, int bits) |
619 | { | |
620 | #ifdef _WIN32 | |
621 | data->dcb.ByteSize = bits; | |
622 | #else | |
623 | data->term.c_cflag &= ~CSIZE; | |
74510d4b ML |
624 | switch (bits) { |
625 | case 8: | |
8094e4a0 | 626 | data->term.c_cflag |= CS8; |
74510d4b ML |
627 | break; |
628 | case 7: | |
8094e4a0 | 629 | data->term.c_cflag |= CS7; |
74510d4b | 630 | break; |
d1d566f2 | 631 | case 6: |
8094e4a0 | 632 | data->term.c_cflag |= CS6; |
d1d566f2 | 633 | break; |
74510d4b ML |
634 | default: |
635 | return SP_ERR_ARG; | |
636 | } | |
8094e4a0 ML |
637 | #endif |
638 | return SP_OK; | |
639 | } | |
74510d4b | 640 | |
8094e4a0 ML |
641 | static int set_parity(struct sp_port_data *data, int parity) |
642 | { | |
643 | #ifdef _WIN32 | |
644 | switch (parity) { | |
645 | /* Note: There's also SPACEPARITY, MARKPARITY (unneeded so far). */ | |
646 | case SP_PARITY_NONE: | |
647 | data->dcb.Parity = NOPARITY; | |
648 | break; | |
649 | case SP_PARITY_EVEN: | |
650 | data->dcb.Parity = EVENPARITY; | |
651 | break; | |
652 | case SP_PARITY_ODD: | |
653 | data->dcb.Parity = ODDPARITY; | |
654 | break; | |
655 | default: | |
656 | return SP_ERR_ARG; | |
657 | } | |
658 | #else | |
659 | data->term.c_iflag &= ~IGNPAR; | |
660 | data->term.c_cflag &= ~(PARENB | PARODD); | |
661 | switch (parity) { | |
662 | case SP_PARITY_NONE: | |
663 | data->term.c_iflag |= IGNPAR; | |
664 | break; | |
665 | case SP_PARITY_EVEN: | |
666 | data->term.c_cflag |= PARENB; | |
667 | break; | |
668 | case SP_PARITY_ODD: | |
669 | data->term.c_cflag |= PARENB | PARODD; | |
670 | break; | |
671 | default: | |
672 | return SP_ERR_ARG; | |
673 | } | |
674 | #endif | |
675 | return SP_OK; | |
676 | } | |
677 | ||
678 | static int set_stopbits(struct sp_port_data *data, int stopbits) | |
679 | { | |
680 | #ifdef _WIN32 | |
74510d4b | 681 | switch (stopbits) { |
8094e4a0 | 682 | /* Note: There's also ONE5STOPBITS == 1.5 (unneeded so far). */ |
74510d4b | 683 | case 1: |
8094e4a0 | 684 | data->dcb.StopBits = ONESTOPBIT; |
74510d4b ML |
685 | break; |
686 | case 2: | |
8094e4a0 | 687 | data->dcb.StopBits = TWOSTOPBITS; |
74510d4b ML |
688 | break; |
689 | default: | |
690 | return SP_ERR_ARG; | |
691 | } | |
8094e4a0 ML |
692 | #else |
693 | data->term.c_cflag &= ~CSTOPB; | |
694 | switch (stopbits) { | |
74510d4b | 695 | case 1: |
8094e4a0 | 696 | data->term.c_cflag &= ~CSTOPB; |
74510d4b ML |
697 | break; |
698 | case 2: | |
8094e4a0 | 699 | data->term.c_cflag |= CSTOPB; |
74510d4b ML |
700 | break; |
701 | default: | |
702 | return SP_ERR_ARG; | |
703 | } | |
8094e4a0 ML |
704 | #endif |
705 | return SP_OK; | |
706 | } | |
74510d4b | 707 | |
d514a26f | 708 | static int set_rts(struct sp_port_data *data, int rts) |
8094e4a0 | 709 | { |
d514a26f ML |
710 | #ifdef _WIN32 |
711 | switch (rts) { | |
712 | case SP_RTS_OFF: | |
713 | data->dcb.fRtsControl = RTS_CONTROL_DISABLE; | |
74510d4b | 714 | break; |
d514a26f ML |
715 | case SP_RTS_ON: |
716 | data->dcb.fRtsControl = RTS_CONTROL_ENABLE; | |
74510d4b | 717 | break; |
d514a26f ML |
718 | case SP_RTS_FLOW_CONTROL: |
719 | data->dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; | |
74510d4b ML |
720 | break; |
721 | default: | |
722 | return SP_ERR_ARG; | |
723 | } | |
d514a26f ML |
724 | #else |
725 | data->rts = rts; | |
8094e4a0 ML |
726 | #endif |
727 | return SP_OK; | |
728 | } | |
729 | ||
d514a26f | 730 | static int set_cts(struct sp_port_data *data, int cts) |
8094e4a0 ML |
731 | { |
732 | #ifdef _WIN32 | |
d514a26f ML |
733 | switch (cts) { |
734 | case SP_CTS_IGNORE: | |
735 | data->dcb.fOutxCtsFlow = FALSE; | |
736 | break; | |
737 | case SP_CTS_FLOW_CONTROL: | |
738 | data->dcb.fOutxCtsFlow = TRUE; | |
739 | break; | |
740 | default: | |
741 | return SP_ERR_ARG; | |
742 | } | |
8094e4a0 | 743 | #else |
d514a26f | 744 | data->cts = cts; |
8094e4a0 ML |
745 | #endif |
746 | return SP_OK; | |
747 | } | |
748 | ||
749 | static int set_dtr(struct sp_port_data *data, int dtr) | |
750 | { | |
751 | #ifdef _WIN32 | |
d514a26f ML |
752 | switch (dtr) { |
753 | case SP_DTR_OFF: | |
a46f0960 | 754 | data->dcb.fDtrControl = DTR_CONTROL_DISABLE; |
d514a26f ML |
755 | break; |
756 | case SP_DTR_ON: | |
757 | data->dcb.fDtrControl = DTR_CONTROL_ENABLE; | |
758 | break; | |
759 | case SP_DTR_FLOW_CONTROL: | |
760 | data->dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; | |
761 | break; | |
762 | default: | |
763 | return SP_ERR_ARG; | |
764 | } | |
8094e4a0 ML |
765 | #else |
766 | data->dtr = dtr; | |
767 | #endif | |
768 | return SP_OK; | |
769 | } | |
770 | ||
d514a26f ML |
771 | static int set_dsr(struct sp_port_data *data, int dsr) |
772 | { | |
773 | #ifdef _WIN32 | |
774 | switch (dsr) { | |
775 | case SP_DSR_IGNORE: | |
776 | data->dcb.fOutxDsrFlow = FALSE; | |
777 | break; | |
778 | case SP_DSR_FLOW_CONTROL: | |
779 | data->dcb.fOutxDsrFlow = TRUE; | |
780 | break; | |
781 | default: | |
782 | return SP_ERR_ARG; | |
783 | } | |
784 | #else | |
785 | data->dsr = dsr; | |
786 | #endif | |
787 | return SP_OK; | |
788 | } | |
789 | ||
790 | static int set_xon_xoff(struct sp_port_data *data, int xon_xoff) | |
791 | { | |
792 | #ifdef _WIN32 | |
793 | switch (xon_xoff) { | |
794 | case SP_XONXOFF_DISABLED: | |
795 | data->dcb.fInX = FALSE; | |
796 | data->dcb.fOutX = FALSE; | |
797 | break; | |
798 | case SP_XONXOFF_IN: | |
799 | data->dcb.fInX = TRUE; | |
800 | data->dcb.fOutX = FALSE; | |
801 | break; | |
802 | case SP_XONXOFF_OUT: | |
803 | data->dcb.fInX = FALSE; | |
804 | data->dcb.fOutX = TRUE; | |
805 | break; | |
806 | case SP_XONXOFF_INOUT: | |
807 | data->dcb.fInX = TRUE; | |
808 | data->dcb.fOutX = TRUE; | |
809 | break; | |
810 | default: | |
811 | return SP_ERR_ARG; | |
812 | } | |
813 | #else | |
814 | data->term.c_iflag &= ~(IXON | IXOFF | IXANY); | |
815 | switch (xon_xoff) { | |
816 | case SP_XONXOFF_DISABLED: | |
817 | break; | |
818 | case SP_XONXOFF_IN: | |
819 | data->term.c_iflag |= IXOFF; | |
820 | break; | |
821 | case SP_XONXOFF_OUT: | |
822 | data->term.c_iflag |= IXON | IXANY; | |
823 | break; | |
824 | case SP_XONXOFF_INOUT: | |
825 | data->term.c_iflag |= IXON | IXOFF | IXANY; | |
826 | break; | |
827 | default: | |
828 | return SP_ERR_ARG; | |
829 | } | |
830 | #endif | |
831 | return SP_OK; | |
832 | } | |
833 | ||
8094e4a0 ML |
834 | static int apply_config(struct sp_port *port, struct sp_port_data *data) |
835 | { | |
836 | #ifdef _WIN32 | |
837 | if (!SetCommState(port->hdl, &data->dcb)) | |
838 | return SP_ERR_FAIL; | |
839 | #else | |
840 | int controlbits; | |
74510d4b | 841 | |
d514a26f ML |
842 | /* Asymmetric use of RTS/CTS not supported yet. */ |
843 | if ((data->rts == SP_RTS_FLOW_CONTROL) != (data->cts == SP_CTS_FLOW_CONTROL)) | |
844 | return SP_ERR_ARG; | |
845 | ||
846 | /* DTR/DSR flow control not supported yet. */ | |
847 | if (data->dtr == SP_DTR_FLOW_CONTROL || data->dsr == SP_DSR_FLOW_CONTROL) | |
848 | return SP_ERR_ARG; | |
74510d4b | 849 | |
d514a26f ML |
850 | if (data->rts == SP_RTS_FLOW_CONTROL) |
851 | data->term.c_iflag |= CRTSCTS; | |
852 | else | |
853 | { | |
74510d4b | 854 | controlbits = TIOCM_RTS; |
d514a26f | 855 | if (ioctl(port->fd, data->rts == SP_RTS_ON ? TIOCMBIS : TIOCMBIC, |
74510d4b ML |
856 | &controlbits) < 0) |
857 | return SP_ERR_FAIL; | |
858 | } | |
859 | ||
d514a26f ML |
860 | controlbits = TIOCM_DTR; |
861 | if (ioctl(port->fd, data->dtr == SP_DTR_ON ? TIOCMBIS : TIOCMBIC, | |
862 | &controlbits) < 0) | |
863 | return SP_ERR_FAIL; | |
864 | ||
865 | /* Write the configured settings. */ | |
866 | if (tcsetattr(port->fd, TCSADRAIN, &data->term) < 0) | |
867 | return SP_ERR_FAIL; | |
74510d4b | 868 | #endif |
8094e4a0 ML |
869 | return SP_OK; |
870 | } | |
871 | ||
872 | #define TRY(x) do { int ret = x; if (ret != SP_OK) return ret; } while (0) | |
18fc2dd1 ML |
873 | #define TRY_SET(x, y) do { if (y >= 0) TRY(set_##x(&data, y)); } while (0) |
874 | #define TRY_SET_CONFIG(x) TRY_SET(x, config->x) | |
8094e4a0 | 875 | |
d1202734 | 876 | int sp_set_config(struct sp_port *port, struct sp_port_config *config) |
8094e4a0 ML |
877 | { |
878 | struct sp_port_data data; | |
879 | ||
880 | TRY(start_config(port, &data)); | |
18fc2dd1 ML |
881 | TRY_SET_CONFIG(baudrate); |
882 | TRY_SET_CONFIG(bits); | |
883 | TRY_SET_CONFIG(parity); | |
884 | TRY_SET_CONFIG(stopbits); | |
885 | TRY_SET_CONFIG(rts); | |
886 | TRY_SET_CONFIG(cts); | |
887 | TRY_SET_CONFIG(dtr); | |
888 | TRY_SET_CONFIG(dsr); | |
889 | TRY_SET_CONFIG(xon_xoff); | |
890 | TRY(apply_config(port, &data)); | |
891 | ||
892 | return SP_OK; | |
893 | } | |
894 | ||
895 | int sp_set_flowcontrol(struct sp_port *port, int flowcontrol) | |
896 | { | |
897 | struct sp_port_data data; | |
898 | ||
899 | TRY(start_config(port, &data)); | |
900 | ||
901 | if (flowcontrol == SP_FLOWCONTROL_XONXOFF) | |
902 | TRY_SET(xon_xoff, SP_XONXOFF_INOUT); | |
903 | else | |
904 | TRY_SET(xon_xoff, SP_XONXOFF_DISABLED); | |
905 | ||
906 | if (flowcontrol == SP_FLOWCONTROL_RTSCTS) { | |
907 | TRY_SET(rts, SP_RTS_FLOW_CONTROL); | |
908 | TRY_SET(cts, SP_CTS_FLOW_CONTROL); | |
909 | } else { | |
910 | TRY_SET(rts, SP_RTS_ON); | |
911 | TRY_SET(cts, SP_CTS_IGNORE); | |
912 | } | |
913 | ||
914 | if (flowcontrol == SP_FLOWCONTROL_DTRDSR) { | |
915 | TRY_SET(dtr, SP_DTR_FLOW_CONTROL); | |
916 | TRY_SET(dsr, SP_DSR_FLOW_CONTROL); | |
917 | } else { | |
918 | TRY_SET(dtr, SP_DTR_ON); | |
919 | TRY_SET(dsr, SP_DSR_IGNORE); | |
920 | } | |
921 | ||
8094e4a0 | 922 | TRY(apply_config(port, &data)); |
74510d4b ML |
923 | |
924 | return SP_OK; | |
925 | } | |
926 | ||
9069c2fb ML |
927 | #define CREATE_SETTER(x) int sp_set_##x(struct sp_port *port, int x) { \ |
928 | struct sp_port_data data; \ | |
929 | TRY(start_config(port, &data)); \ | |
930 | TRY(set_##x(&data, x)); \ | |
931 | TRY(apply_config(port, &data)); \ | |
932 | return SP_OK; \ | |
933 | } | |
934 | ||
935 | CREATE_SETTER(baudrate) | |
936 | CREATE_SETTER(bits) | |
937 | CREATE_SETTER(parity) | |
938 | CREATE_SETTER(stopbits) | |
939 | CREATE_SETTER(rts) | |
940 | CREATE_SETTER(cts) | |
941 | CREATE_SETTER(dtr) | |
942 | CREATE_SETTER(dsr) | |
943 | CREATE_SETTER(xon_xoff) | |
944 | ||
74510d4b ML |
945 | int sp_last_error_code(void) |
946 | { | |
947 | #ifdef _WIN32 | |
948 | return GetLastError(); | |
949 | #else | |
950 | return errno; | |
951 | #endif | |
952 | } | |
953 | ||
74510d4b ML |
954 | char *sp_last_error_message(void) |
955 | { | |
956 | #ifdef _WIN32 | |
957 | LPVOID message; | |
958 | DWORD error = GetLastError(); | |
959 | ||
960 | FormatMessage( | |
961 | FORMAT_MESSAGE_ALLOCATE_BUFFER | | |
962 | FORMAT_MESSAGE_FROM_SYSTEM | | |
963 | FORMAT_MESSAGE_IGNORE_INSERTS, | |
964 | NULL, | |
965 | error, | |
966 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
967 | (LPTSTR) &message, | |
968 | 0, NULL ); | |
969 | ||
970 | return message; | |
971 | #else | |
972 | return strerror(errno); | |
973 | #endif | |
974 | } | |
975 | ||
74510d4b ML |
976 | void sp_free_error_message(char *message) |
977 | { | |
978 | #ifdef _WIN32 | |
979 | LocalFree(message); | |
64eec30d ML |
980 | #else |
981 | (void)message; | |
74510d4b ML |
982 | #endif |
983 | } |