X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=serialport.c;h=9d2a50440046fd67642cd91431f3e55398aa4f29;hb=0765af56440614d73c26944f811720d3a7124f29;hp=047c80574d415966ee0dbda01131fe8ac01280d6;hpb=dec10e31d02f644399d1204c95d50ce61f2ae655;p=libserialport.git diff --git a/serialport.c b/serialport.c index 047c805..9d2a504 100644 --- a/serialport.c +++ b/serialport.c @@ -58,6 +58,31 @@ #include "libserialport.h" +struct sp_port { + char *name; + int nonblocking; +#ifdef _WIN32 + HANDLE hdl; + OVERLAPPED write_ovl; + 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; @@ -108,8 +133,6 @@ 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) -#define TRY(x) do { int ret = x; if (ret != SP_OK) return ret; } while (0) - /* Debug output macros. */ #define DEBUG(fmt, ...) do { if (sp_debug_handler) sp_debug_handler(fmt ".\n", ##__VA_ARGS__); } while (0) #define DEBUG_ERROR(err, msg) DEBUG("%s returning " #err ": " msg, __func__) @@ -134,9 +157,11 @@ void (*sp_debug_handler)(const char *format, ...) = sp_default_debug_handler; #define RETURN_FAIL(msg) do { DEBUG_FAIL(msg); return SP_ERR_FAIL; } while (0) #define RETURN_VALUE(fmt, x) do { 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 = 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, @@ -184,6 +209,34 @@ enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_p RETURN_OK(); } +char *sp_get_port_name(const struct sp_port *port) +{ + TRACE("%p", port); + + if (!port) + return NULL; + + RETURN_VALUE("%s", port->name); +} + +enum sp_return sp_get_port_handle(const struct sp_port *port, void *result_ptr) +{ + TRACE("%p", port); + + if (!port) + RETURN_ERROR(SP_ERR_ARG, "Null port"); + +#ifdef _WIN32 + HANDLE *handle_ptr = result_ptr; + *handle_ptr = port->hdl; +#else + int *fd_ptr = result_ptr; + *fd_ptr = port->fd; +#endif + + RETURN_OK(); +} + enum sp_return sp_copy_port(const struct sp_port *port, struct sp_port **copy_ptr) { TRACE("%p, %p", port, copy_ptr); @@ -246,7 +299,7 @@ fail: enum sp_return sp_list_ports(struct sp_port ***list_ptr) { struct sp_port **list; - int ret = SP_OK; + int ret = SP_ERR_SUPP; TRACE("%p", list_ptr); @@ -269,6 +322,8 @@ enum sp_return sp_list_ports(struct sp_port ***list_ptr) 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) { @@ -339,6 +394,8 @@ out_done: 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"); @@ -402,6 +459,8 @@ out_done: 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); @@ -456,10 +515,13 @@ out: udev_unref(ud); #endif - if (ret == SP_OK) { + switch (ret) { + case SP_OK: *list_ptr = list; RETURN_OK(); - } else { + case SP_ERR_SUPP: + DEBUG_ERROR(SP_ERR_SUPP, "Enumeration not supported on this platform."); + default: if (list) sp_free_port_list(list); *list_ptr = NULL; @@ -520,8 +582,11 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) DEBUG("Opening port %s", port->name); + port->nonblocking = (flags & SP_MODE_NONBLOCK) ? 1 : 0; + #ifdef _WIN32 DWORD desired_access = 0, flags_and_attributes = 0; + COMMTIMEOUTS timeouts; char *escaped_port_name; /* Prefix port name with '\\.\' to work with ports above COM9. */ @@ -545,6 +610,30 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) if (port->hdl == INVALID_HANDLE_VALUE) RETURN_FAIL("CreateFile() failed"); + + /* All timeouts disabled. */ + timeouts.ReadIntervalTimeout = 0; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.ReadTotalTimeoutConstant = 0; + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + + if (port->nonblocking) { + /* Set read timeout such that all reads return immediately. */ + timeouts.ReadIntervalTimeout = MAXDWORD; + /* Prepare OVERLAPPED structure for non-blocking writes. */ + memset(&port->write_ovl, 0, sizeof(port->write_ovl)); + if (!(port->write_ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) { + sp_close(port); + RETURN_FAIL("CreateEvent() failed"); + } + port->writing = FALSE; + } + + if (SetCommTimeouts(port->hdl, &timeouts) == 0) { + sp_close(port); + RETURN_FAIL("SetCommTimeouts() failed"); + } #else int flags_local = 0; struct port_data data; @@ -607,6 +696,11 @@ enum sp_return sp_close(struct sp_port *port) if (CloseHandle(port->hdl) == 0) RETURN_FAIL("CloseHandle() failed"); port->hdl = INVALID_HANDLE_VALUE; + if (port->nonblocking) { + /* Close event handle created for overlapped writes. */ + if (CloseHandle(port->write_ovl.hEvent) == 0) + RETURN_FAIL("CloseHandle() failed"); + } #else /* Returns 0 upon success, -1 upon failure. */ if (close(port->fd) == -1) @@ -626,7 +720,7 @@ enum sp_return sp_flush(struct sp_port *port, enum sp_buffer buffers) if (buffers > SP_BUF_BOTH) RETURN_ERROR(SP_ERR_ARG, "Invalid buffer selection"); - const char *buffer_names[] = {"input", "output", "both"}; + const char *buffer_names[] = {"no", "input", "output", "both"}; DEBUG("Flushing %s buffers on port %s", buffer_names[buffers], port->name); @@ -688,12 +782,60 @@ enum sp_return sp_write(struct sp_port *port, const void *buf, size_t count) DEBUG("Writing up to %d bytes to port %s", count, port->name); + if (count == 0) + RETURN_VALUE("0", 0); + #ifdef _WIN32 DWORD written = 0; + BYTE *ptr = (BYTE *) buf; + + if (port->nonblocking) { + /* Non-blocking write. */ + + /* Check whether previous write is complete. */ + if (port->writing) { + if (HasOverlappedIoCompleted(&port->write_ovl)) { + DEBUG("Previous write completed"); + port->writing = 0; + } else { + DEBUG("Previous write not complete"); + /* Can't take a new write until the previous one finishes. */ + RETURN_VALUE("0", 0); + } + } + + /* Keep writing data until the OS has to actually start an async IO for it. + * At that point we know the buffer is full. */ + while (written < count) + { + /* Copy first byte of user buffer. */ + port->pending_byte = *ptr++; + + /* Start asynchronous write. */ + if (WriteFile(port->hdl, &port->pending_byte, 1, NULL, &port->write_ovl) == 0) { + if (GetLastError() == ERROR_IO_PENDING) { + DEBUG("Asynchronous write started"); + port->writing = 1; + RETURN_VALUE("%d", ++written); + } else { + /* Actual failure of some kind. */ + RETURN_FAIL("WriteFile() failed"); + } + } else { + DEBUG("Single byte written immediately."); + written++; + } + } + + DEBUG("All bytes written immediately."); + + } else { + /* Blocking write. */ + if (WriteFile(port->hdl, buf, count, &written, NULL) == 0) { + RETURN_FAIL("WriteFile() failed"); + } + } - /* Returns non-zero upon success, 0 upon failure. */ - if (WriteFile(port->hdl, buf, count, &written, NULL) == 0) - RETURN_FAIL("WriteFile() failed"); RETURN_VALUE("%d", written); #else /* Returns the number of bytes written, or -1 upon failure. */ @@ -728,8 +870,14 @@ enum sp_return sp_read(struct sp_port *port, void *buf, size_t count) ssize_t bytes_read; /* Returns the number of bytes read, or -1 upon failure. */ - if ((bytes_read = read(port->fd, buf, count)) < 0) - RETURN_FAIL("read() failed"); + if ((bytes_read = read(port->fd, buf, count)) < 0) { + if (port->nonblocking && errno == EAGAIN) + /* Port is opened in nonblocking mode and there are no bytes available. */ + bytes_read = 0; + else + /* This is an actual failure. */ + RETURN_FAIL("read() failed"); + } RETURN_VALUE("%d", bytes_read); #endif } @@ -1406,6 +1554,61 @@ 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) +{ + TRACE("%p", config_ptr); + struct sp_port_config *config; + + if (!config_ptr) + RETURN_ERROR(SP_ERR_ARG, "Null result pointer"); + + *config_ptr = NULL; + + if (!(config = malloc(sizeof(struct sp_port_config)))) + RETURN_ERROR(SP_ERR_MEM, "config malloc failed"); + + config->baudrate = -1; + config->bits = -1; + config->parity = -1; + config->stopbits = -1; + config->rts = -1; + config->cts = -1; + config->dtr = -1; + config->dsr = -1; + + *config_ptr = config; + + RETURN_OK(); +} + +void sp_free_config(struct sp_port_config *config) +{ + TRACE("%p", config); + + if (!config) + DEBUG("Null config"); + else + free(config); + + RETURN(); +} + +enum sp_return sp_get_config(struct sp_port *port, struct sp_port_config *config) +{ + struct port_data data; + + TRACE("%p, %p", port, config); + + CHECK_OPEN_PORT(); + + if (!config) + RETURN_ERROR(SP_ERR_ARG, "Null config"); + + TRY(get_config(port, &data, config)); + + RETURN_OK(); +} + enum sp_return sp_set_config(struct sp_port *port, const struct sp_port_config *config) { struct port_data data; @@ -1424,7 +1627,8 @@ enum sp_return sp_set_config(struct sp_port *port, const struct sp_port_config * RETURN_OK(); } -#define CREATE_SETTER(x, type) int sp_set_##x(struct sp_port *port, type x) { \ +#define CREATE_ACCESSORS(x, type) \ +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); \ @@ -1433,55 +1637,79 @@ enum sp_return sp_set_config(struct sp_port *port, const struct sp_port_config * config.x = x; \ TRY(set_config(port, &data, &config)); \ RETURN_OK(); \ +} \ +enum sp_return sp_get_config_##x(const struct sp_port_config *config, type *x) { \ + TRACE("%p", config); \ + 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) { \ + TRACE("%p, %d", config, x); \ + if (!config) \ + RETURN_ERROR(SP_ERR_ARG, "Null config"); \ + config->x = x; \ + RETURN_OK(); \ } -CREATE_SETTER(baudrate, int) -CREATE_SETTER(bits, int) -CREATE_SETTER(parity, enum sp_parity) -CREATE_SETTER(stopbits, int) -CREATE_SETTER(rts, enum sp_rts) -CREATE_SETTER(cts, enum sp_cts) -CREATE_SETTER(dtr, enum sp_dtr) -CREATE_SETTER(dsr, enum sp_dsr) -CREATE_SETTER(xon_xoff, enum sp_xonxoff) - -enum sp_return sp_set_flowcontrol(struct sp_port *port, enum sp_flowcontrol flowcontrol) +CREATE_ACCESSORS(baudrate, int) +CREATE_ACCESSORS(bits, int) +CREATE_ACCESSORS(parity, enum sp_parity) +CREATE_ACCESSORS(stopbits, int) +CREATE_ACCESSORS(rts, enum sp_rts) +CREATE_ACCESSORS(cts, enum sp_cts) +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) { - struct port_data data; - struct sp_port_config config; - - TRACE("%p, %d", port, flowcontrol); - - CHECK_OPEN_PORT(); + if (!config) + RETURN_ERROR(SP_ERR_ARG, "Null configuration"); if (flowcontrol > SP_FLOWCONTROL_DTRDSR) RETURN_ERROR(SP_ERR_ARG, "Invalid flow control setting"); - TRY(get_config(port, &data, &config)); - if (flowcontrol == SP_FLOWCONTROL_XONXOFF) - config.xon_xoff = SP_XONXOFF_INOUT; + config->xon_xoff = SP_XONXOFF_INOUT; else - config.xon_xoff = SP_XONXOFF_DISABLED; + config->xon_xoff = SP_XONXOFF_DISABLED; if (flowcontrol == SP_FLOWCONTROL_RTSCTS) { - config.rts = SP_RTS_FLOW_CONTROL; - config.cts = SP_CTS_FLOW_CONTROL; + config->rts = SP_RTS_FLOW_CONTROL; + config->cts = SP_CTS_FLOW_CONTROL; } else { - if (config.rts == SP_RTS_FLOW_CONTROL) - config.rts = SP_RTS_ON; - config.cts = SP_CTS_IGNORE; + if (config->rts == SP_RTS_FLOW_CONTROL) + config->rts = SP_RTS_ON; + config->cts = SP_CTS_IGNORE; } if (flowcontrol == SP_FLOWCONTROL_DTRDSR) { - config.dtr = SP_DTR_FLOW_CONTROL; - config.dsr = SP_DSR_FLOW_CONTROL; + config->dtr = SP_DTR_FLOW_CONTROL; + config->dsr = SP_DSR_FLOW_CONTROL; } else { - if (config.dtr == SP_DTR_FLOW_CONTROL) - config.dtr = SP_DTR_ON; - config.dsr = SP_DSR_IGNORE; + if (config->dtr == SP_DTR_FLOW_CONTROL) + config->dtr = SP_DTR_ON; + config->dsr = SP_DSR_IGNORE; } + RETURN_OK(); +} + +enum sp_return sp_set_flowcontrol(struct sp_port *port, enum sp_flowcontrol flowcontrol) +{ + struct port_data data; + struct sp_port_config config; + + TRACE("%p, %d", port, flowcontrol); + + CHECK_OPEN_PORT(); + + TRY(get_config(port, &data, &config)); + + TRY(sp_set_config_flowcontrol(&config, flowcontrol)); + TRY(set_config(port, &data, &config)); RETURN_OK(); @@ -1507,9 +1735,9 @@ enum sp_return sp_get_signals(struct sp_port *port, enum sp_signal *signals) *signals |= SP_SIG_CTS; if (bits & MS_DSR_ON) *signals |= SP_SIG_DSR; - if (bits & MS_RING_ON) - *signals |= SP_SIG_DCD; if (bits & MS_RLSD_ON) + *signals |= SP_SIG_DCD; + if (bits & MS_RING_ON) *signals |= SP_SIG_RI; #else int bits;