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