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