Add a new abstract I/O interface

The purpose of the new I/O interface is to provide a common interface
for all existing I/O implementations (serial, IrDA, bluetooth and USB
HID). With a common interface the dive computer backends can more easily
use different I/O implementations at runtime, without needing
significant code changes. For example bluetooth enabled devices can
easily switch between native bluetooth communication and serial port
emulation mode.

The new interface is modelled after the existing serial communication
api. Implementations where some of those functions are meaningless (e.g.
IrDA, bluetooth and USB), can just leave those functions unimplemented
(causing the call to fail with DC_STATUS_UNSUPPORTED), or implement it
as a no-op (always return DC_STATUS_SUCCESS).
This commit is contained in:
Jef Driesen 2016-01-11 21:22:27 +01:00
parent b75095cefa
commit 3ca27995e1
7 changed files with 660 additions and 0 deletions

View File

@ -6,6 +6,7 @@ libdivecomputer_HEADERS = \
buffer.h \ buffer.h \
descriptor.h \ descriptor.h \
iterator.h \ iterator.h \
iostream.h \
device.h \ device.h \
parser.h \ parser.h \
datetime.h \ datetime.h \

View File

@ -0,0 +1,290 @@
/*
* libdivecomputer
*
* Copyright (C) 2016 Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#ifndef DC_IOSTREAM_H
#define DC_IOSTREAM_H
#include <libdivecomputer/common.h>
#include <libdivecomputer/context.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* Opaque object representing a I/O stream.
*/
typedef struct dc_iostream_t dc_iostream_t;
/**
* 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 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;
/**
* Set the read timeout.
*
* There are three distinct modes available:
*
* 1. Blocking (timeout < 0):
*
* The read operation is blocked until all the requested bytes have
* been received. If the requested number of bytes does not arrive,
* the operation will block forever.
*
* 2. Non-blocking (timeout == 0):
*
* The read operation returns immediately with the bytes that have
* already been received, even if no bytes have been received.
*
* 3. Timeout (timeout > 0):
*
* The read operation is blocked until all the requested bytes have
* been received. If the requested number of bytes does not arrive
* within the specified amount of time, the operation will return
* with the bytes that have already been received.
*
* @param[in] iostream A valid I/O stream.
* @param[in] timeout The timeout in milliseconds.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_set_timeout (dc_iostream_t *iostream, int timeout);
/**
* Set the receive latency.
*
* The effect of this setting is highly platform and driver specific. On
* Windows it does nothing at all, on Linux it controls the low latency
* flag (e.g. only zero vs non-zero latency), and on Mac OS X it sets
* the receive latency as requested.
*
* @param[in] iostream A valid I/O stream.
* @param[in] value The latency in milliseconds.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_set_latency (dc_iostream_t *iostream, unsigned int value);
/**
* Set the state of the half duplex emulation.
*
* @param[in] iostream A valid I/O stream.
* @param[in] value The half duplex state.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_set_halfduplex (dc_iostream_t *iostream, unsigned int value);
/**
* Set the state of the break condition.
*
* @param[in] iostream A valid I/O stream.
* @param[in] value The break condition state.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_set_break (dc_iostream_t *iostream, unsigned int value);
/**
* Set the state of the DTR line.
*
* @param[in] iostream A valid I/O stream.
* @param[in] value The DTR line state.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_set_dtr (dc_iostream_t *iostream, unsigned int value);
/**
* Set the state of the RTS line.
*
* @param[in] iostream A valid I/O stream.
* @param[in] value The RTS line state.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_set_rts (dc_iostream_t *iostream, unsigned int value);
/**
* Query the state of the line signals.
*
* @param[in] iostream A valid I/O stream.
* @param[out] value A location to store the bitmap with the state
* of the line signals.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_get_lines (dc_iostream_t *iostream, unsigned int *value);
/**
* Query the number of available bytes in the input buffer.
*
* @param[in] iostream A valid I/O stream.
* @param[out] value A location to store the number of bytes in the
* input buffer.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_get_available (dc_iostream_t *iostream, size_t *value);
/**
* Configure the line settings.
*
* @param[in] iostream A valid I/O stream.
* @param[in] baudrate The baud rate setting.
* @param[in] databits The number of data bits.
* @param[in] parity The parity setting.
* @param[in] stopbits The number of stop bits.
* @param[in] flowcontrol The flow control setting.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_configure (dc_iostream_t *iostream, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol);
/**
* Read data from the I/O stream.
*
* @param[in] iostream A valid I/O stream.
* @param[out] data The memory buffer to read the data into.
* @param[in] size The number of bytes to read.
* @param[out] actual An (optional) location to store the actual
* number of bytes transferred.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual);
/**
* Write data to the I/O stream.
*
* @param[in] iostream A valid I/O stream.
* @param[in] data The memory buffer to write the data from.
* @param[in] size The number of bytes to write.
* @param[out] actual An (optional) location to store the actual
* number of bytes transferred.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual);
/**
* Flush the internal output buffer and wait until the data has been
* transmitted.
*
* @param[in] iostream A valid I/O stream.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_flush (dc_iostream_t *iostream);
/**
* Discards all data from the internal buffers.
*
* @param[in] iostream A valid I/O stream.
* @param[in] direction The direction of the buffer(s).
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_purge (dc_iostream_t *iostream, dc_direction_t direction);
/**
* Suspend execution of the current thread for the specified amount of
* time.
*
* @param[in] iostream A valid I/O stream.
* @param[in] milliseconds The number of milliseconds to sleep.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_sleep (dc_iostream_t *iostream, unsigned int milliseconds);
/**
* Close the I/O stream and free all resources.
*
* @param[in] iostream A valid I/O stream.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_iostream_close (dc_iostream_t *iostream);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* DC_IOSTREAM_H */

View File

@ -294,6 +294,10 @@
RelativePath="..\src\ihex.c" RelativePath="..\src\ihex.c"
> >
</File> </File>
<File
RelativePath="..\src\iostream.c"
>
</File>
<File <File
RelativePath="..\src\irda.c" RelativePath="..\src\irda.c"
> >
@ -624,6 +628,14 @@
RelativePath="..\src\ihex.h" RelativePath="..\src\ihex.h"
> >
</File> </File>
<File
RelativePath="..\src\iostream-private.h"
>
</File>
<File
RelativePath="..\src\iostream.h"
>
</File>
<File <File
RelativePath="..\src\irda.h" RelativePath="..\src\irda.h"
> >

View File

@ -17,6 +17,7 @@ endif
libdivecomputer_la_SOURCES = \ libdivecomputer_la_SOURCES = \
version.c \ version.c \
descriptor.c \ descriptor.c \
iostream-private.h iostream.c \
iterator-private.h iterator.c \ iterator-private.h iterator.c \
common-private.h common.c \ common-private.h common.c \
context-private.h context.c \ context-private.h context.c \

86
src/iostream-private.h Normal file
View File

@ -0,0 +1,86 @@
/*
* libdivecomputer
*
* Copyright (C) 2016 Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#ifndef DC_IOSTREAM_PRIVATE_H
#define DC_IOSTREAM_PRIVATE_H
#include <libdivecomputer/common.h>
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct dc_iostream_vtable_t dc_iostream_vtable_t;
struct dc_iostream_t {
const dc_iostream_vtable_t *vtable;
dc_context_t *context;
};
struct dc_iostream_vtable_t {
size_t size;
dc_status_t (*set_timeout) (dc_iostream_t *iostream, int timeout);
dc_status_t (*set_latency) (dc_iostream_t *iostream, unsigned int value);
dc_status_t (*set_halfduplex) (dc_iostream_t *iostream, unsigned int value);
dc_status_t (*set_break) (dc_iostream_t *iostream, unsigned int value);
dc_status_t (*set_dtr) (dc_iostream_t *iostream, unsigned int value);
dc_status_t (*set_rts) (dc_iostream_t *iostream, unsigned int value);
dc_status_t (*get_lines) (dc_iostream_t *iostream, unsigned int *value);
dc_status_t (*get_available) (dc_iostream_t *iostream, size_t *value);
dc_status_t (*configure) (dc_iostream_t *iostream, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol);
dc_status_t (*read) (dc_iostream_t *iostream, void *data, size_t size, size_t *actual);
dc_status_t (*write) (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual);
dc_status_t (*flush) (dc_iostream_t *iostream);
dc_status_t (*purge) (dc_iostream_t *iostream, dc_direction_t direction);
dc_status_t (*sleep) (dc_iostream_t *iostream, unsigned int milliseconds);
dc_status_t (*close) (dc_iostream_t *iostream);
};
dc_iostream_t *
dc_iostream_allocate (dc_context_t *context, const dc_iostream_vtable_t *vtable);
void
dc_iostream_deallocate (dc_iostream_t *iostream);
int
dc_iostream_isinstance (dc_iostream_t *iostream, const dc_iostream_vtable_t *vtable);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* DC_IOSTREAM_PRIVATE_H */

254
src/iostream.c Normal file
View File

@ -0,0 +1,254 @@
/*
* libdivecomputer
*
* Copyright (C) 2016 Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#include "iostream-private.h"
#include "context-private.h"
dc_iostream_t *
dc_iostream_allocate (dc_context_t *context, const dc_iostream_vtable_t *vtable)
{
dc_iostream_t *iostream = NULL;
assert(vtable != NULL);
assert(vtable->size >= sizeof(dc_iostream_t));
// Allocate memory.
iostream = (dc_iostream_t *) malloc (vtable->size);
if (iostream == NULL) {
ERROR (context, "Failed to allocate memory.");
return iostream;
}
// Initialize the base class.
iostream->vtable = vtable;
iostream->context = context;
return iostream;
}
void
dc_iostream_deallocate (dc_iostream_t *iostream)
{
free (iostream);
}
int
dc_iostream_isinstance (dc_iostream_t *iostream, const dc_iostream_vtable_t *vtable)
{
if (iostream == NULL)
return 0;
return iostream->vtable == vtable;
}
dc_status_t
dc_iostream_set_timeout (dc_iostream_t *iostream, int timeout)
{
if (iostream == NULL || iostream->vtable->set_timeout == NULL)
return DC_STATUS_UNSUPPORTED;
INFO (iostream->context, "Timeout: value=%i", timeout);
return iostream->vtable->set_timeout (iostream, timeout);
}
dc_status_t
dc_iostream_set_latency (dc_iostream_t *iostream, unsigned int value)
{
if (iostream == NULL || iostream->vtable->set_latency == NULL)
return DC_STATUS_UNSUPPORTED;
INFO (iostream->context, "Latency: value=%i", value);
return iostream->vtable->set_latency (iostream, value);
}
dc_status_t
dc_iostream_set_halfduplex (dc_iostream_t *iostream, unsigned int value)
{
if (iostream == NULL || iostream->vtable->set_halfduplex == NULL)
return DC_STATUS_UNSUPPORTED;
INFO (iostream->context, "Halfduplex: value=%i", value);
return iostream->vtable->set_halfduplex (iostream, value);
}
dc_status_t
dc_iostream_set_break (dc_iostream_t *iostream, unsigned int value)
{
if (iostream == NULL || iostream->vtable->set_break == NULL)
return DC_STATUS_UNSUPPORTED;
INFO (iostream->context, "Break: value=%i", value);
return iostream->vtable->set_break (iostream, value);
}
dc_status_t
dc_iostream_set_dtr (dc_iostream_t *iostream, unsigned int value)
{
if (iostream == NULL || iostream->vtable->set_dtr == NULL)
return DC_STATUS_UNSUPPORTED;
INFO (iostream->context, "DTR: value=%i", value);
return iostream->vtable->set_dtr (iostream, value);
}
dc_status_t
dc_iostream_set_rts (dc_iostream_t *iostream, unsigned int value)
{
if (iostream == NULL || iostream->vtable->set_rts == NULL)
return DC_STATUS_UNSUPPORTED;
INFO (iostream->context, "RTS: value=%i", value);
return iostream->vtable->set_rts (iostream, value);
}
dc_status_t
dc_iostream_get_lines (dc_iostream_t *iostream, unsigned int *value)
{
if (iostream == NULL || iostream->vtable->get_lines == NULL)
return DC_STATUS_UNSUPPORTED;
return iostream->vtable->get_lines (iostream, value);
}
dc_status_t
dc_iostream_get_available (dc_iostream_t *iostream, size_t *value)
{
if (iostream == NULL || iostream->vtable->get_available == NULL)
return DC_STATUS_UNSUPPORTED;
return iostream->vtable->get_available (iostream, value);
}
dc_status_t
dc_iostream_configure (dc_iostream_t *iostream, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol)
{
if (iostream == NULL || iostream->vtable->configure == NULL)
return DC_STATUS_UNSUPPORTED;
INFO (iostream->context, "Configure: baudrate=%i, databits=%i, parity=%i, stopbits=%i, flowcontrol=%i",
baudrate, databits, parity, stopbits, flowcontrol);
return iostream->vtable->configure (iostream, baudrate, databits, parity, stopbits, flowcontrol);
}
dc_status_t
dc_iostream_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
size_t nbytes = 0;
if (iostream == NULL || iostream->vtable->read == NULL) {
status = DC_STATUS_UNSUPPORTED;
goto out;
}
status = iostream->vtable->read (iostream, data, size, &nbytes);
HEXDUMP (iostream->context, DC_LOGLEVEL_INFO, "Read", (unsigned char *) data, nbytes);
out:
if (actual)
*actual = nbytes;
return status;
}
dc_status_t
dc_iostream_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
size_t nbytes = 0;
if (iostream == NULL || iostream->vtable->read == NULL) {
status = DC_STATUS_UNSUPPORTED;
goto out;
}
status = iostream->vtable->write (iostream, data, size, &nbytes);
HEXDUMP (iostream->context, DC_LOGLEVEL_INFO, "Write", (const unsigned char *) data, nbytes);
out:
if (actual)
*actual = nbytes;
return status;
}
dc_status_t
dc_iostream_flush (dc_iostream_t *iostream)
{
if (iostream == NULL || iostream->vtable->flush == NULL)
return DC_STATUS_UNSUPPORTED;
INFO (iostream->context, "Flush: none");
return iostream->vtable->flush (iostream);
}
dc_status_t
dc_iostream_purge (dc_iostream_t *iostream, dc_direction_t direction)
{
if (iostream == NULL || iostream->vtable->purge == NULL)
return DC_STATUS_UNSUPPORTED;
INFO (iostream->context, "Purge: direction=%u", direction);
return iostream->vtable->purge (iostream, direction);
}
dc_status_t
dc_iostream_sleep (dc_iostream_t *iostream, unsigned int milliseconds)
{
if (iostream == NULL || iostream->vtable->sleep == NULL)
return DC_STATUS_UNSUPPORTED;
INFO (iostream->context, "Sleep: value=%u", milliseconds);
return iostream->vtable->sleep (iostream, milliseconds);
}
dc_status_t
dc_iostream_close (dc_iostream_t *iostream)
{
dc_status_t status = DC_STATUS_SUCCESS;
if (iostream == NULL)
return DC_STATUS_SUCCESS;
if (iostream->vtable->close) {
status = iostream->vtable->close (iostream);
}
dc_iostream_deallocate (iostream);
return status;
}

View File

@ -33,6 +33,22 @@ dc_descriptor_get_type
dc_descriptor_get_model dc_descriptor_get_model
dc_descriptor_get_transport dc_descriptor_get_transport
dc_iostream_set_timeout
dc_iostream_set_halfduplex
dc_iostream_set_latency
dc_iostream_set_break
dc_iostream_set_dtr
dc_iostream_set_rts
dc_iostream_get_available
dc_iostream_get_lines
dc_iostream_configure
dc_iostream_read
dc_iostream_write
dc_iostream_flush
dc_iostream_purge
dc_iostream_sleep
dc_iostream_close
dc_parser_new dc_parser_new
dc_parser_new2 dc_parser_new2
dc_parser_get_type dc_parser_get_type