diff --git a/include/libdivecomputer/Makefile.am b/include/libdivecomputer/Makefile.am index e972153..bffc849 100644 --- a/include/libdivecomputer/Makefile.am +++ b/include/libdivecomputer/Makefile.am @@ -7,8 +7,10 @@ libdivecomputer_HEADERS = \ descriptor.h \ iterator.h \ iostream.h \ + ioctl.h \ serial.h \ bluetooth.h \ + ble.h \ irda.h \ usbhid.h \ custom.h \ diff --git a/include/libdivecomputer/ble.h b/include/libdivecomputer/ble.h new file mode 100644 index 0000000..1452873 --- /dev/null +++ b/include/libdivecomputer/ble.h @@ -0,0 +1,39 @@ +/* + * libdivecomputer + * + * Copyright (C) 2019 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_BLE_H +#define DC_BLE_H + +#include "ioctl.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Get the remote device name. + */ +#define DC_IOCTL_BLE_GET_NAME DC_IOCTL_IOR('b', 0, DC_IOCTL_SIZE_VARIABLE) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_BLE_H */ diff --git a/include/libdivecomputer/custom.h b/include/libdivecomputer/custom.h index bccc882..dd50e21 100644 --- a/include/libdivecomputer/custom.h +++ b/include/libdivecomputer/custom.h @@ -32,20 +32,20 @@ extern "C" { 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_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 (*poll) (void *userdata, int timeout); 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 (*ioctl) (void *userdata, unsigned int request, void *data, size_t size); 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); - const char *(*get_name) (void *userdata); } dc_custom_cbs_t; /** diff --git a/include/libdivecomputer/ioctl.h b/include/libdivecomputer/ioctl.h new file mode 100644 index 0000000..dd7bc90 --- /dev/null +++ b/include/libdivecomputer/ioctl.h @@ -0,0 +1,73 @@ +/* + * libdivecomputer + * + * Copyright (C) 2019 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_IOCTL_H +#define DC_IOCTL_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Ioctl direction bits. + * + * Note: WRITE means the application is writing and the driver is + * reading. READ means the application is reading and the driver is + * writing. + */ +#define DC_IOCTL_DIR_NONE 0u +#define DC_IOCTL_DIR_READ 1u +#define DC_IOCTL_DIR_WRITE 2u + +/* + * Ioctl variable size bits. + */ +#define DC_IOCTL_SIZE_VARIABLE 0 + +/* + * Helper macro to encode ioctl numbers. + */ +#define DC_IOCTL_BASE(dir,type,nr,size) \ + (((dir) << 30) | \ + ((size) << 16) | \ + ((type) << 8) | \ + ((nr) << 0)) + +/* + * Macros to encode ioctl numbers. + */ +#define DC_IOCTL_IO(type,nr) DC_IOCTL_BASE(DC_IOCTL_DIR_NONE, (type), (nr), 0) +#define DC_IOCTL_IOR(type,nr,size) DC_IOCTL_BASE(DC_IOCTL_DIR_READ, (type), (nr), (size)) +#define DC_IOCTL_IOW(type,nr,size) DC_IOCTL_BASE(DC_IOCTL_DIR_WRITE, (type), (nr), (size)) +#define DC_IOCTL_IORW(type,nr,size) DC_IOCTL_BASE(DC_IOCTL_DIR_READ | DC_IOCTL_DIR_WRITE, (type), (nr), (size)) + +/* + * Macros to decode ioctl numbers. + */ +#define DC_IOCTL_DIR(request) (((request) >> 30) & 0x0003) +#define DC_IOCTL_SIZE(request) (((request) >> 16) & 0x3FFF) +#define DC_IOCTL_TYPE(request) (((request) >> 8) & 0x00FF) +#define DC_IOCTL_NR(request) (((request) >> 0) & 0x00FF) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_IOCTL_H */ diff --git a/include/libdivecomputer/iostream.h b/include/libdivecomputer/iostream.h index 3b25460..9dedce9 100644 --- a/include/libdivecomputer/iostream.h +++ b/include/libdivecomputer/iostream.h @@ -123,22 +123,6 @@ dc_iostream_get_transport (dc_iostream_t *iostream); 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 break condition. * @@ -211,6 +195,36 @@ dc_iostream_get_available (dc_iostream_t *iostream, size_t *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); +/** + * Poll the I/O stream for available data. + * + * There are three distinct modes available: + * + * 1. Blocking (timeout < 0): + * + * The poll operation is blocked until one or more bytes have been + * received. If no bytes are received, the operation will block + * forever. + * + * 2. Non-blocking (timeout == 0): + * + * The poll operation returns immediately, even if no bytes have + * been received. + * + * 3. Timeout (timeout > 0): + * + * The poll operation is blocked until one or more bytes have been + * received. If no bytes are received within the specified amount of + * time, the operation will return with a timeout. + * + * @param[in] iostream A valid I/O stream. + * @param[in] timeout The timeout in milliseconds. + * @returns #DC_STATUS_SUCCESS on success, #DC_STATUS_TIMEOUT on + * timeout, or another #dc_status_t code on failure. + */ +dc_status_t +dc_iostream_poll (dc_iostream_t *iostream, int timeout); + /** * Read data from the I/O stream. * @@ -239,6 +253,19 @@ dc_iostream_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actu dc_status_t dc_iostream_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual); +/** + * Perform an I/O stream specific request. + * + * @param[in] iostream A valid I/O stream. + * @param[in] request The request to perform. + * @param[in,out] data The request specific data. + * @param[in] size The size of the request specific data. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_iostream_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size); + /** * Flush the internal output buffer and wait until the data has been * transmitted. diff --git a/include/libdivecomputer/serial.h b/include/libdivecomputer/serial.h index f1596b3..917ba7c 100644 --- a/include/libdivecomputer/serial.h +++ b/include/libdivecomputer/serial.h @@ -27,6 +27,7 @@ #include "iostream.h" #include "iterator.h" #include "descriptor.h" +#include "ioctl.h" #ifdef __cplusplus extern "C" { @@ -77,6 +78,16 @@ dc_serial_iterator_new (dc_iterator_t **iterator, dc_context_t *context, dc_desc dc_status_t dc_serial_open (dc_iostream_t **iostream, dc_context_t *context, const char *name); +/** + * Set the receive latency in milliseconds. + * + * 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. + */ +#define DC_IOCTL_SERIAL_SET_LATENCY DC_IOCTL_IOW('s', 0, sizeof(unsigned int)) + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/bluetooth.c b/src/bluetooth.c index 4630523..282a31a 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -99,15 +99,16 @@ static const dc_iterator_vtable_t dc_bluetooth_iterator_vtable = { static const dc_iostream_vtable_t dc_bluetooth_vtable = { sizeof(dc_socket_t), dc_socket_set_timeout, /* set_timeout */ - NULL, /* set_latency */ NULL, /* set_break */ NULL, /* set_dtr */ NULL, /* set_rts */ NULL, /* get_lines */ dc_socket_get_available, /* get_available */ NULL, /* configure */ + dc_socket_poll, /* poll */ dc_socket_read, /* read */ dc_socket_write, /* write */ + dc_socket_ioctl, /* ioctl */ NULL, /* flush */ NULL, /* purge */ dc_socket_sleep, /* sleep */ diff --git a/src/custom.c b/src/custom.c index dc24347..688a8b3 100644 --- a/src/custom.c +++ b/src/custom.c @@ -28,20 +28,20 @@ #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_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_poll (dc_iostream_t *abstract, int timeout); 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_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size); 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); -static const char *dc_custom_get_name (dc_iostream_t *abstract); typedef struct dc_custom_t { /* Base class. */ @@ -54,20 +54,20 @@ typedef struct 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_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_available */ dc_custom_configure, /* configure */ + dc_custom_poll, /* poll */ dc_custom_read, /* read */ dc_custom_write, /* write */ + dc_custom_ioctl, /* ioctl */ dc_custom_flush, /* flush */ dc_custom_purge, /* purge */ dc_custom_sleep, /* sleep */ dc_custom_close, /* close */ - dc_custom_get_name, /* get_name */ }; dc_status_t @@ -106,17 +106,6 @@ dc_custom_set_timeout (dc_iostream_t *abstract, int timeout) 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_SUCCESS; - - return custom->callbacks.set_latency (custom->userdata, value); -} - static dc_status_t dc_custom_set_break (dc_iostream_t *abstract, unsigned int value) { @@ -183,6 +172,17 @@ dc_custom_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned in return custom->callbacks.configure (custom->userdata, baudrate, databits, parity, stopbits, flowcontrol); } +static dc_status_t +dc_custom_poll (dc_iostream_t *abstract, int timeout) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.poll == NULL) + return DC_STATUS_SUCCESS; + + return custom->callbacks.poll (custom->userdata, timeout); +} + static dc_status_t dc_custom_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual) { @@ -205,6 +205,17 @@ dc_custom_write (dc_iostream_t *abstract, const void *data, size_t size, size_t return custom->callbacks.write (custom->userdata, data, size, actual); } +static dc_status_t +dc_custom_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.ioctl == NULL) + return DC_STATUS_SUCCESS; + + return custom->callbacks.ioctl (custom->userdata, request, data, size); +} + static dc_status_t dc_custom_flush (dc_iostream_t *abstract) { @@ -248,14 +259,3 @@ dc_custom_close (dc_iostream_t *abstract) return custom->callbacks.close (custom->userdata); } - -static const char * -dc_custom_get_name (dc_iostream_t *abstract) -{ - dc_custom_t *custom = (dc_custom_t *) abstract; - - if (custom->callbacks.get_name == NULL) - return NULL; - - return custom->callbacks.get_name (custom->userdata); -} diff --git a/src/descriptor.c b/src/descriptor.c index 8338e8c..d542b57 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -48,6 +48,7 @@ static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata); static int dc_filter_garmin (dc_transport_t transport, const void *userdata); static int dc_filter_mares (dc_transport_t transport, const void *userdata); static int dc_filter_divesystem (dc_transport_t transport, const void *userdata); +static int dc_filter_oceanic (dc_transport_t transport, const void *userdata); static int dc_filter_deepblu (dc_transport_t transport, const void *userdata); static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item); @@ -227,7 +228,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Aeris", "A300CS", DC_FAMILY_OCEANIC_ATOM2, 0x454C, DC_TRANSPORT_SERIAL, NULL}, {"Tusa", "Talis", DC_FAMILY_OCEANIC_ATOM2, 0x454E, DC_TRANSPORT_SERIAL, NULL}, {"Beuchat", "Mundial 3", DC_FAMILY_OCEANIC_ATOM2, 0x4550, DC_TRANSPORT_SERIAL, NULL}, - {"Oceanic", "Pro Plus X", DC_FAMILY_OCEANIC_ATOM2, 0x4552, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL}, + {"Oceanic", "Pro Plus X", DC_FAMILY_OCEANIC_ATOM2, 0x4552, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, {"Oceanic", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x4553, DC_TRANSPORT_SERIAL, NULL}, {"Oceanic", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4554, DC_TRANSPORT_SERIAL, NULL}, {"Subgear", "XP-Air", DC_FAMILY_OCEANIC_ATOM2, 0x4555, DC_TRANSPORT_SERIAL, NULL}, @@ -238,13 +239,14 @@ static const dc_descriptor_t g_descriptors[] = { {"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646, DC_TRANSPORT_SERIAL, NULL}, - {"Aqualung", "i300C", DC_FAMILY_OCEANIC_ATOM2, 0x4648, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL}, - {"Aqualung", "i200C", DC_FAMILY_OCEANIC_ATOM2, 0x4649, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL}, + {"Aqualung", "i300C", DC_FAMILY_OCEANIC_ATOM2, 0x4648, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, + {"Aqualung", "i200C", DC_FAMILY_OCEANIC_ATOM2, 0x4649, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, {"Aqualung", "i100", DC_FAMILY_OCEANIC_ATOM2, 0x464E, DC_TRANSPORT_SERIAL, NULL}, - {"Aqualung", "i770R", DC_FAMILY_OCEANIC_ATOM2, 0x4651, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL}, - {"Aqualung", "i550C", DC_FAMILY_OCEANIC_ATOM2, 0x4652, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL}, - {"Oceanic", "Geo 4.0", DC_FAMILY_OCEANIC_ATOM2, 0x4653, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL}, - {"Oceanic", "Pro Plus 4", DC_FAMILY_OCEANIC_ATOM2, 0x4656, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL}, + {"Aqualung", "i770R", DC_FAMILY_OCEANIC_ATOM2, 0x4651, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, + {"Aqualung", "i550C", DC_FAMILY_OCEANIC_ATOM2, 0x4652, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, + {"Oceanic", "Geo 4.0", DC_FAMILY_OCEANIC_ATOM2, 0x4653, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, + {"Oceanic", "Veo 4.0", DC_FAMILY_OCEANIC_ATOM2, 0x4654, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, + {"Oceanic", "Pro Plus 4", DC_FAMILY_OCEANIC_ATOM2, 0x4656, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, /* Mares Nemo */ {"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL}, {"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL}, @@ -439,6 +441,20 @@ dc_match_number_with_prefix (const void *key, const void *value) return 1; } +static int +dc_match_oceanic (const void *key, const void *value) +{ + unsigned int model = *(const unsigned int *) value; + + const char prefix[] = { + (model >> 8) & 0xFF, + (model ) & 0xFF, + 0 + }; + + return dc_match_number_with_prefix (key, &prefix); +} + static int dc_filter_internal (const void *key, const void *values, size_t count, size_t size, dc_match_t match) { @@ -608,6 +624,26 @@ static int dc_filter_divesystem (dc_transport_t transport, const void *userdata) return 1; } +static int dc_filter_oceanic (dc_transport_t transport, const void *userdata) +{ + static const unsigned int model[] = { + 0x4552, // Oceanic Pro Plus X + 0x4648, // Aqualung i300C + 0x4649, // Aqualung i200C + 0x4651, // Aqualung i770R + 0x4652, // Aqualung i550C + 0x4653, // Oceanic Geo 4.0 + 0x4654, // Oceanic Veo 4.0 + 0x4656, // Oceanic Pro Plus 4 + }; + + if (transport == DC_TRANSPORT_BLE) { + return DC_FILTER_INTERNAL (userdata, model, 0, dc_match_oceanic); + } + + return 1; +} + static int dc_filter_deepblu (dc_transport_t transport, const void *userdata) { static const char * const bluetooth[] = { diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index 0521e2e..58e18ea 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -326,15 +326,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device, } if (delay && device->available == 0) { - unsigned int count = delay / 100; - for (unsigned int i = 0; i < count; ++i) { - size_t available = 0; - status = dc_iostream_get_available (device->iostream, &available); - if (status == DC_STATUS_SUCCESS && available > 0) - break; - - dc_iostream_sleep (device->iostream, 100); - } + dc_iostream_poll (device->iostream, delay); } if (cmd != EXIT) { diff --git a/src/iostream-private.h b/src/iostream-private.h index 42c5d5f..ee9f267 100644 --- a/src/iostream-private.h +++ b/src/iostream-private.h @@ -43,8 +43,6 @@ struct dc_iostream_vtable_t { 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_break) (dc_iostream_t *iostream, unsigned int value); dc_status_t (*set_dtr) (dc_iostream_t *iostream, unsigned int value); @@ -57,10 +55,14 @@ struct dc_iostream_vtable_t { 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 (*poll) (dc_iostream_t *iostream, int timeout); + 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 (*ioctl) (dc_iostream_t *iostream, unsigned int request, void *data, size_t size); + dc_status_t (*flush) (dc_iostream_t *iostream); dc_status_t (*purge) (dc_iostream_t *iostream, dc_direction_t direction); @@ -68,8 +70,6 @@ struct dc_iostream_vtable_t { dc_status_t (*sleep) (dc_iostream_t *iostream, unsigned int milliseconds); dc_status_t (*close) (dc_iostream_t *iostream); - - const char *(*get_name) (dc_iostream_t *iostream); }; dc_iostream_t * diff --git a/src/iostream.c b/src/iostream.c index 9588f0e..7505495 100644 --- a/src/iostream.c +++ b/src/iostream.c @@ -23,6 +23,8 @@ #include #include +#include + #include "iostream-private.h" #include "context-private.h" #include "platform.h" @@ -85,17 +87,6 @@ dc_iostream_set_timeout (dc_iostream_t *iostream, int 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_SUCCESS; - - INFO (iostream->context, "Latency: value=%i", value); - - return iostream->vtable->set_latency (iostream, value); -} - dc_status_t dc_iostream_set_break (dc_iostream_t *iostream, unsigned int value) { @@ -183,6 +174,17 @@ dc_iostream_configure (dc_iostream_t *iostream, unsigned int baudrate, unsigned return iostream->vtable->configure (iostream, baudrate, databits, parity, stopbits, flowcontrol); } +dc_status_t +dc_iostream_poll (dc_iostream_t *iostream, int timeout) +{ + if (iostream == NULL || iostream->vtable->poll == NULL) + return DC_STATUS_SUCCESS; + + INFO (iostream->context, "Poll: value=%i", timeout); + + return iostream->vtable->poll (iostream, timeout); +} + dc_status_t dc_iostream_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual) { @@ -256,6 +258,40 @@ dc_iostream_write (dc_iostream_t *iostream, const void *data, size_t size, size_ return DC_STATUS_SUCCESS; } +dc_status_t +dc_iostream_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size) +{ + dc_status_t status = DC_STATUS_SUCCESS; + + if (iostream == NULL || iostream->vtable->ioctl == NULL) + return DC_STATUS_SUCCESS; + + // The size should match the size encoded in the ioctl request, + // unless it's a variable size request. + if (size != DC_IOCTL_SIZE(request) && + !(DC_IOCTL_DIR(request) != DC_IOCTL_DIR_NONE && DC_IOCTL_SIZE(request) == 0)) { + ERROR (iostream->context, "Invalid size for ioctl request 0x%08x (" DC_PRINTF_SIZE ").", request, size); + return DC_STATUS_INVALIDARGS; + } + + INFO (iostream->context, "Ioctl: request=0x%08x (dir=%u, type=%u, nr=%u, size=%u)", + request, + DC_IOCTL_DIR(request), DC_IOCTL_TYPE(request), + DC_IOCTL_NR(request), DC_IOCTL_SIZE(request)); + + if (DC_IOCTL_DIR(request) & DC_IOCTL_DIR_WRITE) { + HEXDUMP (iostream->context, DC_LOGLEVEL_INFO, "Ioctl write", (unsigned char *) data, size); + } + + status = iostream->vtable->ioctl (iostream, request, data, size); + + if (DC_IOCTL_DIR(request) & DC_IOCTL_DIR_READ) { + HEXDUMP (iostream->context, DC_LOGLEVEL_INFO, "Ioctl read", (unsigned char *) data, size); + } + + return status; +} + dc_status_t dc_iostream_flush (dc_iostream_t *iostream) { @@ -305,15 +341,3 @@ dc_iostream_close (dc_iostream_t *iostream) return status; } - -const char * -dc_iostream_get_name (dc_iostream_t *iostream) -{ - if (iostream == NULL) - return NULL; - - if (iostream->vtable->get_name) - return iostream->vtable->get_name (iostream); - - return NULL; -} diff --git a/src/irda.c b/src/irda.c index a78ff31..214e39f 100644 --- a/src/irda.c +++ b/src/irda.c @@ -91,15 +91,16 @@ static const dc_iterator_vtable_t dc_irda_iterator_vtable = { static const dc_iostream_vtable_t dc_irda_vtable = { sizeof(dc_socket_t), dc_socket_set_timeout, /* set_timeout */ - NULL, /* set_latency */ NULL, /* set_break */ NULL, /* set_dtr */ NULL, /* set_rts */ NULL, /* get_lines */ dc_socket_get_available, /* get_available */ NULL, /* configure */ + dc_socket_poll, /* poll */ dc_socket_read, /* read */ dc_socket_write, /* write */ + dc_socket_ioctl, /* ioctl */ NULL, /* flush */ NULL, /* purge */ dc_socket_sleep, /* sleep */ diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index bb553a0..96fe6fe 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -36,15 +36,16 @@ dc_descriptor_get_transports dc_iostream_get_transport dc_iostream_set_timeout -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_poll dc_iostream_read dc_iostream_write +dc_iostream_ioctl dc_iostream_flush dc_iostream_purge dc_iostream_sleep diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 222c5a9..4e3bb87 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -330,6 +330,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device, const unsigned char comman return rc; // Discard any garbage bytes. + dc_iostream_sleep (device->iostream, 100); dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT); device->available = 0; device->offset = 0; @@ -495,7 +496,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ goto error_free; } - // Set the timeout for receiving data (3s = 3000 ms, the BLE dongle can be slow). + // Set the timeout for receiving data (3000 ms). status = dc_iostream_set_timeout (device->iostream, 3000); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to set the timeout."); diff --git a/src/mares_nemo.c b/src/mares_nemo.c index 7529704..f5e22a3 100644 --- a/src/mares_nemo.c +++ b/src/mares_nemo.c @@ -177,13 +177,11 @@ mares_nemo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Wait until some data arrives. - size_t available = 0; - while (dc_iostream_get_available (device->iostream, &available) == DC_STATUS_SUCCESS && available == 0) { + while (dc_iostream_poll (device->iostream, 100) == DC_STATUS_TIMEOUT) { if (device_is_cancelled (abstract)) return DC_STATUS_CANCELLED; device_event_emit (abstract, DC_EVENT_WAITING, NULL); - dc_iostream_sleep (device->iostream, 100); } // Receive the header of the package. diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index f36f27d..f29d001 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -22,6 +22,8 @@ #include // memcpy #include // malloc, free +#include + #include "oceanic_atom2.h" #include "oceanic_common.h" #include "context-private.h" @@ -29,6 +31,7 @@ #include "array.h" #include "ringbuffer.h" #include "checksum.h" +#include "platform.h" #define ISINSTANCE(device) dc_device_isinstance((device), &oceanic_atom2_device_vtable.base) @@ -38,12 +41,14 @@ #define I770R 0x4651 #define GEO40 0x4653 +#define MAXPACKET 256 #define MAXRETRIES 2 #define MAXDELAY 16 #define INVALID 0xFFFFFFFF #define CMD_INIT 0xA8 #define CMD_VERSION 0x84 +#define CMD_HANDSHAKE 0xE5 #define CMD_READ1 0xB1 #define CMD_READ8 0xB4 #define CMD_READ16 0xB8 @@ -129,6 +134,7 @@ static const oceanic_common_version_t oceanic_atom2b_version[] = { {"AQUAI100 \0\0 512K"}, {"AQUA300C \0\0 512K"}, {"OCEGEO40 \0\0 512K"}, + {"VEOSMART \0\0 512K"}, }; static const oceanic_common_version_t oceanic_atom2c_version[] = { @@ -200,34 +206,20 @@ static const oceanic_common_version_t oceanic_reactpro_version[] = { {"REACPRO2 \0\0 512K"}, }; -// Like the i770R, there's some extended pattern for the last -// four digits. The serial communication apparently says "2048" -// for this, but the BLE version says "0001". -// -// The middle two digits are the FW version or something, static const oceanic_common_version_t oceanic_proplusx_version[] = { {"OCEANOCX \0\0 \0\0\0\0"}, }; +static const oceanic_common_version_t aqualung_i770r_version[] = { + {"AQUA770R \0\0 \0\0\0\0"}, +}; + static const oceanic_common_version_t aeris_a300cs_version[] = { {"AER300CS \0\0 2048"}, {"OCEANVTX \0\0 2048"}, {"AQUAI750 \0\0 2048"}, }; -// Not 100% sure what the pattern is. -// I've seen: -// -// "AQUA770R 1A 0001" -// "AQUA770R 1A 0090" -// -// from the same dive computer. On other ones, it's -// apparently the two middle digits that change, on -// the i770R it might be all of them. -static const oceanic_common_version_t aqualung_i770r_version[] = { - {"AQUA770R \0\0 \0\0\0\0"}, -}; - static const oceanic_common_version_t aqualung_i450t_version[] = { {"AQUAI450 \0\0 2048"}, }; @@ -547,11 +539,144 @@ static const oceanic_common_layout_t aqualung_i450t_layout = { 0, /* pt_mode_serial */ }; +/* + * The BLE GATT packet size is up to 20 bytes and the format is: + * + * byte 0: <0xCD> + * Seems to always have this value. Don't ask what it means + * byte 1: + * d=0 means "command", d=1 means "reply from dive computer" + * 1 is always set, afaik + * c=0 means "last packet" in sequence, c=1 means "more packets coming" + * sssss is a 5-bit sequence number for packets + * byte 2: + * starts at 0 for the connection, incremented for each command + * byte 3: + * 1-16 bytes of data per packet. + * byte 4..n: + */ static dc_status_t -oceanic_atom2_packet (oceanic_atom2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int crc_size) +oceanic_atom2_ble_write (oceanic_atom2_device_t *device, const unsigned char data[], unsigned int size) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + unsigned char buf[20]; + unsigned char cmd_seq = device->sequence; + unsigned char pkt_seq = 0; + + unsigned int nbytes = 0; + while (nbytes < size) { + unsigned char status = 0x40; + unsigned int length = size - nbytes; + if (length > sizeof(buf) - 4) { + length = sizeof(buf) - 4; + status |= 0x20; + } + buf[0] = 0xcd; + buf[1] = status | (pkt_seq & 0x1F); + buf[2] = cmd_seq; + buf[3] = length; + memcpy (buf + 4, data, length); + + rc = dc_iostream_write (device->iostream, buf, 4 + length, NULL); + if (rc != DC_STATUS_SUCCESS) + return rc; + + nbytes += length; + pkt_seq++; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +oceanic_atom2_ble_read (oceanic_atom2_device_t *device, unsigned char data[], unsigned int size) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned char buf[20]; + unsigned char cmd_seq = device->sequence; + unsigned char pkt_seq = 0; + + unsigned int nbytes = 0; + while (1) { + size_t transferred = 0; + rc = dc_iostream_read (device->iostream, buf, sizeof(buf), &transferred); + if (rc != DC_STATUS_SUCCESS) + return rc; + + if (transferred < 4) { + ERROR (abstract->context, "Invalid packet size (" DC_PRINTF_SIZE ").", transferred); + return DC_STATUS_PROTOCOL; + } + + // Verify the start byte. + if (buf[0] != 0xcd) { + ERROR (abstract->context, "Unexpected packet start byte (%02x).", buf[0]); + return DC_STATUS_PROTOCOL; + } + + // Verify the status byte. + unsigned char status = buf[1]; + unsigned char expect = 0xc0 | (pkt_seq & 0x1F) | (status & 0x20); + if (status != expect) { + ERROR (abstract->context, "Unexpected packet status byte (%02x %02x).", status, expect); + return DC_STATUS_PROTOCOL; + } + + // Verify the sequence byte. + if (buf[2] != cmd_seq) { + ERROR (abstract->context, "Unexpected packet sequence byte (%02x %02x).", buf[2], cmd_seq); + return DC_STATUS_PROTOCOL; + } + + // Verify the length byte. + unsigned int length = buf[3]; + if (length + 4 > transferred) { + ERROR (abstract->context, "Invalid packet length (%u).", length); + return DC_STATUS_PROTOCOL; + } + + // Append the payload data to the output buffer. If the output + // buffer is too small, the error is not reported immediately + // but delayed until all packets have been received. + if (nbytes < size) { + unsigned int n = length; + if (nbytes + n > size) { + n = size - nbytes; + } + memcpy (data + nbytes, buf + 4, n); + } + nbytes += length; + pkt_seq++; + + // Last packet? + if ((status & 0x20) == 0) + break; + } + + // Verify the expected number of bytes. + if (nbytes != size) { + ERROR (abstract->context, "Unexpected number of bytes received (%u %u).", nbytes, size); + return DC_STATUS_PROTOCOL; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +oceanic_atom2_packet (oceanic_atom2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char ack, unsigned char answer[], unsigned int asize, unsigned int crc_size) { dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; + dc_transport_t transport = dc_iostream_get_transport (device->iostream); + + if (asize > MAXPACKET) { + return DC_STATUS_INVALIDARGS; + } + + if (crc_size > 2 || (crc_size != 0 && asize == 0)) { + return DC_STATUS_INVALIDARGS; + } if (device_is_cancelled (abstract)) return DC_STATUS_CANCELLED; @@ -561,61 +686,60 @@ oceanic_atom2_packet (oceanic_atom2_device_t *device, const unsigned char comman } // Send the command to the dive computer. - status = dc_iostream_write (device->iostream, command, csize, NULL); + if (transport == DC_TRANSPORT_BLE) { + status = oceanic_atom2_ble_write (device, command, csize); + } else { + status = dc_iostream_write (device->iostream, command, csize, NULL); + } if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to send the command."); return status; } - // Get the correct ACK byte. - unsigned int ack = ACK; - if (command[0] == CMD_INIT || command[0] == CMD_QUIT) { - ack = NAK; + // Receive the answer of the dive computer. + unsigned char packet[1 + MAXPACKET + 2]; + if (transport == DC_TRANSPORT_BLE) { + status = oceanic_atom2_ble_read (device, packet, 1 + asize + crc_size); + } else { + status = dc_iostream_read (device->iostream, packet, 1 + asize + crc_size, NULL); } - - // Receive the response (ACK/NAK) of the dive computer. - unsigned char response = 0; - status = dc_iostream_read (device->iostream, &response, 1, NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to receive the answer."); return status; } - // Verify the response of the dive computer. - if (response != ack) { + // Verify the ACK byte of the answer. + if (packet[0] != ack) { ERROR (abstract->context, "Unexpected answer start byte(s)."); return DC_STATUS_PROTOCOL; } if (asize) { - // Receive the answer of the dive computer. - status = dc_iostream_read (device->iostream, answer, asize, NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the answer."); - return status; - } - // Verify the checksum of the answer. unsigned short crc, ccrc; if (crc_size == 2) { - crc = array_uint16_le (answer + asize - 2); - ccrc = checksum_add_uint16 (answer, asize - 2, 0x0000); + crc = array_uint16_le (packet + 1 + asize); + ccrc = checksum_add_uint16 (packet + 1, asize, 0x0000); } else { - crc = answer[asize - 1]; - ccrc = checksum_add_uint8 (answer, asize - 1, 0x00); + crc = packet[1 + asize]; + ccrc = checksum_add_uint8 (packet + 1, asize, 0x00); } if (crc != ccrc) { ERROR (abstract->context, "Unexpected answer checksum."); return DC_STATUS_PROTOCOL; } + + memcpy (answer, packet + 1, asize); } + device->sequence++; + return DC_STATUS_SUCCESS; } static dc_status_t -oceanic_atom2_serial_transfer (oceanic_atom2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int crc_size) +oceanic_atom2_transfer (oceanic_atom2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char ack, unsigned char answer[], unsigned int asize, unsigned int crc_size) { // Send the command to the device. If the device responds with an // ACK byte, the command was received successfully and the answer @@ -625,7 +749,7 @@ oceanic_atom2_serial_transfer (oceanic_atom2_device_t *device, const unsigned ch unsigned int nretries = 0; dc_status_t rc = DC_STATUS_SUCCESS; - while ((rc = oceanic_atom2_packet (device, command, csize, answer, asize, crc_size)) != DC_STATUS_SUCCESS) { + while ((rc = oceanic_atom2_packet (device, command, csize, ack, answer, asize, crc_size)) != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_TIMEOUT && rc != DC_STATUS_PROTOCOL) return rc; @@ -646,213 +770,65 @@ oceanic_atom2_serial_transfer (oceanic_atom2_device_t *device, const unsigned ch } /* - * The BLE GATT packet size is up to 20 bytes and the format is: + * The BLE communication sends a handshake packet that seems + * to be a passphrase based on the BLE name of the device + * (more specifically the serial number encoded in the name). * - * byte 0: <0xCD> - * Seems to always have this value. Don't ask what it means - * byte 1: - * d=0 means "command", d=1 means "reply from dive computer" - * 1 is always set, afaik - * c=0 means "last packet" in sequence, c=1 means "more packets coming" - * sssss is a 5-bit sequence number for packets - * byte 2: - * starts at 0 for the connection, incremented for each command - * byte 3: - * 1-16 bytes of data per packet. - * byte 4..n: + * The packet format is: + * 0xe5 + * < 8 bytes of passphrase > + * one-byte checksum of the passphrase. */ static dc_status_t -oceanic_atom2_ble_write(oceanic_atom2_device_t *device, const unsigned char command[], unsigned int csize) +oceanic_atom2_ble_handshake(oceanic_atom2_device_t *device) { - unsigned char buf[20]; - unsigned char cmd_seq = device->sequence; - unsigned char pkt_seq; + dc_status_t rc = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; - pkt_seq = 0; - while (csize) { - dc_status_t ret; - unsigned char status = 0x40; - unsigned int cpartial = csize; - if (cpartial > 16) { - cpartial = 16; - status |= 0x20; - } - buf[0] = 0xcd; - buf[1] = status | (pkt_seq & 31); - buf[2] = cmd_seq; - buf[3] = cpartial; - memcpy(buf+4, command, cpartial); - command += cpartial; - csize -= cpartial; - ret = dc_iostream_write(device->iostream, buf, 4+cpartial, NULL); - if (ret != DC_STATUS_SUCCESS) - return ret; - pkt_seq++; - } - return DC_STATUS_SUCCESS; -} - -static dc_status_t -oceanic_atom2_ble_read(oceanic_atom2_device_t *device, unsigned char **result_p, unsigned int *size_p) -{ - unsigned char *result = NULL; - unsigned int size = 0, allocated = 0; - unsigned char buf[20]; - unsigned char cmd_seq = device->sequence; - unsigned char pkt_seq; - dc_status_t ret = DC_STATUS_SUCCESS; - - pkt_seq = 0; - for (;;) { - unsigned char status, expect; - size_t transferred = 0; - ret = dc_iostream_read(device->iostream, buf, sizeof(buf), &transferred); - if (ret != DC_STATUS_SUCCESS) - break; - - ret = DC_STATUS_IO; - if (transferred < 5 || transferred > 20) { - ERROR(device->base.base.context, "Odd BLE packet size %zd", transferred); - break; - } - if (buf[0] != 0xcd) - ERROR(device->base.base.context, "Odd first byte (got '%02x', expected 'cd'", buf[0]); - - // Verify status byte - expect = 0xc0; - expect |= (pkt_seq & 31); - status = buf[1]; - if ((status & ~0x20) != expect) - ERROR(device->base.base.context, "Odd status byte (got '%02x', expected '%02x'", buf[1], expect); - - // Verify command sequence byte - expect = cmd_seq; - if (buf[2] != expect) - ERROR(device->base.base.context, "Odd cmd sequence byte (got '%02x', expected '%02x'", buf[2], expect); - - // Verify length byte - expect = buf[3]; - if (expect < 1 || expect > 16) { - ERROR(device->base.base.context, "Odd reply size byte (got %d, expected 1..16", buf[3]); - break; - } - - if (transferred < 4+expect) { - ERROR(device->base.base.context, "Packet too small (got %zd bytes, expected at least %d bytes)", transferred, 4+expect); - break; - } - - if (size + expect > allocated) { - unsigned int newsize = size + expect + 100; - unsigned char *newalloc = realloc(result, newsize); - if (!newalloc) { - ret = DC_STATUS_NOMEMORY; - break; - } - result = newalloc; - allocated = newsize; - } - - memcpy(result + size, buf+4, expect); - size += expect; - pkt_seq++; - - /* More packets? */ - if (status & 0x20) - continue; - - ret = DC_STATUS_SUCCESS; - break; - } - - if (ret != DC_STATUS_SUCCESS) { - free(result); - size = 0; - result = NULL; - } - *result_p = result; - *size_p = size; - return ret; -} - -/* - * Transfer a command and optionally read return data. - * - * NOTE! The NUL byte at the end of a command is a serial transfer thing, - * and we remove it. The correct thing to do would be to add it on the - * serial transfer side instead (or perhaps not send it at all, Jef says - * it may be historical), but right now I've tried to minimize the changes - * that the BLE transfer code made to the code, so instead this tries to - * just skip the extraneous byte. - */ -static dc_status_t -oceanic_atom2_ble_transfer (oceanic_atom2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int crc_size) -{ - unsigned char buf[20]; - unsigned char cmd_seq = device->sequence; - unsigned char pkt_seq; - dc_status_t ret = DC_STATUS_SUCCESS; - int retry = 3; - - /* - * The serial commands have a NUL byte at the end. It's bogus. - * It should be added on the serial transfer side, not removed - * here. - */ - if (csize > 1 && csize < 8 && !command[csize-1]) - csize--; - -retry: - if (--retry < 0) - return ret; - - ret = oceanic_atom2_ble_write(device, command, csize); - if (ret != DC_STATUS_SUCCESS) - return ret; - - pkt_seq = 0; - if (answer) { - unsigned char *buf; - unsigned int size; - ret = oceanic_atom2_ble_read(device, &buf, &size); - if (ret != DC_STATUS_SUCCESS) - goto retry; - if (size > asize && buf[0] == ACK) { - memcpy(answer, buf+1, asize); - device->sequence++; + // Retrieve the bluetooth device name. + // The format of the name is something like 'FQ001124', where the + // two first letters are the ASCII representation of the model + // number (e.g. 'FQ' or 0x4651 for the i770R), and the six digits + // are the serial number. + char name[8 + 1] = {0}; + rc = dc_iostream_ioctl (device->iostream, DC_IOCTL_BLE_GET_NAME, name, sizeof(name)); + if (rc != DC_STATUS_SUCCESS) { + if (rc == DC_STATUS_UNSUPPORTED) { + // Allow skipping the handshake if no name. But the download + // will likely fail. + WARNING (abstract->context, "Bluetooth device name unavailable."); + return DC_STATUS_SUCCESS; } else { - ERROR(device->base.base.context, "Result too small: got %d bytes, expected at least %d bytes", size, asize+1); - ret = DC_STATUS_IO; - goto retry; + return rc; } - free(buf); } - return ret; -} + // Force a null terminated string. + name[sizeof(name) - 1] = 0; -static dc_status_t -oceanic_atom2_transfer (oceanic_atom2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int crc_size) -{ - if (dc_iostream_get_transport(device->iostream) == DC_TRANSPORT_BLE) - return oceanic_atom2_ble_transfer(device, command, csize, answer, asize, crc_size); + // Check the minimum length. + if (strlen (name) < 8) { + ERROR (abstract->context, "Bluetooth device name too short."); + return DC_STATUS_IO; + } - return oceanic_atom2_serial_transfer(device, command, csize, answer, asize, crc_size); -} + // Turn ASCII numbers into just raw byte values. + unsigned char handshake[10] = {CMD_HANDSHAKE}; + for (unsigned int i = 0; i < 6; i++) { + handshake[i + 1] = name[i + 2] - '0'; + } + + // Add simple checksum. + handshake[9] = checksum_add_uint8 (handshake + 1, 8, 0x00); -static dc_status_t -oceanic_atom2_quit (oceanic_atom2_device_t *device) -{ // Send the command to the dive computer. - unsigned char command[4] = {CMD_QUIT, 0x05, 0xA5, 0x00}; - dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), NULL, 0, 0); + rc = oceanic_atom2_transfer (device, handshake, sizeof(handshake), ACK, NULL, 0, 0); if (rc != DC_STATUS_SUCCESS) return rc; return DC_STATUS_SUCCESS; } - dc_status_t oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model) { @@ -943,6 +919,13 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream goto error_free; } + if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) { + status = oceanic_atom2_ble_handshake(device); + if (status != DC_STATUS_SUCCESS) { + goto error_free; + } + } + // Override the base class values. if (OCEANIC_COMMON_MATCH (device->base.version, aeris_f10_version)) { device->base.layout = &aeris_f10_layout; @@ -1036,7 +1019,8 @@ oceanic_atom2_device_close (dc_device_t *abstract) dc_status_t rc = DC_STATUS_SUCCESS; // Send the quit command. - rc = oceanic_atom2_quit (device); + unsigned char command[4] = {CMD_QUIT, 0x05, 0xA5}; + rc = oceanic_atom2_transfer (device, command, sizeof (command), NAK, NULL, 0, 0); if (rc != DC_STATUS_SUCCESS) { dc_status_set_error(&status, rc); } @@ -1054,60 +1038,14 @@ oceanic_atom2_device_keepalive (dc_device_t *abstract) return DC_STATUS_INVALIDARGS; // Send the command to the dive computer. - unsigned char command[4] = {CMD_KEEPALIVE, 0x05, 0xA5, 0x00}; - dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), NULL, 0, 0); + unsigned char command[] = {CMD_KEEPALIVE, 0x05, 0xA5}; + dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), ACK, NULL, 0, 0); if (rc != DC_STATUS_SUCCESS) return rc; - /* No answer: increment sequence number manually */ - device->sequence++; return DC_STATUS_SUCCESS; } -/* - * The BLE communication sends a handshake packet that seems - * to be a passphrase based on the BLE name of the device - * (more specifically the serial number encoded in the name). - * - * The packet format is: - * 0xe5 - * < 8 bytes of passphrase > - * one-byte checksum of the passphrase. - */ -static dc_status_t -oceanic_atom2_send_ble_handshake(oceanic_atom2_device_t *device) -{ - unsigned char handshake[10] = { 0xe5, }, ack[1]; - const char *bt_name = dc_iostream_get_name(device->iostream); - - /* - * Allow skipping the handshake if no name. But the download will - * likely fail. - * - * The format of the name is something like 'FQ001124', where the - * two first letters indicate the kind of device it is, and the - * six digits are the serial number. - * - * Jef theorizes that 'FQ' in hexadecimal is 0x4651, which is - * the model number of the i770R. - */ - if (!bt_name || strlen(bt_name) < 8) - return DC_STATUS_SUCCESS; - - /* Turn ASCII numbers into just raw byte values */ - for (int i = 0; i < 6; i++) - handshake[i+1] = bt_name[i+2] - '0'; - - /* Add simple checksum */ - handshake[9] = checksum_add_uint8(handshake+1, 8, 0x00); - - /* - * .. and send it off. - * - * NOTE! We don't expect any data back, but we do want the ACK. - */ - return oceanic_atom2_ble_transfer(device, handshake, sizeof(handshake), ack, 0, 0); -} dc_status_t oceanic_atom2_device_version (dc_device_t *abstract, unsigned char data[], unsigned int size) @@ -1120,18 +1058,12 @@ oceanic_atom2_device_version (dc_device_t *abstract, unsigned char data[], unsig if (size < PAGESIZE) return DC_STATUS_INVALIDARGS; - unsigned char answer[PAGESIZE + 1] = {0}; - unsigned char command[2] = {CMD_VERSION, 0x00}; - dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), answer, sizeof (answer), 1); + unsigned char command[] = {CMD_VERSION}; + dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), ACK, data, PAGESIZE, 1); if (rc != DC_STATUS_SUCCESS) return rc; - memcpy (data, answer, PAGESIZE); - - if (dc_iostream_get_transport(device->iostream) == DC_TRANSPORT_BLE) - rc = oceanic_atom2_send_ble_handshake(device); - - return rc; + return DC_STATUS_SUCCESS; } @@ -1188,17 +1120,15 @@ oceanic_atom2_device_read (dc_device_t *abstract, unsigned int address, unsigned if (page != device->cached_page || highmem != device->cached_highmem) { // Read the package. unsigned int number = highmem ? page : page * device->bigpage; // This is always PAGESIZE, even in big page mode. - unsigned char answer[256 + 2] = {0}; // Maximum we support for the known commands. - unsigned char command[4] = {read_cmd, + unsigned char command[] = {read_cmd, (number >> 8) & 0xFF, // high (number ) & 0xFF, // low - 0}; - dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), answer, pagesize + crc_size, crc_size); + }; + dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), ACK, device->cache, pagesize, crc_size); if (rc != DC_STATUS_SUCCESS) return rc; // Cache the page. - memcpy (device->cache, answer, pagesize); device->cached_page = page; device->cached_highmem = highmem; } @@ -1236,25 +1166,22 @@ oceanic_atom2_device_write (dc_device_t *abstract, unsigned int address, const u while (nbytes < size) { // Prepare to write the package. unsigned int number = address / PAGESIZE; - unsigned char prepare[4] = {CMD_WRITE, + unsigned char prepare[] = {CMD_WRITE, (number >> 8) & 0xFF, // high (number ) & 0xFF, // low - 0x00}; - dc_status_t rc = oceanic_atom2_transfer (device, prepare, sizeof (prepare), NULL, 0, 0); + }; + dc_status_t rc = oceanic_atom2_transfer (device, prepare, sizeof (prepare), ACK, NULL, 0, 0); if (rc != DC_STATUS_SUCCESS) return rc; // Write the package. - unsigned char command[PAGESIZE + 2] = {0}; + unsigned char command[PAGESIZE + 1] = {0}; memcpy (command, data, PAGESIZE); command[PAGESIZE] = checksum_add_uint8 (command, PAGESIZE, 0x00); - rc = oceanic_atom2_transfer (device, command, sizeof (command), NULL, 0, 0); + rc = oceanic_atom2_transfer (device, command, sizeof (command), ACK, NULL, 0, 0); if (rc != DC_STATUS_SUCCESS) return rc; - /* No answer, increment sequence number manually */ - device->sequence++; - nbytes += PAGESIZE; address += PAGESIZE; data += PAGESIZE; diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index 6ca5e1b..2477883 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -95,6 +95,7 @@ #define I770R 0x4651 #define I550C 0x4652 #define GEO40 0x4653 +#define VEO40 0x4654 #define PROPLUS4 0x4656 #define NORMAL 0 @@ -169,7 +170,8 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned model == INSIGHT2 || model == ZEN || model == I300 || model == I550 || model == I200 || model == I200C || - model == I300C || model == GEO40) { + model == I300C || model == GEO40 || + model == VEO40) { parser->headersize -= PAGESIZE; } else if (model == VT4 || model == VT41) { parser->headersize += PAGESIZE; @@ -294,6 +296,7 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim case I100: case I300C: case GEO40: + case VEO40: case PROPLUS4: datetime->year = ((p[3] & 0xE0) >> 1) + (p[4] & 0x0F) + 2000; datetime->month = (p[4] & 0xF0) >> 4; @@ -746,7 +749,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == MANTA || parser->model == I300 || parser->model == I200 || parser->model == I100 || parser->model == I300C || parser->model == TALIS || - parser->model == I200C || parser->model == GEO40) { + parser->model == I200C || parser->model == GEO40 || + parser->model == VEO40) { have_pressure = 0; } @@ -905,7 +909,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == I450T || parser->model == I300 || parser->model == I200 || parser->model == I100 || parser->model == I300C || parser->model == I200C || - parser->model == GEO40) { + parser->model == GEO40 || parser->model == VEO40) { temperature = data[offset + 3]; } else if (parser->model == OCS || parser->model == TX1) { temperature = data[offset + 1]; @@ -979,7 +983,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == I450T || parser->model == I300 || parser->model == I200 || parser->model == I100 || parser->model == I300C || parser->model == I200C || - parser->model == GEO40) + parser->model == GEO40 || parser->model == VEO40) depth = (data[offset + 4] + (data[offset + 5] << 8)) & 0x0FFF; else if (parser->model == ATOM1) depth = data[offset + 3] * 16; @@ -1033,7 +1037,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == OC1C || parser->model == OCI || parser->model == I100 || parser->model == I300C || parser->model == I450T || parser->model == I200C || - parser->model == GEO40) { + parser->model == GEO40 || parser->model == VEO40) { decostop = (data[offset + 7] & 0xF0) >> 4; decotime = array_uint16_le(data + offset + 6) & 0x0FFF; have_deco = 1; diff --git a/src/serial_posix.c b/src/serial_posix.c index 46ec3de..897b428 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -67,15 +67,16 @@ static dc_status_t dc_serial_iterator_next (dc_iterator_t *iterator, void *item) static dc_status_t dc_serial_iterator_free (dc_iterator_t *iterator); static dc_status_t dc_serial_set_timeout (dc_iostream_t *iostream, int timeout); -static dc_status_t dc_serial_set_latency (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_set_break (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_set_dtr (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_set_rts (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_get_lines (dc_iostream_t *iostream, unsigned int *value); static dc_status_t dc_serial_get_available (dc_iostream_t *iostream, size_t *value); static dc_status_t dc_serial_configure (dc_iostream_t *iostream, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol); +static dc_status_t dc_serial_poll (dc_iostream_t *iostream, int timeout); static dc_status_t dc_serial_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual); static dc_status_t dc_serial_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual); +static dc_status_t dc_serial_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size); static dc_status_t dc_serial_flush (dc_iostream_t *iostream); static dc_status_t dc_serial_purge (dc_iostream_t *iostream, dc_direction_t direction); static dc_status_t dc_serial_sleep (dc_iostream_t *iostream, unsigned int milliseconds); @@ -116,15 +117,16 @@ static const dc_iterator_vtable_t dc_serial_iterator_vtable = { static const dc_iostream_vtable_t dc_serial_vtable = { sizeof(dc_serial_t), dc_serial_set_timeout, /* set_timeout */ - dc_serial_set_latency, /* set_latency */ dc_serial_set_break, /* set_break */ dc_serial_set_dtr, /* set_dtr */ dc_serial_set_rts, /* set_rts */ dc_serial_get_lines, /* get_lines */ dc_serial_get_available, /* get_available */ dc_serial_configure, /* configure */ + dc_serial_poll, /* poll */ dc_serial_read, /* read */ dc_serial_write, /* write */ + dc_serial_ioctl, /* ioctl */ dc_serial_flush, /* flush */ dc_serial_purge, /* purge */ dc_serial_sleep, /* sleep */ @@ -663,6 +665,42 @@ dc_serial_set_latency (dc_iostream_t *abstract, unsigned int milliseconds) return DC_STATUS_SUCCESS; } +static dc_status_t +dc_serial_poll (dc_iostream_t *abstract, int timeout) +{ + dc_serial_t *device = (dc_serial_t *) abstract; + int rc = 0; + + do { + fd_set fds; + FD_ZERO (&fds); + FD_SET (device->fd, &fds); + + struct timeval tv, *ptv = NULL; + if (timeout > 0) { + tv.tv_sec = (timeout / 1000); + tv.tv_usec = (timeout % 1000) * 1000; + ptv = &tv; + } else if (timeout == 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + ptv = &tv; + } + + rc = select (device->fd + 1, &fds, NULL, NULL, ptv); + } while (rc < 0 && errno == EINTR); + + if (rc < 0) { + int errcode = errno; + SYSERROR (abstract->context, errcode); + return syserror (errcode); + } else if (rc == 0) { + return DC_STATUS_TIMEOUT; + } else { + return DC_STATUS_SUCCESS; + } +} + static dc_status_t dc_serial_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual) { @@ -811,6 +849,17 @@ out: return status; } +static dc_status_t +dc_serial_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + switch (request) { + case DC_IOCTL_SERIAL_SET_LATENCY: + return dc_serial_set_latency (abstract, *(unsigned int *) data); + default: + return DC_STATUS_UNSUPPORTED; + } +} + static dc_status_t dc_serial_purge (dc_iostream_t *abstract, dc_direction_t direction) { diff --git a/src/serial_win32.c b/src/serial_win32.c index 124857a..3cb10ff 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -36,15 +36,16 @@ static dc_status_t dc_serial_iterator_next (dc_iterator_t *iterator, void *item) static dc_status_t dc_serial_iterator_free (dc_iterator_t *iterator); static dc_status_t dc_serial_set_timeout (dc_iostream_t *iostream, int timeout); -static dc_status_t dc_serial_set_latency (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_set_break (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_set_dtr (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_set_rts (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_get_lines (dc_iostream_t *iostream, unsigned int *value); static dc_status_t dc_serial_get_available (dc_iostream_t *iostream, size_t *value); static dc_status_t dc_serial_configure (dc_iostream_t *iostream, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol); +static dc_status_t dc_serial_poll (dc_iostream_t *iostream, int timeout); static dc_status_t dc_serial_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual); static dc_status_t dc_serial_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual); +static dc_status_t dc_serial_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size); static dc_status_t dc_serial_flush (dc_iostream_t *iostream); static dc_status_t dc_serial_purge (dc_iostream_t *iostream, dc_direction_t direction); static dc_status_t dc_serial_sleep (dc_iostream_t *iostream, unsigned int milliseconds); @@ -75,6 +76,11 @@ typedef struct dc_serial_t { */ DCB dcb; COMMTIMEOUTS timeouts; + + HANDLE hReadWrite, hPoll; + OVERLAPPED overlapped; + DWORD events; + BOOL pending; } dc_serial_t; static const dc_iterator_vtable_t dc_serial_iterator_vtable = { @@ -86,15 +92,16 @@ static const dc_iterator_vtable_t dc_serial_iterator_vtable = { static const dc_iostream_vtable_t dc_serial_vtable = { sizeof(dc_serial_t), dc_serial_set_timeout, /* set_timeout */ - dc_serial_set_latency, /* set_latency */ dc_serial_set_break, /* set_break */ dc_serial_set_dtr, /* set_dtr */ dc_serial_set_rts, /* set_rts */ dc_serial_get_lines, /* get_lines */ dc_serial_get_available, /* get_available */ dc_serial_configure, /* configure */ + dc_serial_poll, /* poll */ dc_serial_read, /* read */ dc_serial_write, /* write */ + dc_serial_ioctl, /* ioctl */ dc_serial_flush, /* flush */ dc_serial_purge, /* purge */ dc_serial_sleep, /* sleep */ @@ -282,18 +289,41 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name) return DC_STATUS_NOMEMORY; } + // Default values. + memset(&device->overlapped, 0, sizeof(device->overlapped)); + device->events = 0; + device->pending = FALSE; + + // Create a manual reset event for I/O. + device->hReadWrite = CreateEvent (NULL, TRUE, FALSE, NULL); + if (device->hReadWrite == INVALID_HANDLE_VALUE) { + DWORD errcode = GetLastError (); + SYSERROR (context, errcode); + status = syserror (errcode); + goto error_free; + } + + // Create a manual reset event for polling. + device->hPoll = CreateEvent (NULL, TRUE, FALSE, NULL); + if (device->hPoll == INVALID_HANDLE_VALUE) { + DWORD errcode = GetLastError (); + SYSERROR (context, errcode); + status = syserror (errcode); + goto error_free_readwrite; + } + // Open the device. device->hFile = CreateFileA (devname, GENERIC_READ | GENERIC_WRITE, 0, NULL, // No security attributes. OPEN_EXISTING, - 0, // Non-overlapped I/O. + FILE_FLAG_OVERLAPPED, NULL); if (device->hFile == INVALID_HANDLE_VALUE) { DWORD errcode = GetLastError (); SYSERROR (context, errcode); status = syserror (errcode); - goto error_free; + goto error_free_poll; } // Retrieve the current communication settings and timeouts, @@ -308,12 +338,24 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name) goto error_close; } + // Enable event monitoring. + if (!SetCommMask (device->hFile, EV_RXCHAR)) { + DWORD errcode = GetLastError (); + SYSERROR (context, errcode); + status = syserror (errcode); + goto error_close; + } + *out = (dc_iostream_t *) device; return DC_STATUS_SUCCESS; error_close: CloseHandle (device->hFile); +error_free_poll: + CloseHandle (device->hPoll); +error_free_readwrite: + CloseHandle (device->hReadWrite); error_free: dc_iostream_deallocate ((dc_iostream_t *) device); return status; @@ -325,6 +367,9 @@ dc_serial_close (dc_iostream_t *abstract) dc_status_t status = DC_STATUS_SUCCESS; dc_serial_t *device = (dc_serial_t *) abstract; + // Disable event monitoring. + SetCommMask (device->hFile, 0); + // Restore the initial communication settings and timeouts. if (!SetCommState (device->hFile, &device->dcb) || !SetCommTimeouts (device->hFile, &device->timeouts)) { @@ -340,6 +385,9 @@ dc_serial_close (dc_iostream_t *abstract) dc_status_set_error(&status, syserror (errcode)); } + CloseHandle (device->hPoll); + CloseHandle (device->hReadWrite); + return status; } @@ -497,8 +545,60 @@ dc_serial_set_timeout (dc_iostream_t *abstract, int timeout) } static dc_status_t -dc_serial_set_latency (dc_iostream_t *abstract, unsigned int value) +dc_serial_poll (dc_iostream_t *abstract, int timeout) { + dc_serial_t *device = (dc_serial_t *) abstract; + + while (1) { + COMSTAT stats; + if (!ClearCommError (device->hFile, NULL, &stats)) { + DWORD errcode = GetLastError (); + SYSERROR (abstract->context, errcode); + return syserror (errcode); + } + + if (stats.cbInQue) + break; + + if (!device->pending) { + memset(&device->overlapped, 0, sizeof(device->overlapped)); + device->overlapped.hEvent = device->hPoll; + device->events = 0; + if (!WaitCommEvent (device->hFile, &device->events, &device->overlapped)) { + DWORD errcode = GetLastError (); + if (errcode != ERROR_IO_PENDING) { + SYSERROR (abstract->context, errcode); + return syserror (errcode); + } + device->pending = TRUE; + } + } + + if (device->pending) { + DWORD errcode = 0; + DWORD rc = WaitForSingleObject (device->hPoll, timeout >= 0 ? (DWORD) timeout : INFINITE); + switch (rc) { + case WAIT_OBJECT_0: + break; + case WAIT_TIMEOUT: + return DC_STATUS_TIMEOUT; + default: + errcode = GetLastError (); + SYSERROR (abstract->context, errcode); + return syserror (errcode); + } + } + + DWORD dummy = 0; + if (!GetOverlappedResult (device->hFile, &device->overlapped, &dummy, TRUE)) { + DWORD errcode = GetLastError (); + SYSERROR (abstract->context, errcode); + return syserror (errcode); + } + + device->pending = FALSE; + } + return DC_STATUS_SUCCESS; } @@ -509,7 +609,19 @@ dc_serial_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual dc_serial_t *device = (dc_serial_t *) abstract; DWORD dwRead = 0; - if (!ReadFile (device->hFile, data, size, &dwRead, NULL)) { + OVERLAPPED overlapped = {0}; + overlapped.hEvent = device->hReadWrite; + + if (!ReadFile (device->hFile, data, size, NULL, &overlapped)) { + DWORD errcode = GetLastError (); + if (errcode != ERROR_IO_PENDING) { + SYSERROR (abstract->context, errcode); + status = syserror (errcode); + goto out; + } + } + + if (!GetOverlappedResult (device->hFile, &overlapped, &dwRead, TRUE)) { DWORD errcode = GetLastError (); SYSERROR (abstract->context, errcode); status = syserror (errcode); @@ -534,7 +646,19 @@ dc_serial_write (dc_iostream_t *abstract, const void *data, size_t size, size_t dc_serial_t *device = (dc_serial_t *) abstract; DWORD dwWritten = 0; - if (!WriteFile (device->hFile, data, size, &dwWritten, NULL)) { + OVERLAPPED overlapped = {0}; + overlapped.hEvent = device->hReadWrite; + + if (!WriteFile (device->hFile, data, size, NULL, &overlapped)) { + DWORD errcode = GetLastError (); + if (errcode != ERROR_IO_PENDING) { + SYSERROR (abstract->context, errcode); + status = syserror (errcode); + goto out; + } + } + + if (!GetOverlappedResult (device->hFile, &overlapped, &dwWritten, TRUE)) { DWORD errcode = GetLastError (); SYSERROR (abstract->context, errcode); status = syserror (errcode); @@ -552,6 +676,17 @@ out: return status; } +static dc_status_t +dc_serial_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + switch (request) { + case DC_IOCTL_SERIAL_SET_LATENCY: + return DC_STATUS_SUCCESS; + default: + return DC_STATUS_UNSUPPORTED; + } +} + static dc_status_t dc_serial_purge (dc_iostream_t *abstract, dc_direction_t direction) { diff --git a/src/socket.c b/src/socket.c index cbbb684..e9412ab 100644 --- a/src/socket.c +++ b/src/socket.c @@ -186,6 +186,42 @@ dc_socket_get_available (dc_iostream_t *abstract, size_t *value) return DC_STATUS_SUCCESS; } +dc_status_t +dc_socket_poll (dc_iostream_t *abstract, int timeout) +{ + dc_socket_t *socket = (dc_socket_t *) abstract; + int rc = 0; + + do { + fd_set fds; + FD_ZERO (&fds); + FD_SET (socket->fd, &fds); + + struct timeval tv, *ptv = NULL; + if (timeout > 0) { + tv.tv_sec = (timeout / 1000); + tv.tv_usec = (timeout % 1000) * 1000; + ptv = &tv; + } else if (timeout == 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + ptv = &tv; + } + + rc = select (socket->fd + 1, &fds, NULL, NULL, ptv); + } while (rc < 0 && S_ERRNO == S_EINTR); + + if (rc < 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (abstract->context, errcode); + return dc_socket_syserror(errcode); + } else if (rc == 0) { + return DC_STATUS_TIMEOUT; + } else { + return DC_STATUS_SUCCESS; + } +} + dc_status_t dc_socket_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual) { @@ -294,6 +330,12 @@ out: return status; } +dc_status_t +dc_socket_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + return DC_STATUS_UNSUPPORTED; +} + dc_status_t dc_socket_sleep (dc_iostream_t *abstract, unsigned int timeout) { diff --git a/src/socket.h b/src/socket.h index 280c486..e2efa14 100644 --- a/src/socket.h +++ b/src/socket.h @@ -108,12 +108,18 @@ dc_socket_set_timeout (dc_iostream_t *iostream, int timeout); dc_status_t dc_socket_get_available (dc_iostream_t *iostream, size_t *value); +dc_status_t +dc_socket_poll (dc_iostream_t *iostream, int timeout); + dc_status_t dc_socket_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual); dc_status_t dc_socket_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual); +dc_status_t +dc_socket_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size); + dc_status_t dc_socket_sleep (dc_iostream_t *abstract, unsigned int timeout); diff --git a/src/usbhid.c b/src/usbhid.c index a9563c7..e26d42b 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -94,8 +94,10 @@ static dc_status_t dc_usbhid_iterator_next (dc_iterator_t *iterator, void *item) static dc_status_t dc_usbhid_iterator_free (dc_iterator_t *iterator); static dc_status_t dc_usbhid_set_timeout (dc_iostream_t *iostream, int timeout); +static dc_status_t dc_usbhid_poll (dc_iostream_t *iostream, int timeout); static dc_status_t dc_usbhid_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual); static dc_status_t dc_usbhid_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual); +static dc_status_t dc_usbhid_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size); static dc_status_t dc_usbhid_close (dc_iostream_t *iostream); typedef struct dc_usbhid_iterator_t { @@ -137,15 +139,16 @@ static const dc_iterator_vtable_t dc_usbhid_iterator_vtable = { static const dc_iostream_vtable_t dc_usbhid_vtable = { sizeof(dc_usbhid_t), dc_usbhid_set_timeout, /* set_timeout */ - NULL, /* set_latency */ NULL, /* set_break */ NULL, /* set_dtr */ NULL, /* set_rts */ NULL, /* get_lines */ NULL, /* get_available */ NULL, /* configure */ + dc_usbhid_poll, /* poll */ dc_usbhid_read, /* read */ dc_usbhid_write, /* write */ + dc_usbhid_ioctl, /* ioctl */ NULL, /* flush */ NULL, /* purge */ NULL, /* sleep */ @@ -685,6 +688,12 @@ dc_usbhid_set_timeout (dc_iostream_t *abstract, int timeout) return DC_STATUS_SUCCESS; } +static dc_status_t +dc_usbhid_poll (dc_iostream_t *abstract, int timeout) +{ + return DC_STATUS_UNSUPPORTED; +} + static dc_status_t dc_usbhid_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual) { @@ -773,4 +782,10 @@ out: return status; } + +static dc_status_t +dc_usbhid_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + return DC_STATUS_UNSUPPORTED; +} #endif diff --git a/src/uwatec_memomouse.c b/src/uwatec_memomouse.c index c77b598..e605578 100644 --- a/src/uwatec_memomouse.c +++ b/src/uwatec_memomouse.c @@ -309,14 +309,13 @@ uwatec_memomouse_dump_internal (uwatec_memomouse_device_t *device, dc_buffer_t * { dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; - size_t available = 0; // Enable progress notifications. dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); // Waiting for greeting message. - while (dc_iostream_get_available (device->iostream, &available) == DC_STATUS_SUCCESS && available == 0) { + while (dc_iostream_poll (device->iostream, 300) == DC_STATUS_TIMEOUT) { if (device_is_cancelled (abstract)) return DC_STATUS_CANCELLED; @@ -330,8 +329,6 @@ uwatec_memomouse_dump_internal (uwatec_memomouse_device_t *device, dc_buffer_t * ERROR (abstract->context, "Failed to reject the packet."); return status; } - - dc_iostream_sleep (device->iostream, 300); } // Read the ID string. @@ -385,12 +382,11 @@ uwatec_memomouse_dump_internal (uwatec_memomouse_device_t *device, dc_buffer_t * } // Wait for the data packet. - while (dc_iostream_get_available (device->iostream, &available) == DC_STATUS_SUCCESS && available == 0) { + while (dc_iostream_poll (device->iostream, 100) == DC_STATUS_TIMEOUT) { if (device_is_cancelled (abstract)) return DC_STATUS_CANCELLED; device_event_emit (&device->base, DC_EVENT_WAITING, NULL); - dc_iostream_sleep (device->iostream, 100); } // Fetch the current system time.