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)) {