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 <glance@acc.umu.se>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Anton Lundin 2016-09-17 15:20:07 +02:00 committed by Linus Torvalds
parent 7e086f697d
commit 94e7a77d01
8 changed files with 209 additions and 0 deletions

View File

@ -3,6 +3,7 @@ libdivecomputer_HEADERS = \
version.h \
common.h \
context.h \
custom_serial.h \
buffer.h \
descriptor.h \
iterator.h \

View File

@ -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);

View File

@ -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 */

View File

@ -27,6 +27,7 @@
#endif
#include <libdivecomputer/context.h>
#include <libdivecomputer/custom_serial.h>
#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 */

View File

@ -32,6 +32,7 @@
#endif
#include "context-private.h"
#include <libdivecomputer/custom_serial.h>
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)
{

View File

@ -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.

View File

@ -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;

View File

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