diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk index 6c61f26..fcceb81 100644 --- a/contrib/android/Android.mk +++ b/contrib/android/Android.mk @@ -69,6 +69,7 @@ LOCAL_SRC_FILES := \ src/oceans_s1.c \ src/oceans_s1_common.c \ src/oceans_s1_parser.c \ + src/packet.c \ src/parser.c \ src/platform.c \ src/rbstream.c \ diff --git a/contrib/msvc/libdivecomputer.vcxproj b/contrib/msvc/libdivecomputer.vcxproj index 8b3ceab..7f1335c 100644 --- a/contrib/msvc/libdivecomputer.vcxproj +++ b/contrib/msvc/libdivecomputer.vcxproj @@ -237,6 +237,7 @@ + @@ -355,6 +356,7 @@ + diff --git a/src/Makefile.am b/src/Makefile.am index ef44da3..de59dd6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -82,6 +82,7 @@ libdivecomputer_la_SOURCES = \ oceans_s1.h oceans_s1.c oceans_s1_parser.c \ divesoft_freedom.h divesoft_freedom.c divesoft_freedom_parser.c \ hdlc.h hdlc.c \ + packet.h packet.c \ socket.h socket.c \ irda.c \ usb.c \ diff --git a/src/packet.c b/src/packet.c new file mode 100644 index 0000000..cc8304f --- /dev/null +++ b/src/packet.c @@ -0,0 +1,321 @@ +/* + * libdivecomputer + * + * Copyright (C) 2023 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 + +#include "packet.h" + +#include "iostream-private.h" +#include "common-private.h" +#include "context-private.h" + +static dc_status_t dc_packet_set_timeout (dc_iostream_t *abstract, int timeout); +static dc_status_t dc_packet_set_break (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_packet_set_dtr (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_packet_set_rts (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_packet_get_lines (dc_iostream_t *abstract, unsigned int *value); +static dc_status_t dc_packet_get_available (dc_iostream_t *abstract, size_t *value); +static dc_status_t dc_packet_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_packet_poll (dc_iostream_t *abstract, int timeout); +static dc_status_t dc_packet_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual); +static dc_status_t dc_packet_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual); +static dc_status_t dc_packet_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size); +static dc_status_t dc_packet_flush (dc_iostream_t *abstract); +static dc_status_t dc_packet_purge (dc_iostream_t *abstract, dc_direction_t direction); +static dc_status_t dc_packet_sleep (dc_iostream_t *abstract, unsigned int milliseconds); +static dc_status_t dc_packet_close (dc_iostream_t *abstract); + +typedef struct dc_packet_t { + /* Base class. */ + dc_iostream_t base; + /* Internal state. */ + dc_iostream_t *iostream; + unsigned char *cache; + size_t available; + size_t offset; + size_t isize; + size_t osize; +} dc_packet_t; + +static const dc_iostream_vtable_t dc_packet_vtable = { + sizeof(dc_packet_t), + dc_packet_set_timeout, /* set_timeout */ + dc_packet_set_break, /* set_break */ + dc_packet_set_dtr, /* set_dtr */ + dc_packet_set_rts, /* set_rts */ + dc_packet_get_lines, /* get_lines */ + dc_packet_get_available, /* get_available */ + dc_packet_configure, /* configure */ + dc_packet_poll, /* poll */ + dc_packet_read, /* read */ + dc_packet_write, /* write */ + dc_packet_ioctl, /* ioctl */ + dc_packet_flush, /* flush */ + dc_packet_purge, /* purge */ + dc_packet_sleep, /* sleep */ + dc_packet_close, /* close */ +}; + +dc_status_t +dc_packet_open (dc_iostream_t **out, dc_context_t *context, dc_iostream_t *base, size_t isize, size_t osize) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_packet_t *packet = NULL; + unsigned char *buffer = NULL; + + if (out == NULL || base == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + packet = (dc_packet_t *) dc_iostream_allocate (NULL, &dc_packet_vtable, dc_iostream_get_transport(base)); + if (packet == NULL) { + ERROR (context, "Failed to allocate memory."); + status = DC_STATUS_NOMEMORY; + goto error_exit; + } + + // Allocate the read buffer. + if (isize) { + buffer = (unsigned char *) malloc (isize); + if (buffer == NULL) { + ERROR (context, "Failed to allocate memory."); + status = DC_STATUS_NOMEMORY; + goto error_free; + } + } + + packet->iostream = base; + packet->cache = buffer; + packet->available = 0; + packet->offset = 0; + packet->isize = isize; + packet->osize = osize; + + *out = (dc_iostream_t *) packet; + + return DC_STATUS_SUCCESS; + +error_free: + dc_iostream_deallocate ((dc_iostream_t *) packet); +error_exit: + return status; +} + +static dc_status_t +dc_packet_set_timeout (dc_iostream_t *abstract, int timeout) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_set_timeout (packet->iostream, timeout); +} + +static dc_status_t +dc_packet_set_break (dc_iostream_t *abstract, unsigned int value) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_set_break (packet->iostream, value); +} + +static dc_status_t +dc_packet_set_dtr (dc_iostream_t *abstract, unsigned int value) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_set_dtr (packet->iostream, value); +} + +static dc_status_t +dc_packet_set_rts (dc_iostream_t *abstract, unsigned int value) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_set_rts (packet->iostream, value); +} + +static dc_status_t +dc_packet_get_lines (dc_iostream_t *abstract, unsigned int *value) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_get_lines (packet->iostream, value); +} + +static dc_status_t +dc_packet_get_available (dc_iostream_t *abstract, size_t *value) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + if (packet->isize && packet->available) { + if (value) + *value = packet->available; + return DC_STATUS_SUCCESS; + } + + return dc_iostream_get_available (packet->iostream, value); +} + +static dc_status_t +dc_packet_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_configure (packet->iostream, baudrate, databits, parity, stopbits, flowcontrol); +} + +static dc_status_t +dc_packet_poll (dc_iostream_t *abstract, int timeout) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + if (packet->isize && packet->available) + return DC_STATUS_SUCCESS; + + return dc_iostream_poll (packet->iostream, timeout); +} + +static dc_status_t +dc_packet_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_packet_t *packet = (dc_packet_t *) abstract; + size_t nbytes = 0; + + while (nbytes < size) { + // Get the remaining size. + size_t length = size - nbytes; + + if (packet->isize) { + if (packet->available == 0) { + // Read a packet into the cache. + size_t len = 0; + status = dc_iostream_read (packet->iostream, packet->cache, packet->isize, &len); + if (status != DC_STATUS_SUCCESS) + break; + + packet->available = len; + packet->offset = 0; + } + + // Limit to the maximum packet size. + if (length > packet->available) + length = packet->available; + + // Copy the data from the cached packet. + memcpy ((unsigned char *) data + nbytes, packet->cache + packet->offset, length); + packet->available -= length; + packet->offset += length; + } else { + // Read the packet. + status = dc_iostream_read (packet->iostream, (unsigned char *) data + nbytes, length, &length); + if (status != DC_STATUS_SUCCESS) + break; + } + + // Update the total number of bytes. + nbytes += length; + } + + if (actual) + *actual = nbytes; + + return status; +} + +static dc_status_t +dc_packet_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_packet_t *packet = (dc_packet_t *) abstract; + size_t nbytes = 0; + + while (nbytes < size) { + // Get the remaining size. + size_t length = size - nbytes; + + // Limit to the maximum packet size. + if (packet->osize) { + if (length > packet->osize) + length = packet->osize; + } + + // Write the packet. + status = dc_iostream_write (packet->iostream, (const unsigned char *) data + nbytes, length, &length); + if (status != DC_STATUS_SUCCESS) + break; + + // Update the total number of bytes. + nbytes += length; + } + + if (actual) + *actual = nbytes; + + return status; +} + +static dc_status_t +dc_packet_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_ioctl (packet->iostream, request, data, size); +} + +static dc_status_t +dc_packet_flush (dc_iostream_t *abstract) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_flush (packet->iostream); +} + +static dc_status_t +dc_packet_purge (dc_iostream_t *abstract, dc_direction_t direction) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + if (direction & DC_DIRECTION_INPUT) { + packet->available = 0; + packet->offset = 0; + } + + return dc_iostream_purge (packet->iostream, direction); +} + +static dc_status_t +dc_packet_sleep (dc_iostream_t *abstract, unsigned int milliseconds) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_sleep (packet->iostream, milliseconds); +} + +static dc_status_t +dc_packet_close (dc_iostream_t *abstract) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + free (packet->cache); + + return DC_STATUS_SUCCESS; +} diff --git a/src/packet.h b/src/packet.h new file mode 100644 index 0000000..b420f08 --- /dev/null +++ b/src/packet.h @@ -0,0 +1,54 @@ +/* + * libdivecomputer + * + * Copyright (C) 2023 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_PACKET_H +#define DC_PACKET_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Create a packet I/O stream layered on top of another base I/O stream. + * + * This layered I/O allows reading and writing a byte stream from the + * underlying packet oriented transport. It changes the packet oriented + * base transport into a stream oriented transport. + * + * @param[out] iostream A location to store the packet I/O stream. + * @param[in] context A valid context. + * @param[in] base A valid I/O stream. + * @param[in] isize The input packet size in bytes. + * @param[in] osize The output packet size in bytes. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_packet_open (dc_iostream_t **iostream, dc_context_t *context, dc_iostream_t *base, size_t isize, size_t osize); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_PACKET_H */