From 283eaa1ca6dbce8e2ee73340fb84b70bb4f96983 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 9 Jun 2017 21:13:18 +0200 Subject: [PATCH] Add support for a custom I/O implementation Wih the custom I/O implementation, an application can use its own low-level I/O layer instead of using one of the built-in ones. The application only needs to provide a set of callback functions, and libdivecomputer will wrap them into a I/O stream. --- msvc/libdivecomputer.vcproj | 8 ++ src/Makefile.am | 1 + src/custom.c | 263 ++++++++++++++++++++++++++++++++++++ src/custom.h | 67 +++++++++ 4 files changed, 339 insertions(+) create mode 100644 src/custom.c create mode 100644 src/custom.h diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index cea0027..ee31af8 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -246,6 +246,10 @@ RelativePath="..\src\cressi_leonardo_parser.c" > + + @@ -576,6 +580,10 @@ RelativePath="..\src\cressi_leonardo.h" > + + diff --git a/src/Makefile.am b/src/Makefile.am index 5d99e69..24ede8c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,6 +81,7 @@ endif libdivecomputer_la_SOURCES += irda.h irda.c libdivecomputer_la_SOURCES += usbhid.h usbhid.c libdivecomputer_la_SOURCES += bluetooth.h bluetooth.c +libdivecomputer_la_SOURCES += custom.h custom.c if OS_WIN32 libdivecomputer_la_SOURCES += libdivecomputer.rc diff --git a/src/custom.c b/src/custom.c new file mode 100644 index 0000000..e96ba5f --- /dev/null +++ b/src/custom.c @@ -0,0 +1,263 @@ +/* + * libdivecomputer + * + * Copyright (C) 2017 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 // malloc, free + +#include "custom.h" + +#include "iostream-private.h" +#include "common-private.h" +#include "context-private.h" + +static dc_status_t dc_custom_set_timeout (dc_iostream_t *abstract, int timeout); +static dc_status_t dc_custom_set_latency (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_custom_set_halfduplex (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_custom_set_break (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_custom_set_dtr (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_custom_set_rts (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_custom_get_lines (dc_iostream_t *abstract, unsigned int *value); +static dc_status_t dc_custom_get_available (dc_iostream_t *abstract, size_t *value); +static dc_status_t dc_custom_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol); +static dc_status_t dc_custom_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual); +static dc_status_t dc_custom_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual); +static dc_status_t dc_custom_flush (dc_iostream_t *abstract); +static dc_status_t dc_custom_purge (dc_iostream_t *abstract, dc_direction_t direction); +static dc_status_t dc_custom_sleep (dc_iostream_t *abstract, unsigned int milliseconds); +static dc_status_t dc_custom_close (dc_iostream_t *abstract); + +typedef struct dc_custom_t { + /* Base class. */ + dc_iostream_t base; + /* Internal state. */ + dc_custom_cbs_t callbacks; + void *userdata; +} dc_custom_t; + +static const dc_iostream_vtable_t dc_custom_vtable = { + sizeof(dc_custom_t), + dc_custom_set_timeout, /* set_timeout */ + dc_custom_set_latency, /* set_latency */ + dc_custom_set_halfduplex, /* set_halfduplex */ + dc_custom_set_break, /* set_break */ + dc_custom_set_dtr, /* set_dtr */ + dc_custom_set_rts, /* set_rts */ + dc_custom_get_lines, /* get_lines */ + dc_custom_get_available, /* get_received */ + dc_custom_configure, /* configure */ + dc_custom_read, /* read */ + dc_custom_write, /* write */ + dc_custom_flush, /* flush */ + dc_custom_purge, /* purge */ + dc_custom_sleep, /* sleep */ + dc_custom_close, /* close */ +}; + +dc_status_t +dc_custom_open (dc_iostream_t **out, dc_context_t *context, const dc_custom_cbs_t *callbacks, void *userdata) +{ + dc_custom_t *custom = NULL; + + if (out == NULL || callbacks == NULL) + return DC_STATUS_INVALIDARGS; + + INFO (context, "Open: custom"); + + // Allocate memory. + custom = (dc_custom_t *) dc_iostream_allocate (context, &dc_custom_vtable); + if (custom == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + custom->callbacks = *callbacks; + custom->userdata = userdata; + + *out = (dc_iostream_t *) custom; + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +dc_custom_set_timeout (dc_iostream_t *abstract, int timeout) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.set_timeout == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.set_timeout (custom->userdata, timeout); +} + +static dc_status_t +dc_custom_set_latency (dc_iostream_t *abstract, unsigned int value) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.set_latency == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.set_latency (custom->userdata, value); +} + +static dc_status_t +dc_custom_set_halfduplex (dc_iostream_t *abstract, unsigned int value) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.set_halfduplex == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.set_halfduplex (custom->userdata, value); +} + +static dc_status_t +dc_custom_set_break (dc_iostream_t *abstract, unsigned int value) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.set_break == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.set_break (custom->userdata, value); +} + +static dc_status_t +dc_custom_set_dtr (dc_iostream_t *abstract, unsigned int value) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.set_dtr == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.set_dtr (custom->userdata, value); +} + +static dc_status_t +dc_custom_set_rts (dc_iostream_t *abstract, unsigned int value) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.set_rts == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.set_rts (custom->userdata, value); +} + +static dc_status_t +dc_custom_get_lines (dc_iostream_t *abstract, unsigned int *value) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.get_lines == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.get_lines (custom->userdata, value); +} + +static dc_status_t +dc_custom_get_available (dc_iostream_t *abstract, size_t *value) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.get_available == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.get_available (custom->userdata, value); +} + +static dc_status_t +dc_custom_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.configure == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.configure (custom->userdata, baudrate, databits, parity, stopbits, flowcontrol); +} + +static dc_status_t +dc_custom_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.read == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.read (custom->userdata, data, size, actual); +} + +static dc_status_t +dc_custom_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.write == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.write (custom->userdata, data, size, actual); +} + +static dc_status_t +dc_custom_flush (dc_iostream_t *abstract) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.flush == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.flush (custom->userdata); +} + +static dc_status_t +dc_custom_purge (dc_iostream_t *abstract, dc_direction_t direction) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.purge == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.purge (custom->userdata, direction); +} + +static dc_status_t +dc_custom_sleep (dc_iostream_t *abstract, unsigned int milliseconds) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.sleep == NULL) + return DC_STATUS_UNSUPPORTED; + + return custom->callbacks.sleep (custom->userdata, milliseconds); +} + +static dc_status_t +dc_custom_close (dc_iostream_t *abstract) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.close) { + status = custom->callbacks.close (custom->userdata); + } + + return status; +} diff --git a/src/custom.h b/src/custom.h new file mode 100644 index 0000000..f062cd3 --- /dev/null +++ b/src/custom.h @@ -0,0 +1,67 @@ +/* + * libdivecomputer + * + * Copyright (C) 2017 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_CUSTOM_H +#define DC_CUSTOM_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct dc_custom_cbs_t { + dc_status_t (*set_timeout) (void *userdata, int timeout); + dc_status_t (*set_latency) (void *userdata, unsigned int value); + dc_status_t (*set_halfduplex) (void *userdata, unsigned int value); + dc_status_t (*set_break) (void *userdata, unsigned int value); + dc_status_t (*set_dtr) (void *userdata, unsigned int value); + dc_status_t (*set_rts) (void *userdata, unsigned int value); + dc_status_t (*get_lines) (void *userdata, unsigned int *value); + dc_status_t (*get_available) (void *userdata, size_t *value); + 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 (*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 (*flush) (void *userdata); + dc_status_t (*purge) (void *userdata, dc_direction_t direction); + dc_status_t (*sleep) (void *userdata, unsigned int milliseconds); + dc_status_t (*close) (void *userdata); +} dc_custom_cbs_t; + +/** + * Create a custom I/O stream. + * + * @param[out] iostream A location to store the custom I/O stream. + * @param[in] context A valid context object. + * @param[in] callbacks The callback functions to call. + * @param[in] userdata User data to pass to the callback functions. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_custom_open (dc_iostream_t **iostream, dc_context_t *context, const dc_custom_cbs_t *callbacks, void *userdata); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_CUSTOM_H */