From 94e7a77d01b4af26934a943877b0f832dd88dad8 Mon Sep 17 00:00:00 2001 From: Anton Lundin Date: Sat, 17 Sep 2016 15:20:07 +0200 Subject: [PATCH] Re-implement custom serial in a new way This re-implements the custom serial concept in a new way. This way doesn't touch any of the backend code, it just introduces a optional redirection layer in the existing serial backends. This implementation supports more serial operations to, so we can support more backends this way. Hooking into the existing serial backends might look ugly but its probably the best way to make sure this patch conflicts as little as possible with upstream. Signed-off-by: Anton Lundin Signed-off-by: Linus Torvalds --- include/libdivecomputer/Makefile.am | 1 + include/libdivecomputer/context.h | 4 ++ include/libdivecomputer/custom_serial.h | 90 +++++++++++++++++++++++++ src/context-private.h | 18 +++++ src/context.c | 21 ++++++ src/serial.h | 3 + src/serial_posix.c | 36 ++++++++++ src/serial_win32.c | 36 ++++++++++ 8 files changed, 209 insertions(+) create mode 100644 include/libdivecomputer/custom_serial.h diff --git a/include/libdivecomputer/Makefile.am b/include/libdivecomputer/Makefile.am index 778037f..56afd44 100644 --- a/include/libdivecomputer/Makefile.am +++ b/include/libdivecomputer/Makefile.am @@ -3,6 +3,7 @@ libdivecomputer_HEADERS = \ version.h \ common.h \ context.h \ + custom_serial.h \ buffer.h \ descriptor.h \ iterator.h \ diff --git a/include/libdivecomputer/context.h b/include/libdivecomputer/context.h index 861dbeb..e75a845 100644 --- a/include/libdivecomputer/context.h +++ b/include/libdivecomputer/context.h @@ -23,6 +23,7 @@ #define DC_CONTEXT_H #include "common.h" +#include "custom_serial.h" #ifdef __cplusplus extern "C" { @@ -47,6 +48,9 @@ dc_context_new (dc_context_t **context); dc_status_t dc_context_free (dc_context_t *context); +dc_status_t +dc_context_set_custom_serial (dc_context_t *context, dc_custom_serial_t *custom_serial); + dc_status_t dc_context_set_loglevel (dc_context_t *context, dc_loglevel_t loglevel); diff --git a/include/libdivecomputer/custom_serial.h b/include/libdivecomputer/custom_serial.h new file mode 100644 index 0000000..1a2b44e --- /dev/null +++ b/include/libdivecomputer/custom_serial.h @@ -0,0 +1,90 @@ +#ifndef CUSTOM_SERIAL_H +#define CUSTOM_SERIAL_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +// Verbatim copy from src/serial.h + +#ifndef __SERIAL_TYPES__ +#define __SERIAL_TYPES__ +// Don't re-declare when we're internal +/** + * The parity checking scheme. + */ +typedef enum dc_parity_t { + DC_PARITY_NONE, /**< No parity */ + DC_PARITY_ODD, /**< Odd parity */ + DC_PARITY_EVEN, /**< Even parity */ + DC_PARITY_MARK, /**< Mark parity (always 1) */ + DC_PARITY_SPACE /**< Space parity (alwasy 0) */ +} dc_parity_t; + +/** + * The number of stop bits. + */ +typedef enum dc_stopbits_t { + DC_STOPBITS_ONE, /**< 1 stop bit */ + DC_STOPBITS_ONEPOINTFIVE, /**< 1.5 stop bits*/ + DC_STOPBITS_TWO /**< 2 stop bits */ +} dc_stopbits_t; + +/** + * The flow control. + */ +typedef enum dc_flowcontrol_t { + DC_FLOWCONTROL_NONE, /**< No flow control */ + DC_FLOWCONTROL_HARDWARE, /**< Hardware (RTS/CTS) flow control */ + DC_FLOWCONTROL_SOFTWARE /**< Software (XON/XOFF) flow control */ +} dc_flowcontrol_t; + +/** + * The direction of the data transmission. + */ +typedef enum dc_direction_t { + DC_DIRECTION_INPUT = 0x01, /**< Input direction */ + DC_DIRECTION_OUTPUT = 0x02, /**< Output direction */ + DC_DIRECTION_ALL = DC_DIRECTION_INPUT | DC_DIRECTION_OUTPUT /**< All directions */ +} dc_direction_t; + +/** + * The serial line signals. + */ +typedef enum dc_line_t { + DC_LINE_DCD = 0x01, /**< Data carrier detect */ + DC_LINE_CTS = 0x02, /**< Clear to send */ + DC_LINE_DSR = 0x04, /**< Data set ready */ + DC_LINE_RNG = 0x08, /**< Ring indicator */ +} dc_line_t; + +#endif /* __SERIAL_TYPES__ */ + +typedef struct dc_custom_serial_t +{ + void *userdata; + dc_status_t (*open) (void **userdata, const char *name); + dc_status_t (*close) (void **userdata); + dc_status_t (*read) (void **userdata, void* data, size_t size, size_t *actual); + dc_status_t (*write) (void **userdata, const void* data, size_t size, size_t *actual); + dc_status_t (*purge) (void **userdata, dc_direction_t); + dc_status_t (*get_available) (void **userdata, size_t *value); + dc_status_t (*set_timeout) (void **userdata, long timeout); + dc_status_t (*configure) (void **userdata, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol); + dc_status_t (*set_dtr) (void **userdata, int level); + dc_status_t (*set_rts) (void **userdata, int level); + dc_status_t (*set_halfduplex) (void **userdata, unsigned int value); + dc_status_t (*set_break) (void **userdata, unsigned int level); + //dc_serial_set_latency (dc_serial_t *device, unsigned int milliseconds) - Unused + //dc_serial_get_lines (dc_serial_t *device, unsigned int *value) - Unused + //dc_serial_flush (dc_serial_t *device) - No device interaction + //dc_serial_sleep (dc_serial_t *device, unsigned int timeout) - No device interaction +} dc_custom_serial_t; + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CUSTOM_SERIAL_H */ diff --git a/src/context-private.h b/src/context-private.h index 7f50a68..0a0dcb8 100644 --- a/src/context-private.h +++ b/src/context-private.h @@ -27,6 +27,7 @@ #endif #include +#include #ifdef __cplusplus extern "C" { @@ -71,6 +72,23 @@ dc_context_syserror (dc_context_t *context, dc_loglevel_t loglevel, const char * dc_status_t dc_context_hexdump (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *prefix, const unsigned char data[], unsigned int size); +dc_custom_serial_t* +_dc_context_custom_serial (dc_context_t *context); + +#define RETURN_IF_CUSTOM_SERIAL(context, block, function, ...) \ + do { \ + dc_custom_serial_t *c = _dc_context_custom_serial(context); \ + dc_status_t _rc; \ + if (c) { \ + if (c->function) \ + _rc = c->function(&c->userdata, ##__VA_ARGS__); \ + else \ + _rc = DC_STATUS_SUCCESS; \ + block ;\ + return _rc; \ + } \ + } while (0) + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/context.c b/src/context.c index 73bce10..6b300c2 100644 --- a/src/context.c +++ b/src/context.c @@ -32,6 +32,7 @@ #endif #include "context-private.h" +#include struct dc_context_t { dc_loglevel_t loglevel; @@ -45,6 +46,7 @@ struct dc_context_t { struct timeval timestamp; #endif #endif + dc_custom_serial_t *custom_serial; }; #ifdef ENABLE_LOGGING @@ -195,6 +197,8 @@ dc_context_new (dc_context_t **out) #endif #endif + context->custom_serial = NULL; + *out = context; return DC_STATUS_SUCCESS; @@ -208,6 +212,23 @@ dc_context_free (dc_context_t *context) return DC_STATUS_SUCCESS; } +dc_status_t +dc_context_set_custom_serial (dc_context_t *context, dc_custom_serial_t *custom_serial) +{ + if (context == NULL) + return DC_STATUS_INVALIDARGS; + + context->custom_serial = custom_serial; + + return DC_STATUS_SUCCESS; +} + +dc_custom_serial_t* +_dc_context_custom_serial (dc_context_t *context) +{ + return context->custom_serial; +} + dc_status_t dc_context_set_loglevel (dc_context_t *context, dc_loglevel_t loglevel) { diff --git a/src/serial.h b/src/serial.h index ac94ec8..10da402 100644 --- a/src/serial.h +++ b/src/serial.h @@ -34,6 +34,8 @@ extern "C" { */ typedef struct dc_serial_t dc_serial_t; +#ifndef __SERIAL_TYPES__ +#define __SERIAL_TYPES__ /** * The parity checking scheme. */ @@ -81,6 +83,7 @@ typedef enum dc_line_t { DC_LINE_DSR = 0x04, /**< Data set ready */ DC_LINE_RNG = 0x08, /**< Ring indicator */ } dc_line_t; +#endif /* __SERIAL_TYPES__ */ /** * Serial enumeration callback. diff --git a/src/serial_posix.c b/src/serial_posix.c index 5a84757..d8f71fa 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -167,6 +167,8 @@ dc_serial_open (dc_serial_t **out, dc_context_t *context, const char *name) device->baudrate = 0; device->nbits = 0; + RETURN_IF_CUSTOM_SERIAL(context, *out = device, open, name); + // Open the device in non-blocking mode, to return immediately // without waiting for the modem connection to complete. device->fd = open (name, O_RDWR | O_NOCTTY | O_NONBLOCK); @@ -217,6 +219,8 @@ dc_serial_close (dc_serial_t *device) if (device == NULL) return DC_STATUS_SUCCESS; + RETURN_IF_CUSTOM_SERIAL(device->context, free(device), close); + // Restore the initial terminal attributes. if (tcsetattr (device->fd, TCSANOW, &device->tty) != 0) { int errcode = errno; @@ -251,6 +255,8 @@ dc_serial_configure (dc_serial_t *device, unsigned int baudrate, unsigned int da INFO (device->context, "Configure: baudrate=%i, databits=%i, parity=%i, stopbits=%i, flowcontrol=%i", baudrate, databits, parity, stopbits, flowcontrol); + RETURN_IF_CUSTOM_SERIAL(device->context, , configure, baudrate, databits, parity, stopbits, flowcontrol); + // Retrieve the current settings. struct termios tty; memset (&tty, 0, sizeof (tty)); @@ -505,6 +511,8 @@ dc_serial_set_timeout (dc_serial_t *device, int timeout) INFO (device->context, "Timeout: value=%i", timeout); + RETURN_IF_CUSTOM_SERIAL(device->context, , set_timeout, timeout); + device->timeout = timeout; return DC_STATUS_SUCCESS; @@ -516,6 +524,8 @@ dc_serial_set_halfduplex (dc_serial_t *device, unsigned int value) if (device == NULL) return DC_STATUS_INVALIDARGS; + RETURN_IF_CUSTOM_SERIAL(device->context, , set_halfduplex, value); + device->halfduplex = value; return DC_STATUS_SUCCESS; @@ -575,6 +585,14 @@ dc_serial_read (dc_serial_t *device, void *data, size_t size, size_t *actual) goto out; } + RETURN_IF_CUSTOM_SERIAL(device->context, + { + HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Custom Read", (unsigned char *) data, nbytes); + if (actual) + *actual = nbytes; + }, + read, data, size, &nbytes); + // The total timeout. int timeout = device->timeout; @@ -666,6 +684,14 @@ dc_serial_write (dc_serial_t *device, const void *data, size_t size, size_t *act goto out; } + RETURN_IF_CUSTOM_SERIAL(device->context, + { + HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Custom Write", (unsigned char *) data, nbytes); + if (actual) + *actual = nbytes; + }, + write, data, size, actual); + struct timeval tve, tvb; if (device->halfduplex) { // Get the current time. @@ -770,6 +796,8 @@ dc_serial_purge (dc_serial_t *device, dc_direction_t direction) INFO (device->context, "Purge: direction=%u", direction); + RETURN_IF_CUSTOM_SERIAL(device->context, , purge, direction); + int flags = 0; switch (direction) { @@ -814,6 +842,8 @@ dc_serial_set_break (dc_serial_t *device, unsigned int level) INFO (device->context, "Break: value=%i", level); + RETURN_IF_CUSTOM_SERIAL(device->context, , set_break, level); + unsigned long action = (level ? TIOCSBRK : TIOCCBRK); if (ioctl (device->fd, action, NULL) != 0 && NOPTY) { @@ -833,6 +863,8 @@ dc_serial_set_dtr (dc_serial_t *device, unsigned int level) INFO (device->context, "DTR: value=%i", level); + RETURN_IF_CUSTOM_SERIAL(device->context, , set_dtr, level); + unsigned long action = (level ? TIOCMBIS : TIOCMBIC); int value = TIOCM_DTR; @@ -853,6 +885,8 @@ dc_serial_set_rts (dc_serial_t *device, unsigned int level) INFO (device->context, "RTS: value=%i", level); + RETURN_IF_CUSTOM_SERIAL(device->context, , set_rts, level); + unsigned long action = (level ? TIOCMBIS : TIOCMBIC); int value = TIOCM_RTS; @@ -871,6 +905,8 @@ dc_serial_get_available (dc_serial_t *device, size_t *value) if (device == NULL) return DC_STATUS_INVALIDARGS; + RETURN_IF_CUSTOM_SERIAL(device->context, , get_available, value); + int bytes = 0; if (ioctl (device->fd, TIOCINQ, &bytes) != 0) { int errcode = errno; diff --git a/src/serial_win32.c b/src/serial_win32.c index 2d73cb9..07e6fc9 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -157,6 +157,8 @@ dc_serial_open (dc_serial_t **out, dc_context_t *context, const char *name) device->baudrate = 0; device->nbits = 0; + RETURN_IF_CUSTOM_SERIAL(context, *out = device, open, name); + // Open the device. device->hFile = CreateFileA (devname, GENERIC_READ | GENERIC_WRITE, 0, @@ -202,6 +204,8 @@ dc_serial_close (dc_serial_t *device) if (device == NULL) return DC_STATUS_SUCCESS; + RETURN_IF_CUSTOM_SERIAL(device->context, free(device), close); + // Restore the initial communication settings and timeouts. if (!SetCommState (device->hFile, &device->dcb) || !SetCommTimeouts (device->hFile, &device->timeouts)) { @@ -232,6 +236,8 @@ dc_serial_configure (dc_serial_t *device, unsigned int baudrate, unsigned int da INFO (device->context, "Configure: baudrate=%i, databits=%i, parity=%i, stopbits=%i, flowcontrol=%i", baudrate, databits, parity, stopbits, flowcontrol); + RETURN_IF_CUSTOM_SERIAL(device->context, , configure, baudrate, databits, parity, stopbits, flowcontrol); + // Retrieve the current settings. DCB dcb; if (!GetCommState (device->hFile, &dcb)) { @@ -344,6 +350,8 @@ dc_serial_set_timeout (dc_serial_t *device, int timeout) INFO (device->context, "Timeout: value=%i", timeout); + RETURN_IF_CUSTOM_SERIAL(device->context, , set_timeout, timeout); + // Retrieve the current timeouts. COMMTIMEOUTS timeouts; if (!GetCommTimeouts (device->hFile, &timeouts)) { @@ -392,6 +400,8 @@ dc_serial_set_halfduplex (dc_serial_t *device, unsigned int value) if (device == NULL) return DC_STATUS_INVALIDARGS; + RETURN_IF_CUSTOM_SERIAL(device->context, , set_halfduplex, value); + device->halfduplex = value; return DC_STATUS_SUCCESS; @@ -417,6 +427,14 @@ dc_serial_read (dc_serial_t *device, void *data, size_t size, size_t *actual) goto out; } + RETURN_IF_CUSTOM_SERIAL(device->context, + { + HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Custom Read", (unsigned char *) data, nbytes); + if (actual) + *actual = nbytes; + }, + read, data, size, &nbytes); + if (!ReadFile (device->hFile, data, size, &dwRead, NULL)) { DWORD errcode = GetLastError (); SYSERROR (device->context, errcode); @@ -448,6 +466,14 @@ dc_serial_write (dc_serial_t *device, const void *data, size_t size, size_t *act goto out; } + RETURN_IF_CUSTOM_SERIAL(device->context, + { + HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Custom Write", (unsigned char *) data, nbytes); + if (actual) + *actual = nbytes; + }, + write, data, size, actual); + LARGE_INTEGER begin, end, freq; if (device->halfduplex) { // Get the current time. @@ -515,6 +541,8 @@ dc_serial_purge (dc_serial_t *device, dc_direction_t direction) INFO (device->context, "Purge: direction=%u", direction); + RETURN_IF_CUSTOM_SERIAL(device->context, , purge, direction); + DWORD flags = 0; switch (direction) { @@ -565,6 +593,8 @@ dc_serial_set_break (dc_serial_t *device, unsigned int level) INFO (device->context, "Break: value=%i", level); + RETURN_IF_CUSTOM_SERIAL(device->context, , set_break, level); + if (level) { if (!SetCommBreak (device->hFile)) { DWORD errcode = GetLastError (); @@ -590,6 +620,8 @@ dc_serial_set_dtr (dc_serial_t *device, unsigned int level) INFO (device->context, "DTR: value=%i", level); + RETURN_IF_CUSTOM_SERIAL(device->context, , set_dtr, level); + int status = (level ? SETDTR : CLRDTR); if (!EscapeCommFunction (device->hFile, status)) { @@ -609,6 +641,8 @@ dc_serial_set_rts (dc_serial_t *device, unsigned int level) INFO (device->context, "RTS: value=%i", level); + RETURN_IF_CUSTOM_SERIAL(device->context, , set_rts, level); + int status = (level ? SETRTS : CLRRTS); if (!EscapeCommFunction (device->hFile, status)) { @@ -626,6 +660,8 @@ dc_serial_get_available (dc_serial_t *device, size_t *value) if (device == NULL) return DC_STATUS_INVALIDARGS; + RETURN_IF_CUSTOM_SERIAL(device->context, , get_available, value); + COMSTAT stats; if (!ClearCommError (device->hFile, NULL, &stats)) {