From 018198dc17a9462ae045185db472d05fd1745a31 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 26 Dec 2019 10:18:36 +0100 Subject: [PATCH 01/16] Increase the timeout to 3 seconds The BLE communication is significant slower than usb-serial. The first BLE data packet of each response often takes longer than one second to arrive. This causes the first attempt to fail with a timeout. The second attempt will appear to succeed, because it actually receives the response of the first attempt. But now the next command will fail, because it will receive the response of the second attempt of the previous command. Increasing the timeout and adding an extra delay before retrying, avoids this problem. Reported-by: Linus Torvalds --- src/mares_iconhd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 2a714a4..6388bb5 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -326,6 +326,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; @@ -482,8 +483,8 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ goto error_free; } - // Set the timeout for receiving data (1000 ms). - status = dc_iostream_set_timeout (device->iostream, 1000); + // 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."); goto error_free; From ef4bd94717659c98a38c656a660ec1722ede512a Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sun, 5 Jan 2020 21:15:44 +0100 Subject: [PATCH 02/16] Add support for the Oceanic Veo 4.0 --- src/descriptor.c | 1 + src/oceanic_atom2.c | 1 + src/oceanic_atom2_parser.c | 14 +++++++++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index 1002b36..64323b2 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -242,6 +242,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Aqualung", "i770R", DC_FAMILY_OCEANIC_ATOM2, 0x4651, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i550C", DC_FAMILY_OCEANIC_ATOM2, 0x4652, DC_TRANSPORT_SERIAL, NULL}, {"Oceanic", "Geo 4.0", DC_FAMILY_OCEANIC_ATOM2, 0x4653, DC_TRANSPORT_SERIAL, NULL}, + {"Oceanic", "Veo 4.0", DC_FAMILY_OCEANIC_ATOM2, 0x4654, DC_TRANSPORT_SERIAL, NULL}, {"Oceanic", "Pro Plus 4", DC_FAMILY_OCEANIC_ATOM2, 0x4656, DC_TRANSPORT_SERIAL, NULL}, /* Mares Nemo */ {"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL}, diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index 553d2d2..195e4ca 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -128,6 +128,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[] = { diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index ed38802..1f4f852 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -93,6 +93,7 @@ #define I770R 0x4651 #define I550C 0x4652 #define GEO40 0x4653 +#define VEO40 0x4654 #define PROPLUS4 0x4656 #define NORMAL 0 @@ -166,7 +167,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; @@ -290,6 +292,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; @@ -727,7 +730,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; } @@ -886,7 +890,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]; @@ -960,7 +964,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; @@ -1014,7 +1018,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; From f6fa2b84bc191d558da8b256aaed3412761d3c5a Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 6 Sep 2019 22:11:32 +0200 Subject: [PATCH 03/16] Add a poll function to the I/O interface The Linux implementation is very straighforward and just a lightweight wrapper around the select function. But the Windows implementation is much more complex, because the Windows event notification mechanism behaves very different: The WaitCommEvent function does not support a timeout and is always a blocking call. The only way to implement a timeout is to use asynchronous I/O (or overlapped I/O as it's called in the Windows API), to run the operation in the background. This requires some additional book keeping to keep track of the pending background operation. The event mechanism is also edge triggered instead of level triggered, and reading the event with the WaitCommEvent function clears the pending event. Therefore, the state of the input buffer needs to be checked with the ClearCommError function before and after the WaitCommEvent call. The check before is necessary in case the event is already cleared by a previous WaitCommEvent call, while there is still data present in the input buffer. In this case, WaitCommEvent should not be called at all, because it would wait until more data arrives. The check afterwards is necessary in case WaitCommEvent reports a pending event, while the data in the input buffer has already been consumed. In this case, the current event must be ignored and WaitCommEvent needs to be called again, to wait for the next event. --- include/libdivecomputer/custom.h | 1 + include/libdivecomputer/iostream.h | 30 +++++++ src/bluetooth.c | 1 + src/custom.c | 13 +++ src/iostream-private.h | 2 + src/iostream.c | 11 +++ src/irda.c | 1 + src/libdivecomputer.symbols | 1 + src/serial_posix.c | 38 ++++++++ src/serial_win32.c | 138 ++++++++++++++++++++++++++++- src/socket.c | 36 ++++++++ src/socket.h | 3 + src/usbhid.c | 8 ++ 13 files changed, 279 insertions(+), 4 deletions(-) diff --git a/include/libdivecomputer/custom.h b/include/libdivecomputer/custom.h index 4e3f9b0..eca3b1c 100644 --- a/include/libdivecomputer/custom.h +++ b/include/libdivecomputer/custom.h @@ -39,6 +39,7 @@ typedef struct dc_custom_cbs_t { 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 (*flush) (void *userdata); diff --git a/include/libdivecomputer/iostream.h b/include/libdivecomputer/iostream.h index d7d2621..fb0de9a 100644 --- a/include/libdivecomputer/iostream.h +++ b/include/libdivecomputer/iostream.h @@ -211,6 +211,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. * diff --git a/src/bluetooth.c b/src/bluetooth.c index 4630523..649d006 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -106,6 +106,7 @@ static const dc_iostream_vtable_t dc_bluetooth_vtable = { NULL, /* get_lines */ dc_socket_get_available, /* get_available */ NULL, /* configure */ + dc_socket_poll, /* poll */ dc_socket_read, /* read */ dc_socket_write, /* write */ NULL, /* flush */ diff --git a/src/custom.c b/src/custom.c index 8db276b..2bdd37c 100644 --- a/src/custom.c +++ b/src/custom.c @@ -35,6 +35,7 @@ static dc_status_t dc_custom_set_rts (dc_iostream_t *abstract, unsigned int valu 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_flush (dc_iostream_t *abstract); @@ -60,6 +61,7 @@ static const dc_iostream_vtable_t dc_custom_vtable = { 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_flush, /* flush */ @@ -181,6 +183,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) { diff --git a/src/iostream-private.h b/src/iostream-private.h index f9c0883..4ce8529 100644 --- a/src/iostream-private.h +++ b/src/iostream-private.h @@ -57,6 +57,8 @@ 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); diff --git a/src/iostream.c b/src/iostream.c index e775146..677f744 100644 --- a/src/iostream.c +++ b/src/iostream.c @@ -183,6 +183,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) { diff --git a/src/irda.c b/src/irda.c index a78ff31..5ffc162 100644 --- a/src/irda.c +++ b/src/irda.c @@ -98,6 +98,7 @@ static const dc_iostream_vtable_t dc_irda_vtable = { NULL, /* get_lines */ dc_socket_get_available, /* get_available */ NULL, /* configure */ + dc_socket_poll, /* poll */ dc_socket_read, /* read */ dc_socket_write, /* write */ NULL, /* flush */ diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 04e4196..4195d66 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -43,6 +43,7 @@ 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_flush diff --git a/src/serial_posix.c b/src/serial_posix.c index 46ec3de..08cfdc4 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -74,6 +74,7 @@ static dc_status_t dc_serial_set_rts (dc_iostream_t *iostream, unsigned int valu 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_flush (dc_iostream_t *iostream); @@ -123,6 +124,7 @@ static const dc_iostream_vtable_t dc_serial_vtable = { 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_flush, /* flush */ @@ -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) { diff --git a/src/serial_win32.c b/src/serial_win32.c index 124857a..0f8bd18 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -43,6 +43,7 @@ static dc_status_t dc_serial_set_rts (dc_iostream_t *iostream, unsigned int valu 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_flush (dc_iostream_t *iostream); @@ -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 = { @@ -93,6 +99,7 @@ static const dc_iostream_vtable_t dc_serial_vtable = { 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_flush, /* flush */ @@ -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; } @@ -502,6 +550,64 @@ dc_serial_set_latency (dc_iostream_t *abstract, unsigned int value) 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; + + 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; +} + static dc_status_t dc_serial_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual) { @@ -509,7 +615,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 +652,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); diff --git a/src/socket.c b/src/socket.c index cbbb684..3bb8718 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) { diff --git a/src/socket.h b/src/socket.h index 280c486..455b945 100644 --- a/src/socket.h +++ b/src/socket.h @@ -108,6 +108,9 @@ 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); diff --git a/src/usbhid.c b/src/usbhid.c index a9563c7..6fa395a 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -94,6 +94,7 @@ 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_close (dc_iostream_t *iostream); @@ -144,6 +145,7 @@ static const dc_iostream_vtable_t dc_usbhid_vtable = { NULL, /* get_lines */ NULL, /* get_available */ NULL, /* configure */ + dc_usbhid_poll, /* poll */ dc_usbhid_read, /* read */ dc_usbhid_write, /* write */ NULL, /* flush */ @@ -685,6 +687,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) { From af5716d6850de02fcbc9d97b3e73484fd0f41c7a Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 6 Sep 2019 22:15:04 +0200 Subject: [PATCH 04/16] Integrate the new poll function Replace the manual polling, implemented using a combination of the dc_iostream_get_available and dc_iostream_sleep functions, with the new and more efficient poll function. --- src/hw_ostc3.c | 10 +--------- src/mares_nemo.c | 4 +--- src/uwatec_memomouse.c | 8 ++------ 3 files changed, 4 insertions(+), 18 deletions(-) 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/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/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. From 0359a57fdc71dd227f81c8fb024108935c527445 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 19 Apr 2019 20:46:47 +0200 Subject: [PATCH 05/16] Add an ioctl function to the I/O interface This new ioctl function allows to perform I/O stream specific requests through a generic interface. This provides an easy way to extend the I/O interface with some driver specific features, without having to modify the public api. --- include/libdivecomputer/custom.h | 1 + include/libdivecomputer/ioctl.h | 73 ++++++++++++++++++++++++++++++ include/libdivecomputer/iostream.h | 13 ++++++ src/bluetooth.c | 1 + src/custom.c | 13 ++++++ src/iostream-private.h | 2 + src/iostream.c | 36 +++++++++++++++ src/irda.c | 1 + src/libdivecomputer.symbols | 1 + src/serial_posix.c | 8 ++++ src/serial_win32.c | 8 ++++ src/socket.c | 6 +++ src/socket.h | 3 ++ src/usbhid.c | 8 ++++ 14 files changed, 174 insertions(+) create mode 100644 include/libdivecomputer/ioctl.h diff --git a/include/libdivecomputer/custom.h b/include/libdivecomputer/custom.h index eca3b1c..abaf0e7 100644 --- a/include/libdivecomputer/custom.h +++ b/include/libdivecomputer/custom.h @@ -42,6 +42,7 @@ typedef struct dc_custom_cbs_t { 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); 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 fb0de9a..8715496 100644 --- a/include/libdivecomputer/iostream.h +++ b/include/libdivecomputer/iostream.h @@ -269,6 +269,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/src/bluetooth.c b/src/bluetooth.c index 649d006..23e2006 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -109,6 +109,7 @@ static const dc_iostream_vtable_t dc_bluetooth_vtable = { 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 2bdd37c..4a4ead4 100644 --- a/src/custom.c +++ b/src/custom.c @@ -38,6 +38,7 @@ static dc_status_t dc_custom_configure (dc_iostream_t *abstract, unsigned int ba 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); @@ -64,6 +65,7 @@ static const dc_iostream_vtable_t dc_custom_vtable = { 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 */ @@ -216,6 +218,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) { diff --git a/src/iostream-private.h b/src/iostream-private.h index 4ce8529..cdf1b16 100644 --- a/src/iostream-private.h +++ b/src/iostream-private.h @@ -63,6 +63,8 @@ struct dc_iostream_vtable_t { 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); diff --git a/src/iostream.c b/src/iostream.c index 677f744..fbfeec9 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" @@ -236,6 +238,40 @@ out: return status; } +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) { diff --git a/src/irda.c b/src/irda.c index 5ffc162..66e1409 100644 --- a/src/irda.c +++ b/src/irda.c @@ -101,6 +101,7 @@ static const dc_iostream_vtable_t dc_irda_vtable = { 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 4195d66..613e360 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -46,6 +46,7 @@ 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/serial_posix.c b/src/serial_posix.c index 08cfdc4..4b478aa 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -77,6 +77,7 @@ static dc_status_t dc_serial_configure (dc_iostream_t *iostream, unsigned int ba 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); @@ -127,6 +128,7 @@ static const dc_iostream_vtable_t dc_serial_vtable = { 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 */ @@ -849,6 +851,12 @@ out: return status; } +static dc_status_t +dc_serial_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + 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 0f8bd18..d21247a 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -46,6 +46,7 @@ static dc_status_t dc_serial_configure (dc_iostream_t *iostream, unsigned int ba 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); @@ -102,6 +103,7 @@ static const dc_iostream_vtable_t dc_serial_vtable = { 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 */ @@ -682,6 +684,12 @@ out: return status; } +static dc_status_t +dc_serial_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + 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 3bb8718..e9412ab 100644 --- a/src/socket.c +++ b/src/socket.c @@ -330,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 455b945..e2efa14 100644 --- a/src/socket.h +++ b/src/socket.h @@ -117,6 +117,9 @@ 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 6fa395a..f56d115 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -97,6 +97,7 @@ 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 { @@ -148,6 +149,7 @@ static const dc_iostream_vtable_t dc_usbhid_vtable = { dc_usbhid_poll, /* poll */ dc_usbhid_read, /* read */ dc_usbhid_write, /* write */ + dc_usbhid_ioctl, /* ioctl */ NULL, /* flush */ NULL, /* purge */ NULL, /* sleep */ @@ -781,4 +783,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 From c205299c026ebe668b7f9cac3c3aefcdbbbaebce Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 19 Apr 2019 20:52:12 +0200 Subject: [PATCH 06/16] Re-implement the set_latency function as an ioctl The set_latency function is the perfect example of a feature that should be implemented as an ioctl: it's only implemented by a single driver, and the functionality is also highly platform specific. --- include/libdivecomputer/custom.h | 1 - include/libdivecomputer/iostream.h | 16 ---------------- include/libdivecomputer/serial.h | 11 +++++++++++ src/bluetooth.c | 1 - src/custom.c | 13 ------------- src/iostream-private.h | 2 -- src/iostream.c | 11 ----------- src/irda.c | 1 - src/libdivecomputer.symbols | 1 - src/serial_posix.c | 9 ++++++--- src/serial_win32.c | 15 ++++++--------- src/usbhid.c | 1 - 12 files changed, 23 insertions(+), 59 deletions(-) diff --git a/include/libdivecomputer/custom.h b/include/libdivecomputer/custom.h index abaf0e7..dd50e21 100644 --- a/include/libdivecomputer/custom.h +++ b/include/libdivecomputer/custom.h @@ -32,7 +32,6 @@ 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); diff --git a/include/libdivecomputer/iostream.h b/include/libdivecomputer/iostream.h index 8715496..b09cba1 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. * 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 23e2006..282a31a 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -99,7 +99,6 @@ 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 */ diff --git a/src/custom.c b/src/custom.c index 4a4ead4..688a8b3 100644 --- a/src/custom.c +++ b/src/custom.c @@ -28,7 +28,6 @@ #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); @@ -55,7 +54,6 @@ 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 */ @@ -108,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) { diff --git a/src/iostream-private.h b/src/iostream-private.h index cdf1b16..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); diff --git a/src/iostream.c b/src/iostream.c index fbfeec9..51713d1 100644 --- a/src/iostream.c +++ b/src/iostream.c @@ -87,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) { diff --git a/src/irda.c b/src/irda.c index 66e1409..214e39f 100644 --- a/src/irda.c +++ b/src/irda.c @@ -91,7 +91,6 @@ 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 */ diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 613e360..b17d60b 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -36,7 +36,6 @@ 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 diff --git a/src/serial_posix.c b/src/serial_posix.c index 4b478aa..897b428 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -67,7 +67,6 @@ 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); @@ -118,7 +117,6 @@ 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 */ @@ -854,7 +852,12 @@ out: static dc_status_t dc_serial_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) { - return DC_STATUS_UNSUPPORTED; + 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 diff --git a/src/serial_win32.c b/src/serial_win32.c index d21247a..3cb10ff 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -36,7 +36,6 @@ 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); @@ -93,7 +92,6 @@ 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 */ @@ -546,12 +544,6 @@ dc_serial_set_timeout (dc_iostream_t *abstract, int timeout) return DC_STATUS_SUCCESS; } -static dc_status_t -dc_serial_set_latency (dc_iostream_t *abstract, unsigned int value) -{ - return DC_STATUS_SUCCESS; -} - static dc_status_t dc_serial_poll (dc_iostream_t *abstract, int timeout) { @@ -687,7 +679,12 @@ out: static dc_status_t dc_serial_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) { - return DC_STATUS_UNSUPPORTED; + switch (request) { + case DC_IOCTL_SERIAL_SET_LATENCY: + return DC_STATUS_SUCCESS; + default: + return DC_STATUS_UNSUPPORTED; + } } static dc_status_t diff --git a/src/usbhid.c b/src/usbhid.c index f56d115..e26d42b 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -139,7 +139,6 @@ 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 */ From e71278a13f2ecc1ef0be77d958c8bde4e127aade Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 19 Apr 2019 20:58:35 +0200 Subject: [PATCH 07/16] Add an ioctl to retrieve the remote device name --- include/libdivecomputer/Makefile.am | 1 + include/libdivecomputer/ble.h | 39 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 include/libdivecomputer/ble.h diff --git a/include/libdivecomputer/Makefile.am b/include/libdivecomputer/Makefile.am index e972153..d7c8379 100644 --- a/include/libdivecomputer/Makefile.am +++ b/include/libdivecomputer/Makefile.am @@ -9,6 +9,7 @@ libdivecomputer_HEADERS = \ iostream.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 */ From 91309a3d54b5a41739111472b56bc3c2e8c42261 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 1 Feb 2019 15:25:38 +0100 Subject: [PATCH 08/16] Remove an unnecessary function --- src/oceanic_atom2.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index 195e4ca..a4341fe 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -632,19 +632,6 @@ oceanic_atom2_transfer (oceanic_atom2_device_t *device, const unsigned char comm } -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); - 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) { @@ -827,7 +814,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, 0x00}; + rc = oceanic_atom2_transfer (device, command, sizeof (command), NULL, 0, 0); if (rc != DC_STATUS_SUCCESS) { dc_status_set_error(&status, rc); } From 4b2156d3782d8c06b70508e359083c8aa9b7e56b Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 1 Feb 2019 15:45:49 +0100 Subject: [PATCH 09/16] Fix a bug in the ACK/NAK handling The write command is send as two separate packets. The first packet contains the B2 command and the page number, and the second packet contains the payload and checksum. Because the payload can contain arbitrary data, the first byte of a packet is not necessary a command byte. But the code to select the correct ack byte is based on this assumption. Fixed by passing the expected ack byte. --- src/oceanic_atom2.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index a4341fe..e09b851 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -534,7 +534,7 @@ static const oceanic_common_layout_t aqualung_i450t_layout = { }; 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_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; @@ -553,12 +553,6 @@ oceanic_atom2_packet (oceanic_atom2_device_t *device, const unsigned char comman return status; } - // Get the correct ACK byte. - unsigned int ack = ACK; - if (command[0] == CMD_INIT || command[0] == CMD_QUIT) { - ack = NAK; - } - // Receive the response (ACK/NAK) of the dive computer. unsigned char response = 0; status = dc_iostream_read (device->iostream, &response, 1, NULL); @@ -601,7 +595,7 @@ oceanic_atom2_packet (oceanic_atom2_device_t *device, const unsigned char comman 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) +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 @@ -611,7 +605,7 @@ oceanic_atom2_transfer (oceanic_atom2_device_t *device, const unsigned char comm 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; @@ -815,7 +809,7 @@ oceanic_atom2_device_close (dc_device_t *abstract) // Send the quit command. unsigned char command[4] = {CMD_QUIT, 0x05, 0xA5, 0x00}; - rc = oceanic_atom2_transfer (device, command, sizeof (command), NULL, 0, 0); + rc = oceanic_atom2_transfer (device, command, sizeof (command), NAK, NULL, 0, 0); if (rc != DC_STATUS_SUCCESS) { dc_status_set_error(&status, rc); } @@ -834,7 +828,7 @@ oceanic_atom2_device_keepalive (dc_device_t *abstract) // 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); + dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), ACK, NULL, 0, 0); if (rc != DC_STATUS_SUCCESS) return rc; @@ -855,7 +849,7 @@ oceanic_atom2_device_version (dc_device_t *abstract, unsigned char data[], unsig 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); + dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), ACK, answer, sizeof (answer), 1); if (rc != DC_STATUS_SUCCESS) return rc; @@ -923,7 +917,7 @@ oceanic_atom2_device_read (dc_device_t *abstract, unsigned int address, unsigned (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, answer, pagesize + crc_size, crc_size); if (rc != DC_STATUS_SUCCESS) return rc; @@ -970,7 +964,7 @@ oceanic_atom2_device_write (dc_device_t *abstract, unsigned int address, const u (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; @@ -978,7 +972,7 @@ oceanic_atom2_device_write (dc_device_t *abstract, unsigned int address, const u unsigned char command[PAGESIZE + 2] = {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; From 437db813d5e3f188c7109ebffff9c3dbb65a561e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 1 Feb 2019 15:48:40 +0100 Subject: [PATCH 10/16] Remove the trailing zero byte from all commands The trailing zero byte is present for historic reasons only. At the time the Oceanic protocol was implemented, the Oceanic application send this extra zero byte too, and we simply copied this behaviour. But more recent versions no longer send it. Probably a small (harmless) bug that was fixed. --- src/oceanic_atom2.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index e09b851..56c28d8 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -808,7 +808,7 @@ oceanic_atom2_device_close (dc_device_t *abstract) dc_status_t rc = DC_STATUS_SUCCESS; // Send the quit command. - unsigned char command[4] = {CMD_QUIT, 0x05, 0xA5, 0x00}; + 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); @@ -827,7 +827,7 @@ 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}; + 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; @@ -848,7 +848,7 @@ oceanic_atom2_device_version (dc_device_t *abstract, unsigned char data[], unsig return DC_STATUS_INVALIDARGS; unsigned char answer[PAGESIZE + 1] = {0}; - unsigned char command[2] = {CMD_VERSION, 0x00}; + unsigned char command[] = {CMD_VERSION}; dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), ACK, answer, sizeof (answer), 1); if (rc != DC_STATUS_SUCCESS) return rc; @@ -913,10 +913,10 @@ oceanic_atom2_device_read (dc_device_t *abstract, unsigned int address, unsigned // 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), ACK, answer, pagesize + crc_size, crc_size); if (rc != DC_STATUS_SUCCESS) return rc; @@ -960,16 +960,16 @@ 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), 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), ACK, NULL, 0, 0); From 4923d3761e4bc3948691b294b6736753c18fe7ea Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 1 Feb 2019 15:53:14 +0100 Subject: [PATCH 11/16] Read the entire data packet in a single operation Refactor the packet receiving code to read the ack byte, the payload data and the checksum all at once, with just a single read operation. This is not only a bit more efficient, but will also simplify the BLE support. --- src/oceanic_atom2.c | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index 56c28d8..82fab6e 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -38,6 +38,7 @@ #define I770R 0x4651 #define GEO40 0x4653 +#define MAXPACKET 256 #define MAXRETRIES 2 #define MAXDELAY 16 #define INVALID 0xFFFFFFFF @@ -539,6 +540,14 @@ oceanic_atom2_packet (oceanic_atom2_device_t *device, const unsigned char comman dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; + 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; @@ -553,41 +562,36 @@ oceanic_atom2_packet (oceanic_atom2_device_t *device, const unsigned char comman return status; } - // Receive the response (ACK/NAK) of the dive computer. - unsigned char response = 0; - status = dc_iostream_read (device->iostream, &response, 1, NULL); + // Receive the answer of the dive computer. + unsigned char packet[1 + MAXPACKET + 2]; + status = dc_iostream_read (device->iostream, packet, 1 + asize + crc_size, 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); } return DC_STATUS_SUCCESS; @@ -847,14 +851,11 @@ 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[] = {CMD_VERSION}; - dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), ACK, answer, sizeof (answer), 1); + 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); - return DC_STATUS_SUCCESS; } @@ -912,17 +913,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[] = {read_cmd, (number >> 8) & 0xFF, // high (number ) & 0xFF, // low }; - dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), ACK, 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; } From 6ba0726a4299accf6eef1257e283859fdaf15eca Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 5 Feb 2019 14:01:49 +0100 Subject: [PATCH 12/16] Implement the BLE packet sending and receiving Based-on-code-by: Linus Torvalds --- src/oceanic_atom2.c | 142 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 2 deletions(-) diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index 82fab6e..682fec9 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -29,6 +29,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) @@ -59,6 +60,7 @@ typedef struct oceanic_atom2_device_t { oceanic_common_device_t base; dc_iostream_t *iostream; + unsigned int sequence; unsigned int delay; unsigned int bigpage; unsigned char cache[256]; @@ -534,11 +536,136 @@ 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_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; @@ -556,7 +683,11 @@ 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; @@ -564,7 +695,11 @@ oceanic_atom2_packet (oceanic_atom2_device_t *device, const unsigned char comman // Receive the answer of the dive computer. unsigned char packet[1 + MAXPACKET + 2]; - status = dc_iostream_read (device->iostream, packet, 1 + asize + crc_size, NULL); + 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); + } if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to receive the answer."); return status; @@ -594,6 +729,8 @@ oceanic_atom2_packet (oceanic_atom2_device_t *device, const unsigned char comman memcpy (answer, packet + 1, asize); } + device->sequence++; + return DC_STATUS_SUCCESS; } @@ -652,6 +789,7 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream // Set the default values. device->iostream = iostream; device->delay = 0; + device->sequence = 0; device->bigpage = 1; // no big pages device->cached_page = INVALID; device->cached_highmem = INVALID; From 4baf140d250c696aa15b2694fe9fa824ae73159f Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 5 Feb 2019 14:05:49 +0100 Subject: [PATCH 13/16] Implement the BLE handshaking The BLE communication sends a handshake packet containing a passphrase based on the serial number of the device. Sadly, we can't actually read the serial number from the device until after this handshake has successfully completed, which makes it a bit of a chicken-and-egg problem from a communication standpoint. However, the serial number is also exposed in the bluetooth device name the device advertizes, which is the reason for the newly added DC_IOCTL_BLE_GET_NAME ioctl. Thanks to Janice McLaughlin for pointing out the logic of this magic handshake. Based-on-code-by: Linus Torvalds --- src/oceanic_atom2.c | 69 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index 682fec9..e897259 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" @@ -46,6 +48,7 @@ #define CMD_INIT 0xA8 #define CMD_VERSION 0x84 +#define CMD_HANDSHAKE 0xE5 #define CMD_READ1 0xB1 #define CMD_READ8 0xB4 #define CMD_READ16 0xB8 @@ -766,6 +769,65 @@ oceanic_atom2_transfer (oceanic_atom2_device_t *device, const unsigned char comm 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_ble_handshake(oceanic_atom2_device_t *device) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + // 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 { + return rc; + } + } + + // Force a null terminated string. + name[sizeof(name) - 1] = 0; + + // Check the minimum length. + if (strlen (name) < 8) { + ERROR (abstract->context, "Bluetooth device name too short."); + return DC_STATUS_IO; + } + + // 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); + + // Send the command to the dive computer. + 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) @@ -857,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; From 4bc5ee90ef53d1f573e9fc8101bfd423f5a0012d Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 5 Feb 2019 14:07:43 +0100 Subject: [PATCH 14/16] Fix the BLE device detection for the i770R and Pro Plus X It seems that the BLE communication protocol is somewhat different from the serial one in the version string: while the serial version tends to show the memory size, the BLE version string has some other numeric pattern. Linus Torvalds reports the BLE pattern for the i770R is normally just "0001", allthough he once also observed "0090" with the same dive computer. A communication trace from a Pro Plus X also showed "0001". We don't have enough information to guess the meaning of the number. Regardless, for those two dive computers supporting BLE, make the pattern simply ignore the last four digits, since they clearly vary. Based-on-code-by: Linus Torvalds --- src/oceanic_atom2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index e897259..c892486 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -207,11 +207,11 @@ static const oceanic_common_version_t oceanic_reactpro_version[] = { }; static const oceanic_common_version_t oceanic_proplusx_version[] = { - {"OCEANOCX \0\0 2048"}, + {"OCEANOCX \0\0 \0\0\0\0"}, }; static const oceanic_common_version_t aqualung_i770r_version[] = { - {"AQUA770R \0\0 2048"}, + {"AQUA770R \0\0 \0\0\0\0"}, }; static const oceanic_common_version_t aeris_a300cs_version[] = { From cfd54ff80e3514f9b8bad7385541633dfcb3fb0e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 5 Feb 2019 14:08:22 +0100 Subject: [PATCH 15/16] Advertise the BLE support in the device descriptors The bluetooth device filtering is based on the fact that the format of the bluetooth device 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. --- src/descriptor.c | 51 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index 64323b2..3734df5 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -47,6 +47,7 @@ static int dc_filter_hw (dc_transport_t transport, const void *userdata); static int dc_filter_tecdiving (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 dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item); @@ -225,7 +226,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, 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}, @@ -236,14 +237,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, NULL}, - {"Aqualung", "i200C", DC_FAMILY_OCEANIC_ATOM2, 0x4649, DC_TRANSPORT_SERIAL, 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, NULL}, - {"Aqualung", "i550C", DC_FAMILY_OCEANIC_ATOM2, 0x4652, DC_TRANSPORT_SERIAL, NULL}, - {"Oceanic", "Geo 4.0", DC_FAMILY_OCEANIC_ATOM2, 0x4653, DC_TRANSPORT_SERIAL, NULL}, - {"Oceanic", "Veo 4.0", DC_FAMILY_OCEANIC_ATOM2, 0x4654, DC_TRANSPORT_SERIAL, NULL}, - {"Oceanic", "Pro Plus 4", DC_FAMILY_OCEANIC_ATOM2, 0x4656, DC_TRANSPORT_SERIAL, 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}, @@ -434,6 +435,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) { @@ -590,6 +605,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; +} + dc_status_t dc_descriptor_iterator (dc_iterator_t **out) { From f65e3cf39e3fb9ad575a37a2f86518e56b36eb06 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 16 Jan 2020 20:18:40 +0100 Subject: [PATCH 16/16] Install the ioctl header file --- include/libdivecomputer/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/include/libdivecomputer/Makefile.am b/include/libdivecomputer/Makefile.am index d7c8379..bffc849 100644 --- a/include/libdivecomputer/Makefile.am +++ b/include/libdivecomputer/Makefile.am @@ -7,6 +7,7 @@ libdivecomputer_HEADERS = \ descriptor.h \ iterator.h \ iostream.h \ + ioctl.h \ serial.h \ bluetooth.h \ ble.h \