diff --git a/include/libdivecomputer/Makefile.am b/include/libdivecomputer/Makefile.am index 82ba21f..bbe7971 100644 --- a/include/libdivecomputer/Makefile.am +++ b/include/libdivecomputer/Makefile.am @@ -6,6 +6,7 @@ libdivecomputer_HEADERS = \ buffer.h \ descriptor.h \ iterator.h \ + iostream.h \ device.h \ parser.h \ datetime.h \ diff --git a/include/libdivecomputer/iostream.h b/include/libdivecomputer/iostream.h new file mode 100644 index 0000000..dab5daf --- /dev/null +++ b/include/libdivecomputer/iostream.h @@ -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 +#include + +#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 */ diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index 242276a..cea0027 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -294,6 +294,10 @@ RelativePath="..\src\ihex.c" > + + @@ -624,6 +628,14 @@ RelativePath="..\src\ihex.h" > + + + + diff --git a/src/Makefile.am b/src/Makefile.am index 97062e1..5d99e69 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,6 +17,7 @@ endif libdivecomputer_la_SOURCES = \ version.c \ descriptor.c \ + iostream-private.h iostream.c \ iterator-private.h iterator.c \ common-private.h common.c \ context-private.h context.c \ diff --git a/src/iostream-private.h b/src/iostream-private.h new file mode 100644 index 0000000..7f7be84 --- /dev/null +++ b/src/iostream-private.h @@ -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 +#include +#include + +#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 */ diff --git a/src/iostream.c b/src/iostream.c new file mode 100644 index 0000000..e6c40ea --- /dev/null +++ b/src/iostream.c @@ -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 +#include +#include + +#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; +} diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 3b16700..daed9ef 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -33,6 +33,22 @@ dc_descriptor_get_type dc_descriptor_get_model 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_new2 dc_parser_get_type