X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=serialport.c;h=f74fea7bc82e6e2ef5d3d4e67b48095571c9ca7e;hb=ea17bfca3d955fdf976ec13a0d6c3bb6a9f3b9e9;hp=c2946a2f08c427a7a7fe5448989f88dfedefa7cb;hpb=da2ede60e441566b074d512ad70b51b5751834ae;p=libserialport.git diff --git a/serialport.c b/serialport.c index c2946a2..f74fea7 100644 --- a/serialport.c +++ b/serialport.c @@ -5,6 +5,7 @@ * Copyright (C) 2010-2012 Uwe Hermann * Copyright (C) 2013 Martin Ling * Copyright (C) 2013 Matthias Heidbrink + * Copyright (C) 2014 Aurelien Jacobs * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as @@ -20,123 +21,8 @@ * along with this program. If not, see . */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef _WIN32 -#include -#include -#else -#include -#include -#include -#include -#include -#include -#endif -#ifdef __APPLE__ -#include -#include -#include -#include -#endif -#ifdef __linux__ -#ifdef HAVE_LIBUDEV -#include "libudev.h" -#endif -#ifndef __ANDROID__ -#include "linux/serial.h" -#endif -#include "linux_termios.h" - -/* TCGETX/TCSETX is not available everywhere. */ -#if defined(TCGETX) && defined(TCSETX) && defined(HAVE_TERMIOX) -#define USE_TERMIOX -#endif -#endif - -/* TIOCINQ/TIOCOUTQ is not available everywhere. */ -#if !defined(TIOCINQ) && defined(FIONREAD) -#define TIOCINQ FIONREAD -#endif -#if !defined(TIOCOUTQ) && defined(FIONWRITE) -#define TIOCOUTQ FIONWRITE -#endif - -/* Non-standard baudrates are not available everywhere. */ -#if defined(HAVE_TERMIOS_SPEED) || defined(HAVE_TERMIOS2_SPEED) -#define USE_TERMIOS_SPEED -#endif - #include "libserialport.h" - -struct sp_port { - char *name; -#ifdef _WIN32 - HANDLE hdl; - COMMTIMEOUTS timeouts; - OVERLAPPED write_ovl; - OVERLAPPED read_ovl; - OVERLAPPED wait_ovl; - DWORD events; - BYTE pending_byte; - BOOL writing; -#else - int fd; -#endif -}; - -struct sp_port_config { - int baudrate; - int bits; - enum sp_parity parity; - int stopbits; - enum sp_rts rts; - enum sp_cts cts; - enum sp_dtr dtr; - enum sp_dsr dsr; - enum sp_xonxoff xon_xoff; -}; - -struct port_data { -#ifdef _WIN32 - DCB dcb; -#else - struct termios term; - int controlbits; - int termiox_supported; - int rts_flow; - int cts_flow; - int dtr_flow; - int dsr_flow; -#endif -}; - -#ifdef _WIN32 -typedef HANDLE event_handle; -#else -typedef int event_handle; -#endif - -/* Standard baud rates. */ -#ifdef _WIN32 -#define BAUD_TYPE DWORD -#define BAUD(n) {CBR_##n, n} -#else -#define BAUD_TYPE speed_t -#define BAUD(n) {B##n, n} -#endif - -struct std_baudrate { - BAUD_TYPE index; - int value; -}; +#include "libserialport_internal.h" const struct std_baudrate std_baudrates[] = { #ifdef _WIN32 @@ -160,52 +46,18 @@ const struct std_baudrate std_baudrates[] = { void (*sp_debug_handler)(const char *format, ...) = sp_default_debug_handler; -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) -#define NUM_STD_BAUDRATES ARRAY_SIZE(std_baudrates) - -/* Debug output macros. */ -#define DEBUG(fmt, ...) do { if (sp_debug_handler) sp_debug_handler(fmt ".\n", ##__VA_ARGS__); } while (0) -#define DEBUG_ERROR(err, fmt, ...) DEBUG("%s returning " #err ": " fmt, __func__, ##__VA_ARGS__) -#define DEBUG_FAIL(fmt, ...) do { \ - char *errmsg = sp_last_error_message(); \ - DEBUG("%s returning SP_ERR_FAIL: "fmt": %s", __func__,##__VA_ARGS__,errmsg); \ - sp_free_error_message(errmsg); \ -} while (0); -#define RETURN() do { DEBUG("%s returning", __func__); return; } while(0) -#define RETURN_CODE(x) do { DEBUG("%s returning " #x, __func__); return x; } while (0) -#define RETURN_CODEVAL(x) do { \ - switch (x) { \ - case SP_OK: RETURN_CODE(SP_OK); \ - case SP_ERR_ARG: RETURN_CODE(SP_ERR_ARG); \ - case SP_ERR_FAIL: RETURN_CODE(SP_ERR_FAIL); \ - case SP_ERR_MEM: RETURN_CODE(SP_ERR_MEM); \ - case SP_ERR_SUPP: RETURN_CODE(SP_ERR_SUPP); \ - } \ -} while (0) -#define RETURN_OK() RETURN_CODE(SP_OK); -#define RETURN_ERROR(err, ...) do { DEBUG_ERROR(err, __VA_ARGS__); return err; } while (0) -#define RETURN_FAIL(...) do { DEBUG_FAIL(__VA_ARGS__); return SP_ERR_FAIL; } while (0) -#define RETURN_VALUE(fmt, x) do { \ - typeof(x) _x = x; \ - DEBUG("%s returning " fmt, __func__, _x); \ - return _x; \ -} while (0) -#define SET_ERROR(val, err, msg) do { DEBUG_ERROR(err, msg); val = err; } while (0) -#define SET_FAIL(val, msg) do { DEBUG_FAIL(msg); val = SP_ERR_FAIL; } while (0) -#define TRACE(fmt, ...) DEBUG("%s(" fmt ") called", __func__, ##__VA_ARGS__) - -#define TRY(x) do { int ret = x; if (ret != SP_OK) RETURN_CODEVAL(ret); } while (0) - -/* Helper functions. */ -static struct sp_port **list_append(struct sp_port **list, const char *portname); static enum sp_return get_config(struct sp_port *port, struct port_data *data, struct sp_port_config *config); + static enum sp_return set_config(struct sp_port *port, struct port_data *data, const struct sp_port_config *config); -enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_ptr) +SP_API enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_ptr) { struct sp_port *port; +#ifndef NO_PORT_METADATA + enum sp_return ret; +#endif int len; TRACE("%s, %p", portname, port_ptr); @@ -218,7 +70,7 @@ enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_p if (!portname) RETURN_ERROR(SP_ERR_ARG, "Null port name"); - DEBUG("Building structure for port %s", portname); + DEBUG_FMT("Building structure for port %s", portname); if (!(port = malloc(sizeof(struct sp_port)))) RETURN_ERROR(SP_ERR_MEM, "Port structure malloc failed"); @@ -233,27 +85,144 @@ enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_p memcpy(port->name, portname, len); #ifdef _WIN32 + port->usb_path = NULL; port->hdl = INVALID_HANDLE_VALUE; #else port->fd = -1; #endif + port->description = NULL; + port->transport = SP_TRANSPORT_NATIVE; + port->usb_bus = -1; + port->usb_address = -1; + port->usb_vid = -1; + port->usb_pid = -1; + port->usb_manufacturer = NULL; + port->usb_product = NULL; + port->usb_serial = NULL; + port->bluetooth_address = NULL; + +#ifndef NO_PORT_METADATA + if ((ret = get_port_details(port)) != SP_OK) { + sp_free_port(port); + return ret; + } +#endif + *port_ptr = port; RETURN_OK(); } -char *sp_get_port_name(const struct sp_port *port) +SP_API char *sp_get_port_name(const struct sp_port *port) +{ + TRACE("%p", port); + + if (!port) + return NULL; + + RETURN_STRING(port->name); +} + +SP_API char *sp_get_port_description(struct sp_port *port) +{ + TRACE("%p", port); + + if (!port || !port->description) + return NULL; + + RETURN_STRING(port->description); +} + +SP_API enum sp_transport sp_get_port_transport(struct sp_port *port) +{ + TRACE("%p", port); + + if (!port) + RETURN_ERROR(SP_ERR_ARG, "Null port"); + + RETURN_INT(port->transport); +} + +SP_API enum sp_return sp_get_port_usb_bus_address(const struct sp_port *port, + int *usb_bus,int *usb_address) +{ + TRACE("%p", port); + + if (!port) + RETURN_ERROR(SP_ERR_ARG, "Null port"); + if (port->transport != SP_TRANSPORT_USB) + RETURN_ERROR(SP_ERR_ARG, "Port does not use USB transport"); + if (port->usb_bus < 0 || port->usb_address < 0) + RETURN_ERROR(SP_ERR_SUPP, "Bus and address values are not available"); + + if (usb_bus) *usb_bus = port->usb_bus; + if (usb_address) *usb_address = port->usb_address; + + RETURN_OK(); +} + +SP_API enum sp_return sp_get_port_usb_vid_pid(const struct sp_port *port, + int *usb_vid, int *usb_pid) { TRACE("%p", port); if (!port) + RETURN_ERROR(SP_ERR_ARG, "Null port"); + if (port->transport != SP_TRANSPORT_USB) + RETURN_ERROR(SP_ERR_ARG, "Port does not use USB transport"); + if (port->usb_vid < 0 || port->usb_pid < 0) + RETURN_ERROR(SP_ERR_SUPP, "VID:PID values are not available"); + + if (usb_vid) *usb_vid = port->usb_vid; + if (usb_pid) *usb_pid = port->usb_pid; + + RETURN_OK(); +} + +SP_API char *sp_get_port_usb_manufacturer(const struct sp_port *port) +{ + TRACE("%p", port); + + if (!port || port->transport != SP_TRANSPORT_USB || !port->usb_manufacturer) + return NULL; + + RETURN_STRING(port->usb_manufacturer); +} + +SP_API char *sp_get_port_usb_product(const struct sp_port *port) +{ + TRACE("%p", port); + + if (!port || port->transport != SP_TRANSPORT_USB || !port->usb_product) return NULL; - RETURN_VALUE("%s", port->name); + RETURN_STRING(port->usb_product); } -enum sp_return sp_get_port_handle(const struct sp_port *port, void *result_ptr) +SP_API char *sp_get_port_usb_serial(const struct sp_port *port) +{ + TRACE("%p", port); + + if (!port || port->transport != SP_TRANSPORT_USB || !port->usb_serial) + return NULL; + + RETURN_STRING(port->usb_serial); +} + +SP_API char *sp_get_port_bluetooth_address(const struct sp_port *port) +{ + TRACE("%p", port); + + if (!port || port->transport != SP_TRANSPORT_BLUETOOTH + || !port->bluetooth_address) + return NULL; + + RETURN_STRING(port->bluetooth_address); +} + +SP_API enum sp_return sp_get_port_handle(const struct sp_port *port, + void *result_ptr) { TRACE("%p, %p", port, result_ptr); @@ -271,7 +240,8 @@ enum sp_return sp_get_port_handle(const struct sp_port *port, void *result_ptr) RETURN_OK(); } -enum sp_return sp_copy_port(const struct sp_port *port, struct sp_port **copy_ptr) +SP_API enum sp_return sp_copy_port(const struct sp_port *port, + struct sp_port **copy_ptr) { TRACE("%p, %p", port, copy_ptr); @@ -288,10 +258,10 @@ enum sp_return sp_copy_port(const struct sp_port *port, struct sp_port **copy_pt DEBUG("Copying port structure"); - RETURN_VALUE("%p", sp_get_port_by_name(port->name, copy_ptr)); + RETURN_INT(sp_get_port_by_name(port->name, copy_ptr)); } -void sp_free_port(struct sp_port *port) +SP_API void sp_free_port(struct sp_port *port) { TRACE("%p", port); @@ -304,13 +274,28 @@ void sp_free_port(struct sp_port *port) if (port->name) free(port->name); + if (port->description) + free(port->description); + if (port->usb_manufacturer) + free(port->usb_manufacturer); + if (port->usb_product) + free(port->usb_product); + if (port->usb_serial) + free(port->usb_serial); + if (port->bluetooth_address) + free(port->bluetooth_address); +#ifdef _WIN32 + if (port->usb_path) + free(port->usb_path); +#endif free(port); RETURN(); } -static struct sp_port **list_append(struct sp_port **list, const char *portname) +SP_PRIV struct sp_port **list_append(struct sp_port **list, + const char *portname) { void *tmp; unsigned int count; @@ -329,10 +314,10 @@ fail: return NULL; } -enum sp_return sp_list_ports(struct sp_port ***list_ptr) +SP_API enum sp_return sp_list_ports(struct sp_port ***list_ptr) { struct sp_port **list; - int ret = SP_ERR_SUPP; + int ret; TRACE("%p", list_ptr); @@ -346,206 +331,10 @@ enum sp_return sp_list_ports(struct sp_port ***list_ptr) list[0] = NULL; -#ifdef _WIN32 - HKEY key; - TCHAR *value, *data; - DWORD max_value_len, max_data_size, max_data_len; - DWORD value_len, data_size, data_len; - DWORD type, index = 0; - char *name; - int name_len; - - ret = SP_OK; - - DEBUG("Opening registry key"); - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"), - 0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS) { - SET_FAIL(ret, "RegOpenKeyEx() failed"); - goto out_done; - } - DEBUG("Querying registry key value and data sizes"); - if (RegQueryInfoKey(key, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &max_value_len, &max_data_size, NULL, NULL) != ERROR_SUCCESS) { - SET_FAIL(ret, "RegQueryInfoKey() failed"); - goto out_close; - } - max_data_len = max_data_size / sizeof(TCHAR); - if (!(value = malloc((max_value_len + 1) * sizeof(TCHAR)))) { - SET_ERROR(ret, SP_ERR_MEM, "registry value malloc failed"); - goto out_close; - } - if (!(data = malloc((max_data_len + 1) * sizeof(TCHAR)))) { - SET_ERROR(ret, SP_ERR_MEM, "registry data malloc failed"); - goto out_free_value; - } - DEBUG("Iterating over values"); - while ( - value_len = max_value_len + 1, - data_size = max_data_size, - RegEnumValue(key, index, value, &value_len, - NULL, &type, (LPBYTE)data, &data_size) == ERROR_SUCCESS) - { - data_len = data_size / sizeof(TCHAR); - data[data_len] = '\0'; -#ifdef UNICODE - name_len = WideCharToMultiByte(CP_ACP, 0, data, -1, NULL, 0, NULL, NULL); +#ifdef NO_ENUMERATION + ret = SP_ERR_SUPP; #else - name_len = data_len + 1; -#endif - if (!(name = malloc(name_len))) { - SET_ERROR(ret, SP_ERR_MEM, "registry port name malloc failed"); - goto out; - } -#ifdef UNICODE - WideCharToMultiByte(CP_ACP, 0, data, -1, name, name_len, NULL, NULL); -#else - strcpy(name, data); -#endif - if (type == REG_SZ) { - DEBUG("Found port %s", name); - if (!(list = list_append(list, name))) { - SET_ERROR(ret, SP_ERR_MEM, "list append failed"); - goto out; - } - } - index++; - } -out: - free(data); -out_free_value: - free(value); -out_close: - RegCloseKey(key); -out_done: -#endif -#ifdef __APPLE__ - mach_port_t master; - CFMutableDictionaryRef classes; - io_iterator_t iter; - char *path; - io_object_t port; - CFTypeRef cf_path; - Boolean result; - - ret = SP_OK; - - DEBUG("Getting IOKit master port"); - if (IOMasterPort(MACH_PORT_NULL, &master) != KERN_SUCCESS) { - SET_FAIL(ret, "IOMasterPort() failed"); - goto out_done; - } - - DEBUG("Creating matching dictionary"); - if (!(classes = IOServiceMatching(kIOSerialBSDServiceValue))) { - SET_FAIL(ret, "IOServiceMatching() failed"); - goto out_done; - } - - CFDictionarySetValue(classes, - CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes)); - - DEBUG("Getting matching services"); - if (IOServiceGetMatchingServices(master, classes, &iter) != KERN_SUCCESS) { - SET_FAIL(ret, "IOServiceGetMatchingServices() failed"); - goto out_done; - } - - if (!(path = malloc(PATH_MAX))) { - SET_ERROR(ret, SP_ERR_MEM, "device path malloc failed"); - goto out_release; - } - - DEBUG("Iterating over results"); - while ((port = IOIteratorNext(iter))) { - cf_path = IORegistryEntryCreateCFProperty(port, - CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); - if (cf_path) { - result = CFStringGetCString(cf_path, - path, PATH_MAX, kCFStringEncodingASCII); - CFRelease(cf_path); - if (result) { - DEBUG("Found port %s", path); - if (!(list = list_append(list, path))) { - SET_ERROR(ret, SP_ERR_MEM, "list append failed"); - IOObjectRelease(port); - goto out; - } - } - } - IOObjectRelease(port); - } -out: - free(path); -out_release: - IOObjectRelease(iter); -out_done: -#endif -#if defined(__linux__) && defined(HAVE_LIBUDEV) - struct udev *ud; - struct udev_enumerate *ud_enumerate; - struct udev_list_entry *ud_list; - struct udev_list_entry *ud_entry; - const char *path; - struct udev_device *ud_dev, *ud_parent; - const char *name; - const char *driver; - int fd, ioctl_result; - struct serial_struct serial_info; - - ret = SP_OK; - - DEBUG("Enumerating tty devices"); - ud = udev_new(); - ud_enumerate = udev_enumerate_new(ud); - udev_enumerate_add_match_subsystem(ud_enumerate, "tty"); - udev_enumerate_scan_devices(ud_enumerate); - ud_list = udev_enumerate_get_list_entry(ud_enumerate); - DEBUG("Iterating over results"); - udev_list_entry_foreach(ud_entry, ud_list) { - path = udev_list_entry_get_name(ud_entry); - DEBUG("Found device %s", path); - ud_dev = udev_device_new_from_syspath(ud, path); - /* If there is no parent device, this is a virtual tty. */ - ud_parent = udev_device_get_parent(ud_dev); - if (ud_parent == NULL) { - DEBUG("No parent device, assuming virtual tty"); - udev_device_unref(ud_dev); - continue; - } - name = udev_device_get_devnode(ud_dev); - /* The serial8250 driver has a hardcoded number of ports. - * The only way to tell which actually exist on a given system - * is to try to open them and make an ioctl call. */ - driver = udev_device_get_driver(ud_parent); - if (driver && !strcmp(driver, "serial8250")) { - DEBUG("serial8250 device, attempting to open"); - if ((fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) { - DEBUG("open failed, skipping"); - goto skip; - } - ioctl_result = ioctl(fd, TIOCGSERIAL, &serial_info); - close(fd); - if (ioctl_result != 0) { - DEBUG("ioctl failed, skipping"); - goto skip; - } - if (serial_info.type == PORT_UNKNOWN) { - DEBUG("port type is unknown, skipping"); - goto skip; - } - } - DEBUG("Found port %s", name); - list = list_append(list, name); -skip: - udev_device_unref(ud_dev); - if (!list) { - SET_ERROR(ret, SP_ERR_MEM, "list append failed"); - goto out; - } - } -out: - udev_enumerate_unref(ud_enumerate); - udev_unref(ud); + ret = list_ports(&list); #endif switch (ret) { @@ -562,7 +351,7 @@ out: } } -void sp_free_port_list(struct sp_port **list) +SP_API void sp_free_port_list(struct sp_port **list) { unsigned int i; @@ -604,7 +393,7 @@ void sp_free_port_list(struct sp_port **list) CHECK_PORT_HANDLE(); \ } while (0) -enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) +SP_API enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) { struct port_data data; struct sp_port_config config; @@ -614,10 +403,10 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) CHECK_PORT(); - if (flags > (SP_MODE_READ | SP_MODE_WRITE)) + if (flags > SP_MODE_READ_WRITE) RETURN_ERROR(SP_ERR_ARG, "Invalid flags"); - DEBUG("Opening port %s", port->name); + DEBUG_FMT("Opening port %s", port->name); #ifdef _WIN32 DWORD desired_access = 0, flags_and_attributes = 0, errors; @@ -691,7 +480,7 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) int flags_local = O_NONBLOCK | O_NOCTTY; /* Map 'flags' to the OS-specific settings. */ - if (flags & (SP_MODE_READ | SP_MODE_WRITE)) + if ((flags & SP_MODE_READ_WRITE) == SP_MODE_READ_WRITE) flags_local |= O_RDWR; else if (flags & SP_MODE_READ) flags_local |= O_RDONLY; @@ -770,13 +559,13 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) RETURN_OK(); } -enum sp_return sp_close(struct sp_port *port) +SP_API enum sp_return sp_close(struct sp_port *port) { TRACE("%p", port); CHECK_OPEN_PORT(); - DEBUG("Closing port %s", port->name); + DEBUG_FMT("Closing port %s", port->name); #ifdef _WIN32 /* Returns non-zero upon success, 0 upon failure. */ @@ -804,7 +593,7 @@ enum sp_return sp_close(struct sp_port *port) RETURN_OK(); } -enum sp_return sp_flush(struct sp_port *port, enum sp_buffer buffers) +SP_API enum sp_return sp_flush(struct sp_port *port, enum sp_buffer buffers) { TRACE("%p, 0x%x", port, buffers); @@ -815,7 +604,8 @@ enum sp_return sp_flush(struct sp_port *port, enum sp_buffer buffers) const char *buffer_names[] = {"no", "input", "output", "both"}; - DEBUG("Flushing %s buffers on port %s", buffer_names[buffers], port->name); + DEBUG_FMT("Flushing %s buffers on port %s", + buffer_names[buffers], port->name); #ifdef _WIN32 DWORD flags = 0; @@ -829,11 +619,11 @@ enum sp_return sp_flush(struct sp_port *port, enum sp_buffer buffers) RETURN_FAIL("PurgeComm() failed"); #else int flags = 0; - if (buffers & SP_BUF_BOTH) + if (buffers == SP_BUF_BOTH) flags = TCIOFLUSH; - else if (buffers & SP_BUF_INPUT) + else if (buffers == SP_BUF_INPUT) flags = TCIFLUSH; - else if (buffers & SP_BUF_OUTPUT) + else if (buffers == SP_BUF_OUTPUT) flags = TCOFLUSH; /* Returns 0 upon success, -1 upon failure. */ @@ -843,13 +633,13 @@ enum sp_return sp_flush(struct sp_port *port, enum sp_buffer buffers) RETURN_OK(); } -enum sp_return sp_drain(struct sp_port *port) +SP_API enum sp_return sp_drain(struct sp_port *port) { TRACE("%p", port); CHECK_OPEN_PORT(); - DEBUG("Draining port %s", port->name); + DEBUG_FMT("Draining port %s", port->name); #ifdef _WIN32 /* Returns non-zero upon success, 0 upon failure. */ @@ -879,7 +669,8 @@ enum sp_return sp_drain(struct sp_port *port) #endif } -enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, size_t count, unsigned int timeout) +SP_API enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, + size_t count, unsigned int timeout) { TRACE("%p, %p, %d, %d", port, buf, count, timeout); @@ -889,12 +680,14 @@ enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, size_t c RETURN_ERROR(SP_ERR_ARG, "Null buffer"); if (timeout) - DEBUG("Writing %d bytes to port %s, timeout %d ms", count, port->name, timeout); + DEBUG_FMT("Writing %d bytes to port %s, timeout %d ms", + count, port->name, timeout); else - DEBUG("Writing %d bytes to port %s, no timeout", count, port->name); + DEBUG_FMT("Writing %d bytes to port %s, no timeout", + count, port->name); if (count == 0) - RETURN_VALUE("0", 0); + RETURN_INT(0); #ifdef _WIN32 DWORD bytes_written = 0; @@ -920,14 +713,14 @@ enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, size_t c if (GetLastError() == ERROR_IO_PENDING) { DEBUG("Waiting for write to complete"); GetOverlappedResult(port->hdl, &port->write_ovl, &bytes_written, TRUE); - DEBUG("Write completed, %d/%d bytes written", bytes_written, count); - RETURN_VALUE("%d", bytes_written); + DEBUG_FMT("Write completed, %d/%d bytes written", bytes_written, count); + RETURN_INT(bytes_written); } else { RETURN_FAIL("WriteFile() failed"); } } else { DEBUG("Write completed immediately"); - RETURN_VALUE("%d", count); + RETURN_INT(count); } #else size_t bytes_written = 0; @@ -956,7 +749,7 @@ enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, size_t c gettimeofday(&now, NULL); if (timercmp(&now, &end, >)) { DEBUG("write timed out"); - RETURN_VALUE("%d", bytes_written); + RETURN_INT(bytes_written); } timersub(&end, &now, &delta); } @@ -970,7 +763,7 @@ enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, size_t c } } else if (result == 0) { DEBUG("write timed out"); - RETURN_VALUE("%d", bytes_written); + RETURN_INT(bytes_written); } /* Do write. */ @@ -989,11 +782,12 @@ enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, size_t c ptr += result; } - RETURN_VALUE("%d", bytes_written); + RETURN_INT(bytes_written); #endif } -enum sp_return sp_nonblocking_write(struct sp_port *port, const void *buf, size_t count) +SP_API enum sp_return sp_nonblocking_write(struct sp_port *port, + const void *buf, size_t count) { TRACE("%p, %p, %d", port, buf, count); @@ -1002,10 +796,10 @@ enum sp_return sp_nonblocking_write(struct sp_port *port, const void *buf, size_ if (!buf) RETURN_ERROR(SP_ERR_ARG, "Null buffer"); - DEBUG("Writing up to %d bytes to port %s", count, port->name); + DEBUG_FMT("Writing up to %d bytes to port %s", count, port->name); if (count == 0) - RETURN_VALUE("0", 0); + RETURN_INT(0); #ifdef _WIN32 DWORD written = 0; @@ -1019,7 +813,7 @@ enum sp_return sp_nonblocking_write(struct sp_port *port, const void *buf, size_ } else { DEBUG("Previous write not complete"); /* Can't take a new write until the previous one finishes. */ - RETURN_VALUE("0", 0); + RETURN_INT(0); } } @@ -1046,7 +840,7 @@ enum sp_return sp_nonblocking_write(struct sp_port *port, const void *buf, size_ } else { DEBUG("Asynchronous write running"); port->writing = 1; - RETURN_VALUE("%d", ++written); + RETURN_INT(++written); } } else { /* Actual failure of some kind. */ @@ -1060,7 +854,7 @@ enum sp_return sp_nonblocking_write(struct sp_port *port, const void *buf, size_ DEBUG("All bytes written immediately"); - RETURN_VALUE("%d", written); + RETURN_INT(written); #else /* Returns the number of bytes written, or -1 upon failure. */ ssize_t written = write(port->fd, buf, count); @@ -1068,11 +862,12 @@ enum sp_return sp_nonblocking_write(struct sp_port *port, const void *buf, size_ if (written < 0) RETURN_FAIL("write() failed"); else - RETURN_VALUE("%d", written); + RETURN_INT(written); #endif } -enum sp_return sp_blocking_read(struct sp_port *port, void *buf, size_t count, unsigned int timeout) +SP_API enum sp_return sp_blocking_read(struct sp_port *port, void *buf, + size_t count, unsigned int timeout) { TRACE("%p, %p, %d, %d", port, buf, count, timeout); @@ -1082,15 +877,18 @@ enum sp_return sp_blocking_read(struct sp_port *port, void *buf, size_t count, u RETURN_ERROR(SP_ERR_ARG, "Null buffer"); if (timeout) - DEBUG("Reading %d bytes from port %s, timeout %d ms", count, port->name, timeout); + DEBUG_FMT("Reading %d bytes from port %s, timeout %d ms", + count, port->name, timeout); else - DEBUG("Reading %d bytes from port %s, no timeout", count, port->name); + DEBUG_FMT("Reading %d bytes from port %s, no timeout", + count, port->name); if (count == 0) - RETURN_VALUE("0", 0); + RETURN_INT(0); #ifdef _WIN32 DWORD bytes_read = 0; + DWORD wait_result = 0; /* Set timeout. */ port->timeouts.ReadIntervalTimeout = 0; @@ -1103,7 +901,7 @@ enum sp_return sp_blocking_read(struct sp_port *port, void *buf, size_t count, u if (GetLastError() == ERROR_IO_PENDING) { DEBUG("Waiting for read to complete"); GetOverlappedResult(port->hdl, &port->read_ovl, &bytes_read, TRUE); - DEBUG("Read completed, %d/%d bytes read", bytes_read, count); + DEBUG_FMT("Read completed, %d/%d bytes read", bytes_read, count); } else { RETURN_FAIL("ReadFile() failed"); } @@ -1112,13 +910,18 @@ enum sp_return sp_blocking_read(struct sp_port *port, void *buf, size_t count, u bytes_read = count; } - /* Start background operation for subsequent events. */ - if (WaitCommEvent(port->hdl, &port->events, &port->wait_ovl) == 0) { - if (GetLastError() != ERROR_IO_PENDING) - RETURN_FAIL("WaitCommEvent() failed"); + /* Restart wait operation if needed. */ + if (GetOverlappedResult(port->hdl, &port->wait_ovl, &wait_result, FALSE)) { + /* Previous wait completed, start a new one. */ + if (WaitCommEvent(port->hdl, &port->events, &port->wait_ovl) == 0) { + if (GetLastError() != ERROR_IO_PENDING) + RETURN_FAIL("WaitCommEvent() failed"); + } + } else if (GetLastError() != ERROR_IO_INCOMPLETE) { + RETURN_FAIL("GetOverlappedResult() failed"); } - RETURN_VALUE("%d", bytes_read); + RETURN_INT(bytes_read); #else size_t bytes_read = 0; @@ -1147,7 +950,7 @@ enum sp_return sp_blocking_read(struct sp_port *port, void *buf, size_t count, u gettimeofday(&now, NULL); if (timercmp(&now, &end, >)) /* Timeout has expired. */ - RETURN_VALUE("%d", bytes_read); + RETURN_INT(bytes_read); timersub(&end, &now, &delta); } result = select(port->fd + 1, &fds, NULL, NULL, timeout ? &delta : NULL); @@ -1160,7 +963,7 @@ enum sp_return sp_blocking_read(struct sp_port *port, void *buf, size_t count, u } } else if (result == 0) { DEBUG("read timed out"); - RETURN_VALUE("%d", bytes_read); + RETURN_INT(bytes_read); } /* Do read. */ @@ -1179,11 +982,12 @@ enum sp_return sp_blocking_read(struct sp_port *port, void *buf, size_t count, u ptr += result; } - RETURN_VALUE("%d", bytes_read); + RETURN_INT(bytes_read); #endif } -enum sp_return sp_nonblocking_read(struct sp_port *port, void *buf, size_t count) +SP_API enum sp_return sp_nonblocking_read(struct sp_port *port, void *buf, + size_t count) { TRACE("%p, %p, %d", port, buf, count); @@ -1192,10 +996,11 @@ enum sp_return sp_nonblocking_read(struct sp_port *port, void *buf, size_t count if (!buf) RETURN_ERROR(SP_ERR_ARG, "Null buffer"); - DEBUG("Reading up to %d bytes from port %s", count, port->name); + DEBUG_FMT("Reading up to %d bytes from port %s", count, port->name); #ifdef _WIN32 DWORD bytes_read; + DWORD wait_result; /* Set timeout. */ port->timeouts.ReadIntervalTimeout = MAXDWORD; @@ -1211,15 +1016,18 @@ enum sp_return sp_nonblocking_read(struct sp_port *port, void *buf, size_t count if (GetOverlappedResult(port->hdl, &port->read_ovl, &bytes_read, TRUE) == 0) RETURN_FAIL("GetOverlappedResult() failed"); - if (bytes_read > 0) { - /* Start background operation for subsequent events. */ + /* Restart wait operation if needed. */ + if (GetOverlappedResult(port->hdl, &port->wait_ovl, &wait_result, FALSE)) { + /* Previous wait completed, start a new one. */ if (WaitCommEvent(port->hdl, &port->events, &port->wait_ovl) == 0) { if (GetLastError() != ERROR_IO_PENDING) RETURN_FAIL("WaitCommEvent() failed"); } + } else if (GetLastError() != ERROR_IO_INCOMPLETE) { + RETURN_FAIL("GetOverlappedResult() failed"); } - RETURN_VALUE("%d", bytes_read); + RETURN_INT(bytes_read); #else ssize_t bytes_read; @@ -1232,17 +1040,17 @@ enum sp_return sp_nonblocking_read(struct sp_port *port, void *buf, size_t count /* This is an actual failure. */ RETURN_FAIL("read() failed"); } - RETURN_VALUE("%d", bytes_read); + RETURN_INT(bytes_read); #endif } -enum sp_return sp_input_waiting(struct sp_port *port) +SP_API enum sp_return sp_input_waiting(struct sp_port *port) { TRACE("%p", port); CHECK_OPEN_PORT(); - DEBUG("Checking input bytes waiting on port %s", port->name); + DEBUG_FMT("Checking input bytes waiting on port %s", port->name); #ifdef _WIN32 DWORD errors; @@ -1250,22 +1058,22 @@ enum sp_return sp_input_waiting(struct sp_port *port) if (ClearCommError(port->hdl, &errors, &comstat) == 0) RETURN_FAIL("ClearCommError() failed"); - RETURN_VALUE("%d", comstat.cbInQue); + RETURN_INT(comstat.cbInQue); #else int bytes_waiting; if (ioctl(port->fd, TIOCINQ, &bytes_waiting) < 0) RETURN_FAIL("TIOCINQ ioctl failed"); - RETURN_VALUE("%d", bytes_waiting); + RETURN_INT(bytes_waiting); #endif } -enum sp_return sp_output_waiting(struct sp_port *port) +SP_API enum sp_return sp_output_waiting(struct sp_port *port) { TRACE("%p", port); CHECK_OPEN_PORT(); - DEBUG("Checking output bytes waiting on port %s", port->name); + DEBUG_FMT("Checking output bytes waiting on port %s", port->name); #ifdef _WIN32 DWORD errors; @@ -1273,16 +1081,16 @@ enum sp_return sp_output_waiting(struct sp_port *port) if (ClearCommError(port->hdl, &errors, &comstat) == 0) RETURN_FAIL("ClearCommError() failed"); - RETURN_VALUE("%d", comstat.cbOutQue); + RETURN_INT(comstat.cbOutQue); #else int bytes_waiting; if (ioctl(port->fd, TIOCOUTQ, &bytes_waiting) < 0) RETURN_FAIL("TIOCOUTQ ioctl failed"); - RETURN_VALUE("%d", bytes_waiting); + RETURN_INT(bytes_waiting); #endif } -enum sp_return sp_new_event_set(struct sp_event_set **result_ptr) +SP_API enum sp_return sp_new_event_set(struct sp_event_set **result_ptr) { struct sp_event_set *result; @@ -1330,7 +1138,7 @@ static enum sp_return add_handle(struct sp_event_set *event_set, RETURN_OK(); } -enum sp_return sp_add_port_events(struct sp_event_set *event_set, +SP_API enum sp_return sp_add_port_events(struct sp_event_set *event_set, const struct sp_port *port, enum sp_event mask) { TRACE("%p, %p, %d", event_set, port, mask); @@ -1360,7 +1168,7 @@ enum sp_return sp_add_port_events(struct sp_event_set *event_set, RETURN_OK(); } -void sp_free_event_set(struct sp_event_set *event_set) +SP_API void sp_free_event_set(struct sp_event_set *event_set) { TRACE("%p", event_set); @@ -1381,7 +1189,8 @@ void sp_free_event_set(struct sp_event_set *event_set) RETURN(); } -enum sp_return sp_wait(struct sp_event_set *event_set, unsigned int timeout) +SP_API enum sp_return sp_wait(struct sp_event_set *event_set, + unsigned int timeout) { TRACE("%p, %d", event_set, timeout); @@ -1581,7 +1390,7 @@ static enum sp_return get_config(struct sp_port *port, struct port_data *data, TRACE("%p, %p, %p", port, data, config); - DEBUG("Getting configuration for port %s", port->name); + DEBUG_FMT("Getting configuration for port %s", port->name); #ifdef _WIN32 if (!GetCommState(port->hdl, &data->dcb)) @@ -1798,7 +1607,7 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, TRACE("%p, %p, %p", port, data, config); - DEBUG("Setting configuration for port %s", port->name); + DEBUG_FMT("Setting configuration for port %s", port->name); #ifdef _WIN32 if (config->baudrate >= 0) { @@ -2170,7 +1979,7 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, RETURN_OK(); } -enum sp_return sp_new_config(struct sp_port_config **config_ptr) +SP_API enum sp_return sp_new_config(struct sp_port_config **config_ptr) { struct sp_port_config *config; @@ -2198,7 +2007,7 @@ enum sp_return sp_new_config(struct sp_port_config **config_ptr) RETURN_OK(); } -void sp_free_config(struct sp_port_config *config) +SP_API void sp_free_config(struct sp_port_config *config) { TRACE("%p", config); @@ -2210,7 +2019,8 @@ void sp_free_config(struct sp_port_config *config) RETURN(); } -enum sp_return sp_get_config(struct sp_port *port, struct sp_port_config *config) +SP_API enum sp_return sp_get_config(struct sp_port *port, + struct sp_port_config *config) { struct port_data data; @@ -2226,7 +2036,8 @@ enum sp_return sp_get_config(struct sp_port *port, struct sp_port_config *config RETURN_OK(); } -enum sp_return sp_set_config(struct sp_port *port, const struct sp_port_config *config) +SP_API enum sp_return sp_set_config(struct sp_port *port, + const struct sp_port_config *config) { struct port_data data; struct sp_port_config prev_config; @@ -2245,7 +2056,7 @@ enum sp_return sp_set_config(struct sp_port *port, const struct sp_port_config * } #define CREATE_ACCESSORS(x, type) \ -enum sp_return sp_set_##x(struct sp_port *port, type x) { \ +SP_API enum sp_return sp_set_##x(struct sp_port *port, type x) { \ struct port_data data; \ struct sp_port_config config; \ TRACE("%p, %d", port, x); \ @@ -2255,14 +2066,16 @@ enum sp_return sp_set_##x(struct sp_port *port, type x) { \ TRY(set_config(port, &data, &config)); \ RETURN_OK(); \ } \ -enum sp_return sp_get_config_##x(const struct sp_port_config *config, type *x) { \ +SP_API enum sp_return sp_get_config_##x(const struct sp_port_config *config, \ + type *x) { \ TRACE("%p, %p", config, x); \ if (!config) \ RETURN_ERROR(SP_ERR_ARG, "Null config"); \ *x = config->x; \ RETURN_OK(); \ } \ -enum sp_return sp_set_config_##x(struct sp_port_config *config, type x) { \ +SP_API enum sp_return sp_set_config_##x(struct sp_port_config *config, \ + type x) { \ TRACE("%p, %d", config, x); \ if (!config) \ RETURN_ERROR(SP_ERR_ARG, "Null config"); \ @@ -2280,7 +2093,8 @@ CREATE_ACCESSORS(dtr, enum sp_dtr) CREATE_ACCESSORS(dsr, enum sp_dsr) CREATE_ACCESSORS(xon_xoff, enum sp_xonxoff) -enum sp_return sp_set_config_flowcontrol(struct sp_port_config *config, enum sp_flowcontrol flowcontrol) +SP_API enum sp_return sp_set_config_flowcontrol(struct sp_port_config *config, + enum sp_flowcontrol flowcontrol) { if (!config) RETURN_ERROR(SP_ERR_ARG, "Null configuration"); @@ -2314,7 +2128,8 @@ enum sp_return sp_set_config_flowcontrol(struct sp_port_config *config, enum sp_ RETURN_OK(); } -enum sp_return sp_set_flowcontrol(struct sp_port *port, enum sp_flowcontrol flowcontrol) +SP_API enum sp_return sp_set_flowcontrol(struct sp_port *port, + enum sp_flowcontrol flowcontrol) { struct port_data data; struct sp_port_config config; @@ -2332,7 +2147,8 @@ enum sp_return sp_set_flowcontrol(struct sp_port *port, enum sp_flowcontrol flow RETURN_OK(); } -enum sp_return sp_get_signals(struct sp_port *port, enum sp_signal *signals) +SP_API enum sp_return sp_get_signals(struct sp_port *port, + enum sp_signal *signals) { TRACE("%p, %p", port, signals); @@ -2341,7 +2157,7 @@ enum sp_return sp_get_signals(struct sp_port *port, enum sp_signal *signals) if (!signals) RETURN_ERROR(SP_ERR_ARG, "Null result pointer"); - DEBUG("Getting control signals for port %s", port->name); + DEBUG_FMT("Getting control signals for port %s", port->name); *signals = 0; #ifdef _WIN32 @@ -2372,7 +2188,7 @@ enum sp_return sp_get_signals(struct sp_port *port, enum sp_signal *signals) RETURN_OK(); } -enum sp_return sp_start_break(struct sp_port *port) +SP_API enum sp_return sp_start_break(struct sp_port *port) { TRACE("%p", port); @@ -2388,7 +2204,7 @@ enum sp_return sp_start_break(struct sp_port *port) RETURN_OK(); } -enum sp_return sp_end_break(struct sp_port *port) +SP_API enum sp_return sp_end_break(struct sp_port *port) { TRACE("%p", port); @@ -2404,19 +2220,19 @@ enum sp_return sp_end_break(struct sp_port *port) RETURN_OK(); } -int sp_last_error_code(void) +SP_API int sp_last_error_code(void) { - TRACE(""); + TRACE_VOID(); #ifdef _WIN32 - RETURN_VALUE("%d", GetLastError()); + RETURN_INT(GetLastError()); #else - RETURN_VALUE("%d", errno); + RETURN_INT(errno); #endif } -char *sp_last_error_message(void) +SP_API char *sp_last_error_message(void) { - TRACE(""); + TRACE_VOID(); #ifdef _WIN32 LPVOID message; @@ -2432,13 +2248,13 @@ char *sp_last_error_message(void) (LPTSTR) &message, 0, NULL ); - RETURN_VALUE("%s", message); + RETURN_STRING(message); #else - RETURN_VALUE("%s", strerror(errno)); + RETURN_STRING(strerror(errno)); #endif } -void sp_free_error_message(char *message) +SP_API void sp_free_error_message(char *message) { TRACE("%s", message); @@ -2451,7 +2267,7 @@ void sp_free_error_message(char *message) RETURN(); } -void sp_set_debug_handler(void (*handler)(const char *format, ...)) +SP_API void sp_set_debug_handler(void (*handler)(const char *format, ...)) { TRACE("%p", handler); @@ -2460,7 +2276,7 @@ void sp_set_debug_handler(void (*handler)(const char *format, ...)) RETURN(); } -void sp_default_debug_handler(const char *format, ...) +SP_API void sp_default_debug_handler(const char *format, ...) { va_list args; va_start(args, format); @@ -2471,42 +2287,42 @@ void sp_default_debug_handler(const char *format, ...) va_end(args); } -int sp_get_major_package_version(void) +SP_API int sp_get_major_package_version(void) { return SP_PACKAGE_VERSION_MAJOR; } -int sp_get_minor_package_version(void) +SP_API int sp_get_minor_package_version(void) { return SP_PACKAGE_VERSION_MINOR; } -int sp_get_micro_package_version(void) +SP_API int sp_get_micro_package_version(void) { return SP_PACKAGE_VERSION_MICRO; } -const char *sp_get_package_version_string(void) +SP_API const char *sp_get_package_version_string(void) { return SP_PACKAGE_VERSION_STRING; } -int sp_get_current_lib_version(void) +SP_API int sp_get_current_lib_version(void) { return SP_LIB_VERSION_CURRENT; } -int sp_get_revision_lib_version(void) +SP_API int sp_get_revision_lib_version(void) { return SP_LIB_VERSION_REVISION; } -int sp_get_age_lib_version(void) +SP_API int sp_get_age_lib_version(void) { return SP_LIB_VERSION_AGE; } -const char *sp_get_lib_version_string(void) +SP_API const char *sp_get_lib_version_string(void) { return SP_LIB_VERSION_STRING; }