From 296fad2d9d3387b8b3096c959fd48a659d126814 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 8 Mar 2017 15:04:17 +0100 Subject: [PATCH 01/18] Cleanup the iterator internals --- src/descriptor.c | 37 +++++++++++------------------ src/iterator-private.h | 14 ++++++++++- src/iterator.c | 54 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 74 insertions(+), 31 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index 49df56d..2f5c9e3 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -48,6 +48,8 @@ #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) +static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item); + struct dc_descriptor_t { const char *vendor; const char *product; @@ -55,6 +57,17 @@ struct dc_descriptor_t { unsigned int model; }; +typedef struct dc_descriptor_iterator_t { + dc_iterator_t base; + size_t current; +} dc_descriptor_iterator_t; + +static const dc_iterator_vtable_t dc_descriptor_iterator_vtable = { + sizeof(dc_descriptor_iterator_t), + dc_descriptor_iterator_next, + NULL, +}; + /* * The model numbers in the table are the actual model numbers reported by the * device. For devices where there is no model number available (or known), an @@ -331,19 +344,6 @@ static const dc_descriptor_t g_descriptors[] = { {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5}, }; -typedef struct dc_descriptor_iterator_t { - dc_iterator_t base; - size_t current; -} dc_descriptor_iterator_t; - -static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item); -static dc_status_t dc_descriptor_iterator_free (dc_iterator_t *iterator); - -static const dc_iterator_vtable_t dc_descriptor_iterator_vtable = { - dc_descriptor_iterator_free, - dc_descriptor_iterator_next -}; - dc_status_t dc_descriptor_iterator (dc_iterator_t **out) { @@ -352,11 +352,10 @@ dc_descriptor_iterator (dc_iterator_t **out) if (out == NULL) return DC_STATUS_INVALIDARGS; - iterator = (dc_descriptor_iterator_t *) malloc (sizeof (dc_descriptor_iterator_t)); + iterator = (dc_descriptor_iterator_t *) dc_iterator_allocate (NULL, &dc_descriptor_iterator_vtable); if (iterator == NULL) return DC_STATUS_NOMEMORY; - iterator->base.vtable = &dc_descriptor_iterator_vtable; iterator->current = 0; *out = (dc_iterator_t *) iterator; @@ -364,14 +363,6 @@ dc_descriptor_iterator (dc_iterator_t **out) return DC_STATUS_SUCCESS; } -static dc_status_t -dc_descriptor_iterator_free (dc_iterator_t *iterator) -{ - free (iterator); - - return DC_STATUS_SUCCESS; -} - static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *abstract, void *out) { diff --git a/src/iterator-private.h b/src/iterator-private.h index 105d40f..0cc1bfe 100644 --- a/src/iterator-private.h +++ b/src/iterator-private.h @@ -22,6 +22,7 @@ #ifndef DC_ITERATOR_PRIVATE_H #define DC_ITERATOR_PRIVATE_H +#include #include #ifdef __cplusplus @@ -32,13 +33,24 @@ typedef struct dc_iterator_vtable_t dc_iterator_vtable_t; struct dc_iterator_t { const dc_iterator_vtable_t *vtable; + dc_context_t *context; }; struct dc_iterator_vtable_t { - dc_status_t (*free) (dc_iterator_t *iterator); + size_t size; dc_status_t (*next) (dc_iterator_t *iterator, void *item); + dc_status_t (*free) (dc_iterator_t *iterator); }; +dc_iterator_t * +dc_iterator_allocate (dc_context_t *context, const dc_iterator_vtable_t *vtable); + +void +dc_iterator_deallocate (dc_iterator_t *iterator); + +int +dc_iterator_isinstance (dc_iterator_t *iterator, const dc_iterator_vtable_t *vtable); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/iterator.c b/src/iterator.c index 100f85a..83ad070 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -21,16 +21,51 @@ #include #include +#include +#include "context-private.h" #include "iterator-private.h" +dc_iterator_t * +dc_iterator_allocate (dc_context_t *context, const dc_iterator_vtable_t *vtable) +{ + dc_iterator_t *iterator = NULL; + + assert(vtable != NULL); + assert(vtable->size >= sizeof(dc_iterator_t)); + + // Allocate memory. + iterator = (dc_iterator_t *) malloc (vtable->size); + if (iterator == NULL) { + ERROR (context, "Failed to allocate memory."); + return iterator; + } + + iterator->vtable = vtable; + iterator->context = context; + + return iterator; +} + +void +dc_iterator_deallocate (dc_iterator_t *iterator) +{ + free (iterator); +} + +int +dc_iterator_isinstance (dc_iterator_t *iterator, const dc_iterator_vtable_t *vtable) +{ + if (iterator == NULL) + return 0; + + return iterator->vtable == vtable; +} + dc_status_t dc_iterator_next (dc_iterator_t *iterator, void *item) { - if (iterator == NULL) - return DC_STATUS_UNSUPPORTED; - - if (iterator->vtable->next == NULL) + if (iterator == NULL || iterator->vtable->next == NULL) return DC_STATUS_UNSUPPORTED; if (item == NULL) @@ -42,11 +77,16 @@ dc_iterator_next (dc_iterator_t *iterator, void *item) dc_status_t dc_iterator_free (dc_iterator_t *iterator) { + dc_status_t status = DC_STATUS_SUCCESS; + if (iterator == NULL) return DC_STATUS_SUCCESS; - if (iterator->vtable->free == NULL) - return DC_STATUS_UNSUPPORTED; + if (iterator->vtable->free) { + status = iterator->vtable->free (iterator); + } - return iterator->vtable->free (iterator); + dc_iterator_deallocate (iterator); + + return status; } From ca91500ed5b42a8050f12ff20513bab1f1c40e4b Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 8 Mar 2017 16:38:07 +0100 Subject: [PATCH 02/18] Re-write the device discovery using the iterator api Replacing the callback based interface with an iterator based interface, results in a more extensible abstraction with a common interface for each of the built-in I/O implementations (serial, usbhid, irda and bluetooth). --- src/bluetooth.c | 376 +++++++++++++++++++++++++++++---------------- src/bluetooth.h | 58 ++++--- src/irda.c | 303 ++++++++++++++++++++++++------------ src/irda.h | 60 +++++--- src/serial.h | 34 ++-- src/serial_posix.c | 121 ++++++++++++--- src/serial_win32.c | 137 ++++++++++++++--- src/usbhid.c | 237 ++++++++++++++++++++++++++++ src/usbhid.h | 43 ++++++ src/uwatec_smart.c | 68 +++++--- 10 files changed, 1095 insertions(+), 342 deletions(-) diff --git a/src/bluetooth.c b/src/bluetooth.c index 1c3211e..337f1a9 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -50,6 +50,7 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" #ifdef _WIN32 #define DC_ADDRESS_FORMAT "%012I64X" @@ -64,7 +65,33 @@ #define ISINSTANCE(device) dc_iostream_isinstance((device), &dc_bluetooth_vtable) +struct dc_bluetooth_device_t { + dc_bluetooth_address_t address; + char name[248]; +}; + #ifdef BLUETOOTH +static dc_status_t dc_bluetooth_iterator_next (dc_iterator_t *iterator, void *item); +static dc_status_t dc_bluetooth_iterator_free (dc_iterator_t *iterator); + +typedef struct dc_bluetooth_iterator_t { + dc_iterator_t base; +#ifdef _WIN32 + HANDLE hLookup; +#else + int fd; + inquiry_info *devices; + size_t count; + size_t current; +#endif +} dc_bluetooth_iterator_t; + +static const dc_iterator_vtable_t dc_bluetooth_iterator_vtable = { + sizeof(dc_bluetooth_iterator_t), + dc_bluetooth_iterator_next, + dc_bluetooth_iterator_free, +}; + static const dc_iostream_vtable_t dc_bluetooth_vtable = { sizeof(dc_socket_t), dc_socket_set_timeout, /* set_timeout */ @@ -194,6 +221,220 @@ error: #endif #endif +dc_bluetooth_address_t +dc_bluetooth_device_get_address (dc_bluetooth_device_t *device) +{ + if (device == NULL) + return 0; + + return device->address; +} + +const char * +dc_bluetooth_device_get_name (dc_bluetooth_device_t *device) +{ + if (device == NULL || device->name[0] == '\0') + return NULL; + + return device->name; +} + +void +dc_bluetooth_device_free (dc_bluetooth_device_t *device) +{ + free (device); +} + +dc_status_t +dc_bluetooth_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_t *descriptor) +{ +#ifdef BLUETOOTH + dc_status_t status = DC_STATUS_SUCCESS; + dc_bluetooth_iterator_t *iterator = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + iterator = (dc_bluetooth_iterator_t *) dc_iterator_allocate (context, &dc_bluetooth_iterator_vtable); + if (iterator == NULL) { + SYSERROR (context, S_ENOMEM); + return DC_STATUS_NOMEMORY; + } + +#ifdef _WIN32 + WSAQUERYSET wsaq; + memset(&wsaq, 0, sizeof (wsaq)); + wsaq.dwSize = sizeof (wsaq); + wsaq.dwNameSpace = NS_BTH; + wsaq.lpcsaBuffer = NULL; + + HANDLE hLookup = NULL; + if (WSALookupServiceBegin(&wsaq, LUP_CONTAINERS | LUP_FLUSHCACHE, &hLookup) != 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == WSASERVICE_NOT_FOUND) { + // No remote bluetooth devices found. + hLookup = NULL; + } else { + SYSERROR (context, errcode); + status = dc_socket_syserror(errcode); + goto error_free; + } + } + + iterator->hLookup = hLookup; +#else + // Get the resource number for the first available bluetooth adapter. + int dev = hci_get_route (NULL); + if (dev < 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (context, errcode); + status = dc_socket_syserror(errcode); + goto error_free; + } + + // Open a socket to the bluetooth adapter. + int fd = hci_open_dev (dev); + if (fd < 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (context, errcode); + status = dc_socket_syserror(errcode); + goto error_free; + } + + // Perform the bluetooth device discovery. The inquiry lasts for at + // most MAX_PERIODS * 1.28 seconds, and at most MAX_DEVICES devices + // will be returned. + inquiry_info *devices = NULL; + int ndevices = hci_inquiry (dev, MAX_PERIODS, MAX_DEVICES, NULL, &devices, IREQ_CACHE_FLUSH); + if (ndevices < 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (context, errcode); + status = dc_socket_syserror(errcode); + goto error_close; + } + + iterator->fd = fd; + iterator->devices = devices; + iterator->count = ndevices; + iterator->current = 0; +#endif + + *out = (dc_iterator_t *) iterator; + + return DC_STATUS_SUCCESS; + +#ifndef _WIN32 +error_close: + hci_close_dev(fd); +#endif +error_free: + dc_iterator_deallocate ((dc_iterator_t *) iterator); + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +#ifdef BLUETOOTH +static dc_status_t +dc_bluetooth_iterator_next (dc_iterator_t *abstract, void *out) +{ + dc_bluetooth_iterator_t *iterator = (dc_bluetooth_iterator_t *) abstract; + dc_bluetooth_device_t *device = NULL; + +#ifdef _WIN32 + if (iterator->hLookup == NULL) { + return DC_STATUS_DONE; + } + + unsigned char buf[4096]; + LPWSAQUERYSET pwsaResults = (LPWSAQUERYSET) buf; + memset(pwsaResults, 0, sizeof(WSAQUERYSET)); + pwsaResults->dwSize = sizeof(WSAQUERYSET); + pwsaResults->dwNameSpace = NS_BTH; + pwsaResults->lpBlob = NULL; + + while (1) { + DWORD dwSize = sizeof(buf); + if (WSALookupServiceNext (iterator->hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &dwSize, pwsaResults) != 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == WSA_E_NO_MORE || errcode == WSAENOMORE) { + break; // No more results. + } + SYSERROR (abstract->context, errcode); + return dc_socket_syserror(errcode); + } + + if (pwsaResults->dwNumberOfCsAddrs == 0 || + pwsaResults->lpcsaBuffer == NULL || + pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr == NULL) { + ERROR (abstract->context, "Invalid results returned"); + return DC_STATUS_IO; + } + + SOCKADDR_BTH *sa = (SOCKADDR_BTH *) pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr; + dc_bluetooth_address_t address = sa->btAddr; + const char *name = (char *) pwsaResults->lpszServiceInstanceName; +#else + while (iterator->current < iterator->count) { + inquiry_info *dev = &iterator->devices[iterator->current++]; + + dc_bluetooth_address_t address = dc_address_get (&dev->bdaddr); + + // Get the user friendly name. + char buf[HCI_MAX_NAME_LENGTH], *name = buf; + int rc = hci_read_remote_name (iterator->fd, &dev->bdaddr, sizeof(buf), buf, 0); + if (rc < 0) { + name = NULL; + } + + // Null terminate the string. + buf[sizeof(buf) - 1] = '\0'; +#endif + + INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", + address, name ? name : ""); + + device = (dc_bluetooth_device_t *) malloc (sizeof(dc_bluetooth_device_t)); + if (device == NULL) { + SYSERROR (abstract->context, S_ENOMEM); + return DC_STATUS_NOMEMORY; + } + + device->address = address; + if (name) { + strncpy(device->name, name, sizeof(device->name) - 1); + device->name[sizeof(device->name) - 1] = '\0'; + } else { + memset(device->name, 0, sizeof(device->name)); + } + + *(dc_bluetooth_device_t **) out = device; + + return DC_STATUS_SUCCESS; + } + + return DC_STATUS_DONE; +} + +static dc_status_t +dc_bluetooth_iterator_free (dc_iterator_t *abstract) +{ + dc_bluetooth_iterator_t *iterator = (dc_bluetooth_iterator_t *) abstract; + +#ifdef _WIN32 + if (iterator->hLookup) { + WSALookupServiceEnd (iterator->hLookup); + } +#else + bt_free(iterator->devices); + hci_close_dev(iterator->fd); +#endif + + return DC_STATUS_SUCCESS; +} +#endif + dc_status_t dc_bluetooth_open (dc_iostream_t **out, dc_context_t *context) { @@ -233,141 +474,6 @@ error_free: #endif } -dc_status_t -dc_bluetooth_discover (dc_iostream_t *abstract, dc_bluetooth_callback_t callback, void *userdata) -{ -#ifdef BLUETOOTH - dc_status_t status = DC_STATUS_SUCCESS; - - if (!ISINSTANCE (abstract)) - return DC_STATUS_INVALIDARGS; - -#ifdef _WIN32 - WSAQUERYSET wsaq; - memset(&wsaq, 0, sizeof (wsaq)); - wsaq.dwSize = sizeof (wsaq); - wsaq.dwNameSpace = NS_BTH; - wsaq.lpcsaBuffer = NULL; - - HANDLE hLookup; - if (WSALookupServiceBegin(&wsaq, LUP_CONTAINERS | LUP_FLUSHCACHE, &hLookup) != 0) { - s_errcode_t errcode = S_ERRNO; - if (errcode == WSASERVICE_NOT_FOUND) { - // No remote bluetooth devices found. - status = DC_STATUS_SUCCESS; - } else { - SYSERROR (abstract->context, errcode); - status = dc_socket_syserror(errcode); - } - goto error_exit; - } - - unsigned char buf[4096]; - LPWSAQUERYSET pwsaResults = (LPWSAQUERYSET) buf; - memset(pwsaResults, 0, sizeof(WSAQUERYSET)); - pwsaResults->dwSize = sizeof(WSAQUERYSET); - pwsaResults->dwNameSpace = NS_BTH; - pwsaResults->lpBlob = NULL; - - while (1) { - DWORD dwSize = sizeof(buf); - if (WSALookupServiceNext (hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &dwSize, pwsaResults) != 0) { - s_errcode_t errcode = S_ERRNO; - if (errcode == WSA_E_NO_MORE || errcode == WSAENOMORE) { - break; // No more results. - } - SYSERROR (abstract->context, errcode); - status = dc_socket_syserror(errcode); - goto error_close; - } - - if (pwsaResults->dwNumberOfCsAddrs == 0 || - pwsaResults->lpcsaBuffer == NULL || - pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr == NULL) { - ERROR (abstract->context, "Invalid results returned"); - status = DC_STATUS_IO; - goto error_close; - } - - SOCKADDR_BTH *sa = (SOCKADDR_BTH *) pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr; - dc_bluetooth_address_t address = sa->btAddr; - const char *name = (char *) pwsaResults->lpszServiceInstanceName; - - INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", address, name); - - if (callback) callback (address, name, userdata); - - } - -error_close: - WSALookupServiceEnd (hLookup); -#else - // Get the resource number for the first available bluetooth adapter. - int dev = hci_get_route (NULL); - if (dev < 0) { - s_errcode_t errcode = S_ERRNO; - SYSERROR (abstract->context, errcode); - status = dc_socket_syserror(errcode); - goto error_exit; - } - - // Open a socket to the bluetooth adapter. - int fd = hci_open_dev (dev); - if (fd < 0) { - s_errcode_t errcode = S_ERRNO; - SYSERROR (abstract->context, errcode); - status = dc_socket_syserror(errcode); - goto error_exit; - } - - // Allocate a buffer to store the results of the discovery. - inquiry_info *devices = (inquiry_info *) malloc (MAX_DEVICES * sizeof(inquiry_info)); - if (devices == NULL) { - s_errcode_t errcode = S_ERRNO; - SYSERROR (abstract->context, errcode); - status = dc_socket_syserror(errcode); - goto error_close; - } - - // Perform the bluetooth device discovery. The inquiry lasts for at - // most MAX_PERIODS * 1.28 seconds, and at most MAX_DEVICES devices - // will be returned. - int ndevices = hci_inquiry (dev, MAX_PERIODS, MAX_DEVICES, NULL, &devices, IREQ_CACHE_FLUSH); - if (ndevices < 0) { - s_errcode_t errcode = S_ERRNO; - SYSERROR (abstract->context, errcode); - status = dc_socket_syserror(errcode); - goto error_free; - } - - for (unsigned int i = 0; i < ndevices; ++i) { - dc_bluetooth_address_t address = dc_address_get (&devices[i].bdaddr); - - // Get the user friendly name. - char buf[HCI_MAX_NAME_LENGTH], *name = buf; - int rc = hci_read_remote_name (fd, &devices[i].bdaddr, sizeof(buf), buf, 0); - if (rc < 0) { - name = NULL; - } - - INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", address, name); - - if (callback) callback (address, name, userdata); - } - -error_free: - free(devices); -error_close: - hci_close_dev(fd); -#endif - -error_exit: - return status; -#else - return DC_STATUS_UNSUPPORTED; -#endif -} - dc_status_t dc_bluetooth_connect (dc_iostream_t *abstract, dc_bluetooth_address_t address, unsigned int port) { diff --git a/src/bluetooth.h b/src/bluetooth.h index 1a8e947..264e833 100644 --- a/src/bluetooth.h +++ b/src/bluetooth.h @@ -25,6 +25,8 @@ #include #include #include +#include +#include #ifdef __cplusplus extern "C" { @@ -40,13 +42,45 @@ typedef unsigned long long dc_bluetooth_address_t; #endif /** - * Bluetooth enumeration callback. - * - * @param[in] address The bluetooth device address. - * @param[in] name The bluetooth device name. - * @param[in] userdata The user data pointer. + * Opaque object representing a bluetooth device. */ -typedef void (*dc_bluetooth_callback_t) (dc_bluetooth_address_t address, const char *name, void *userdata); +typedef struct dc_bluetooth_device_t dc_bluetooth_device_t; + +/** + * Get the address of the bluetooth device. + * + * @param[in] device A valid bluetooth device. + */ +dc_bluetooth_address_t +dc_bluetooth_device_get_address (dc_bluetooth_device_t *device); + +/** + * Get the name of the bluetooth device. + * + * @param[in] device A valid bluetooth device. + */ +const char * +dc_bluetooth_device_get_name (dc_bluetooth_device_t *device); + +/** + * Destroy the bluetooth device and free all resources. + * + * @param[in] device A valid bluetooth device. + */ +void +dc_bluetooth_device_free (dc_bluetooth_device_t *device); + +/** + * Create an iterator to enumerate the bluetooth devices. + * + * @param[out] iterator A location to store the iterator. + * @param[in] context A valid context object. + * @param[in] descriptor A valid device descriptor or NULL. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_iterator_new (dc_iterator_t **iterator, dc_context_t *context, dc_descriptor_t *descriptor); /** * Open an bluetooth connection. @@ -59,18 +93,6 @@ typedef void (*dc_bluetooth_callback_t) (dc_bluetooth_address_t address, const c dc_status_t dc_bluetooth_open (dc_iostream_t **iostream, dc_context_t *context); -/** - * Enumerate the bluetooth devices. - * - * @param[in] iostream A valid bluetooth connection. - * @param[in] callback The callback function to call. - * @param[in] userdata User data to pass to the callback function. - * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code - * on failure. - */ -dc_status_t -dc_bluetooth_discover (dc_iostream_t *iostream, dc_bluetooth_callback_t callback, void *userdata); - /** * Connect to an bluetooth device. * diff --git a/src/irda.c b/src/irda.c index b38a502..7106dbc 100644 --- a/src/irda.c +++ b/src/irda.c @@ -47,12 +47,46 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" #include "array.h" #include "platform.h" #define ISINSTANCE(device) dc_iostream_isinstance((device), &dc_irda_vtable) +#define DISCOVER_MAX_DEVICES 16 // Maximum number of devices. +#define DISCOVER_MAX_RETRIES 4 // Maximum number of retries. + +#ifdef _WIN32 +#define DISCOVER_BUFSIZE sizeof (DEVICELIST) + \ + sizeof (IRDA_DEVICE_INFO) * (DISCOVER_MAX_DEVICES - 1) +#else +#define DISCOVER_BUFSIZE sizeof (struct irda_device_list) + \ + sizeof (struct irda_device_info) * (DISCOVER_MAX_DEVICES - 1) +#endif + +struct dc_irda_device_t { + unsigned int address; + unsigned int charset; + unsigned int hints; + char name[22]; +}; + #ifdef IRDA +static dc_status_t dc_irda_iterator_next (dc_iterator_t *iterator, void *item); + +typedef struct dc_irda_iterator_t { + dc_iterator_t base; + dc_irda_device_t items[DISCOVER_MAX_DEVICES]; + size_t count; + size_t current; +} dc_irda_iterator_t; + +static const dc_iterator_vtable_t dc_irda_iterator_vtable = { + sizeof(dc_irda_iterator_t), + dc_irda_iterator_next, + NULL, +}; + static const dc_iostream_vtable_t dc_irda_vtable = { sizeof(dc_socket_t), dc_socket_set_timeout, /* set_timeout */ @@ -73,6 +107,180 @@ static const dc_iostream_vtable_t dc_irda_vtable = { }; #endif +unsigned int +dc_irda_device_get_address (dc_irda_device_t *device) +{ + if (device == NULL) + return 0; + + return device->address; +} + +const char * +dc_irda_device_get_name (dc_irda_device_t *device) +{ + if (device == NULL || device->name[0] == '\0') + return NULL; + + return device->name; +} + +void +dc_irda_device_free (dc_irda_device_t *device) +{ + free (device); +} + +dc_status_t +dc_irda_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_t *descriptor) +{ +#ifdef IRDA + dc_status_t status = DC_STATUS_SUCCESS; + dc_irda_iterator_t *iterator = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + iterator = (dc_irda_iterator_t *) dc_iterator_allocate (context, &dc_irda_iterator_vtable); + if (iterator == NULL) { + SYSERROR (context, S_ENOMEM); + return DC_STATUS_NOMEMORY; + } + + // Initialize the socket library. + status = dc_socket_init (context); + if (status != DC_STATUS_SUCCESS) { + goto error_free; + } + + // Open the socket. + int fd = socket (AF_IRDA, SOCK_STREAM, 0); + if (fd == S_INVALID) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (context, errcode); + status = dc_socket_syserror(errcode); + goto error_socket_exit; + } + + unsigned char data[DISCOVER_BUFSIZE] = {0}; +#ifdef _WIN32 + DEVICELIST *list = (DEVICELIST *) data; +#else + struct irda_device_list *list = (struct irda_device_list *) data; +#endif + s_socklen_t size = sizeof (data); + + int rc = 0; + unsigned int nretries = 0; + while ((rc = getsockopt (fd, SOL_IRLMP, IRLMP_ENUMDEVICES, (char *) data, &size)) != 0 || +#ifdef _WIN32 + list->numDevice == 0) +#else + list->len == 0) +#endif + { + // Automatically retry the discovery when no devices were found. + // On Linux, getsockopt fails with EAGAIN when no devices are + // discovered, while on Windows it succeeds and sets the number + // of devices to zero. Both situations are handled the same here. + if (rc != 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode != S_EAGAIN) { + SYSERROR (context, errcode); + status = dc_socket_syserror(errcode); + goto error_socket_close; + } + } + + // Abort if the maximum number of retries is reached. + if (nretries++ >= DISCOVER_MAX_RETRIES) { + break; + } + + // Restore the size parameter in case it was + // modified by the previous getsockopt call. + size = sizeof (data); + +#ifdef _WIN32 + Sleep (1000); +#else + sleep (1); +#endif + } + + S_CLOSE (fd); + dc_socket_exit (context); + + unsigned int count = 0; +#ifdef _WIN32 + for (size_t i = 0; i < list->numDevice; ++i) { + const char *name = list->Device[i].irdaDeviceName; + unsigned int address = array_uint32_le (list->Device[i].irdaDeviceID); + unsigned int charset = list->Device[i].irdaCharSet; + unsigned int hints = (list->Device[i].irdaDeviceHints1 << 8) + + list->Device[i].irdaDeviceHints2; +#else + for (size_t i = 0; i < list->len; ++i) { + const char *name = list->dev[i].info; + unsigned int address = list->dev[i].daddr; + unsigned int charset = list->dev[i].charset; + unsigned int hints = array_uint16_be (list->dev[i].hints); +#endif + + INFO (context, "Discover: address=%08x, name=%s, charset=%02x, hints=%04x", + address, name, charset, hints); + + strncpy(iterator->items[count].name, name, sizeof(iterator->items[count].name) - 1); + iterator->items[count].name[sizeof(iterator->items[count].name) - 1] = '\0'; + iterator->items[count].address = address; + iterator->items[count].charset = charset; + iterator->items[count].hints = hints; + count++; + } + + iterator->current = 0; + iterator->count = count; + + *out = (dc_iterator_t *) iterator; + + return DC_STATUS_SUCCESS; + +error_socket_close: + S_CLOSE (fd); +error_socket_exit: + dc_socket_exit (context); +error_free: + dc_iterator_deallocate ((dc_iterator_t *) iterator); + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +#ifdef IRDA +static dc_status_t +dc_irda_iterator_next (dc_iterator_t *abstract, void *out) +{ + dc_irda_iterator_t *iterator = (dc_irda_iterator_t *) abstract; + dc_irda_device_t *device = NULL; + + if (iterator->current >= iterator->count) + return DC_STATUS_DONE; + + device = (dc_irda_device_t *) malloc (sizeof(dc_irda_device_t)); + if (device == NULL) { + SYSERROR (abstract->context, S_ENOMEM); + return DC_STATUS_NOMEMORY; + } + + *device = iterator->items[iterator->current++]; + + *(dc_irda_device_t **) out = device; + + return DC_STATUS_SUCCESS; +} +#endif + dc_status_t dc_irda_open (dc_iostream_t **out, dc_context_t *context) { @@ -108,101 +316,6 @@ error_free: #endif } -#define DISCOVER_MAX_DEVICES 16 // Maximum number of devices. -#define DISCOVER_MAX_RETRIES 4 // Maximum number of retries. - -#ifdef _WIN32 -#define DISCOVER_BUFSIZE sizeof (DEVICELIST) + \ - sizeof (IRDA_DEVICE_INFO) * (DISCOVER_MAX_DEVICES - 1) -#else -#define DISCOVER_BUFSIZE sizeof (struct irda_device_list) + \ - sizeof (struct irda_device_info) * (DISCOVER_MAX_DEVICES - 1) -#endif - -dc_status_t -dc_irda_discover (dc_iostream_t *abstract, dc_irda_callback_t callback, void *userdata) -{ -#ifdef IRDA - dc_socket_t *device = (dc_socket_t *) abstract; - - if (!ISINSTANCE (abstract)) - return DC_STATUS_INVALIDARGS; - - unsigned char data[DISCOVER_BUFSIZE] = {0}; -#ifdef _WIN32 - DEVICELIST *list = (DEVICELIST *) data; - int size = sizeof (data); -#else - struct irda_device_list *list = (struct irda_device_list *) data; - socklen_t size = sizeof (data); -#endif - - int rc = 0; - unsigned int nretries = 0; - while ((rc = getsockopt (device->fd, SOL_IRLMP, IRLMP_ENUMDEVICES, (char*) data, &size)) != 0 || -#ifdef _WIN32 - list->numDevice == 0) -#else - list->len == 0) -#endif - { - // Automatically retry the discovery when no devices were found. - // On Linux, getsockopt fails with EAGAIN when no devices are - // discovered, while on Windows it succeeds and sets the number - // of devices to zero. Both situations are handled the same here. - if (rc != 0) { - s_errcode_t errcode = S_ERRNO; - if (errcode != S_EAGAIN) { - SYSERROR (abstract->context, errcode); - return dc_socket_syserror(errcode); - } - } - - // Abort if the maximum number of retries is reached. - if (nretries++ >= DISCOVER_MAX_RETRIES) - return DC_STATUS_SUCCESS; - - // Restore the size parameter in case it was - // modified by the previous getsockopt call. - size = sizeof (data); - -#ifdef _WIN32 - Sleep (1000); -#else - sleep (1); -#endif - } - - if (callback) { -#ifdef _WIN32 - for (unsigned int i = 0; i < list->numDevice; ++i) { - const char *name = list->Device[i].irdaDeviceName; - unsigned int address = array_uint32_le (list->Device[i].irdaDeviceID); - unsigned int charset = list->Device[i].irdaCharSet; - unsigned int hints = (list->Device[i].irdaDeviceHints1 << 8) + - list->Device[i].irdaDeviceHints2; -#else - for (unsigned int i = 0; i < list->len; ++i) { - const char *name = list->dev[i].info; - unsigned int address = list->dev[i].daddr; - unsigned int charset = list->dev[i].charset; - unsigned int hints = array_uint16_be (list->dev[i].hints); -#endif - - INFO (abstract->context, - "Discover: address=%08x, name=%s, charset=%02x, hints=%04x", - address, name, charset, hints); - - callback (address, name, charset, hints, userdata); - } - } - - return DC_STATUS_SUCCESS; -#else - return DC_STATUS_UNSUPPORTED; -#endif -} - dc_status_t dc_irda_connect_name (dc_iostream_t *abstract, unsigned int address, const char *name) { diff --git a/src/irda.h b/src/irda.h index c413963..c0ab8d6 100644 --- a/src/irda.h +++ b/src/irda.h @@ -25,21 +25,53 @@ #include #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /** - * IrDA enumeration callback. - * - * @param[in] address The IrDA device address. - * @param[in] name The IrDA device name. - * @param[in] charset The IrDA device character set. - * @param[in] hints The IrDA device hints. - * @param[in] userdata The user data pointer. + * Opaque object representing an IrDA device. */ -typedef void (*dc_irda_callback_t) (unsigned int address, const char *name, unsigned int charset, unsigned int hints, void *userdata); +typedef struct dc_irda_device_t dc_irda_device_t; + +/** + * Get the address of the IrDA device. + * + * @param[in] device A valid IrDA device. + */ +unsigned int +dc_irda_device_get_address (dc_irda_device_t *device); + +/** + * Get the name of the IrDA device. + * + * @param[in] device A valid IrDA device. + */ +const char * +dc_irda_device_get_name (dc_irda_device_t *device); + +/** + * Destroy the IrDA device and free all resources. + * + * @param[in] device A valid IrDA device. + */ +void +dc_irda_device_free (dc_irda_device_t *device); + +/** + * Create an iterator to enumerate the IrDA devices. + * + * @param[out] iterator A location to store the iterator. + * @param[in] context A valid context object. + * @param[in] descriptor A valid device descriptor or NULL. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_irda_iterator_new (dc_iterator_t **iterator, dc_context_t *context, dc_descriptor_t *descriptor); /** * Open an IrDA connection. @@ -52,18 +84,6 @@ typedef void (*dc_irda_callback_t) (unsigned int address, const char *name, unsi dc_status_t dc_irda_open (dc_iostream_t **iostream, dc_context_t *context); -/** - * Enumerate the IrDA devices. - * - * @param[in] iostream A valid IrDA connection. - * @param[in] callback The callback function to call. - * @param[in] userdata User data to pass to the callback function. - * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code - * on failure. - */ -dc_status_t -dc_irda_discover (dc_iostream_t *iostream, dc_irda_callback_t callback, void *userdata); - /** * Connect to an IrDA device. * diff --git a/src/serial.h b/src/serial.h index d6620e0..844c5b2 100644 --- a/src/serial.h +++ b/src/serial.h @@ -25,29 +25,45 @@ #include #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /** - * Serial enumeration callback. - * - * @param[in] name The name of the device node. - * @param[in] userdata The user data pointer. + * Opaque object representing a serial device. */ -typedef void (*dc_serial_callback_t) (const char *name, void *userdata); +typedef struct dc_serial_device_t dc_serial_device_t; /** - * Enumerate the serial ports. + * Get the device node of the serial device. * - * @param[in] callback The callback function to call. - * @param[in] userdata User data to pass to the callback function. + * @param[in] device A valid serial device. + */ +const char * +dc_serial_device_get_name (dc_serial_device_t *device); + +/** + * Destroy the serial device and free all resources. + * + * @param[in] device A valid serial device. + */ +void +dc_serial_device_free (dc_serial_device_t *device); + +/** + * Create an iterator to enumerate the serial devices. + * + * @param[out] iterator A location to store the iterator. + * @param[in] context A valid context object. + * @param[in] descriptor A valid device descriptor or NULL. * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code * on failure. */ dc_status_t -dc_serial_enumerate (dc_serial_callback_t callback, void *userdata); +dc_serial_iterator_new (dc_iterator_t **iterator, dc_context_t *context, dc_descriptor_t *descriptor); /** * Open a serial connection. diff --git a/src/serial_posix.c b/src/serial_posix.c index 356bc77..4108ab8 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -58,6 +58,12 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" + +#define DIRNAME "/dev" + +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); @@ -75,6 +81,15 @@ static dc_status_t dc_serial_purge (dc_iostream_t *iostream, dc_direction_t dire static dc_status_t dc_serial_sleep (dc_iostream_t *iostream, unsigned int milliseconds); static dc_status_t dc_serial_close (dc_iostream_t *iostream); +struct dc_serial_device_t { + char name[256]; +}; + +typedef struct dc_serial_iterator_t { + dc_iterator_t base; + DIR *dp; +} dc_serial_iterator_t; + typedef struct dc_serial_t { dc_iostream_t base; /* @@ -94,6 +109,12 @@ typedef struct dc_serial_t { unsigned int nbits; } dc_serial_t; +static const dc_iterator_vtable_t dc_serial_iterator_vtable = { + sizeof(dc_serial_iterator_t), + dc_serial_iterator_next, + dc_serial_iterator_free, +}; + static const dc_iostream_vtable_t dc_serial_vtable = { sizeof(dc_serial_t), dc_serial_set_timeout, /* set_timeout */ @@ -131,12 +152,60 @@ syserror(int errcode) } } -dc_status_t -dc_serial_enumerate (dc_serial_callback_t callback, void *userdata) +const char * +dc_serial_device_get_name (dc_serial_device_t *device) { - DIR *dp = NULL; + if (device == NULL || device->name[0] == '\0') + return NULL; + + return device->name; +} + +void +dc_serial_device_free (dc_serial_device_t *device) +{ + free (device); +} + +dc_status_t +dc_serial_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_t *descriptor) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_serial_iterator_t *iterator = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + iterator = (dc_serial_iterator_t *) dc_iterator_allocate (context, &dc_serial_iterator_vtable); + if (iterator == NULL) { + SYSERROR (context, ENOMEM); + return DC_STATUS_NOMEMORY; + } + + iterator->dp = opendir (DIRNAME); + if (iterator->dp == NULL) { + int errcode = errno; + SYSERROR (context, errcode); + status = syserror (errcode); + goto error_free; + } + + *out = (dc_iterator_t *) iterator; + + return DC_STATUS_SUCCESS; + +error_free: + dc_iterator_deallocate ((dc_iterator_t *) iterator); + return status; +} + +static dc_status_t +dc_serial_iterator_next (dc_iterator_t *abstract, void *out) +{ + dc_serial_iterator_t *iterator = (dc_serial_iterator_t *) abstract; + dc_serial_device_t *device = NULL; + struct dirent *ep = NULL; - const char *dirname = "/dev"; const char *patterns[] = { #if defined (__APPLE__) "tty.*", @@ -149,28 +218,40 @@ dc_serial_enumerate (dc_serial_callback_t callback, void *userdata) NULL }; - dp = opendir (dirname); - if (dp == NULL) { - return DC_STATUS_IO; - } - - while ((ep = readdir (dp)) != NULL) { + while ((ep = readdir (iterator->dp)) != NULL) { for (size_t i = 0; patterns[i] != NULL; ++i) { - if (fnmatch (patterns[i], ep->d_name, 0) == 0) { - char filename[1024]; - int n = snprintf (filename, sizeof (filename), "%s/%s", dirname, ep->d_name); - if (n >= sizeof (filename)) { - closedir (dp); - return DC_STATUS_NOMEMORY; - } + if (fnmatch (patterns[i], ep->d_name, 0) != 0) + continue; - callback (filename, userdata); - break; + char filename[sizeof(device->name)]; + int n = snprintf (filename, sizeof (filename), "%s/%s", DIRNAME, ep->d_name); + if (n < 0 || (size_t) n >= sizeof (filename)) { + return DC_STATUS_NOMEMORY; } + + device = (dc_serial_device_t *) malloc (sizeof(dc_serial_device_t)); + if (device == NULL) { + SYSERROR (abstract->context, ENOMEM); + return DC_STATUS_NOMEMORY; + } + + strncpy(device->name, filename, sizeof(device->name)); + + *(dc_serial_device_t **) out = device; + + return DC_STATUS_SUCCESS; } } - closedir (dp); + return DC_STATUS_DONE; +} + +static dc_status_t +dc_serial_iterator_free (dc_iterator_t *abstract) +{ + dc_serial_iterator_t *iterator = (dc_serial_iterator_t *) abstract; + + closedir (iterator->dp); return DC_STATUS_SUCCESS; } diff --git a/src/serial_win32.c b/src/serial_win32.c index 4454a74..3968d3f 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -29,6 +29,10 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" + +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); @@ -46,6 +50,17 @@ static dc_status_t dc_serial_purge (dc_iostream_t *iostream, dc_direction_t dire static dc_status_t dc_serial_sleep (dc_iostream_t *iostream, unsigned int milliseconds); static dc_status_t dc_serial_close (dc_iostream_t *iostream); +struct dc_serial_device_t { + char name[256]; +}; + +typedef struct dc_serial_iterator_t { + dc_iterator_t base; + HKEY hKey; + DWORD count; + DWORD current; +} dc_serial_iterator_t; + typedef struct dc_serial_t { dc_iostream_t base; /* @@ -65,6 +80,12 @@ typedef struct dc_serial_t { unsigned int nbits; } dc_serial_t; +static const dc_iterator_vtable_t dc_serial_iterator_vtable = { + sizeof(dc_serial_iterator_t), + dc_serial_iterator_next, + dc_serial_iterator_free, +}; + static const dc_iostream_vtable_t dc_serial_vtable = { sizeof(dc_serial_t), dc_serial_set_timeout, /* set_timeout */ @@ -101,37 +122,92 @@ syserror(DWORD errcode) } } -dc_status_t -dc_serial_enumerate (dc_serial_callback_t callback, void *userdata) +const char * +dc_serial_device_get_name (dc_serial_device_t *device) { + if (device == NULL || device->name[0] == '\0') + return NULL; + + return device->name; +} + +void +dc_serial_device_free (dc_serial_device_t *device) +{ + free (device); +} + +dc_status_t +dc_serial_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_t *descriptor) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_serial_iterator_t *iterator = NULL; + HKEY hKey = NULL; + DWORD count = 0; + LONG rc = 0; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + iterator = (dc_serial_iterator_t *) dc_iterator_allocate (context, &dc_serial_iterator_vtable); + if (iterator == NULL) { + SYSERROR (context, ERROR_OUTOFMEMORY); + return DC_STATUS_NOMEMORY; + } + // Open the registry key. - HKEY hKey; - LONG rc = RegOpenKeyExA (HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &hKey); + rc = RegOpenKeyExA (HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &hKey); if (rc != ERROR_SUCCESS) { - if (rc == ERROR_FILE_NOT_FOUND) - return DC_STATUS_SUCCESS; - else - return DC_STATUS_IO; + if (rc == ERROR_FILE_NOT_FOUND) { + hKey = NULL; + } else { + SYSERROR (context, rc); + status = syserror (rc); + goto error_free; + } } // Get the number of values. - DWORD count = 0; - rc = RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL); - if (rc != ERROR_SUCCESS) { - RegCloseKey(hKey); - return DC_STATUS_IO; + if (hKey) { + rc = RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL); + if (rc != ERROR_SUCCESS) { + SYSERROR (context, rc); + status = syserror (rc); + goto error_close; + } } - for (DWORD i = 0; i < count; ++i) { + iterator->hKey = hKey; + iterator->count = count; + iterator->current = 0; + + *out = (dc_iterator_t *) iterator; + + return DC_STATUS_SUCCESS; + +error_close: + RegCloseKey (hKey); +error_free: + dc_iterator_deallocate ((dc_iterator_t *) iterator); + return status; +} + +static dc_status_t +dc_serial_iterator_next (dc_iterator_t *abstract, void *out) +{ + dc_serial_iterator_t *iterator = (dc_serial_iterator_t *) abstract; + dc_serial_device_t *device = NULL; + + while (iterator->current < iterator->count) { // Get the value name, data and type. - char name[512], data[512]; + char name[256], data[sizeof(device->name)]; DWORD name_len = sizeof (name); DWORD data_len = sizeof (data); DWORD type = 0; - rc = RegEnumValueA (hKey, i, name, &name_len, NULL, &type, (LPBYTE) data, &data_len); + LONG rc = RegEnumValueA (iterator->hKey, iterator->current++, name, &name_len, NULL, &type, (LPBYTE) data, &data_len); if (rc != ERROR_SUCCESS) { - RegCloseKey(hKey); - return DC_STATUS_IO; + SYSERROR (abstract->context, rc); + return syserror (rc); } // Ignore non-string values. @@ -140,17 +216,36 @@ dc_serial_enumerate (dc_serial_callback_t callback, void *userdata) // Prevent a possible buffer overflow. if (data_len >= sizeof (data)) { - RegCloseKey(hKey); return DC_STATUS_NOMEMORY; } // Null terminate the string. data[data_len] = 0; - callback (data, userdata); + device = (dc_serial_device_t *) malloc (sizeof(dc_serial_device_t)); + if (device == NULL) { + SYSERROR (abstract->context, ERROR_OUTOFMEMORY); + return DC_STATUS_NOMEMORY; + } + + strncpy(device->name, data, sizeof(device->name)); + + *(dc_serial_device_t **) out = device; + + return DC_STATUS_SUCCESS; } - RegCloseKey(hKey); + return DC_STATUS_DONE; +} + +static dc_status_t +dc_serial_iterator_free (dc_iterator_t *abstract) +{ + dc_serial_iterator_t *iterator = (dc_serial_iterator_t *) abstract; + + if (iterator->hKey) { + RegCloseKey (iterator->hKey); + } return DC_STATUS_SUCCESS; } diff --git a/src/usbhid.c b/src/usbhid.c index dab5efe..ba16b3b 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -54,6 +54,7 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" #include "platform.h" #ifdef _WIN32 @@ -66,12 +67,30 @@ typedef pthread_mutex_t dc_mutex_t; #define ISINSTANCE(device) dc_iostream_isinstance((device), &dc_usbhid_vtable) +struct dc_usbhid_device_t { + unsigned short vid, pid; +}; + #ifdef USBHID +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_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); +typedef struct dc_usbhid_iterator_t { + dc_iterator_t base; +#if defined(USE_LIBUSB) + struct libusb_device **devices; + size_t count; + size_t current; +#elif defined(USE_HIDAPI) + struct hid_device_info *devices, *current; +#endif +} dc_usbhid_iterator_t; + typedef struct dc_usbhid_t { /* Base class. */ dc_iostream_t base; @@ -88,6 +107,12 @@ typedef struct dc_usbhid_t { #endif } dc_usbhid_t; +static const dc_iterator_vtable_t dc_usbhid_iterator_vtable = { + sizeof(dc_usbhid_iterator_t), + dc_usbhid_iterator_next, + dc_usbhid_iterator_free, +}; + static const dc_iostream_vtable_t dc_usbhid_vtable = { sizeof(dc_usbhid_t), dc_usbhid_set_timeout, /* set_timeout */ @@ -211,6 +236,218 @@ dc_usbhid_exit (void) } #endif +unsigned int +dc_usbhid_device_get_vid (dc_usbhid_device_t *device) +{ + if (device == NULL) + return 0; + + return device->vid; +} + +unsigned int +dc_usbhid_device_get_pid (dc_usbhid_device_t *device) +{ + if (device == NULL) + return 0; + + return device->pid; +} + +void +dc_usbhid_device_free(dc_usbhid_device_t *device) +{ + free (device); +} + +dc_status_t +dc_usbhid_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_t *descriptor) +{ +#ifdef USBHID + dc_status_t status = DC_STATUS_SUCCESS; + dc_usbhid_iterator_t *iterator = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + iterator = (dc_usbhid_iterator_t *) dc_iterator_allocate (context, &dc_usbhid_iterator_vtable); + if (iterator == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Initialize the usb library. + status = dc_usbhid_init (context); + if (status != DC_STATUS_SUCCESS) { + goto error_free; + } + +#if defined(USE_LIBUSB) + // Enumerate the USB devices. + struct libusb_device **devices = NULL; + ssize_t ndevices = libusb_get_device_list (g_usbhid_ctx, &devices); + if (ndevices < 0) { + ERROR (context, "Failed to enumerate the usb devices (%s).", + libusb_error_name (ndevices)); + status = syserror (ndevices); + goto error_usb_exit; + } + + iterator->devices = devices; + iterator->count = ndevices; + iterator->current = 0; +#elif defined(USE_HIDAPI) + struct hid_device_info *devices = hid_enumerate(0x0, 0x0); + if (devices == NULL) { + status = DC_STATUS_IO; + goto error_usb_exit; + } + + iterator->devices = devices; + iterator->current = devices; +#endif + + *out = (dc_iterator_t *) iterator; + + return DC_STATUS_SUCCESS; + +error_usb_exit: + dc_usbhid_exit (); +error_free: + dc_iterator_deallocate ((dc_iterator_t *) iterator); + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +#ifdef USBHID +static dc_status_t +dc_usbhid_iterator_next (dc_iterator_t *abstract, void *out) +{ + dc_usbhid_iterator_t *iterator = (dc_usbhid_iterator_t *) abstract; + dc_usbhid_device_t *device = NULL; + +#if defined(USE_LIBUSB) + while (iterator->current < iterator->count) { + struct libusb_device *current = iterator->devices[iterator->current++]; + + // Get the device descriptor. + struct libusb_device_descriptor dev; + int rc = libusb_get_device_descriptor (current, &dev); + if (rc < 0) { + ERROR (abstract->context, "Failed to get the device descriptor (%s).", + libusb_error_name (rc)); + return syserror (rc); + } + + // Get the active configuration descriptor. + struct libusb_config_descriptor *config = NULL; + rc = libusb_get_active_config_descriptor (current, &config); + if (rc != LIBUSB_SUCCESS) { + ERROR (abstract->context, "Failed to get the configuration descriptor (%s).", + libusb_error_name (rc)); + return syserror (rc); + } + + // Find the first HID interface. + const struct libusb_interface_descriptor *interface = NULL; + for (unsigned int i = 0; i < config->bNumInterfaces; i++) { + const struct libusb_interface *iface = &config->interface[i]; + for (int j = 0; j < iface->num_altsetting; j++) { + const struct libusb_interface_descriptor *desc = &iface->altsetting[j]; + if (desc->bInterfaceClass == LIBUSB_CLASS_HID && interface == NULL) { + interface = desc; + } + } + } + + if (interface == NULL) { + libusb_free_config_descriptor (config); + continue; + } + + // Find the first input and output interrupt endpoints. + const struct libusb_endpoint_descriptor *ep_in = NULL, *ep_out = NULL; + for (unsigned int i = 0; i < interface->bNumEndpoints; i++) { + const struct libusb_endpoint_descriptor *desc = &interface->endpoint[i]; + + unsigned int type = desc->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK; + unsigned int direction = desc->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK; + + if (type != LIBUSB_TRANSFER_TYPE_INTERRUPT) { + continue; + } + + if (direction == LIBUSB_ENDPOINT_IN && ep_in == NULL) { + ep_in = desc; + } + + if (direction == LIBUSB_ENDPOINT_OUT && ep_out == NULL) { + ep_out = desc; + } + } + + if (ep_in == NULL || ep_out == NULL) { + libusb_free_config_descriptor (config); + continue; + } + + device = (dc_usbhid_device_t *) malloc (sizeof(dc_usbhid_device_t)); + if (device == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + libusb_free_config_descriptor (config); + return DC_STATUS_NOMEMORY; + } + + device->vid = dev.idVendor; + device->pid = dev.idProduct; + + *(dc_usbhid_device_t **) out = device; + + libusb_free_config_descriptor (config); + + return DC_STATUS_SUCCESS; + } +#elif defined(USE_HIDAPI) + while (iterator->current) { + struct hid_device_info *current = iterator->current; + iterator->current = current->next; + + device = (dc_usbhid_device_t *) malloc (sizeof(dc_usbhid_device_t)); + if (device == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + device->vid = current->vendor_id; + device->pid = current->product_id; + + *(dc_usbhid_device_t **) out = device; + + return DC_STATUS_SUCCESS; + } +#endif + + return DC_STATUS_DONE; +} + +static dc_status_t +dc_usbhid_iterator_free (dc_iterator_t *abstract) +{ + dc_usbhid_iterator_t *iterator = (dc_usbhid_iterator_t *) abstract; + +#if defined(USE_LIBUSB) + libusb_free_device_list (iterator->devices, 1); +#elif defined(USE_HIDAPI) + hid_free_enumeration (iterator->devices); +#endif + dc_usbhid_exit (); + + return DC_STATUS_SUCCESS; +} +#endif + dc_status_t dc_usbhid_open (dc_iostream_t **out, dc_context_t *context, unsigned int vid, unsigned int pid) { diff --git a/src/usbhid.h b/src/usbhid.h index 1e85f0a..16b302b 100644 --- a/src/usbhid.h +++ b/src/usbhid.h @@ -25,11 +25,54 @@ #include #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ +/** + * Opaque object representing a USB HID device. + */ +typedef struct dc_usbhid_device_t dc_usbhid_device_t; + +/** + * Get the vendor id (VID) of the USB HID device. + * + * @param[in] device A valid USB HID device. + */ +unsigned int +dc_usbhid_device_get_vid (dc_usbhid_device_t *device); + +/** + * Get the product id (PID) of the USB HID device. + * + * @param[in] device A valid USB HID device. + */ +unsigned int +dc_usbhid_device_get_pid (dc_usbhid_device_t *device); + +/** + * Destroy the USB HID device and free all resources. + * + * @param[in] device A valid USB HID device. + */ +void +dc_usbhid_device_free(dc_usbhid_device_t *device); + +/** + * Create an iterator to enumerate the USB HID devices. + * + * @param[out] iterator A location to store the iterator. + * @param[in] context A valid context object. + * @param[in] descriptor A valid device descriptor or NULL. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_usbhid_iterator_new (dc_iterator_t **iterator, dc_context_t *context, dc_descriptor_t *descriptor); + /** * Open a USB HID connection. * diff --git a/src/uwatec_smart.c b/src/uwatec_smart.c index 4581881..f82f3e1 100644 --- a/src/uwatec_smart.c +++ b/src/uwatec_smart.c @@ -36,7 +36,6 @@ typedef struct uwatec_smart_device_t { dc_device_t base; dc_iostream_t *iostream; - unsigned int address; unsigned int timestamp; unsigned int devtime; dc_ticks_t systime; @@ -62,8 +61,8 @@ static const dc_device_vtable_t uwatec_smart_device_vtable = { static dc_status_t uwatec_smart_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata); -static void -uwatec_smart_discovery (unsigned int address, const char *name, unsigned int charset, unsigned int hints, void *userdata) +static int +uwatec_smart_filter (const char *name) { static const char *names[] = { "Aladin Smart Com", @@ -75,16 +74,16 @@ uwatec_smart_discovery (unsigned int address, const char *name, unsigned int cha "UWATEC Galileo Sol", }; - uwatec_smart_device_t *device = (uwatec_smart_device_t*) userdata; - if (device == NULL || name == NULL) - return; + if (name == NULL) + return 0; for (size_t i = 0; i < C_ARRAY_SIZE(names); ++i) { if (strcasecmp(name, names[i]) == 0) { - device->address = address; - return; + return 1; } } + + return 0; } @@ -152,6 +151,8 @@ uwatec_smart_device_open (dc_device_t **out, dc_context_t *context) { dc_status_t status = DC_STATUS_SUCCESS; uwatec_smart_device_t *device = NULL; + dc_iterator_t *iterator = NULL; + dc_irda_device_t *dev = NULL; if (out == NULL) return DC_STATUS_INVALIDARGS; @@ -165,33 +166,48 @@ uwatec_smart_device_open (dc_device_t **out, dc_context_t *context) // Set the default values. device->iostream = NULL; - device->address = 0; device->timestamp = 0; device->systime = (dc_ticks_t) -1; device->devtime = 0; + // Create the irda device iterator. + status = dc_irda_iterator_new (&iterator, context, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to create the irda iterator."); + goto error_free; + } + + // Enumerate the irda devices. + while (1) { + dc_irda_device_t *current = NULL; + status = dc_iterator_next (iterator, ¤t); + if (status != DC_STATUS_SUCCESS) { + if (status == DC_STATUS_DONE) { + ERROR (context, "No dive computer found."); + status = DC_STATUS_NODEVICE; + } else { + ERROR (context, "Failed to enumerate the irda devices."); + } + goto error_iterator_free; + } + + if (uwatec_smart_filter (dc_irda_device_get_name (current))) { + dev = current; + break; + } + + dc_irda_device_free (current); + } + // Open the irda socket. status = dc_irda_open (&device->iostream, context); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to open the irda socket."); - goto error_free; - } - - // Discover the device. - status = dc_irda_discover (device->iostream, uwatec_smart_discovery, device); - if (status != DC_STATUS_SUCCESS) { - ERROR (context, "Failed to discover the device."); - goto error_close; - } - - if (device->address == 0) { - ERROR (context, "No dive computer found."); - status = DC_STATUS_IO; - goto error_close; + goto error_device_free; } // Connect the device. - status = dc_irda_connect_lsap (device->iostream, device->address, 1); + status = dc_irda_connect_lsap (device->iostream, dc_irda_device_get_address (dev), 1); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to connect the device."); goto error_close; @@ -210,6 +226,10 @@ uwatec_smart_device_open (dc_device_t **out, dc_context_t *context) error_close: dc_iostream_close (device->iostream); +error_device_free: + dc_irda_device_free (dev); +error_iterator_free: + dc_iterator_free (iterator); error_free: dc_device_deallocate ((dc_device_t *) device); return status; From 30e406081780621b952791989c1f627c3865871e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 17 Jul 2017 23:41:48 +0200 Subject: [PATCH 03/18] Add suport for applying a filter function The device descriptors are extended with a filter function. During the device discovery, this filter function is used to return only devices that match a known dive computer. The filtering is optional, and can be disabled by passing a NULL pointer for the device descriptor when creating the iterator with one of the dc_xxx_iterator_new() functions. --- src/Makefile.am | 2 +- src/bluetooth.c | 7 + src/descriptor-private.h | 44 ++++ src/descriptor.c | 469 ++++++++++++++++++++------------------- src/irda.c | 7 + src/serial_posix.c | 8 + src/serial_win32.c | 7 + src/usbhid.c | 13 ++ 8 files changed, 326 insertions(+), 231 deletions(-) create mode 100644 src/descriptor-private.h diff --git a/src/Makefile.am b/src/Makefile.am index 3b1357b..e631c37 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,7 +16,7 @@ endif libdivecomputer_la_SOURCES = \ version.c \ - descriptor.c \ + descriptor-private.h descriptor.c \ iostream-private.h iostream.c \ iterator-private.h iterator.c \ common-private.h common.c \ diff --git a/src/bluetooth.c b/src/bluetooth.c index 337f1a9..4581cd7 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -51,6 +51,7 @@ #include "context-private.h" #include "iostream-private.h" #include "iterator-private.h" +#include "descriptor-private.h" #ifdef _WIN32 #define DC_ADDRESS_FORMAT "%012I64X" @@ -76,6 +77,7 @@ static dc_status_t dc_bluetooth_iterator_free (dc_iterator_t *iterator); typedef struct dc_bluetooth_iterator_t { dc_iterator_t base; + dc_filter_t filter; #ifdef _WIN32 HANDLE hLookup; #else @@ -318,6 +320,7 @@ dc_bluetooth_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descri iterator->count = ndevices; iterator->current = 0; #endif + iterator->filter = dc_descriptor_get_filter (descriptor); *out = (dc_iterator_t *) iterator; @@ -395,6 +398,10 @@ dc_bluetooth_iterator_next (dc_iterator_t *abstract, void *out) INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", address, name ? name : ""); + if (iterator->filter && !iterator->filter (DC_TRANSPORT_BLUETOOTH, name)) { + continue; + } + device = (dc_bluetooth_device_t *) malloc (sizeof(dc_bluetooth_device_t)); if (device == NULL) { SYSERROR (abstract->context, S_ENOMEM); diff --git a/src/descriptor-private.h b/src/descriptor-private.h new file mode 100644 index 0000000..5023f7f --- /dev/null +++ b/src/descriptor-private.h @@ -0,0 +1,44 @@ +/* + * libdivecomputer + * + * Copyright (C) 2017 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef DC_DESCRIPTOR_PRIVATE_H +#define DC_DESCRIPTOR_PRIVATE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct dc_usb_desc_t { + unsigned short vid; + unsigned short pid; +} dc_usb_desc_t; + +typedef int (*dc_filter_t) (dc_transport_t transport, const void *userdata); + +dc_filter_t +dc_descriptor_get_filter (dc_descriptor_t *descriptor); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_DESCRIPTOR_PRIVATE_H */ diff --git a/src/descriptor.c b/src/descriptor.c index 2f5c9e3..3991c3c 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -42,8 +42,7 @@ #include #include -#include - +#include "descriptor-private.h" #include "iterator-private.h" #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) @@ -55,6 +54,7 @@ struct dc_descriptor_t { const char *product; dc_family_t type; unsigned int model; + dc_filter_t filter; }; typedef struct dc_descriptor_iterator_t { @@ -77,271 +77,271 @@ static const dc_iterator_vtable_t dc_descriptor_iterator_vtable = { static const dc_descriptor_t g_descriptors[] = { /* Suunto Solution */ - {"Suunto", "Solution", DC_FAMILY_SUUNTO_SOLUTION, 0}, + {"Suunto", "Solution", DC_FAMILY_SUUNTO_SOLUTION, 0, NULL}, /* Suunto Eon */ - {"Suunto", "Eon", DC_FAMILY_SUUNTO_EON, 0}, - {"Suunto", "Solution Alpha", DC_FAMILY_SUUNTO_EON, 0}, - {"Suunto", "Solution Nitrox", DC_FAMILY_SUUNTO_EON, 0}, + {"Suunto", "Eon", DC_FAMILY_SUUNTO_EON, 0, NULL}, + {"Suunto", "Solution Alpha", DC_FAMILY_SUUNTO_EON, 0, NULL}, + {"Suunto", "Solution Nitrox", DC_FAMILY_SUUNTO_EON, 0, NULL}, /* Suunto Vyper */ - {"Suunto", "Spyder", DC_FAMILY_SUUNTO_VYPER, 0x01}, - {"Suunto", "Stinger", DC_FAMILY_SUUNTO_VYPER, 0x03}, - {"Suunto", "Mosquito", DC_FAMILY_SUUNTO_VYPER, 0x04}, - {"Suunto", "D3", DC_FAMILY_SUUNTO_VYPER, 0x05}, - {"Suunto", "Vyper", DC_FAMILY_SUUNTO_VYPER, 0x0A}, - {"Suunto", "Vytec", DC_FAMILY_SUUNTO_VYPER, 0X0B}, - {"Suunto", "Cobra", DC_FAMILY_SUUNTO_VYPER, 0X0C}, - {"Suunto", "Gekko", DC_FAMILY_SUUNTO_VYPER, 0X0D}, - {"Suunto", "Zoop", DC_FAMILY_SUUNTO_VYPER, 0x16}, + {"Suunto", "Spyder", DC_FAMILY_SUUNTO_VYPER, 0x01, NULL}, + {"Suunto", "Stinger", DC_FAMILY_SUUNTO_VYPER, 0x03, NULL}, + {"Suunto", "Mosquito", DC_FAMILY_SUUNTO_VYPER, 0x04, NULL}, + {"Suunto", "D3", DC_FAMILY_SUUNTO_VYPER, 0x05, NULL}, + {"Suunto", "Vyper", DC_FAMILY_SUUNTO_VYPER, 0x0A, NULL}, + {"Suunto", "Vytec", DC_FAMILY_SUUNTO_VYPER, 0X0B, NULL}, + {"Suunto", "Cobra", DC_FAMILY_SUUNTO_VYPER, 0X0C, NULL}, + {"Suunto", "Gekko", DC_FAMILY_SUUNTO_VYPER, 0X0D, NULL}, + {"Suunto", "Zoop", DC_FAMILY_SUUNTO_VYPER, 0x16, NULL}, /* Suunto Vyper 2 */ - {"Suunto", "Vyper 2", DC_FAMILY_SUUNTO_VYPER2, 0x10}, - {"Suunto", "Cobra 2", DC_FAMILY_SUUNTO_VYPER2, 0x11}, - {"Suunto", "Vyper Air", DC_FAMILY_SUUNTO_VYPER2, 0x13}, - {"Suunto", "Cobra 3", DC_FAMILY_SUUNTO_VYPER2, 0x14}, - {"Suunto", "HelO2", DC_FAMILY_SUUNTO_VYPER2, 0x15}, + {"Suunto", "Vyper 2", DC_FAMILY_SUUNTO_VYPER2, 0x10, NULL}, + {"Suunto", "Cobra 2", DC_FAMILY_SUUNTO_VYPER2, 0x11, NULL}, + {"Suunto", "Vyper Air", DC_FAMILY_SUUNTO_VYPER2, 0x13, NULL}, + {"Suunto", "Cobra 3", DC_FAMILY_SUUNTO_VYPER2, 0x14, NULL}, + {"Suunto", "HelO2", DC_FAMILY_SUUNTO_VYPER2, 0x15, NULL}, /* Suunto D9 */ - {"Suunto", "D9", DC_FAMILY_SUUNTO_D9, 0x0E}, - {"Suunto", "D6", DC_FAMILY_SUUNTO_D9, 0x0F}, - {"Suunto", "D4", DC_FAMILY_SUUNTO_D9, 0x12}, - {"Suunto", "D4i", DC_FAMILY_SUUNTO_D9, 0x19}, - {"Suunto", "D6i", DC_FAMILY_SUUNTO_D9, 0x1A}, - {"Suunto", "D9tx", DC_FAMILY_SUUNTO_D9, 0x1B}, - {"Suunto", "DX", DC_FAMILY_SUUNTO_D9, 0x1C}, - {"Suunto", "Vyper Novo", DC_FAMILY_SUUNTO_D9, 0x1D}, - {"Suunto", "Zoop Novo", DC_FAMILY_SUUNTO_D9, 0x1E}, - {"Suunto", "D4f", DC_FAMILY_SUUNTO_D9, 0x20}, + {"Suunto", "D9", DC_FAMILY_SUUNTO_D9, 0x0E, NULL}, + {"Suunto", "D6", DC_FAMILY_SUUNTO_D9, 0x0F, NULL}, + {"Suunto", "D4", DC_FAMILY_SUUNTO_D9, 0x12, NULL}, + {"Suunto", "D4i", DC_FAMILY_SUUNTO_D9, 0x19, NULL}, + {"Suunto", "D6i", DC_FAMILY_SUUNTO_D9, 0x1A, NULL}, + {"Suunto", "D9tx", DC_FAMILY_SUUNTO_D9, 0x1B, NULL}, + {"Suunto", "DX", DC_FAMILY_SUUNTO_D9, 0x1C, NULL}, + {"Suunto", "Vyper Novo", DC_FAMILY_SUUNTO_D9, 0x1D, NULL}, + {"Suunto", "Zoop Novo", DC_FAMILY_SUUNTO_D9, 0x1E, NULL}, + {"Suunto", "D4f", DC_FAMILY_SUUNTO_D9, 0x20, NULL}, /* Suunto EON Steel */ #ifdef USBHID - {"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0}, - {"Suunto", "EON Core", DC_FAMILY_SUUNTO_EONSTEEL, 1}, + {"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0, NULL}, + {"Suunto", "EON Core", DC_FAMILY_SUUNTO_EONSTEEL, 1, NULL}, #endif /* Uwatec Aladin */ - {"Uwatec", "Aladin Air Twin", DC_FAMILY_UWATEC_ALADIN, 0x1C}, - {"Uwatec", "Aladin Sport Plus", DC_FAMILY_UWATEC_ALADIN, 0x3E}, - {"Uwatec", "Aladin Pro", DC_FAMILY_UWATEC_ALADIN, 0x3F}, - {"Uwatec", "Aladin Air Z", DC_FAMILY_UWATEC_ALADIN, 0x44}, - {"Uwatec", "Aladin Air Z O2", DC_FAMILY_UWATEC_ALADIN, 0xA4}, - {"Uwatec", "Aladin Air Z Nitrox", DC_FAMILY_UWATEC_ALADIN, 0xF4}, - {"Uwatec", "Aladin Pro Ultra", DC_FAMILY_UWATEC_ALADIN, 0xFF}, + {"Uwatec", "Aladin Air Twin", DC_FAMILY_UWATEC_ALADIN, 0x1C, NULL}, + {"Uwatec", "Aladin Sport Plus", DC_FAMILY_UWATEC_ALADIN, 0x3E, NULL}, + {"Uwatec", "Aladin Pro", DC_FAMILY_UWATEC_ALADIN, 0x3F, NULL}, + {"Uwatec", "Aladin Air Z", DC_FAMILY_UWATEC_ALADIN, 0x44, NULL}, + {"Uwatec", "Aladin Air Z O2", DC_FAMILY_UWATEC_ALADIN, 0xA4, NULL}, + {"Uwatec", "Aladin Air Z Nitrox", DC_FAMILY_UWATEC_ALADIN, 0xF4, NULL}, + {"Uwatec", "Aladin Pro Ultra", DC_FAMILY_UWATEC_ALADIN, 0xFF, NULL}, /* Uwatec Memomouse */ - {"Uwatec", "Memomouse", DC_FAMILY_UWATEC_MEMOMOUSE, 0}, + {"Uwatec", "Memomouse", DC_FAMILY_UWATEC_MEMOMOUSE, 0, NULL}, /* Uwatec Smart */ #ifdef IRDA - {"Uwatec", "Smart Pro", DC_FAMILY_UWATEC_SMART, 0x10}, - {"Uwatec", "Galileo Sol", DC_FAMILY_UWATEC_SMART, 0x11}, - {"Uwatec", "Galileo Luna", DC_FAMILY_UWATEC_SMART, 0x11}, - {"Uwatec", "Galileo Terra", DC_FAMILY_UWATEC_SMART, 0x11}, - {"Uwatec", "Aladin Tec", DC_FAMILY_UWATEC_SMART, 0x12}, - {"Uwatec", "Aladin Prime", DC_FAMILY_UWATEC_SMART, 0x12}, - {"Uwatec", "Aladin Tec 2G", DC_FAMILY_UWATEC_SMART, 0x13}, - {"Uwatec", "Aladin 2G", DC_FAMILY_UWATEC_SMART, 0x13}, - {"Subgear","XP-10", DC_FAMILY_UWATEC_SMART, 0x13}, - {"Uwatec", "Smart Com", DC_FAMILY_UWATEC_SMART, 0x14}, - {"Uwatec", "Aladin 2G", DC_FAMILY_UWATEC_SMART, 0x15}, - {"Uwatec", "Aladin Tec 3G", DC_FAMILY_UWATEC_SMART, 0x15}, - {"Uwatec", "Aladin Sport", DC_FAMILY_UWATEC_SMART, 0x15}, - {"Subgear","XP-3G", DC_FAMILY_UWATEC_SMART, 0x15}, - {"Uwatec", "Smart Tec", DC_FAMILY_UWATEC_SMART, 0x18}, - {"Uwatec", "Galileo Trimix",DC_FAMILY_UWATEC_SMART, 0x19}, - {"Uwatec", "Smart Z", DC_FAMILY_UWATEC_SMART, 0x1C}, - {"Subgear","XP Air", DC_FAMILY_UWATEC_SMART, 0x1C}, + {"Uwatec", "Smart Pro", DC_FAMILY_UWATEC_SMART, 0x10, NULL}, + {"Uwatec", "Galileo Sol", DC_FAMILY_UWATEC_SMART, 0x11, NULL}, + {"Uwatec", "Galileo Luna", DC_FAMILY_UWATEC_SMART, 0x11, NULL}, + {"Uwatec", "Galileo Terra", DC_FAMILY_UWATEC_SMART, 0x11, NULL}, + {"Uwatec", "Aladin Tec", DC_FAMILY_UWATEC_SMART, 0x12, NULL}, + {"Uwatec", "Aladin Prime", DC_FAMILY_UWATEC_SMART, 0x12, NULL}, + {"Uwatec", "Aladin Tec 2G", DC_FAMILY_UWATEC_SMART, 0x13, NULL}, + {"Uwatec", "Aladin 2G", DC_FAMILY_UWATEC_SMART, 0x13, NULL}, + {"Subgear","XP-10", DC_FAMILY_UWATEC_SMART, 0x13, NULL}, + {"Uwatec", "Smart Com", DC_FAMILY_UWATEC_SMART, 0x14, NULL}, + {"Uwatec", "Aladin 2G", DC_FAMILY_UWATEC_SMART, 0x15, NULL}, + {"Uwatec", "Aladin Tec 3G", DC_FAMILY_UWATEC_SMART, 0x15, NULL}, + {"Uwatec", "Aladin Sport", DC_FAMILY_UWATEC_SMART, 0x15, NULL}, + {"Subgear","XP-3G", DC_FAMILY_UWATEC_SMART, 0x15, NULL}, + {"Uwatec", "Smart Tec", DC_FAMILY_UWATEC_SMART, 0x18, NULL}, + {"Uwatec", "Galileo Trimix",DC_FAMILY_UWATEC_SMART, 0x19, NULL}, + {"Uwatec", "Smart Z", DC_FAMILY_UWATEC_SMART, 0x1C, NULL}, + {"Subgear","XP Air", DC_FAMILY_UWATEC_SMART, 0x1C, NULL}, #endif /* Scubapro/Uwatec Meridian */ - {"Scubapro", "Meridian", DC_FAMILY_UWATEC_MERIDIAN, 0x20}, - {"Scubapro", "Mantis", DC_FAMILY_UWATEC_MERIDIAN, 0x20}, - {"Scubapro", "Chromis", DC_FAMILY_UWATEC_MERIDIAN, 0x24}, - {"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_MERIDIAN, 0x26}, + {"Scubapro", "Meridian", DC_FAMILY_UWATEC_MERIDIAN, 0x20, NULL}, + {"Scubapro", "Mantis", DC_FAMILY_UWATEC_MERIDIAN, 0x20, NULL}, + {"Scubapro", "Chromis", DC_FAMILY_UWATEC_MERIDIAN, 0x24, NULL}, + {"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_MERIDIAN, 0x26, NULL}, /* Scubapro G2 */ #ifdef USBHID - {"Scubapro", "Aladin Sport Matrix", DC_FAMILY_UWATEC_G2, 0x17}, - {"Scubapro", "Aladin Square", DC_FAMILY_UWATEC_G2, 0x22}, - {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x32}, + {"Scubapro", "Aladin Sport Matrix", DC_FAMILY_UWATEC_G2, 0x17, NULL}, + {"Scubapro", "Aladin Square", DC_FAMILY_UWATEC_G2, 0x22, NULL}, + {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x32, NULL}, #endif /* Reefnet */ - {"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1}, - {"Reefnet", "Sensus Pro", DC_FAMILY_REEFNET_SENSUSPRO, 2}, - {"Reefnet", "Sensus Ultra", DC_FAMILY_REEFNET_SENSUSULTRA, 3}, + {"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1, NULL}, + {"Reefnet", "Sensus Pro", DC_FAMILY_REEFNET_SENSUSPRO, 2, NULL}, + {"Reefnet", "Sensus Ultra", DC_FAMILY_REEFNET_SENSUSULTRA, 3, NULL}, /* Oceanic VT Pro */ - {"Aeris", "500 AI", DC_FAMILY_OCEANIC_VTPRO, 0x4151}, - {"Oceanic", "Versa Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4155}, - {"Aeris", "Atmos 2", DC_FAMILY_OCEANIC_VTPRO, 0x4158}, - {"Oceanic", "Pro Plus 2", DC_FAMILY_OCEANIC_VTPRO, 0x4159}, - {"Aeris", "Atmos AI", DC_FAMILY_OCEANIC_VTPRO, 0x4244}, - {"Oceanic", "VT Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4245}, - {"Sherwood", "Wisdom", DC_FAMILY_OCEANIC_VTPRO, 0x4246}, - {"Aeris", "Elite", DC_FAMILY_OCEANIC_VTPRO, 0x424F}, + {"Aeris", "500 AI", DC_FAMILY_OCEANIC_VTPRO, 0x4151, NULL}, + {"Oceanic", "Versa Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4155, NULL}, + {"Aeris", "Atmos 2", DC_FAMILY_OCEANIC_VTPRO, 0x4158, NULL}, + {"Oceanic", "Pro Plus 2", DC_FAMILY_OCEANIC_VTPRO, 0x4159, NULL}, + {"Aeris", "Atmos AI", DC_FAMILY_OCEANIC_VTPRO, 0x4244, NULL}, + {"Oceanic", "VT Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4245, NULL}, + {"Sherwood", "Wisdom", DC_FAMILY_OCEANIC_VTPRO, 0x4246, NULL}, + {"Aeris", "Elite", DC_FAMILY_OCEANIC_VTPRO, 0x424F, NULL}, /* Oceanic Veo 250 */ - {"Genesis", "React Pro", DC_FAMILY_OCEANIC_VEO250, 0x4247}, - {"Oceanic", "Veo 200", DC_FAMILY_OCEANIC_VEO250, 0x424B}, - {"Oceanic", "Veo 250", DC_FAMILY_OCEANIC_VEO250, 0x424C}, - {"Seemann", "XP5", DC_FAMILY_OCEANIC_VEO250, 0x4251}, - {"Oceanic", "Veo 180", DC_FAMILY_OCEANIC_VEO250, 0x4252}, - {"Aeris", "XR-2", DC_FAMILY_OCEANIC_VEO250, 0x4255}, - {"Sherwood", "Insight", DC_FAMILY_OCEANIC_VEO250, 0x425A}, - {"Hollis", "DG02", DC_FAMILY_OCEANIC_VEO250, 0x4352}, + {"Genesis", "React Pro", DC_FAMILY_OCEANIC_VEO250, 0x4247, NULL}, + {"Oceanic", "Veo 200", DC_FAMILY_OCEANIC_VEO250, 0x424B, NULL}, + {"Oceanic", "Veo 250", DC_FAMILY_OCEANIC_VEO250, 0x424C, NULL}, + {"Seemann", "XP5", DC_FAMILY_OCEANIC_VEO250, 0x4251, NULL}, + {"Oceanic", "Veo 180", DC_FAMILY_OCEANIC_VEO250, 0x4252, NULL}, + {"Aeris", "XR-2", DC_FAMILY_OCEANIC_VEO250, 0x4255, NULL}, + {"Sherwood", "Insight", DC_FAMILY_OCEANIC_VEO250, 0x425A, NULL}, + {"Hollis", "DG02", DC_FAMILY_OCEANIC_VEO250, 0x4352, NULL}, /* Oceanic Atom 2.0 */ - {"Oceanic", "Atom 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4250}, - {"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4257}, - {"Oceanic", "VT3", DC_FAMILY_OCEANIC_ATOM2, 0x4258}, - {"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4259}, - {"Oceanic", "Atom 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4342}, - {"Oceanic", "Geo", DC_FAMILY_OCEANIC_ATOM2, 0x4344}, - {"Aeris", "Manta", DC_FAMILY_OCEANIC_ATOM2, 0x4345}, - {"Aeris", "XR-1 NX", DC_FAMILY_OCEANIC_ATOM2, 0x4346}, - {"Oceanic", "Datamask", DC_FAMILY_OCEANIC_ATOM2, 0x4347}, - {"Aeris", "Compumask", DC_FAMILY_OCEANIC_ATOM2, 0x4348}, - {"Aeris", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x434D}, - {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x434E}, - {"Sherwood", "Wisdom 2", DC_FAMILY_OCEANIC_ATOM2, 0x4350}, - {"Sherwood", "Insight 2", DC_FAMILY_OCEANIC_ATOM2, 0x4353}, - {"Genesis", "React Pro White", DC_FAMILY_OCEANIC_ATOM2, 0x4354}, - {"Tusa", "Element II (IQ-750)", DC_FAMILY_OCEANIC_ATOM2, 0x4357}, - {"Oceanic", "Veo 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4358}, - {"Oceanic", "Veo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4359}, - {"Oceanic", "Veo 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x435A}, - {"Tusa", "Zen (IQ-900)", DC_FAMILY_OCEANIC_ATOM2, 0x4441}, - {"Tusa", "Zen Air (IQ-950)", DC_FAMILY_OCEANIC_ATOM2, 0x4442}, - {"Aeris", "Atmos AI 2", DC_FAMILY_OCEANIC_ATOM2, 0x4443}, - {"Oceanic", "Pro Plus 2.1", DC_FAMILY_OCEANIC_ATOM2, 0x4444}, - {"Oceanic", "Geo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4446}, - {"Oceanic", "VT4", DC_FAMILY_OCEANIC_ATOM2, 0x4447}, - {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4449}, - {"Beuchat", "Voyager 2G", DC_FAMILY_OCEANIC_ATOM2, 0x444B}, - {"Oceanic", "Atom 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x444C}, - {"Hollis", "DG03", DC_FAMILY_OCEANIC_ATOM2, 0x444D}, - {"Oceanic", "OCS", DC_FAMILY_OCEANIC_ATOM2, 0x4450}, - {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4451}, - {"Oceanic", "VT 4.1", DC_FAMILY_OCEANIC_ATOM2, 0x4452}, - {"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4453}, - {"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4455}, - {"Oceanic", "Atom 3.1", DC_FAMILY_OCEANIC_ATOM2, 0x4456}, - {"Aeris", "A300 AI", DC_FAMILY_OCEANIC_ATOM2, 0x4457}, - {"Sherwood", "Wisdom 3", DC_FAMILY_OCEANIC_ATOM2, 0x4458}, - {"Aeris", "A300", DC_FAMILY_OCEANIC_ATOM2, 0x445A}, - {"Hollis", "TX1", DC_FAMILY_OCEANIC_ATOM2, 0x4542}, - {"Beuchat", "Mundial 2", DC_FAMILY_OCEANIC_ATOM2, 0x4543}, - {"Sherwood", "Amphos", DC_FAMILY_OCEANIC_ATOM2, 0x4545}, - {"Sherwood", "Amphos Air", DC_FAMILY_OCEANIC_ATOM2, 0x4546}, - {"Oceanic", "Pro Plus 3", DC_FAMILY_OCEANIC_ATOM2, 0x4548}, - {"Aeris", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4549}, - {"Oceanic", "OCi", DC_FAMILY_OCEANIC_ATOM2, 0x454B}, - {"Aeris", "A300CS", DC_FAMILY_OCEANIC_ATOM2, 0x454C}, - {"Beuchat", "Mundial 3", DC_FAMILY_OCEANIC_ATOM2, 0x4550}, - {"Oceanic", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x4553}, - {"Oceanic", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4554}, - {"Subgear", "XP-Air", DC_FAMILY_OCEANIC_ATOM2, 0x4555}, - {"Sherwood", "Vision", DC_FAMILY_OCEANIC_ATOM2, 0x4556}, - {"Oceanic", "VTX", DC_FAMILY_OCEANIC_ATOM2, 0x4557}, - {"Aqualung", "i300", DC_FAMILY_OCEANIC_ATOM2, 0x4559}, - {"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A}, - {"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641}, - {"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642}, - {"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646}, + {"Oceanic", "Atom 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4250, NULL}, + {"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4257, NULL}, + {"Oceanic", "VT3", DC_FAMILY_OCEANIC_ATOM2, 0x4258, NULL}, + {"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4259, NULL}, + {"Oceanic", "Atom 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4342, NULL}, + {"Oceanic", "Geo", DC_FAMILY_OCEANIC_ATOM2, 0x4344, NULL}, + {"Aeris", "Manta", DC_FAMILY_OCEANIC_ATOM2, 0x4345, NULL}, + {"Aeris", "XR-1 NX", DC_FAMILY_OCEANIC_ATOM2, 0x4346, NULL}, + {"Oceanic", "Datamask", DC_FAMILY_OCEANIC_ATOM2, 0x4347, NULL}, + {"Aeris", "Compumask", DC_FAMILY_OCEANIC_ATOM2, 0x4348, NULL}, + {"Aeris", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x434D, NULL}, + {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x434E, NULL}, + {"Sherwood", "Wisdom 2", DC_FAMILY_OCEANIC_ATOM2, 0x4350, NULL}, + {"Sherwood", "Insight 2", DC_FAMILY_OCEANIC_ATOM2, 0x4353, NULL}, + {"Genesis", "React Pro White", DC_FAMILY_OCEANIC_ATOM2, 0x4354, NULL}, + {"Tusa", "Element II (IQ-750)", DC_FAMILY_OCEANIC_ATOM2, 0x4357, NULL}, + {"Oceanic", "Veo 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4358, NULL}, + {"Oceanic", "Veo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4359, NULL}, + {"Oceanic", "Veo 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x435A, NULL}, + {"Tusa", "Zen (IQ-900)", DC_FAMILY_OCEANIC_ATOM2, 0x4441, NULL}, + {"Tusa", "Zen Air (IQ-950)", DC_FAMILY_OCEANIC_ATOM2, 0x4442, NULL}, + {"Aeris", "Atmos AI 2", DC_FAMILY_OCEANIC_ATOM2, 0x4443, NULL}, + {"Oceanic", "Pro Plus 2.1", DC_FAMILY_OCEANIC_ATOM2, 0x4444, NULL}, + {"Oceanic", "Geo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4446, NULL}, + {"Oceanic", "VT4", DC_FAMILY_OCEANIC_ATOM2, 0x4447, NULL}, + {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4449, NULL}, + {"Beuchat", "Voyager 2G", DC_FAMILY_OCEANIC_ATOM2, 0x444B, NULL}, + {"Oceanic", "Atom 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x444C, NULL}, + {"Hollis", "DG03", DC_FAMILY_OCEANIC_ATOM2, 0x444D, NULL}, + {"Oceanic", "OCS", DC_FAMILY_OCEANIC_ATOM2, 0x4450, NULL}, + {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4451, NULL}, + {"Oceanic", "VT 4.1", DC_FAMILY_OCEANIC_ATOM2, 0x4452, NULL}, + {"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4453, NULL}, + {"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4455, NULL}, + {"Oceanic", "Atom 3.1", DC_FAMILY_OCEANIC_ATOM2, 0x4456, NULL}, + {"Aeris", "A300 AI", DC_FAMILY_OCEANIC_ATOM2, 0x4457, NULL}, + {"Sherwood", "Wisdom 3", DC_FAMILY_OCEANIC_ATOM2, 0x4458, NULL}, + {"Aeris", "A300", DC_FAMILY_OCEANIC_ATOM2, 0x445A, NULL}, + {"Hollis", "TX1", DC_FAMILY_OCEANIC_ATOM2, 0x4542, NULL}, + {"Beuchat", "Mundial 2", DC_FAMILY_OCEANIC_ATOM2, 0x4543, NULL}, + {"Sherwood", "Amphos", DC_FAMILY_OCEANIC_ATOM2, 0x4545, NULL}, + {"Sherwood", "Amphos Air", DC_FAMILY_OCEANIC_ATOM2, 0x4546, NULL}, + {"Oceanic", "Pro Plus 3", DC_FAMILY_OCEANIC_ATOM2, 0x4548, NULL}, + {"Aeris", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4549, NULL}, + {"Oceanic", "OCi", DC_FAMILY_OCEANIC_ATOM2, 0x454B, NULL}, + {"Aeris", "A300CS", DC_FAMILY_OCEANIC_ATOM2, 0x454C, NULL}, + {"Beuchat", "Mundial 3", DC_FAMILY_OCEANIC_ATOM2, 0x4550, NULL}, + {"Oceanic", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x4553, NULL}, + {"Oceanic", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4554, NULL}, + {"Subgear", "XP-Air", DC_FAMILY_OCEANIC_ATOM2, 0x4555, NULL}, + {"Sherwood", "Vision", DC_FAMILY_OCEANIC_ATOM2, 0x4556, NULL}, + {"Oceanic", "VTX", DC_FAMILY_OCEANIC_ATOM2, 0x4557, NULL}, + {"Aqualung", "i300", DC_FAMILY_OCEANIC_ATOM2, 0x4559, NULL}, + {"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A, NULL}, + {"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641, NULL}, + {"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642, NULL}, + {"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646, NULL}, /* Mares Nemo */ - {"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0}, - {"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0}, - {"Mares", "Nemo Titanium",DC_FAMILY_MARES_NEMO, 0}, - {"Mares", "Nemo Excel", DC_FAMILY_MARES_NEMO, 17}, - {"Mares", "Nemo Apneist", DC_FAMILY_MARES_NEMO, 18}, + {"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0, NULL}, + {"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0, NULL}, + {"Mares", "Nemo Titanium",DC_FAMILY_MARES_NEMO, 0, NULL}, + {"Mares", "Nemo Excel", DC_FAMILY_MARES_NEMO, 17, NULL}, + {"Mares", "Nemo Apneist", DC_FAMILY_MARES_NEMO, 18, NULL}, /* Mares Puck */ - {"Mares", "Puck", DC_FAMILY_MARES_PUCK, 7}, - {"Mares", "Puck Air", DC_FAMILY_MARES_PUCK, 19}, - {"Mares", "Nemo Air", DC_FAMILY_MARES_PUCK, 4}, - {"Mares", "Nemo Wide", DC_FAMILY_MARES_PUCK, 1}, + {"Mares", "Puck", DC_FAMILY_MARES_PUCK, 7, NULL}, + {"Mares", "Puck Air", DC_FAMILY_MARES_PUCK, 19, NULL}, + {"Mares", "Nemo Air", DC_FAMILY_MARES_PUCK, 4, NULL}, + {"Mares", "Nemo Wide", DC_FAMILY_MARES_PUCK, 1, NULL}, /* Mares Darwin */ - {"Mares", "Darwin", DC_FAMILY_MARES_DARWIN , 0}, - {"Mares", "M1", DC_FAMILY_MARES_DARWIN , 0}, - {"Mares", "M2", DC_FAMILY_MARES_DARWIN , 0}, - {"Mares", "Darwin Air", DC_FAMILY_MARES_DARWIN , 1}, - {"Mares", "Airlab", DC_FAMILY_MARES_DARWIN , 1}, + {"Mares", "Darwin", DC_FAMILY_MARES_DARWIN , 0, NULL}, + {"Mares", "M1", DC_FAMILY_MARES_DARWIN , 0, NULL}, + {"Mares", "M2", DC_FAMILY_MARES_DARWIN , 0, NULL}, + {"Mares", "Darwin Air", DC_FAMILY_MARES_DARWIN , 1, NULL}, + {"Mares", "Airlab", DC_FAMILY_MARES_DARWIN , 1, NULL}, /* Mares Icon HD */ - {"Mares", "Matrix", DC_FAMILY_MARES_ICONHD , 0x0F}, - {"Mares", "Smart", DC_FAMILY_MARES_ICONHD , 0x000010}, - {"Mares", "Smart Apnea", DC_FAMILY_MARES_ICONHD , 0x010010}, - {"Mares", "Icon HD", DC_FAMILY_MARES_ICONHD , 0x14}, - {"Mares", "Icon HD Net Ready", DC_FAMILY_MARES_ICONHD , 0x15}, - {"Mares", "Puck Pro", DC_FAMILY_MARES_ICONHD , 0x18}, - {"Mares", "Nemo Wide 2", DC_FAMILY_MARES_ICONHD , 0x19}, - {"Mares", "Puck 2", DC_FAMILY_MARES_ICONHD , 0x1F}, - {"Mares", "Quad Air", DC_FAMILY_MARES_ICONHD , 0x23}, - {"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29}, + {"Mares", "Matrix", DC_FAMILY_MARES_ICONHD , 0x0F, NULL}, + {"Mares", "Smart", DC_FAMILY_MARES_ICONHD , 0x000010, NULL}, + {"Mares", "Smart Apnea", DC_FAMILY_MARES_ICONHD , 0x010010, NULL}, + {"Mares", "Icon HD", DC_FAMILY_MARES_ICONHD , 0x14, NULL}, + {"Mares", "Icon HD Net Ready", DC_FAMILY_MARES_ICONHD , 0x15, NULL}, + {"Mares", "Puck Pro", DC_FAMILY_MARES_ICONHD , 0x18, NULL}, + {"Mares", "Nemo Wide 2", DC_FAMILY_MARES_ICONHD , 0x19, NULL}, + {"Mares", "Puck 2", DC_FAMILY_MARES_ICONHD , 0x1F, NULL}, + {"Mares", "Quad Air", DC_FAMILY_MARES_ICONHD , 0x23, NULL}, + {"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29, NULL}, /* Heinrichs Weikamp */ - {"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0}, - {"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1}, - {"Heinrichs Weikamp", "OSTC 2N", DC_FAMILY_HW_OSTC, 2}, - {"Heinrichs Weikamp", "OSTC 2C", DC_FAMILY_HW_OSTC, 3}, - {"Heinrichs Weikamp", "Frog", DC_FAMILY_HW_FROG, 0}, - {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x11}, - {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x13}, - {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x1B}, - {"Heinrichs Weikamp", "OSTC 3", DC_FAMILY_HW_OSTC3, 0x0A}, - {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x13}, - {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x1A}, - {"Heinrichs Weikamp", "OSTC 4", DC_FAMILY_HW_OSTC3, 0x3B}, - {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x05}, - {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x07}, - {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x12}, - {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x13}, + {"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0, NULL}, + {"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, NULL}, + {"Heinrichs Weikamp", "OSTC 2N", DC_FAMILY_HW_OSTC, 2, NULL}, + {"Heinrichs Weikamp", "OSTC 2C", DC_FAMILY_HW_OSTC, 3, NULL}, + {"Heinrichs Weikamp", "Frog", DC_FAMILY_HW_FROG, 0, NULL}, + {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x11, NULL}, + {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x13, NULL}, + {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x1B, NULL}, + {"Heinrichs Weikamp", "OSTC 3", DC_FAMILY_HW_OSTC3, 0x0A, NULL}, + {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x13, NULL}, + {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x1A, NULL}, + {"Heinrichs Weikamp", "OSTC 4", DC_FAMILY_HW_OSTC3, 0x3B, NULL}, + {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x05, NULL}, + {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x07, NULL}, + {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x12, NULL}, + {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x13, NULL}, /* Cressi Edy */ - {"Tusa", "IQ-700", DC_FAMILY_CRESSI_EDY, 0x05}, - {"Cressi", "Edy", DC_FAMILY_CRESSI_EDY, 0x08}, + {"Tusa", "IQ-700", DC_FAMILY_CRESSI_EDY, 0x05, NULL}, + {"Cressi", "Edy", DC_FAMILY_CRESSI_EDY, 0x08, NULL}, /* Cressi Leonardo */ - {"Cressi", "Leonardo", DC_FAMILY_CRESSI_LEONARDO, 1}, - {"Cressi", "Giotto", DC_FAMILY_CRESSI_LEONARDO, 4}, - {"Cressi", "Newton", DC_FAMILY_CRESSI_LEONARDO, 5}, - {"Cressi", "Drake", DC_FAMILY_CRESSI_LEONARDO, 6}, + {"Cressi", "Leonardo", DC_FAMILY_CRESSI_LEONARDO, 1, NULL}, + {"Cressi", "Giotto", DC_FAMILY_CRESSI_LEONARDO, 4, NULL}, + {"Cressi", "Newton", DC_FAMILY_CRESSI_LEONARDO, 5, NULL}, + {"Cressi", "Drake", DC_FAMILY_CRESSI_LEONARDO, 6, NULL}, /* Zeagle N2iTiON3 */ - {"Zeagle", "N2iTiON3", DC_FAMILY_ZEAGLE_N2ITION3, 0}, - {"Apeks", "Quantum X", DC_FAMILY_ZEAGLE_N2ITION3, 0}, - {"Dive Rite", "NiTek Trio", DC_FAMILY_ZEAGLE_N2ITION3, 0}, - {"Scubapro", "XTender 5", DC_FAMILY_ZEAGLE_N2ITION3, 0}, + {"Zeagle", "N2iTiON3", DC_FAMILY_ZEAGLE_N2ITION3, 0, NULL}, + {"Apeks", "Quantum X", DC_FAMILY_ZEAGLE_N2ITION3, 0, NULL}, + {"Dive Rite", "NiTek Trio", DC_FAMILY_ZEAGLE_N2ITION3, 0, NULL}, + {"Scubapro", "XTender 5", DC_FAMILY_ZEAGLE_N2ITION3, 0, NULL}, /* Atomic Aquatics Cobalt */ #ifdef HAVE_LIBUSB - {"Atomic Aquatics", "Cobalt", DC_FAMILY_ATOMICS_COBALT, 0}, - {"Atomic Aquatics", "Cobalt 2", DC_FAMILY_ATOMICS_COBALT, 2}, + {"Atomic Aquatics", "Cobalt", DC_FAMILY_ATOMICS_COBALT, 0, NULL}, + {"Atomic Aquatics", "Cobalt 2", DC_FAMILY_ATOMICS_COBALT, 2, NULL}, #endif /* Shearwater Predator */ - {"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2}, + {"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2, NULL}, /* Shearwater Petrel */ - {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3}, - {"Shearwater", "Petrel 2", DC_FAMILY_SHEARWATER_PETREL, 3}, - {"Shearwater", "Nerd", DC_FAMILY_SHEARWATER_PETREL, 4}, - {"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5}, - {"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6}, - {"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7}, + {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3, NULL}, + {"Shearwater", "Petrel 2", DC_FAMILY_SHEARWATER_PETREL, 3, NULL}, + {"Shearwater", "Nerd", DC_FAMILY_SHEARWATER_PETREL, 4, NULL}, + {"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5, NULL}, + {"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6, NULL}, + {"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7, NULL}, /* Dive Rite NiTek Q */ - {"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0}, + {"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0, NULL}, /* Citizen Hyper Aqualand */ - {"Citizen", "Hyper Aqualand", DC_FAMILY_CITIZEN_AQUALAND, 0}, + {"Citizen", "Hyper Aqualand", DC_FAMILY_CITIZEN_AQUALAND, 0, NULL}, /* DiveSystem/Ratio iDive */ - {"DiveSystem", "Orca", DC_FAMILY_DIVESYSTEM_IDIVE, 0x02}, - {"DiveSystem", "iDive Pro", DC_FAMILY_DIVESYSTEM_IDIVE, 0x03}, - {"DiveSystem", "iDive DAN", DC_FAMILY_DIVESYSTEM_IDIVE, 0x04}, - {"DiveSystem", "iDive Tech", DC_FAMILY_DIVESYSTEM_IDIVE, 0x05}, - {"DiveSystem", "iDive Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x06}, - {"DiveSystem", "iDive Stealth", DC_FAMILY_DIVESYSTEM_IDIVE, 0x07}, - {"DiveSystem", "iDive Free", DC_FAMILY_DIVESYSTEM_IDIVE, 0x08}, - {"DiveSystem", "iDive Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x09}, - {"DiveSystem", "iDive X3M", DC_FAMILY_DIVESYSTEM_IDIVE, 0x0A}, - {"DiveSystem", "iDive Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x0B}, - {"Ratio", "iX3M Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x22}, - {"Ratio", "iX3M Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x23}, - {"Ratio", "iX3M Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x24}, - {"Ratio", "iX3M Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x25}, - {"Ratio", "iX3M Pro Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x32}, - {"Ratio", "iX3M Pro Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x34}, - {"Ratio", "iX3M Pro Tech+",DC_FAMILY_DIVESYSTEM_IDIVE, 0x35}, - {"Ratio", "iDive Free", DC_FAMILY_DIVESYSTEM_IDIVE, 0x40}, - {"Ratio", "iDive Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x42}, - {"Ratio", "iDive Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x44}, - {"Ratio", "iDive Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x45}, - {"Seac", "Jack", DC_FAMILY_DIVESYSTEM_IDIVE, 0x1000}, + {"DiveSystem", "Orca", DC_FAMILY_DIVESYSTEM_IDIVE, 0x02, NULL}, + {"DiveSystem", "iDive Pro", DC_FAMILY_DIVESYSTEM_IDIVE, 0x03, NULL}, + {"DiveSystem", "iDive DAN", DC_FAMILY_DIVESYSTEM_IDIVE, 0x04, NULL}, + {"DiveSystem", "iDive Tech", DC_FAMILY_DIVESYSTEM_IDIVE, 0x05, NULL}, + {"DiveSystem", "iDive Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x06, NULL}, + {"DiveSystem", "iDive Stealth", DC_FAMILY_DIVESYSTEM_IDIVE, 0x07, NULL}, + {"DiveSystem", "iDive Free", DC_FAMILY_DIVESYSTEM_IDIVE, 0x08, NULL}, + {"DiveSystem", "iDive Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x09, NULL}, + {"DiveSystem", "iDive X3M", DC_FAMILY_DIVESYSTEM_IDIVE, 0x0A, NULL}, + {"DiveSystem", "iDive Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x0B, NULL}, + {"Ratio", "iX3M Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x22, NULL}, + {"Ratio", "iX3M Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x23, NULL}, + {"Ratio", "iX3M Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x24, NULL}, + {"Ratio", "iX3M Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x25, NULL}, + {"Ratio", "iX3M Pro Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x32, NULL}, + {"Ratio", "iX3M Pro Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x34, NULL}, + {"Ratio", "iX3M Pro Tech+",DC_FAMILY_DIVESYSTEM_IDIVE, 0x35, NULL}, + {"Ratio", "iDive Free", DC_FAMILY_DIVESYSTEM_IDIVE, 0x40, NULL}, + {"Ratio", "iDive Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x42, NULL}, + {"Ratio", "iDive Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x44, NULL}, + {"Ratio", "iDive Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x45, NULL}, + {"Seac", "Jack", DC_FAMILY_DIVESYSTEM_IDIVE, 0x1000, NULL}, /* Cochran Commander */ - {"Cochran", "Commander TM", DC_FAMILY_COCHRAN_COMMANDER, 0}, - {"Cochran", "Commander I", DC_FAMILY_COCHRAN_COMMANDER, 1}, - {"Cochran", "Commander II", DC_FAMILY_COCHRAN_COMMANDER, 2}, - {"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 3}, - {"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 4}, - {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5}, + {"Cochran", "Commander TM", DC_FAMILY_COCHRAN_COMMANDER, 0, NULL}, + {"Cochran", "Commander I", DC_FAMILY_COCHRAN_COMMANDER, 1, NULL}, + {"Cochran", "Commander II", DC_FAMILY_COCHRAN_COMMANDER, 2, NULL}, + {"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 3, NULL}, + {"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 4, NULL}, + {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5, NULL}, }; dc_status_t @@ -443,3 +443,12 @@ dc_descriptor_get_transport (dc_descriptor_t *descriptor) else return DC_TRANSPORT_SERIAL; } + +dc_filter_t +dc_descriptor_get_filter (dc_descriptor_t *descriptor) +{ + if (descriptor == NULL) + return NULL; + + return descriptor->filter; +} diff --git a/src/irda.c b/src/irda.c index 7106dbc..da5773f 100644 --- a/src/irda.c +++ b/src/irda.c @@ -48,6 +48,7 @@ #include "context-private.h" #include "iostream-private.h" #include "iterator-private.h" +#include "descriptor-private.h" #include "array.h" #include "platform.h" @@ -211,6 +212,8 @@ dc_irda_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_ S_CLOSE (fd); dc_socket_exit (context); + dc_filter_t filter = dc_descriptor_get_filter (descriptor); + unsigned int count = 0; #ifdef _WIN32 for (size_t i = 0; i < list->numDevice; ++i) { @@ -230,6 +233,10 @@ dc_irda_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_ INFO (context, "Discover: address=%08x, name=%s, charset=%02x, hints=%04x", address, name, charset, hints); + if (filter && !filter (DC_TRANSPORT_IRDA, name)) { + continue; + } + strncpy(iterator->items[count].name, name, sizeof(iterator->items[count].name) - 1); iterator->items[count].name[sizeof(iterator->items[count].name) - 1] = '\0'; iterator->items[count].address = address; diff --git a/src/serial_posix.c b/src/serial_posix.c index 4108ab8..c1697d5 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -59,6 +59,7 @@ #include "context-private.h" #include "iostream-private.h" #include "iterator-private.h" +#include "descriptor-private.h" #define DIRNAME "/dev" @@ -87,6 +88,7 @@ struct dc_serial_device_t { typedef struct dc_serial_iterator_t { dc_iterator_t base; + dc_filter_t filter; DIR *dp; } dc_serial_iterator_t; @@ -190,6 +192,8 @@ dc_serial_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descripto goto error_free; } + iterator->filter = dc_descriptor_get_filter (descriptor); + *out = (dc_iterator_t *) iterator; return DC_STATUS_SUCCESS; @@ -229,6 +233,10 @@ dc_serial_iterator_next (dc_iterator_t *abstract, void *out) return DC_STATUS_NOMEMORY; } + if (iterator->filter && !iterator->filter (DC_TRANSPORT_SERIAL, filename)) { + continue; + } + device = (dc_serial_device_t *) malloc (sizeof(dc_serial_device_t)); if (device == NULL) { SYSERROR (abstract->context, ENOMEM); diff --git a/src/serial_win32.c b/src/serial_win32.c index 3968d3f..c9b31e9 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -30,6 +30,7 @@ #include "context-private.h" #include "iostream-private.h" #include "iterator-private.h" +#include "descriptor-private.h" 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); @@ -56,6 +57,7 @@ struct dc_serial_device_t { typedef struct dc_serial_iterator_t { dc_iterator_t base; + dc_filter_t filter; HKEY hKey; DWORD count; DWORD current; @@ -177,6 +179,7 @@ dc_serial_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descripto } } + iterator->filter = dc_descriptor_get_filter (descriptor); iterator->hKey = hKey; iterator->count = count; iterator->current = 0; @@ -222,6 +225,10 @@ dc_serial_iterator_next (dc_iterator_t *abstract, void *out) // Null terminate the string. data[data_len] = 0; + if (iterator->filter && !iterator->filter (DC_TRANSPORT_SERIAL, data)) { + continue; + } + device = (dc_serial_device_t *) malloc (sizeof(dc_serial_device_t)); if (device == NULL) { SYSERROR (abstract->context, ERROR_OUTOFMEMORY); diff --git a/src/usbhid.c b/src/usbhid.c index ba16b3b..b2dfa16 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -54,6 +54,7 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "descriptor-private.h" #include "iterator-private.h" #include "platform.h" @@ -82,6 +83,7 @@ static dc_status_t dc_usbhid_close (dc_iostream_t *iostream); typedef struct dc_usbhid_iterator_t { dc_iterator_t base; + dc_filter_t filter; #if defined(USE_LIBUSB) struct libusb_device **devices; size_t count; @@ -306,6 +308,7 @@ dc_usbhid_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descripto iterator->devices = devices; iterator->current = devices; #endif + iterator->filter = dc_descriptor_get_filter (descriptor); *out = (dc_iterator_t *) iterator; @@ -341,6 +344,11 @@ dc_usbhid_iterator_next (dc_iterator_t *abstract, void *out) return syserror (rc); } + dc_usb_desc_t usb = {dev.idVendor, dev.idProduct}; + if (iterator->filter && !iterator->filter (DC_TRANSPORT_USBHID, &usb)) { + continue; + } + // Get the active configuration descriptor. struct libusb_config_descriptor *config = NULL; rc = libusb_get_active_config_descriptor (current, &config); @@ -414,6 +422,11 @@ dc_usbhid_iterator_next (dc_iterator_t *abstract, void *out) struct hid_device_info *current = iterator->current; iterator->current = current->next; + dc_usb_desc_t usb = {current->vendor_id, current->product_id}; + if (iterator->filter && !iterator->filter (DC_TRANSPORT_USBHID, &usb)) { + continue; + } + device = (dc_usbhid_device_t *) malloc (sizeof(dc_usbhid_device_t)); if (device == NULL) { ERROR (abstract->context, "Failed to allocate memory."); From 0ce3bd3e6dd715aa78a73390db4203f29b003b39 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 20 Jul 2017 11:35:27 +0200 Subject: [PATCH 04/18] Implement some filter functions --- src/descriptor.c | 187 ++++++++++++++++++++++++++++++++++++----------- src/platform.h | 1 + 2 files changed, 146 insertions(+), 42 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index 3991c3c..47fc028 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -41,12 +41,19 @@ #include #include +#include #include "descriptor-private.h" #include "iterator-private.h" +#include "platform.h" #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) +static int dc_filter_uwatec (dc_transport_t transport, const void *userdata); +static int dc_filter_suunto (dc_transport_t transport, const void *userdata); +static int dc_filter_shearwater (dc_transport_t transport, const void *userdata); +static int dc_filter_hw (dc_transport_t transport, const void *userdata); + static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item); struct dc_descriptor_t { @@ -111,8 +118,8 @@ static const dc_descriptor_t g_descriptors[] = { {"Suunto", "D4f", DC_FAMILY_SUUNTO_D9, 0x20, NULL}, /* Suunto EON Steel */ #ifdef USBHID - {"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0, NULL}, - {"Suunto", "EON Core", DC_FAMILY_SUUNTO_EONSTEEL, 1, NULL}, + {"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0, dc_filter_suunto}, + {"Suunto", "EON Core", DC_FAMILY_SUUNTO_EONSTEEL, 1, dc_filter_suunto}, #endif /* Uwatec Aladin */ {"Uwatec", "Aladin Air Twin", DC_FAMILY_UWATEC_ALADIN, 0x1C, NULL}, @@ -126,24 +133,24 @@ static const dc_descriptor_t g_descriptors[] = { {"Uwatec", "Memomouse", DC_FAMILY_UWATEC_MEMOMOUSE, 0, NULL}, /* Uwatec Smart */ #ifdef IRDA - {"Uwatec", "Smart Pro", DC_FAMILY_UWATEC_SMART, 0x10, NULL}, - {"Uwatec", "Galileo Sol", DC_FAMILY_UWATEC_SMART, 0x11, NULL}, - {"Uwatec", "Galileo Luna", DC_FAMILY_UWATEC_SMART, 0x11, NULL}, - {"Uwatec", "Galileo Terra", DC_FAMILY_UWATEC_SMART, 0x11, NULL}, - {"Uwatec", "Aladin Tec", DC_FAMILY_UWATEC_SMART, 0x12, NULL}, - {"Uwatec", "Aladin Prime", DC_FAMILY_UWATEC_SMART, 0x12, NULL}, - {"Uwatec", "Aladin Tec 2G", DC_FAMILY_UWATEC_SMART, 0x13, NULL}, - {"Uwatec", "Aladin 2G", DC_FAMILY_UWATEC_SMART, 0x13, NULL}, - {"Subgear","XP-10", DC_FAMILY_UWATEC_SMART, 0x13, NULL}, - {"Uwatec", "Smart Com", DC_FAMILY_UWATEC_SMART, 0x14, NULL}, - {"Uwatec", "Aladin 2G", DC_FAMILY_UWATEC_SMART, 0x15, NULL}, - {"Uwatec", "Aladin Tec 3G", DC_FAMILY_UWATEC_SMART, 0x15, NULL}, - {"Uwatec", "Aladin Sport", DC_FAMILY_UWATEC_SMART, 0x15, NULL}, - {"Subgear","XP-3G", DC_FAMILY_UWATEC_SMART, 0x15, NULL}, - {"Uwatec", "Smart Tec", DC_FAMILY_UWATEC_SMART, 0x18, NULL}, - {"Uwatec", "Galileo Trimix",DC_FAMILY_UWATEC_SMART, 0x19, NULL}, - {"Uwatec", "Smart Z", DC_FAMILY_UWATEC_SMART, 0x1C, NULL}, - {"Subgear","XP Air", DC_FAMILY_UWATEC_SMART, 0x1C, NULL}, + {"Uwatec", "Smart Pro", DC_FAMILY_UWATEC_SMART, 0x10, dc_filter_uwatec}, + {"Uwatec", "Galileo Sol", DC_FAMILY_UWATEC_SMART, 0x11, dc_filter_uwatec}, + {"Uwatec", "Galileo Luna", DC_FAMILY_UWATEC_SMART, 0x11, dc_filter_uwatec}, + {"Uwatec", "Galileo Terra", DC_FAMILY_UWATEC_SMART, 0x11, dc_filter_uwatec}, + {"Uwatec", "Aladin Tec", DC_FAMILY_UWATEC_SMART, 0x12, dc_filter_uwatec}, + {"Uwatec", "Aladin Prime", DC_FAMILY_UWATEC_SMART, 0x12, dc_filter_uwatec}, + {"Uwatec", "Aladin Tec 2G", DC_FAMILY_UWATEC_SMART, 0x13, dc_filter_uwatec}, + {"Uwatec", "Aladin 2G", DC_FAMILY_UWATEC_SMART, 0x13, dc_filter_uwatec}, + {"Subgear","XP-10", DC_FAMILY_UWATEC_SMART, 0x13, dc_filter_uwatec}, + {"Uwatec", "Smart Com", DC_FAMILY_UWATEC_SMART, 0x14, dc_filter_uwatec}, + {"Uwatec", "Aladin 2G", DC_FAMILY_UWATEC_SMART, 0x15, dc_filter_uwatec}, + {"Uwatec", "Aladin Tec 3G", DC_FAMILY_UWATEC_SMART, 0x15, dc_filter_uwatec}, + {"Uwatec", "Aladin Sport", DC_FAMILY_UWATEC_SMART, 0x15, dc_filter_uwatec}, + {"Subgear","XP-3G", DC_FAMILY_UWATEC_SMART, 0x15, dc_filter_uwatec}, + {"Uwatec", "Smart Tec", DC_FAMILY_UWATEC_SMART, 0x18, dc_filter_uwatec}, + {"Uwatec", "Galileo Trimix",DC_FAMILY_UWATEC_SMART, 0x19, dc_filter_uwatec}, + {"Uwatec", "Smart Z", DC_FAMILY_UWATEC_SMART, 0x1C, dc_filter_uwatec}, + {"Subgear","XP Air", DC_FAMILY_UWATEC_SMART, 0x1C, dc_filter_uwatec}, #endif /* Scubapro/Uwatec Meridian */ {"Scubapro", "Meridian", DC_FAMILY_UWATEC_MERIDIAN, 0x20, NULL}, @@ -152,9 +159,9 @@ static const dc_descriptor_t g_descriptors[] = { {"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_MERIDIAN, 0x26, NULL}, /* Scubapro G2 */ #ifdef USBHID - {"Scubapro", "Aladin Sport Matrix", DC_FAMILY_UWATEC_G2, 0x17, NULL}, - {"Scubapro", "Aladin Square", DC_FAMILY_UWATEC_G2, 0x22, NULL}, - {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x32, NULL}, + {"Scubapro", "Aladin Sport Matrix", DC_FAMILY_UWATEC_G2, 0x17, dc_filter_uwatec}, + {"Scubapro", "Aladin Square", DC_FAMILY_UWATEC_G2, 0x22, dc_filter_uwatec}, + {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x32, dc_filter_uwatec}, #endif /* Reefnet */ {"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1, NULL}, @@ -269,18 +276,18 @@ static const dc_descriptor_t g_descriptors[] = { {"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, NULL}, {"Heinrichs Weikamp", "OSTC 2N", DC_FAMILY_HW_OSTC, 2, NULL}, {"Heinrichs Weikamp", "OSTC 2C", DC_FAMILY_HW_OSTC, 3, NULL}, - {"Heinrichs Weikamp", "Frog", DC_FAMILY_HW_FROG, 0, NULL}, - {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x11, NULL}, - {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x13, NULL}, - {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x1B, NULL}, - {"Heinrichs Weikamp", "OSTC 3", DC_FAMILY_HW_OSTC3, 0x0A, NULL}, - {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x13, NULL}, - {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x1A, NULL}, - {"Heinrichs Weikamp", "OSTC 4", DC_FAMILY_HW_OSTC3, 0x3B, NULL}, - {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x05, NULL}, - {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x07, NULL}, - {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x12, NULL}, - {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x13, NULL}, + {"Heinrichs Weikamp", "Frog", DC_FAMILY_HW_FROG, 0, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x11, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x13, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x1B, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC 3", DC_FAMILY_HW_OSTC3, 0x0A, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x13, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x1A, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC 4", DC_FAMILY_HW_OSTC3, 0x3B, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x05, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x07, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x12, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x13, dc_filter_hw}, /* Cressi Edy */ {"Tusa", "IQ-700", DC_FAMILY_CRESSI_EDY, 0x05, NULL}, {"Cressi", "Edy", DC_FAMILY_CRESSI_EDY, 0x08, NULL}, @@ -300,14 +307,14 @@ static const dc_descriptor_t g_descriptors[] = { {"Atomic Aquatics", "Cobalt 2", DC_FAMILY_ATOMICS_COBALT, 2, NULL}, #endif /* Shearwater Predator */ - {"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2, NULL}, + {"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2, dc_filter_shearwater}, /* Shearwater Petrel */ - {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3, NULL}, - {"Shearwater", "Petrel 2", DC_FAMILY_SHEARWATER_PETREL, 3, NULL}, - {"Shearwater", "Nerd", DC_FAMILY_SHEARWATER_PETREL, 4, NULL}, - {"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5, NULL}, - {"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6, NULL}, - {"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7, NULL}, + {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3, dc_filter_shearwater}, + {"Shearwater", "Petrel 2", DC_FAMILY_SHEARWATER_PETREL, 3, dc_filter_shearwater}, + {"Shearwater", "Nerd", DC_FAMILY_SHEARWATER_PETREL, 4, dc_filter_shearwater}, + {"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5, dc_filter_shearwater}, + {"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6, dc_filter_shearwater}, + {"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7, dc_filter_shearwater}, /* Dive Rite NiTek Q */ {"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0, NULL}, /* Citizen Hyper Aqualand */ @@ -344,6 +351,102 @@ static const dc_descriptor_t g_descriptors[] = { {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5, NULL}, }; +static int +dc_filter_internal_name (const char *name, const char *values[], size_t count) +{ + if (name == NULL) + return 0; + + for (size_t i = 0; i < count; ++i) { + if (strcasecmp (name, values[i]) == 0) { + return 1; + } + } + + return 0; +} + +static int +dc_filter_internal_usb (const dc_usb_desc_t *desc, const dc_usb_desc_t values[], size_t count) +{ + if (desc == NULL) + return 0; + + for (size_t i = 0; i < count; ++i) { + if (desc->vid == values[i].vid && + desc->pid == values[i].pid) { + return 1; + } + } + + return 0; +} + +static int dc_filter_uwatec (dc_transport_t transport, const void *userdata) +{ + static const char *irda[] = { + "Aladin Smart Com", + "Aladin Smart Pro", + "Aladin Smart Tec", + "Aladin Smart Z", + "Uwatec Aladin", + "UWATEC Galileo", + "UWATEC Galileo Sol", + }; + static const dc_usb_desc_t usbhid[] = { + {0x2e6c, 0x3201}, // G2 + {0xc251, 0x2006}, // Aladin Square + }; + + if (transport == DC_TRANSPORT_IRDA) { + return dc_filter_internal_name ((const char *) userdata, irda, C_ARRAY_SIZE(irda)); + } else if (transport == DC_TRANSPORT_USBHID) { + return dc_filter_internal_usb ((const dc_usb_desc_t *) userdata, usbhid, C_ARRAY_SIZE(usbhid)); + } + + return 1; +} + +static int dc_filter_suunto (dc_transport_t transport, const void *userdata) +{ + static const dc_usb_desc_t usbhid[] = { + {0x1493, 0x0030}, // Eon Steel + {0x1493, 0x0033}, // Eon Core + }; + + if (transport == DC_TRANSPORT_USBHID) { + return dc_filter_internal_usb ((const dc_usb_desc_t *) userdata, usbhid, C_ARRAY_SIZE(usbhid)); + } + + return 1; +} + +static int dc_filter_hw (dc_transport_t transport, const void *userdata) +{ + if (transport == DC_TRANSPORT_BLUETOOTH) { + return strncasecmp ((const char *) userdata, "OSTC", 4) == 0 || + strncasecmp ((const char *) userdata, "FROG", 4) == 0; + } + + return 1; +} + +static int dc_filter_shearwater (dc_transport_t transport, const void *userdata) +{ + static const char *bluetooth[] = { + "Predator", + "Petrel", + "Nerd", + "Perdix", + }; + + if (transport == DC_TRANSPORT_BLUETOOTH) { + return dc_filter_internal_name ((const char *) userdata, bluetooth, C_ARRAY_SIZE(bluetooth)); + } + + return 1; +} + dc_status_t dc_descriptor_iterator (dc_iterator_t **out) { diff --git a/src/platform.h b/src/platform.h index 877e320..ab82fb7 100644 --- a/src/platform.h +++ b/src/platform.h @@ -35,6 +35,7 @@ extern "C" { #ifdef _MSC_VER #define snprintf _snprintf #define strcasecmp _stricmp +#define strncasecmp _strnicmp #if _MSC_VER < 1800 // The rint() function is only available in MSVC 2013 and later // versions. Our replacement macro isn't entirely correct, because the From 85eef19f8f9b1510eb3ae6e79f96ee77b7b1a8b1 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 19 Dec 2017 20:25:07 +0100 Subject: [PATCH 05/18] Integrate the connect step into the open function There is no need to expose the two step connection setup of the underlying socket interface in the public api. Doing so may complicate the implementation on platforms where the native api is not based on the socket interface (e.g. Mac OS X). Note that the function to connect based on the IrDA service name is removed. It's not used anywhere in libdivecomputer and since IrDA is an outdated technology nowadays, it's unlikely we'll need it in the future. --- src/bluetooth.c | 48 +++++++++++--------------- src/bluetooth.h | 12 +------ src/irda.c | 84 ++++++++++------------------------------------ src/irda.h | 24 +------------ src/uwatec_smart.c | 9 +---- 5 files changed, 40 insertions(+), 137 deletions(-) diff --git a/src/bluetooth.c b/src/bluetooth.c index 4581cd7..e6dc09d 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -443,7 +443,7 @@ dc_bluetooth_iterator_free (dc_iterator_t *abstract) #endif dc_status_t -dc_bluetooth_open (dc_iostream_t **out, dc_context_t *context) +dc_bluetooth_open (dc_iostream_t **out, dc_context_t *context, dc_bluetooth_address_t address, unsigned int port) { #ifdef BLUETOOTH dc_status_t status = DC_STATUS_SUCCESS; @@ -452,6 +452,8 @@ dc_bluetooth_open (dc_iostream_t **out, dc_context_t *context) if (out == NULL) return DC_STATUS_INVALIDARGS; + INFO (context, "Open: address=" DC_ADDRESS_FORMAT ", port=%u", address, port); + // Allocate memory. device = (dc_socket_t *) dc_iostream_allocate (context, &dc_bluetooth_vtable); if (device == NULL) { @@ -469,29 +471,6 @@ dc_bluetooth_open (dc_iostream_t **out, dc_context_t *context) goto error_free; } - *out = (dc_iostream_t *) device; - - return DC_STATUS_SUCCESS; - -error_free: - dc_iostream_deallocate ((dc_iostream_t *) device); - return status; -#else - return DC_STATUS_UNSUPPORTED; -#endif -} - -dc_status_t -dc_bluetooth_connect (dc_iostream_t *abstract, dc_bluetooth_address_t address, unsigned int port) -{ -#ifdef BLUETOOTH - dc_socket_t *device = (dc_socket_t *) abstract; - - if (!ISINSTANCE (abstract)) - return DC_STATUS_INVALIDARGS; - - INFO (abstract->context, "Connect: address=" DC_ADDRESS_FORMAT ", port=%d", address, port); - #ifdef _WIN32 SOCKADDR_BTH sa; sa.addressFamily = AF_BTH; @@ -507,16 +486,29 @@ dc_bluetooth_connect (dc_iostream_t *abstract, dc_bluetooth_address_t address, u sa.rc_family = AF_BLUETOOTH; dc_address_set (&sa.rc_bdaddr, address); if (port == 0) { - dc_status_t rc = dc_bluetooth_sdp (&sa.rc_channel, abstract->context, &sa.rc_bdaddr); - if (rc != DC_STATUS_SUCCESS) { - return rc; + status = dc_bluetooth_sdp (&sa.rc_channel, context, &sa.rc_bdaddr); + if (status != DC_STATUS_SUCCESS) { + goto error_close; } } else { sa.rc_channel = port; } #endif - return dc_socket_connect (&device->base, (struct sockaddr *) &sa, sizeof (sa)); + status = dc_socket_connect (&device->base, (struct sockaddr *) &sa, sizeof (sa)); + if (status != DC_STATUS_SUCCESS) { + goto error_close; + } + + *out = (dc_iostream_t *) device; + + return DC_STATUS_SUCCESS; + +error_close: + dc_socket_close (&device->base); +error_free: + dc_iostream_deallocate ((dc_iostream_t *) device); + return status; #else return DC_STATUS_UNSUPPORTED; #endif diff --git a/src/bluetooth.h b/src/bluetooth.h index 264e833..bc5bb2a 100644 --- a/src/bluetooth.h +++ b/src/bluetooth.h @@ -87,23 +87,13 @@ dc_bluetooth_iterator_new (dc_iterator_t **iterator, dc_context_t *context, dc_d * * @param[out] iostream A location to store the bluetooth connection. * @param[in] context A valid context object. - * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code - * on failure. - */ -dc_status_t -dc_bluetooth_open (dc_iostream_t **iostream, dc_context_t *context); - -/** - * Connect to an bluetooth device. - * - * @param[in] iostream A valid bluetooth connection. * @param[in] address The bluetooth device address. * @param[in] port The bluetooth port number. * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code * on failure. */ dc_status_t -dc_bluetooth_connect (dc_iostream_t *iostream, dc_bluetooth_address_t address, unsigned int port); +dc_bluetooth_open (dc_iostream_t **iostream, dc_context_t *context, dc_bluetooth_address_t address, unsigned int port); #ifdef __cplusplus } diff --git a/src/irda.c b/src/irda.c index da5773f..1ac754b 100644 --- a/src/irda.c +++ b/src/irda.c @@ -289,7 +289,7 @@ dc_irda_iterator_next (dc_iterator_t *abstract, void *out) #endif dc_status_t -dc_irda_open (dc_iostream_t **out, dc_context_t *context) +dc_irda_open (dc_iostream_t **out, dc_context_t *context, unsigned int address, unsigned int lsap) { #ifdef IRDA dc_status_t status = DC_STATUS_SUCCESS; @@ -298,6 +298,8 @@ dc_irda_open (dc_iostream_t **out, dc_context_t *context) if (out == NULL) return DC_STATUS_INVALIDARGS; + INFO (context, "Open: address=%08x, lsap=%u", address, lsap); + // Allocate memory. device = (dc_socket_t *) dc_iostream_allocate (context, &dc_irda_vtable); if (device == NULL) { @@ -311,71 +313,6 @@ dc_irda_open (dc_iostream_t **out, dc_context_t *context) goto error_free; } - *out = (dc_iostream_t *) device; - - return DC_STATUS_SUCCESS; - -error_free: - dc_iostream_deallocate ((dc_iostream_t *) device); - return status; -#else - return DC_STATUS_UNSUPPORTED; -#endif -} - -dc_status_t -dc_irda_connect_name (dc_iostream_t *abstract, unsigned int address, const char *name) -{ -#ifdef IRDA - dc_socket_t *device = (dc_socket_t *) abstract; - - if (!ISINSTANCE (abstract)) - return DC_STATUS_INVALIDARGS; - - INFO (abstract->context, "Connect: address=%08x, name=%s", address, name ? name : ""); - -#ifdef _WIN32 - SOCKADDR_IRDA peer; - peer.irdaAddressFamily = AF_IRDA; - peer.irdaDeviceID[0] = (address ) & 0xFF; - peer.irdaDeviceID[1] = (address >> 8) & 0xFF; - peer.irdaDeviceID[2] = (address >> 16) & 0xFF; - peer.irdaDeviceID[3] = (address >> 24) & 0xFF; - if (name) { - strncpy (peer.irdaServiceName, name, sizeof(peer.irdaServiceName) - 1); - peer.irdaServiceName[sizeof(peer.irdaServiceName) - 1] = '\0'; - } else { - memset (peer.irdaServiceName, 0x00, sizeof(peer.irdaServiceName)); - } -#else - struct sockaddr_irda peer; - peer.sir_family = AF_IRDA; - peer.sir_addr = address; - if (name) { - strncpy (peer.sir_name, name, sizeof(peer.sir_name) - 1); - peer.sir_name[sizeof(peer.sir_name) - 1] = '\0'; - } else { - memset (peer.sir_name, 0x00, sizeof(peer.sir_name)); - } -#endif - - return dc_socket_connect (&device->base, (struct sockaddr *) &peer, sizeof (peer)); -#else - return DC_STATUS_UNSUPPORTED; -#endif -} - -dc_status_t -dc_irda_connect_lsap (dc_iostream_t *abstract, unsigned int address, unsigned int lsap) -{ -#ifdef IRDA - dc_socket_t *device = (dc_socket_t *) abstract; - - if (!ISINSTANCE (abstract)) - return DC_STATUS_INVALIDARGS; - - INFO (abstract->context, "Connect: address=%08x, lsap=%u", address, lsap); - #ifdef _WIN32 SOCKADDR_IRDA peer; peer.irdaAddressFamily = AF_IRDA; @@ -392,7 +329,20 @@ dc_irda_connect_lsap (dc_iostream_t *abstract, unsigned int address, unsigned in memset (peer.sir_name, 0x00, sizeof(peer.sir_name)); #endif - return dc_socket_connect (&device->base, (struct sockaddr *) &peer, sizeof (peer)); + status = dc_socket_connect (&device->base, (struct sockaddr *) &peer, sizeof (peer)); + if (status != DC_STATUS_SUCCESS) { + goto error_close; + } + + *out = (dc_iostream_t *) device; + + return DC_STATUS_SUCCESS; + +error_close: + dc_socket_close (&device->base); +error_free: + dc_iostream_deallocate ((dc_iostream_t *) device); + return status; #else return DC_STATUS_UNSUPPORTED; #endif diff --git a/src/irda.h b/src/irda.h index c0ab8d6..92e9557 100644 --- a/src/irda.h +++ b/src/irda.h @@ -78,35 +78,13 @@ dc_irda_iterator_new (dc_iterator_t **iterator, dc_context_t *context, dc_descri * * @param[out] iostream A location to store the IrDA connection. * @param[in] context A valid context object. - * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code - * on failure. - */ -dc_status_t -dc_irda_open (dc_iostream_t **iostream, dc_context_t *context); - -/** - * Connect to an IrDA device. - * - * @param[in] iostream A valid IrDA connection. - * @param[in] address The IrDA device address. - * @param[in] name The IrDA service name. - * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code - * on failure. - */ -dc_status_t -dc_irda_connect_name (dc_iostream_t *iostream, unsigned int address, const char *name); - -/** - * Connect to an IrDA device. - * - * @param[in] iostream A valid IrDA connection. * @param[in] address The IrDA device address. * @param[in] lsap The IrDA LSAP number. * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code * on failure. */ dc_status_t -dc_irda_connect_lsap (dc_iostream_t *iostream, unsigned int address, unsigned int lsap); +dc_irda_open (dc_iostream_t **iostream, dc_context_t *context, unsigned int address, unsigned int lsap); #ifdef __cplusplus } diff --git a/src/uwatec_smart.c b/src/uwatec_smart.c index f82f3e1..a3be17d 100644 --- a/src/uwatec_smart.c +++ b/src/uwatec_smart.c @@ -200,19 +200,12 @@ uwatec_smart_device_open (dc_device_t **out, dc_context_t *context) } // Open the irda socket. - status = dc_irda_open (&device->iostream, context); + status = dc_irda_open (&device->iostream, context, dc_irda_device_get_address (dev), 1); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to open the irda socket."); goto error_device_free; } - // Connect the device. - status = dc_irda_connect_lsap (device->iostream, dc_irda_device_get_address (dev), 1); - if (status != DC_STATUS_SUCCESS) { - ERROR (context, "Failed to connect the device."); - goto error_close; - } - // Perform the handshaking. status = uwatec_smart_handshake (device); if (status != DC_STATUS_SUCCESS) { From 9117b593fc4552b6140e9c51594a4289584df99c Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 5 Feb 2018 19:53:19 +0100 Subject: [PATCH 06/18] Suppress the warning if no O2 sensors are present The warning about disabling the O2 sensors due to factory default calibration values, applies only if there is at least one calibrated O2 sensor present. This has no effect on the calibration bits, because those are already zero if there are no calibrated O2 sensors present. --- src/shearwater_predator_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 62ae5c6..dd9992a 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -326,7 +326,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) nsensors++; } } - if (nsensors == ndefaults) { + if (nsensors && nsensors == ndefaults) { // If all (calibrated) sensors still have their factory default // calibration values (2100), they are probably not calibrated // properly. To avoid returning incorrect ppO2 values to the From 05f55222101ae4b248aaf353d1a47514126389ee Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 5 Feb 2018 20:02:15 +0100 Subject: [PATCH 07/18] Let the ringbuffer function handle a full ringbuffer The ringbuffer_distance() function has a parameter to specify whether a ringbuffer with identical begin/end pointers should be considered an empty or a full ringbuffer. Hence there is no need to handle the case of a full ringbuffer manually. --- src/oceanic_common.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/oceanic_common.c b/src/oceanic_common.c index 1ab491d..f6f78d6 100644 --- a/src/oceanic_common.c +++ b/src/oceanic_common.c @@ -32,7 +32,7 @@ #define VTABLE(abstract) ((const oceanic_common_device_vtable_t *) abstract->vtable) -#define RB_LOGBOOK_DISTANCE(a,b,l) ringbuffer_distance (a, b, 0, l->rb_logbook_begin, l->rb_logbook_end) +#define RB_LOGBOOK_DISTANCE(a,b,l) ringbuffer_distance (a, b, 1, l->rb_logbook_begin, l->rb_logbook_end) #define RB_LOGBOOK_INCR(a,b,l) ringbuffer_increment (a, b, l->rb_logbook_begin, l->rb_logbook_end) #define RB_PROFILE_DISTANCE(a,b,l) ringbuffer_distance (a, b, 0, l->rb_profile_begin, l->rb_profile_end) @@ -218,23 +218,22 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr return DC_STATUS_DATAFORMAT; } - // Calculate the end pointer and the number of bytes. - unsigned int rb_logbook_end, rb_logbook_size; + // Calculate the end pointer. + unsigned int rb_logbook_end = 0; if (layout->pt_mode_global == 0) { rb_logbook_end = RB_LOGBOOK_INCR (rb_logbook_last, layout->rb_logbook_entry_size, layout); - rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_last, layout) + layout->rb_logbook_entry_size; } else { rb_logbook_end = rb_logbook_last; - rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_last, layout); - // In a typical ringbuffer implementation with only two begin/end - // pointers, there is no distinction possible between an empty and - // a full ringbuffer. We always consider the ringbuffer full in - // that case, because an empty ringbuffer can be detected by - // inspecting the logbook entries once they are downloaded. - if (rb_logbook_first == rb_logbook_last) - rb_logbook_size = layout->rb_logbook_end - layout->rb_logbook_begin; } + // Calculate the number of bytes. + // In a typical ringbuffer implementation with only two begin/end + // pointers, there is no distinction possible between an empty and a + // full ringbuffer. We always consider the ringbuffer full in that + // case, because an empty ringbuffer can be detected by inspecting + // the logbook entries once they are downloaded. + unsigned int rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_end, layout); + // Update and emit a progress event. progress->current += PAGESIZE; progress->maximum += PAGESIZE; From 0ac15dffd37075c902f2b59da6fb49008142e236 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 5 Feb 2018 20:16:48 +0100 Subject: [PATCH 08/18] Add a workaround for invalid logbook begin pointers Unfortunately there are several devices where an invalid logbook begin pointer occurs relative frequently. Typical examples are the Oceanic VT 4.1 and the Sherwood Wisdom 2. In such cases, the strict validation of the pointer causes the download to fail, without being able to download any dives at all. Since the begin pointer is only needed to detect the oldest logbook entry, we can fall back to downloading the entire logbook ringbuffer. If we're lucky (and we usually are), we can detect the oldest entry by inspecting the logbook entries once they are downloaded (e.g. presence of uninitialized entries) and then the download will finish succesfully. In the worst case scenario, we'll be able to download at least some dives before hitting another error. --- src/oceanic_common.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/oceanic_common.c b/src/oceanic_common.c index f6f78d6..9f7341a 100644 --- a/src/oceanic_common.c +++ b/src/oceanic_common.c @@ -208,13 +208,10 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr // Get the logbook pointers. unsigned int rb_logbook_first = array_uint16_le (pointers + 4); unsigned int rb_logbook_last = array_uint16_le (pointers + 6); - if (rb_logbook_first < layout->rb_logbook_begin || - rb_logbook_first >= layout->rb_logbook_end || - rb_logbook_last < layout->rb_logbook_begin || + if (rb_logbook_last < layout->rb_logbook_begin || rb_logbook_last >= layout->rb_logbook_end) { - ERROR (abstract->context, "Invalid logbook pointer detected (0x%04x 0x%04x).", - rb_logbook_first, rb_logbook_last); + ERROR (abstract->context, "Invalid logbook end pointer detected (0x%04x).", rb_logbook_last); return DC_STATUS_DATAFORMAT; } @@ -232,7 +229,17 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr // full ringbuffer. We always consider the ringbuffer full in that // case, because an empty ringbuffer can be detected by inspecting // the logbook entries once they are downloaded. - unsigned int rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_end, layout); + unsigned int rb_logbook_size = 0; + if (rb_logbook_first < layout->rb_logbook_begin || + rb_logbook_first >= layout->rb_logbook_end) + { + // Fall back to downloading the entire logbook ringbuffer as + // workaround for an invalid logbook begin pointer! + ERROR (abstract->context, "Invalid logbook begin pointer detected (0x%04x).", rb_logbook_first); + rb_logbook_size = layout->rb_logbook_end - layout->rb_logbook_begin; + } else { + rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_end, layout); + } // Update and emit a progress event. progress->current += PAGESIZE; From efd47cd9a1333e4cd52bc2c126092ad30b45fe55 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 15 Feb 2018 20:17:47 +0100 Subject: [PATCH 09/18] Add support for the OSTC 2 TR The OSTC 2 TR add supports for one or more tank pressure transmitters, and the active transmitter is linked to the active gas mix. --- src/descriptor.c | 1 + src/hw_ostc_parser.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/descriptor.c b/src/descriptor.c index 47fc028..7a65e71 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -288,6 +288,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x07, dc_filter_hw}, {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x12, dc_filter_hw}, {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x13, dc_filter_hw}, + {"Heinrichs Weikamp", "OSTC 2 TR", DC_FAMILY_HW_OSTC3, 0x33, dc_filter_hw}, /* Cressi Edy */ {"Tusa", "IQ-700", DC_FAMILY_CRESSI_EDY, 0x05, NULL}, {"Cressi", "Edy", DC_FAMILY_CRESSI_EDY, 0x08, NULL}, diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index acf1592..bf28be9 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -641,6 +641,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call switch (info[i].type) { case 0: // Temperature case 1: // Deco / NDL + case 6: // Tank pressure if (info[i].size != 2) { ERROR(abstract->context, "Unexpected sample size."); return DC_STATUS_DATAFORMAT; @@ -674,6 +675,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call unsigned int time = 0; unsigned int nsamples = 0; + unsigned int tank = parser->initial != UNDEFINED ? parser->initial : 0; unsigned int offset = header; if (version == 0x23 || version == 0x24) @@ -809,6 +811,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call idx--; /* Convert to a zero based index. */ sample.gasmix = idx; if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); + tank = idx; offset++; length--; } @@ -909,6 +912,12 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call sample.cns = data[offset] / 100.0; if (callback) callback (DC_SAMPLE_CNS, sample, userdata); break; + case 6: // Tank pressure + value = array_uint16_le (data + offset); + sample.pressure.tank = tank; + sample.pressure.value = value / 10.0; + if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); + break; default: // Not yet used. break; } From becaf02ab63a67d584fcaa64fe649b69bb7a931f Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 15 Feb 2018 08:25:28 +0100 Subject: [PATCH 10/18] Add functions for converting bluetooth addresses --- src/bluetooth.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/bluetooth.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/src/bluetooth.c b/src/bluetooth.c index e6dc09d..08347d5 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -24,6 +24,7 @@ #endif #include // malloc, free +#include #include "socket.h" @@ -223,6 +224,54 @@ error: #endif #endif +char * +dc_bluetooth_addr2str(dc_bluetooth_address_t address, char *str, size_t size) +{ + if (str == NULL || size < DC_BLUETOOTH_SIZE) + return NULL; + + int n = snprintf(str, size, "%02X:%02X:%02X:%02X:%02X:%02X", + (unsigned char)((address >> 40) & 0xFF), + (unsigned char)((address >> 32) & 0xFF), + (unsigned char)((address >> 24) & 0xFF), + (unsigned char)((address >> 16) & 0xFF), + (unsigned char)((address >> 8) & 0xFF), + (unsigned char)((address >> 0) & 0xFF)); + if (n < 0 || (size_t) n >= size) + return NULL; + + return str; +} + +dc_bluetooth_address_t +dc_bluetooth_str2addr(const char *str) +{ + dc_bluetooth_address_t address = 0; + + if (str == NULL) + return 0; + + unsigned char c = 0; + while ((c = *str++) != '\0') { + if (c == ':') { + continue; + } else if (c >= '0' && c <= '9') { + c -= '0'; + } else if (c >= 'A' && c <= 'F') { + c -= 'A' - 10; + } else if (c >= 'a' && c <= 'f') { + c -= 'a' - 10; + } else { + return 0; /* Invalid character! */ + } + + address <<= 4; + address |= c; + } + + return address; +} + dc_bluetooth_address_t dc_bluetooth_device_get_address (dc_bluetooth_device_t *device) { diff --git a/src/bluetooth.h b/src/bluetooth.h index bc5bb2a..5b443a4 100644 --- a/src/bluetooth.h +++ b/src/bluetooth.h @@ -32,6 +32,12 @@ extern "C" { #endif /* __cplusplus */ +/** + * The minimum number of bytes (including the terminating null byte) for + * formatting a bluetooth address as a string. + */ +#define DC_BLUETOOTH_SIZE 18 + /** * Bluetooth address (48 bits). */ @@ -41,6 +47,34 @@ typedef unsigned __int64 dc_bluetooth_address_t; typedef unsigned long long dc_bluetooth_address_t; #endif +/** + * Convert a bluetooth address to a string. + * + * The bluetooth address is formatted as XX:XX:XX:XX:XX:XX, where each + * XX is a hexadecimal number specifying an octet of the 48-bit address. + * The minimum size for the buffer is #DC_BLUETOOTH_SIZE bytes. + * + * @param[in] address A bluetooth address. + * @param[in] str The memory buffer to store the result. + * @param[in] size The size of the memory buffer. + * @returns The null-terminated string on success, or NULL on failure. + */ +char * +dc_bluetooth_addr2str(dc_bluetooth_address_t address, char *str, size_t size); + +/** + * Convert a string to a bluetooth address. + * + * The string is expected to be in the format XX:XX:XX:XX:XX:XX, where + * each XX is a hexadecimal number specifying an octet of the 48-bit + * address. + * + * @param[in] address A null-terminated string. + * @returns The bluetooth address on success, or zero on failure. + */ +dc_bluetooth_address_t +dc_bluetooth_str2addr(const char *address); + /** * Opaque object representing a bluetooth device. */ From a8adb16c0d4a5cb0cda4a0671822e7078dd502cf Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 23 Feb 2018 20:03:13 +0100 Subject: [PATCH 11/18] Add a high resolution timer module The new timer module provides an cross-platform interface for a high resolution monotonic clock. The timestamps are always relative to the creation of the timer and their unit is one microseconds. The timers can be used for logging, measuring elapsed time and implementing timeouts. --- configure.ac | 2 + msvc/libdivecomputer.vcproj | 8 ++ src/Makefile.am | 1 + src/timer.c | 161 ++++++++++++++++++++++++++++++++++++ src/timer.h | 51 ++++++++++++ 5 files changed, 223 insertions(+) create mode 100644 src/timer.c create mode 100644 src/timer.h diff --git a/configure.ac b/configure.ac index c1e126d..2d32a35 100644 --- a/configure.ac +++ b/configure.ac @@ -157,6 +157,7 @@ AC_CHECK_HEADERS([IOKit/serial/ioss.h]) AC_CHECK_HEADERS([getopt.h]) AC_CHECK_HEADERS([sys/param.h]) AC_CHECK_HEADERS([pthread.h]) +AC_CHECK_HEADERS([mach/mach_time.h]) # Checks for global variable declarations. AC_CHECK_DECLS([optreset]) @@ -169,6 +170,7 @@ AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[ # Checks for library functions. AC_FUNC_STRERROR_R AC_CHECK_FUNCS([localtime_r gmtime_r timegm _mkgmtime]) +AC_CHECK_FUNCS([clock_gettime mach_absolute_time]) AC_CHECK_FUNCS([getopt_long]) # Checks for supported compiler options. diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index c54c3d7..1b1cd96 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -482,6 +482,10 @@ RelativePath="..\src\suunto_vyper_parser.c" > + + @@ -820,6 +824,10 @@ RelativePath="..\src\suunto_vyper2.h" > + + diff --git a/src/Makefile.am b/src/Makefile.am index e631c37..7ca5302 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,6 +24,7 @@ libdivecomputer_la_SOURCES = \ device-private.h device.c \ parser-private.h parser.c \ datetime.c \ + timer.h timer.c \ suunto_common.h suunto_common.c \ suunto_common2.h suunto_common2.c \ suunto_solution.h suunto_solution.c suunto_solution_parser.c \ diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000..36d1f8f --- /dev/null +++ b/src/timer.c @@ -0,0 +1,161 @@ +/* + * libdivecomputer + * + * Copyright (C) 2018 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifdef _WIN32 +#define NOGDI +#include +#else +#include +#include +#ifdef HAVE_MACH_MACH_TIME_H +#include +#endif +#endif + +#include "timer.h" + +struct dc_timer_t { +#if defined (_WIN32) + LARGE_INTEGER timestamp; + LARGE_INTEGER frequency; +#elif defined (HAVE_CLOCK_GETTIME) + struct timespec timestamp; +#elif defined (HAVE_MACH_ABSOLUTE_TIME) + uint64_t timestamp; + mach_timebase_info_data_t info; +#else + struct timeval timestamp; +#endif +}; + +dc_status_t +dc_timer_new (dc_timer_t **out) +{ + dc_timer_t *timer = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + timer = (dc_timer_t *) malloc (sizeof (dc_timer_t)); + if (timer == NULL) { + return DC_STATUS_NOMEMORY; + } + +#if defined (_WIN32) + if (!QueryPerformanceFrequency(&timer->frequency) || + !QueryPerformanceCounter(&timer->timestamp)) { + free(timer); + return DC_STATUS_IO; + } +#elif defined (HAVE_CLOCK_GETTIME) + if (clock_gettime(CLOCK_MONOTONIC, &timer->timestamp) != 0) { + free(timer); + return DC_STATUS_IO; + } +#elif defined (HAVE_MACH_ABSOLUTE_TIME) + if (mach_timebase_info(&timer->info) != KERN_SUCCESS) { + free(timer); + return DC_STATUS_IO; + } + + timer->timestamp = mach_absolute_time(); +#else + if (gettimeofday (&timer->timestamp, NULL) != 0) { + free(timer); + return DC_STATUS_IO; + } +#endif + + *out = timer; + + return DC_STATUS_SUCCESS; +} + +dc_status_t +dc_timer_now (dc_timer_t *timer, dc_usecs_t *usecs) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_usecs_t value = 0; + + if (timer == NULL) { + status = DC_STATUS_INVALIDARGS; + goto out; + } + +#if defined (_WIN32) + LARGE_INTEGER now; + if (!QueryPerformanceCounter(&now)) { + status = DC_STATUS_IO; + goto out; + } + + value = (now.QuadPart - timer->timestamp.QuadPart) * 1000000 / timer->frequency.QuadPart; +#elif defined (HAVE_CLOCK_GETTIME) + struct timespec now, delta; + if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) { + status = DC_STATUS_IO; + goto out; + } + + if (now.tv_nsec < timer->timestamp.tv_nsec) { + delta.tv_nsec = 1000000000 + now.tv_nsec - timer->timestamp.tv_nsec; + delta.tv_sec = now.tv_sec - timer->timestamp.tv_sec - 1; + } else { + delta.tv_nsec = now.tv_nsec - timer->timestamp.tv_nsec; + delta.tv_sec = now.tv_sec - timer->timestamp.tv_sec; + } + + value = (dc_usecs_t) delta.tv_sec * 1000000 + delta.tv_nsec / 1000; +#elif defined (HAVE_MACH_ABSOLUTE_TIME) + uint64_t now = mach_absolute_time(); + value = (now - timer->timestamp) * timer->info.numer / timer->info.denom; +#else + struct timeval now, delta; + if (gettimeofday (&now, NULL) != 0) { + status = DC_STATUS_IO; + goto out; + } + + timersub (&now, &timer->timestamp, &delta); + + value = (dc_usecs_t) delta.tv_sec * 1000000 + delta.tv_usec; +#endif + +out: + if (usecs) + *usecs = value; + + return status; +} + +dc_status_t +dc_timer_free (dc_timer_t *timer) +{ + free (timer); + + return DC_STATUS_SUCCESS; +} diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 0000000..651991f --- /dev/null +++ b/src/timer.h @@ -0,0 +1,51 @@ +/* + * libdivecomputer + * + * Copyright (C) 2018 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_TIMER_H +#define DC_TIMER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if defined (_WIN32) && !defined (__GNUC__) +typedef unsigned __int64 dc_usecs_t; +#else +typedef unsigned long long dc_usecs_t; +#endif + +typedef struct dc_timer_t dc_timer_t; + +dc_status_t +dc_timer_new (dc_timer_t **timer); + +dc_status_t +dc_timer_now (dc_timer_t *timer, dc_usecs_t *usecs); + +dc_status_t +dc_timer_free (dc_timer_t *timer); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_TIMER_H */ From eb4c150024edd7476aa5268b325be0091416e1ca Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 23 Feb 2018 20:20:34 +0100 Subject: [PATCH 12/18] Use the new timer for the timeout calculations Replace the gettimeofday() based implementation with the new monotonic timers. This makes the implementation more robust against unexpected adjustments of the clock. --- src/serial_posix.c | 62 ++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/src/serial_posix.c b/src/serial_posix.c index c1697d5..8922c3a 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -30,7 +30,6 @@ #include // fcntl #include // tcgetattr, tcsetattr, cfsetispeed, cfsetospeed, tcflush, tcsendbreak #include // ioctl -#include // gettimeofday #include // nanosleep #ifdef HAVE_LINUX_SERIAL_H #include @@ -60,6 +59,7 @@ #include "iostream-private.h" #include "iterator-private.h" #include "descriptor-private.h" +#include "timer.h" #define DIRNAME "/dev" @@ -99,6 +99,7 @@ typedef struct dc_serial_t { */ int fd; int timeout; + dc_timer_t *timer; /* * Serial port settings are saved into this variable immediately * after the port is opened. These settings are restored when the @@ -290,6 +291,13 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name) device->baudrate = 0; device->nbits = 0; + // Create a high resolution timer. + status = dc_timer_new (&device->timer); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to create a high resolution timer."); + goto error_free; + } + // Open the device in non-blocking mode, to return immediately // without waiting for the modem connection to complete. device->fd = open (name, O_RDWR | O_NOCTTY | O_NONBLOCK); @@ -297,7 +305,7 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name) int errcode = errno; SYSERROR (context, errcode); status = syserror (errcode); - goto error_free; + goto error_timer_free; } #ifndef ENABLE_PTY @@ -327,6 +335,8 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name) error_close: close (device->fd); +error_timer_free: + dc_timer_free (device->timer); error_free: dc_iostream_deallocate ((dc_iostream_t *) device); return status; @@ -361,6 +371,8 @@ dc_serial_close (dc_iostream_t *abstract) dc_status_set_error(&status, syserror (errcode)); } + dc_timer_free (device->timer); + return status; } @@ -682,11 +694,8 @@ dc_serial_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual dc_serial_t *device = (dc_serial_t *) abstract; size_t nbytes = 0; - // The total timeout. - int timeout = device->timeout; - // The absolute target time. - struct timeval tve; + dc_usecs_t target = 0; int init = 1; while (nbytes < size) { @@ -694,35 +703,40 @@ dc_serial_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual FD_ZERO (&fds); FD_SET (device->fd, &fds); - struct timeval tvt; - if (timeout > 0) { - struct timeval now; - if (gettimeofday (&now, NULL) != 0) { - int errcode = errno; - SYSERROR (abstract->context, errcode); - status = syserror (errcode); + struct timeval tv, *ptv = NULL; + if (device->timeout > 0) { + dc_usecs_t timeout = 0; + + dc_usecs_t now = 0; + status = dc_timer_now (device->timer, &now); + if (status != DC_STATUS_SUCCESS) { goto out; } if (init) { // Calculate the initial timeout. - tvt.tv_sec = (timeout / 1000); - tvt.tv_usec = (timeout % 1000) * 1000; + timeout = device->timeout * 1000; // Calculate the target time. - timeradd (&now, &tvt, &tve); + target = now + timeout; + init = 0; } else { // Calculate the remaining timeout. - if (timercmp (&now, &tve, <)) - timersub (&tve, &now, &tvt); - else - timerclear (&tvt); + if (now < target) { + timeout = target - now; + } else { + timeout = 0; + } } - init = 0; - } else if (timeout == 0) { - timerclear (&tvt); + tv.tv_sec = timeout / 1000000; + tv.tv_usec = timeout % 1000000; + ptv = &tv; + } else if (device->timeout == 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + ptv = &tv; } - int rc = select (device->fd + 1, &fds, NULL, NULL, timeout >= 0 ? &tvt : NULL); + int rc = select (device->fd + 1, &fds, NULL, NULL, ptv); if (rc < 0) { int errcode = errno; if (errcode == EINTR) From ee31f24cfb20c76dd0ba1d11ba087aaa51330ae1 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 23 Feb 2018 20:43:45 +0100 Subject: [PATCH 13/18] Use the new timer for the timestamps in the logging Replace the platform specific code with the new timers. --- src/context.c | 41 ++++++++++++----------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/src/context.c b/src/context.c index 73bce10..181fef1 100644 --- a/src/context.c +++ b/src/context.c @@ -27,11 +27,10 @@ #ifdef _WIN32 #define NOGDI #include -#else -#include #endif #include "context-private.h" +#include "timer.h" struct dc_context_t { dc_loglevel_t loglevel; @@ -39,11 +38,7 @@ struct dc_context_t { void *userdata; #ifdef ENABLE_LOGGING char msg[8192 + 32]; -#ifdef _WIN32 - LARGE_INTEGER timestamp, frequency; -#else - struct timeval timestamp; -#endif + dc_timer_t *timer; #endif }; @@ -134,23 +129,11 @@ logfunc (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsign { const char *loglevels[] = {"NONE", "ERROR", "WARNING", "INFO", "DEBUG", "ALL"}; - unsigned long seconds = 0, microseconds = 0; + dc_usecs_t now = 0; + dc_timer_now (context->timer, &now); -#ifdef _WIN32 - LARGE_INTEGER now, delta; - QueryPerformanceCounter(&now); - delta.QuadPart = now.QuadPart - context->timestamp.QuadPart; - delta.QuadPart *= 1000000; - delta.QuadPart /= context->frequency.QuadPart; - seconds = delta.QuadPart / 1000000; - microseconds = delta.QuadPart % 1000000; -#else - struct timeval now, delta; - gettimeofday (&now, NULL); - timersub (&now, &context->timestamp, &delta); - seconds = delta.tv_sec; - microseconds = delta.tv_usec; -#endif + unsigned long seconds = now / 1000000; + unsigned long microseconds = now % 1000000; if (loglevel == DC_LOGLEVEL_ERROR || loglevel == DC_LOGLEVEL_WARNING) { fprintf (stderr, "[%li.%06li] %s: %s [in %s:%d (%s)]\n", @@ -187,12 +170,8 @@ dc_context_new (dc_context_t **out) #ifdef ENABLE_LOGGING memset (context->msg, 0, sizeof (context->msg)); -#ifdef _WIN32 - QueryPerformanceFrequency(&context->frequency); - QueryPerformanceCounter(&context->timestamp); -#else - gettimeofday (&context->timestamp, NULL); -#endif + context->timer = NULL; + dc_timer_new (&context->timer); #endif *out = context; @@ -203,6 +182,10 @@ dc_context_new (dc_context_t **out) dc_status_t dc_context_free (dc_context_t *context) { + if (context == NULL) + return DC_STATUS_SUCCESS; + + dc_timer_free (context->timer); free (context); return DC_STATUS_SUCCESS; From 4897a8351bb5142c3fa13c97d41a5832b75205ab Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 24 Feb 2018 09:16:23 +0100 Subject: [PATCH 14/18] Handle the half-duplex emulation in the vyper2 backend The half-duplex emulation is basically a workaround for a timing problem in the vyper2 backend. Since no other dive computer backend uses or needs this feature, it makes no sense to support this at the I/O layer. It can be implemented directly in the vyper2 backend instead. --- src/suunto_vyper2.c | 57 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/src/suunto_vyper2.c b/src/suunto_vyper2.c index 2591878..596b06e 100644 --- a/src/suunto_vyper2.c +++ b/src/suunto_vyper2.c @@ -28,6 +28,7 @@ #include "serial.h" #include "checksum.h" #include "array.h" +#include "timer.h" #define ISINSTANCE(device) dc_device_isinstance((device), (const dc_device_vtable_t *) &suunto_vyper2_device_vtable) @@ -36,6 +37,7 @@ typedef struct suunto_vyper2_device_t { suunto_common2_device_t base; dc_iostream_t *iostream; + dc_timer_t *timer; } suunto_vyper2_device_t; static dc_status_t suunto_vyper2_device_packet (dc_device_t *abstract, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size); @@ -95,11 +97,18 @@ suunto_vyper2_device_open (dc_device_t **out, dc_context_t *context, const char // Set the default values. device->iostream = NULL; + // Create a high resolution timer. + status = dc_timer_new (&device->timer); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to create a high resolution timer."); + goto error_free; + } + // Open the device. status = dc_serial_open (&device->iostream, context, name); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to open the serial port."); - goto error_free; + goto error_timer_free; } // Set the serial communication protocol (9600 8N1). @@ -133,13 +142,6 @@ suunto_vyper2_device_open (dc_device_t **out, dc_context_t *context, const char goto error_close; } - // Enable half-duplex emulation. - status = dc_iostream_set_halfduplex (device->iostream, 1); - if (status != DC_STATUS_SUCCESS) { - ERROR (context, "Failed to set half duplex."); - goto error_close; - } - // Read the version info. status = suunto_common2_device_version ((dc_device_t *) device, device->base.version, sizeof (device->base.version)); if (status != DC_STATUS_SUCCESS) { @@ -160,6 +162,8 @@ suunto_vyper2_device_open (dc_device_t **out, dc_context_t *context, const char error_close: dc_iostream_close (device->iostream); +error_timer_free: + dc_timer_free (device->timer); error_free: dc_device_deallocate ((dc_device_t *) device); return status; @@ -173,6 +177,8 @@ suunto_vyper2_device_close (dc_device_t *abstract) suunto_vyper2_device_t *device = (suunto_vyper2_device_t*) abstract; dc_status_t rc = DC_STATUS_SUCCESS; + dc_timer_free (device->timer); + // Close the device. rc = dc_iostream_close (device->iostream); if (rc != DC_STATUS_SUCCESS) { @@ -201,6 +207,13 @@ suunto_vyper2_device_packet (dc_device_t *abstract, const unsigned char command[ return status; } + // Get the current timestamp. + dc_usecs_t begin = 0; + status = dc_timer_now (device->timer, &begin); + if (status != DC_STATUS_SUCCESS) { + return status; + } + // Send the command to the dive computer. status = dc_iostream_write (device->iostream, command, csize, NULL); if (status != DC_STATUS_SUCCESS) { @@ -208,6 +221,34 @@ suunto_vyper2_device_packet (dc_device_t *abstract, const unsigned char command[ return status; } + // Get the current timestamp. + dc_usecs_t end = 0; + status = dc_timer_now (device->timer, &end); + if (status != DC_STATUS_SUCCESS) { + return status; + } + + // Calculate the elapsed time. + dc_usecs_t elapsed = end - begin; + + // Calculate the expected duration. A 2 millisecond fudge factor is added + // because it improves the success rate significantly. + unsigned int baudrate = 9600; + unsigned int nbits = 1 + 8 /* databits */ + 1 /* stopbits */ + 0 /* parity */; + dc_usecs_t expected = (dc_usecs_t) csize * 1000000 * nbits / baudrate + 2000; + + // Wait for the remaining time. + if (elapsed < expected) { + dc_usecs_t remaining = expected - elapsed; + + // The remaining time is rounded up to the nearest millisecond + // because the sleep function doesn't support a higher + // resolution on all platforms. The higher resolution is + // pointless anyway, since we already added a fudge factor + // above. + dc_iostream_sleep (device->iostream, (remaining + 999) / 1000); + } + // Clear RTS to receive the reply. status = dc_iostream_set_rts (device->iostream, 0); if (status != DC_STATUS_SUCCESS) { From 38ff1f75dd32f24a37bc4486c4850eb15a73a46c Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 24 Feb 2018 09:48:17 +0100 Subject: [PATCH 15/18] Remove the half-duplex emulation from the I/O api Now that the half-duplex emulation code isn't used anymore, it can be removed from the I/O stream api. --- include/libdivecomputer/iostream.h | 11 ----- src/bluetooth.c | 1 - src/custom.c | 13 ------ src/custom.h | 1 - src/iostream-private.h | 2 - src/iostream.c | 11 ----- src/irda.c | 1 - src/libdivecomputer.symbols | 1 - src/serial_posix.c | 64 ------------------------------ src/serial_win32.c | 63 ----------------------------- src/socket.c | 6 --- src/socket.h | 3 -- src/usbhid.c | 1 - 13 files changed, 178 deletions(-) diff --git a/include/libdivecomputer/iostream.h b/include/libdivecomputer/iostream.h index dab5daf..832680d 100644 --- a/include/libdivecomputer/iostream.h +++ b/include/libdivecomputer/iostream.h @@ -129,17 +129,6 @@ dc_iostream_set_timeout (dc_iostream_t *iostream, int timeout); dc_status_t dc_iostream_set_latency (dc_iostream_t *iostream, unsigned int value); -/** - * Set the state of the half duplex emulation. - * - * @param[in] iostream A valid I/O stream. - * @param[in] value The half duplex state. - * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code - * on failure. - */ -dc_status_t -dc_iostream_set_halfduplex (dc_iostream_t *iostream, unsigned int value); - /** * Set the state of the break condition. * diff --git a/src/bluetooth.c b/src/bluetooth.c index 08347d5..772101f 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -99,7 +99,6 @@ static const dc_iostream_vtable_t dc_bluetooth_vtable = { sizeof(dc_socket_t), dc_socket_set_timeout, /* set_timeout */ dc_socket_set_latency, /* set_latency */ - dc_socket_set_halfduplex, /* set_halfduplex */ dc_socket_set_break, /* set_break */ dc_socket_set_dtr, /* set_dtr */ dc_socket_set_rts, /* set_rts */ diff --git a/src/custom.c b/src/custom.c index e96ba5f..519a39e 100644 --- a/src/custom.c +++ b/src/custom.c @@ -29,7 +29,6 @@ static dc_status_t dc_custom_set_timeout (dc_iostream_t *abstract, int timeout); static dc_status_t dc_custom_set_latency (dc_iostream_t *abstract, unsigned int value); -static dc_status_t dc_custom_set_halfduplex (dc_iostream_t *abstract, unsigned int value); static dc_status_t dc_custom_set_break (dc_iostream_t *abstract, unsigned int value); static dc_status_t dc_custom_set_dtr (dc_iostream_t *abstract, unsigned int value); static dc_status_t dc_custom_set_rts (dc_iostream_t *abstract, unsigned int value); @@ -55,7 +54,6 @@ static const dc_iostream_vtable_t dc_custom_vtable = { sizeof(dc_custom_t), dc_custom_set_timeout, /* set_timeout */ dc_custom_set_latency, /* set_latency */ - dc_custom_set_halfduplex, /* set_halfduplex */ dc_custom_set_break, /* set_break */ dc_custom_set_dtr, /* set_dtr */ dc_custom_set_rts, /* set_rts */ @@ -117,17 +115,6 @@ dc_custom_set_latency (dc_iostream_t *abstract, unsigned int value) return custom->callbacks.set_latency (custom->userdata, value); } -static dc_status_t -dc_custom_set_halfduplex (dc_iostream_t *abstract, unsigned int value) -{ - dc_custom_t *custom = (dc_custom_t *) abstract; - - if (custom->callbacks.set_halfduplex == NULL) - return DC_STATUS_UNSUPPORTED; - - return custom->callbacks.set_halfduplex (custom->userdata, value); -} - static dc_status_t dc_custom_set_break (dc_iostream_t *abstract, unsigned int value) { diff --git a/src/custom.h b/src/custom.h index f062cd3..f1ab5da 100644 --- a/src/custom.h +++ b/src/custom.h @@ -33,7 +33,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_halfduplex) (void *userdata, unsigned int value); dc_status_t (*set_break) (void *userdata, unsigned int value); dc_status_t (*set_dtr) (void *userdata, unsigned int value); dc_status_t (*set_rts) (void *userdata, unsigned int value); diff --git a/src/iostream-private.h b/src/iostream-private.h index 7f7be84..b9bd4f1 100644 --- a/src/iostream-private.h +++ b/src/iostream-private.h @@ -44,8 +44,6 @@ struct dc_iostream_vtable_t { dc_status_t (*set_latency) (dc_iostream_t *iostream, unsigned int value); - dc_status_t (*set_halfduplex) (dc_iostream_t *iostream, unsigned int value); - dc_status_t (*set_break) (dc_iostream_t *iostream, unsigned int value); dc_status_t (*set_dtr) (dc_iostream_t *iostream, unsigned int value); diff --git a/src/iostream.c b/src/iostream.c index e6c40ea..c1e3017 100644 --- a/src/iostream.c +++ b/src/iostream.c @@ -85,17 +85,6 @@ dc_iostream_set_latency (dc_iostream_t *iostream, unsigned int value) return iostream->vtable->set_latency (iostream, value); } -dc_status_t -dc_iostream_set_halfduplex (dc_iostream_t *iostream, unsigned int value) -{ - if (iostream == NULL || iostream->vtable->set_halfduplex == NULL) - return DC_STATUS_UNSUPPORTED; - - INFO (iostream->context, "Halfduplex: value=%i", value); - - return iostream->vtable->set_halfduplex (iostream, value); -} - dc_status_t dc_iostream_set_break (dc_iostream_t *iostream, unsigned int value) { diff --git a/src/irda.c b/src/irda.c index 1ac754b..d1aecfb 100644 --- a/src/irda.c +++ b/src/irda.c @@ -92,7 +92,6 @@ static const dc_iostream_vtable_t dc_irda_vtable = { sizeof(dc_socket_t), dc_socket_set_timeout, /* set_timeout */ dc_socket_set_latency, /* set_latency */ - dc_socket_set_halfduplex, /* set_halfduplex */ dc_socket_set_break, /* set_break */ dc_socket_set_dtr, /* set_dtr */ dc_socket_set_rts, /* set_rts */ diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index daed9ef..a0c3724 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -34,7 +34,6 @@ dc_descriptor_get_model dc_descriptor_get_transport dc_iostream_set_timeout -dc_iostream_set_halfduplex dc_iostream_set_latency dc_iostream_set_break dc_iostream_set_dtr diff --git a/src/serial_posix.c b/src/serial_posix.c index 8922c3a..352655b 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -68,7 +68,6 @@ 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_halfduplex (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); @@ -106,10 +105,6 @@ typedef struct dc_serial_t { * serial port is closed. */ struct termios tty; - /* Half-duplex settings */ - int halfduplex; - unsigned int baudrate; - unsigned int nbits; } dc_serial_t; static const dc_iterator_vtable_t dc_serial_iterator_vtable = { @@ -122,7 +117,6 @@ 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_halfduplex, /* set_halfduplex */ dc_serial_set_break, /* set_break */ dc_serial_set_dtr, /* set_dtr */ dc_serial_set_rts, /* set_rts */ @@ -286,11 +280,6 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name) // Default to blocking reads. device->timeout = -1; - // Default to full-duplex. - device->halfduplex = 0; - device->baudrate = 0; - device->nbits = 0; - // Create a high resolution timer. status = dc_timer_new (&device->timer); if (status != DC_STATUS_SUCCESS) { @@ -619,9 +608,6 @@ dc_serial_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned in #endif } - device->baudrate = baudrate; - device->nbits = 1 + databits + stopbits + (parity ? 1 : 0); - return DC_STATUS_SUCCESS; } @@ -635,16 +621,6 @@ dc_serial_set_timeout (dc_iostream_t *abstract, int timeout) return DC_STATUS_SUCCESS; } -static dc_status_t -dc_serial_set_halfduplex (dc_iostream_t *abstract, unsigned int value) -{ - dc_serial_t *device = (dc_serial_t *) abstract; - - device->halfduplex = value; - - return DC_STATUS_SUCCESS; -} - static dc_status_t dc_serial_set_latency (dc_iostream_t *abstract, unsigned int milliseconds) { @@ -781,17 +757,6 @@ dc_serial_write (dc_iostream_t *abstract, const void *data, size_t size, size_t dc_serial_t *device = (dc_serial_t *) abstract; size_t nbytes = 0; - struct timeval tve, tvb; - if (device->halfduplex) { - // Get the current time. - if (gettimeofday (&tvb, NULL) != 0) { - int errcode = errno; - SYSERROR (abstract->context, errcode); - status = syserror (errcode); - goto out; - } - } - while (nbytes < size) { fd_set fds; FD_ZERO (&fds); @@ -839,35 +804,6 @@ dc_serial_write (dc_iostream_t *abstract, const void *data, size_t size, size_t } } - if (device->halfduplex) { - // Get the current time. - if (gettimeofday (&tve, NULL) != 0) { - int errcode = errno; - SYSERROR (abstract->context, errcode); - status = syserror (errcode); - goto out; - } - - // Calculate the elapsed time (microseconds). - struct timeval tvt; - timersub (&tve, &tvb, &tvt); - unsigned long elapsed = tvt.tv_sec * 1000000 + tvt.tv_usec; - - // Calculate the expected duration (microseconds). A 2 millisecond fudge - // factor is added because it improves the success rate significantly. - unsigned long expected = 1000000.0 * device->nbits / device->baudrate * size + 0.5 + 2000; - - // Wait for the remaining time. - if (elapsed < expected) { - unsigned long remaining = expected - elapsed; - - // The remaining time is rounded up to the nearest millisecond to - // match the Windows implementation. The higher resolution is - // pointless anyway, since we already added a fudge factor above. - dc_serial_sleep (abstract, (remaining + 999) / 1000); - } - } - out: if (actual) *actual = nbytes; diff --git a/src/serial_win32.c b/src/serial_win32.c index c9b31e9..b0c6050 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -37,7 +37,6 @@ 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_halfduplex (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); @@ -76,10 +75,6 @@ typedef struct dc_serial_t { */ DCB dcb; COMMTIMEOUTS timeouts; - /* Half-duplex settings */ - int halfduplex; - unsigned int baudrate; - unsigned int nbits; } dc_serial_t; static const dc_iterator_vtable_t dc_serial_iterator_vtable = { @@ -92,7 +87,6 @@ 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_halfduplex, /* set_halfduplex */ dc_serial_set_break, /* set_break */ dc_serial_set_dtr, /* set_dtr */ dc_serial_set_rts, /* set_rts */ @@ -288,11 +282,6 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name) return DC_STATUS_NOMEMORY; } - // Default to full-duplex. - device->halfduplex = 0; - device->baudrate = 0; - device->nbits = 0; - // Open the device. device->hFile = CreateFileA (devname, GENERIC_READ | GENERIC_WRITE, 0, @@ -457,9 +446,6 @@ dc_serial_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned in return syserror (errcode); } - device->baudrate = baudrate; - device->nbits = 1 + databits + stopbits + (parity ? 1 : 0); - return DC_STATUS_SUCCESS; } @@ -510,16 +496,6 @@ dc_serial_set_timeout (dc_iostream_t *abstract, int timeout) return DC_STATUS_SUCCESS; } -static dc_status_t -dc_serial_set_halfduplex (dc_iostream_t *abstract, unsigned int value) -{ - dc_serial_t *device = (dc_serial_t *) abstract; - - device->halfduplex = value; - - return DC_STATUS_SUCCESS; -} - static dc_status_t dc_serial_set_latency (dc_iostream_t *abstract, unsigned int value) { @@ -558,18 +534,6 @@ 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; - LARGE_INTEGER begin, end, freq; - if (device->halfduplex) { - // Get the current time. - if (!QueryPerformanceFrequency(&freq) || - !QueryPerformanceCounter(&begin)) { - DWORD errcode = GetLastError (); - SYSERROR (abstract->context, errcode); - status = syserror (errcode); - goto out; - } - } - if (!WriteFile (device->hFile, data, size, &dwWritten, NULL)) { DWORD errcode = GetLastError (); SYSERROR (abstract->context, errcode); @@ -577,33 +541,6 @@ dc_serial_write (dc_iostream_t *abstract, const void *data, size_t size, size_t goto out; } - if (device->halfduplex) { - // Get the current time. - if (!QueryPerformanceCounter(&end)) { - DWORD errcode = GetLastError (); - SYSERROR (abstract->context, errcode); - status = syserror (errcode); - goto out; - } - - // Calculate the elapsed time (microseconds). - unsigned long elapsed = 1000000.0 * (end.QuadPart - begin.QuadPart) / freq.QuadPart + 0.5; - - // Calculate the expected duration (microseconds). A 2 millisecond fudge - // factor is added because it improves the success rate significantly. - unsigned long expected = 1000000.0 * device->nbits / device->baudrate * size + 0.5 + 2000; - - // Wait for the remaining time. - if (elapsed < expected) { - unsigned long remaining = expected - elapsed; - - // The remaining time is rounded up to the nearest millisecond - // because the Windows Sleep() function doesn't have a higher - // resolution. - dc_serial_sleep (abstract, (remaining + 999) / 1000); - } - } - if (dwWritten != size) { status = DC_STATUS_TIMEOUT; } diff --git a/src/socket.c b/src/socket.c index 5ce392f..1842a75 100644 --- a/src/socket.c +++ b/src/socket.c @@ -169,12 +169,6 @@ dc_socket_set_latency (dc_iostream_t *iostream, unsigned int value) return DC_STATUS_SUCCESS; } -dc_status_t -dc_socket_set_halfduplex (dc_iostream_t *iostream, unsigned int value) -{ - return DC_STATUS_SUCCESS; -} - dc_status_t dc_socket_set_break (dc_iostream_t *iostream, unsigned int value) { diff --git a/src/socket.h b/src/socket.h index d612603..626003c 100644 --- a/src/socket.h +++ b/src/socket.h @@ -107,9 +107,6 @@ dc_socket_set_timeout (dc_iostream_t *iostream, int timeout); dc_status_t dc_socket_set_latency (dc_iostream_t *iostream, unsigned int value); -dc_status_t -dc_socket_set_halfduplex (dc_iostream_t *iostream, unsigned int value); - dc_status_t dc_socket_set_break (dc_iostream_t *iostream, unsigned int value); diff --git a/src/usbhid.c b/src/usbhid.c index b2dfa16..244e175 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -119,7 +119,6 @@ static const dc_iostream_vtable_t dc_usbhid_vtable = { sizeof(dc_usbhid_t), dc_usbhid_set_timeout, /* set_timeout */ NULL, /* set_latency */ - NULL, /* set_halfduplex */ NULL, /* set_break */ NULL, /* set_dtr */ NULL, /* set_rts */ From f5e10ff9bcb1963f8b1d1b2971244b39287d46e5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 11 Mar 2018 12:08:13 -0700 Subject: [PATCH 16/18] get rid of annoying compile warnings This gets rid of the warnings in the build, mostly by just disabling them entirely, and in one case by adding a default statement to make the compiler not warn about lacking case statements for a switch() statement. Some of the warnings really don't work that well for libdivecomputer (warning about pointer signs is very annoying when we mix regular strings and various "unsigned char *" raw data). And some of the warnings we should probably re-enable once by one, and actually fix up. But even the ones we should re-enable are right now more of a pain than they are worth, and nobody has had the energy to do so. And as long as there are *so* many warnings, nobody likely will. In the meantime, this gets rid of the harmless warnings, so that we can see if any *bad* warnings happen. Signed-off-by: Linus Torvalds --- configure.ac | 6 +++++- src/suunto_eonsteel_parser.c | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index cf0a56d..927d8e5 100644 --- a/configure.ac +++ b/configure.ac @@ -187,12 +187,16 @@ AX_APPEND_COMPILE_FLAGS([ \ -Wrestrict \ -Wformat=2 \ -Wwrite-strings \ - -Wcast-qual \ -Wpointer-arith \ -Wstrict-prototypes \ -Wmissing-prototypes \ -Wmissing-declarations \ -Wno-unused-parameter \ + -Wno-unused-function \ + -Wno-unused-variable \ + -Wno-unused-but-set-variable \ + -Wno-pointer-sign \ + -Wno-shadow \ ]) # Windows specific compiler options. diff --git a/src/suunto_eonsteel_parser.c b/src/suunto_eonsteel_parser.c index ffdd172..b48b701 100644 --- a/src/suunto_eonsteel_parser.c +++ b/src/suunto_eonsteel_parser.c @@ -1462,6 +1462,8 @@ static int traverse_sample_fields(suunto_eonsteel_parser_t *eon, const struct ty set_depth_field(eon, array_uint16_le(data)); data += 2; continue; + default: + break; } break; } From f4e43afad9f113a4e83e7d9f507ffe3648842fc0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 11 Mar 2018 11:59:40 -0700 Subject: [PATCH 17/18] remove 'serial' number from descriptor array I added this long ago as a merge fixup (see commit f9db4ca97c4b: "Merge git://git.libdivecomputer.org/libdivecomputer into Subsurface-branch"), because some of the OSTC parser_create() functions take a serial number. However, the serial number is never actually taken from the descriptor array, and if it was, it would be incorrect anyway. None of the entries in the descriptor array actually have any, so it's always zero. So just remove it, because it's a big pain whenever upstream changes the descriptor array format. Signed-off-by: Linus Torvalds --- include/libdivecomputer/descriptor.h | 3 --- src/descriptor.c | 10 ---------- src/parser.c | 2 +- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/include/libdivecomputer/descriptor.h b/include/libdivecomputer/descriptor.h index 8601b0f..a78b600 100644 --- a/include/libdivecomputer/descriptor.h +++ b/include/libdivecomputer/descriptor.h @@ -58,9 +58,6 @@ dc_descriptor_get_type (dc_descriptor_t *descriptor); unsigned int dc_descriptor_get_model (dc_descriptor_t *descriptor); -unsigned int -dc_descriptor_get_serial (dc_descriptor_t *descriptor); - dc_transport_t dc_descriptor_get_transport (dc_descriptor_t *descriptor); diff --git a/src/descriptor.c b/src/descriptor.c index c549f7a..3849ea0 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -53,7 +53,6 @@ struct dc_descriptor_t { const char *product; dc_family_t type; unsigned int model; - unsigned int serial; }; /* @@ -436,15 +435,6 @@ dc_descriptor_get_model (dc_descriptor_t *descriptor) return descriptor->model; } -unsigned int -dc_descriptor_get_serial (dc_descriptor_t *descriptor) -{ - if (descriptor == NULL) - return 0; - - return descriptor->serial; -} - dc_transport_t dc_descriptor_get_transport (dc_descriptor_t *descriptor) { diff --git a/src/parser.c b/src/parser.c index 4e6071f..f5f55e3 100644 --- a/src/parser.c +++ b/src/parser.c @@ -195,7 +195,7 @@ dc_parser_new2 (dc_parser_t **out, dc_context_t *context, dc_descriptor_t *descr return dc_parser_new_internal (out, context, dc_descriptor_get_type (descriptor), dc_descriptor_get_model (descriptor), - dc_descriptor_get_serial (descriptor), + 0, devtime, systime); } From 5b54386dbdf1e23300b2f3b57f369abcaa1b2714 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 11 Mar 2018 12:14:37 -0700 Subject: [PATCH 18/18] remove the transfer type comments from the descriptor array This basically reverts commit 4a3f7a7cce54 ("Mark DC descriptors with supported transport"). The commit wasn't wrong, but it ends up having caused a *lot* of pain for merging, because it adds the transfer type marker to most lines in that descriptor array, and the end result is very painful to merge if upstream libdivecomputer ever changes any of the array entries (which happens fairly regularly, either because of format changes, or because a new entry is added). It's probably better and simpler to simply have some entirely separate model for figuring out which dive computer supports which protocol. There are enough common rules (like "old Suunto models are all FTDI") that such a separate table probably would be simpler and clearer anyway. This does not implement such a table, though - it just removes the information entirely. Signed-off-by: Linus Torvalds --- src/descriptor.c | 284 +++++++++++++++++++++++------------------------ 1 file changed, 142 insertions(+), 142 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index 3849ea0..21e3a81 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -64,53 +64,53 @@ struct dc_descriptor_t { static const dc_descriptor_t g_descriptors[] = { /* Suunto Solution */ - {"Suunto", "Solution", DC_FAMILY_SUUNTO_SOLUTION, 0}, // FTDI + {"Suunto", "Solution", DC_FAMILY_SUUNTO_SOLUTION, 0}, /* Suunto Eon */ - {"Suunto", "Eon", DC_FAMILY_SUUNTO_EON, 0}, // FTDI - {"Suunto", "Solution Alpha", DC_FAMILY_SUUNTO_EON, 0}, // FTDI - {"Suunto", "Solution Nitrox", DC_FAMILY_SUUNTO_EON, 0}, // FTDI + {"Suunto", "Eon", DC_FAMILY_SUUNTO_EON, 0}, + {"Suunto", "Solution Alpha", DC_FAMILY_SUUNTO_EON, 0}, + {"Suunto", "Solution Nitrox", DC_FAMILY_SUUNTO_EON, 0}, /* Suunto Vyper */ - {"Suunto", "Spyder", DC_FAMILY_SUUNTO_VYPER, 0x01}, // FTDI - {"Suunto", "Stinger", DC_FAMILY_SUUNTO_VYPER, 0x03}, // FTDI - {"Suunto", "Mosquito", DC_FAMILY_SUUNTO_VYPER, 0x04}, // FTDI - {"Suunto", "D3", DC_FAMILY_SUUNTO_VYPER, 0x05}, // FTDI - {"Suunto", "Vyper", DC_FAMILY_SUUNTO_VYPER, 0x0A}, // FTDI - {"Suunto", "Vytec", DC_FAMILY_SUUNTO_VYPER, 0X0B}, // FTDI - {"Suunto", "Cobra", DC_FAMILY_SUUNTO_VYPER, 0X0C}, // FTDI - {"Suunto", "Gekko", DC_FAMILY_SUUNTO_VYPER, 0X0D}, // FTDI - {"Suunto", "Zoop", DC_FAMILY_SUUNTO_VYPER, 0x16}, // FTDI + {"Suunto", "Spyder", DC_FAMILY_SUUNTO_VYPER, 0x01}, + {"Suunto", "Stinger", DC_FAMILY_SUUNTO_VYPER, 0x03}, + {"Suunto", "Mosquito", DC_FAMILY_SUUNTO_VYPER, 0x04}, + {"Suunto", "D3", DC_FAMILY_SUUNTO_VYPER, 0x05}, + {"Suunto", "Vyper", DC_FAMILY_SUUNTO_VYPER, 0x0A}, + {"Suunto", "Vytec", DC_FAMILY_SUUNTO_VYPER, 0X0B}, + {"Suunto", "Cobra", DC_FAMILY_SUUNTO_VYPER, 0X0C}, + {"Suunto", "Gekko", DC_FAMILY_SUUNTO_VYPER, 0X0D}, + {"Suunto", "Zoop", DC_FAMILY_SUUNTO_VYPER, 0x16}, /* Suunto Vyper 2 */ - {"Suunto", "Vyper 2", DC_FAMILY_SUUNTO_VYPER2, 0x10}, // FTDI - {"Suunto", "Cobra 2", DC_FAMILY_SUUNTO_VYPER2, 0x11}, // FTDI - {"Suunto", "Vyper Air", DC_FAMILY_SUUNTO_VYPER2, 0x13}, // FTDI - {"Suunto", "Cobra 3", DC_FAMILY_SUUNTO_VYPER2, 0x14}, // FTDI - {"Suunto", "HelO2", DC_FAMILY_SUUNTO_VYPER2, 0x15}, // FTDI + {"Suunto", "Vyper 2", DC_FAMILY_SUUNTO_VYPER2, 0x10}, + {"Suunto", "Cobra 2", DC_FAMILY_SUUNTO_VYPER2, 0x11}, + {"Suunto", "Vyper Air", DC_FAMILY_SUUNTO_VYPER2, 0x13}, + {"Suunto", "Cobra 3", DC_FAMILY_SUUNTO_VYPER2, 0x14}, + {"Suunto", "HelO2", DC_FAMILY_SUUNTO_VYPER2, 0x15}, /* Suunto D9 */ - {"Suunto", "D9", DC_FAMILY_SUUNTO_D9, 0x0E}, // FTDI - {"Suunto", "D6", DC_FAMILY_SUUNTO_D9, 0x0F}, // FTDI - {"Suunto", "D4", DC_FAMILY_SUUNTO_D9, 0x12}, // FTDI - {"Suunto", "D4i", DC_FAMILY_SUUNTO_D9, 0x19}, // FTDI - {"Suunto", "D6i", DC_FAMILY_SUUNTO_D9, 0x1A}, // FTDI - {"Suunto", "D9tx", DC_FAMILY_SUUNTO_D9, 0x1B}, // FTDI - {"Suunto", "DX", DC_FAMILY_SUUNTO_D9, 0x1C}, // FTDI - {"Suunto", "Vyper Novo", DC_FAMILY_SUUNTO_D9, 0x1D}, // FTDI - {"Suunto", "Zoop Novo", DC_FAMILY_SUUNTO_D9, 0x1E}, // FTDI - {"Suunto", "D4f", DC_FAMILY_SUUNTO_D9, 0x20}, // FTDI + {"Suunto", "D9", DC_FAMILY_SUUNTO_D9, 0x0E}, + {"Suunto", "D6", DC_FAMILY_SUUNTO_D9, 0x0F}, + {"Suunto", "D4", DC_FAMILY_SUUNTO_D9, 0x12}, + {"Suunto", "D4i", DC_FAMILY_SUUNTO_D9, 0x19}, + {"Suunto", "D6i", DC_FAMILY_SUUNTO_D9, 0x1A}, + {"Suunto", "D9tx", DC_FAMILY_SUUNTO_D9, 0x1B}, + {"Suunto", "DX", DC_FAMILY_SUUNTO_D9, 0x1C}, + {"Suunto", "Vyper Novo", DC_FAMILY_SUUNTO_D9, 0x1D}, + {"Suunto", "Zoop Novo", DC_FAMILY_SUUNTO_D9, 0x1E}, + {"Suunto", "D4f", DC_FAMILY_SUUNTO_D9, 0x20}, /* Suunto EON Steel */ #if defined(USBHID) || defined(ENABLE_BLE) - {"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0}, // BLE - {"Suunto", "EON Core", DC_FAMILY_SUUNTO_EONSTEEL, 1}, // BLE + {"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0}, + {"Suunto", "EON Core", DC_FAMILY_SUUNTO_EONSTEEL, 1}, #endif /* Uwatec Aladin */ - {"Uwatec", "Aladin Air Twin", DC_FAMILY_UWATEC_ALADIN, 0x1C}, // FTDI - {"Uwatec", "Aladin Sport Plus", DC_FAMILY_UWATEC_ALADIN, 0x3E}, // FTDI - {"Uwatec", "Aladin Pro", DC_FAMILY_UWATEC_ALADIN, 0x3F}, // FTDI - {"Uwatec", "Aladin Air Z", DC_FAMILY_UWATEC_ALADIN, 0x44}, // FTDI - {"Uwatec", "Aladin Air Z O2", DC_FAMILY_UWATEC_ALADIN, 0xA4}, // FTDI - {"Uwatec", "Aladin Air Z Nitrox", DC_FAMILY_UWATEC_ALADIN, 0xF4}, // FTDI - {"Uwatec", "Aladin Pro Ultra", DC_FAMILY_UWATEC_ALADIN, 0xFF}, // FTDI + {"Uwatec", "Aladin Air Twin", DC_FAMILY_UWATEC_ALADIN, 0x1C}, + {"Uwatec", "Aladin Sport Plus", DC_FAMILY_UWATEC_ALADIN, 0x3E}, + {"Uwatec", "Aladin Pro", DC_FAMILY_UWATEC_ALADIN, 0x3F}, + {"Uwatec", "Aladin Air Z", DC_FAMILY_UWATEC_ALADIN, 0x44}, + {"Uwatec", "Aladin Air Z O2", DC_FAMILY_UWATEC_ALADIN, 0xA4}, + {"Uwatec", "Aladin Air Z Nitrox", DC_FAMILY_UWATEC_ALADIN, 0xF4}, + {"Uwatec", "Aladin Pro Ultra", DC_FAMILY_UWATEC_ALADIN, 0xFF}, /* Uwatec Memomouse */ - {"Uwatec", "Memomouse", DC_FAMILY_UWATEC_MEMOMOUSE, 0}, // FTDI + {"Uwatec", "Memomouse", DC_FAMILY_UWATEC_MEMOMOUSE, 0}, /* Uwatec Smart */ #ifdef IRDA {"Uwatec", "Smart Pro", DC_FAMILY_UWATEC_SMART, 0x10}, @@ -139,90 +139,90 @@ static const dc_descriptor_t g_descriptors[] = { {"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_MERIDIAN, 0x26}, /* Scubapro G2 */ #if defined(USBHID) || defined(ENABLE_BLE) - {"Scubapro", "Aladin Sport Matrix", DC_FAMILY_UWATEC_G2, 0x17}, // BLE + {"Scubapro", "Aladin Sport Matrix", DC_FAMILY_UWATEC_G2, 0x17}, {"Scubapro", "Aladin Square", DC_FAMILY_UWATEC_G2, 0x22}, - {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x32}, // BLE + {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x32}, #endif /* Reefnet */ {"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1}, {"Reefnet", "Sensus Pro", DC_FAMILY_REEFNET_SENSUSPRO, 2}, {"Reefnet", "Sensus Ultra", DC_FAMILY_REEFNET_SENSUSULTRA, 3}, /* Oceanic VT Pro */ - {"Aeris", "500 AI", DC_FAMILY_OCEANIC_VTPRO, 0x4151}, // FTDI - {"Oceanic", "Versa Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4155}, // FTDI - {"Aeris", "Atmos 2", DC_FAMILY_OCEANIC_VTPRO, 0x4158}, // FTDI - {"Oceanic", "Pro Plus 2", DC_FAMILY_OCEANIC_VTPRO, 0x4159}, // FTDI - {"Aeris", "Atmos AI", DC_FAMILY_OCEANIC_VTPRO, 0x4244}, // FTDI - {"Oceanic", "VT Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4245}, // FTDI - {"Sherwood", "Wisdom", DC_FAMILY_OCEANIC_VTPRO, 0x4246}, // FTDI - {"Aeris", "Elite", DC_FAMILY_OCEANIC_VTPRO, 0x424F}, // FTDI + {"Aeris", "500 AI", DC_FAMILY_OCEANIC_VTPRO, 0x4151}, + {"Oceanic", "Versa Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4155}, + {"Aeris", "Atmos 2", DC_FAMILY_OCEANIC_VTPRO, 0x4158}, + {"Oceanic", "Pro Plus 2", DC_FAMILY_OCEANIC_VTPRO, 0x4159}, + {"Aeris", "Atmos AI", DC_FAMILY_OCEANIC_VTPRO, 0x4244}, + {"Oceanic", "VT Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4245}, + {"Sherwood", "Wisdom", DC_FAMILY_OCEANIC_VTPRO, 0x4246}, + {"Aeris", "Elite", DC_FAMILY_OCEANIC_VTPRO, 0x424F}, /* Oceanic Veo 250 */ - {"Genesis", "React Pro", DC_FAMILY_OCEANIC_VEO250, 0x4247}, // FTDI - {"Oceanic", "Veo 200", DC_FAMILY_OCEANIC_VEO250, 0x424B}, // FTDI - {"Oceanic", "Veo 250", DC_FAMILY_OCEANIC_VEO250, 0x424C}, // FTDI - {"Seemann", "XP5", DC_FAMILY_OCEANIC_VEO250, 0x4251}, // FTDI - {"Oceanic", "Veo 180", DC_FAMILY_OCEANIC_VEO250, 0x4252}, // FTDI - {"Aeris", "XR-2", DC_FAMILY_OCEANIC_VEO250, 0x4255}, // FTDI - {"Sherwood", "Insight", DC_FAMILY_OCEANIC_VEO250, 0x425A}, // FTDI - {"Hollis", "DG02", DC_FAMILY_OCEANIC_VEO250, 0x4352}, // FTDI + {"Genesis", "React Pro", DC_FAMILY_OCEANIC_VEO250, 0x4247}, + {"Oceanic", "Veo 200", DC_FAMILY_OCEANIC_VEO250, 0x424B}, + {"Oceanic", "Veo 250", DC_FAMILY_OCEANIC_VEO250, 0x424C}, + {"Seemann", "XP5", DC_FAMILY_OCEANIC_VEO250, 0x4251}, + {"Oceanic", "Veo 180", DC_FAMILY_OCEANIC_VEO250, 0x4252}, + {"Aeris", "XR-2", DC_FAMILY_OCEANIC_VEO250, 0x4255}, + {"Sherwood", "Insight", DC_FAMILY_OCEANIC_VEO250, 0x425A}, + {"Hollis", "DG02", DC_FAMILY_OCEANIC_VEO250, 0x4352}, /* Oceanic Atom 2.0 */ - {"Oceanic", "Atom 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4250}, // FTDI - {"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4257}, // FTDI - {"Oceanic", "VT3", DC_FAMILY_OCEANIC_ATOM2, 0x4258}, // FTDI - {"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4259}, // FTDI - {"Oceanic", "Atom 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4342}, // FTDI - {"Oceanic", "Geo", DC_FAMILY_OCEANIC_ATOM2, 0x4344}, // FTDI - {"Aeris", "Manta", DC_FAMILY_OCEANIC_ATOM2, 0x4345}, // FTDI - {"Aeris", "XR-1 NX", DC_FAMILY_OCEANIC_ATOM2, 0x4346}, // FTDI - {"Oceanic", "Datamask", DC_FAMILY_OCEANIC_ATOM2, 0x4347}, // FTDI - {"Aeris", "Compumask", DC_FAMILY_OCEANIC_ATOM2, 0x4348}, // FTDI - {"Aeris", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x434D}, // FTDI - {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x434E}, // FTDI - {"Sherwood", "Wisdom 2", DC_FAMILY_OCEANIC_ATOM2, 0x4350}, // FTDI - {"Sherwood", "Insight 2", DC_FAMILY_OCEANIC_ATOM2, 0x4353}, // FTDI - {"Genesis", "React Pro White", DC_FAMILY_OCEANIC_ATOM2, 0x4354}, // FTDI - {"Tusa", "Element II (IQ-750)", DC_FAMILY_OCEANIC_ATOM2, 0x4357}, // FTDI - {"Oceanic", "Veo 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4358}, // FTDI - {"Oceanic", "Veo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4359}, // FTDI - {"Oceanic", "Veo 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x435A}, // FTDI - {"Tusa", "Zen (IQ-900)", DC_FAMILY_OCEANIC_ATOM2, 0x4441}, // FTDI - {"Tusa", "Zen Air (IQ-950)", DC_FAMILY_OCEANIC_ATOM2, 0x4442}, // FTDI - {"Aeris", "Atmos AI 2", DC_FAMILY_OCEANIC_ATOM2, 0x4443}, // FTDI - {"Oceanic", "Pro Plus 2.1", DC_FAMILY_OCEANIC_ATOM2, 0x4444}, // FTDI - {"Oceanic", "Geo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4446}, // FTDI - {"Oceanic", "VT4", DC_FAMILY_OCEANIC_ATOM2, 0x4447}, // FTDI - {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4449}, // FTDI - {"Beuchat", "Voyager 2G", DC_FAMILY_OCEANIC_ATOM2, 0x444B}, // FTDI - {"Oceanic", "Atom 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x444C}, // FTDI - {"Hollis", "DG03", DC_FAMILY_OCEANIC_ATOM2, 0x444D}, // FTDI - {"Oceanic", "OCS", DC_FAMILY_OCEANIC_ATOM2, 0x4450}, // FTDI - {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4451}, // FTDI - {"Oceanic", "VT 4.1", DC_FAMILY_OCEANIC_ATOM2, 0x4452}, // FTDI - {"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4453}, // FTDI - {"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4455}, // FTDI - {"Oceanic", "Atom 3.1", DC_FAMILY_OCEANIC_ATOM2, 0x4456}, // FTDI - {"Aeris", "A300 AI", DC_FAMILY_OCEANIC_ATOM2, 0x4457}, // FTDI - {"Sherwood", "Wisdom 3", DC_FAMILY_OCEANIC_ATOM2, 0x4458}, // FTDI - {"Aeris", "A300", DC_FAMILY_OCEANIC_ATOM2, 0x445A}, // FTDI - {"Hollis", "TX1", DC_FAMILY_OCEANIC_ATOM2, 0x4542}, // FTDI - {"Beuchat", "Mundial 2", DC_FAMILY_OCEANIC_ATOM2, 0x4543}, // FTDI - {"Sherwood", "Amphos", DC_FAMILY_OCEANIC_ATOM2, 0x4545}, // FTDI - {"Sherwood", "Amphos Air", DC_FAMILY_OCEANIC_ATOM2, 0x4546}, // FTDI - {"Oceanic", "Pro Plus 3", DC_FAMILY_OCEANIC_ATOM2, 0x4548}, // FTDI - {"Aeris", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4549}, // FTDI - {"Oceanic", "OCi", DC_FAMILY_OCEANIC_ATOM2, 0x454B}, // FTDI - {"Aeris", "A300CS", DC_FAMILY_OCEANIC_ATOM2, 0x454C}, // FTDI - {"Beuchat", "Mundial 3", DC_FAMILY_OCEANIC_ATOM2, 0x4550}, // FTDI - {"Oceanic", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x4553}, // FTDI - {"Oceanic", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4554}, // FTDI - {"Subgear", "XP-Air", DC_FAMILY_OCEANIC_ATOM2, 0x4555}, // FTDI - {"Sherwood", "Vision", DC_FAMILY_OCEANIC_ATOM2, 0x4556}, // FTDI - {"Oceanic", "VTX", DC_FAMILY_OCEANIC_ATOM2, 0x4557}, // FTDI - {"Aqualung", "i300", DC_FAMILY_OCEANIC_ATOM2, 0x4559}, // FTDI - {"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A}, // FTDI - {"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641}, // FTDI - {"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642}, // FTDI - {"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646}, // FTDI + {"Oceanic", "Atom 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4250}, + {"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4257}, + {"Oceanic", "VT3", DC_FAMILY_OCEANIC_ATOM2, 0x4258}, + {"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4259}, + {"Oceanic", "Atom 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4342}, + {"Oceanic", "Geo", DC_FAMILY_OCEANIC_ATOM2, 0x4344}, + {"Aeris", "Manta", DC_FAMILY_OCEANIC_ATOM2, 0x4345}, + {"Aeris", "XR-1 NX", DC_FAMILY_OCEANIC_ATOM2, 0x4346}, + {"Oceanic", "Datamask", DC_FAMILY_OCEANIC_ATOM2, 0x4347}, + {"Aeris", "Compumask", DC_FAMILY_OCEANIC_ATOM2, 0x4348}, + {"Aeris", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x434D}, + {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x434E}, + {"Sherwood", "Wisdom 2", DC_FAMILY_OCEANIC_ATOM2, 0x4350}, + {"Sherwood", "Insight 2", DC_FAMILY_OCEANIC_ATOM2, 0x4353}, + {"Genesis", "React Pro White", DC_FAMILY_OCEANIC_ATOM2, 0x4354}, + {"Tusa", "Element II (IQ-750)", DC_FAMILY_OCEANIC_ATOM2, 0x4357}, + {"Oceanic", "Veo 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4358}, + {"Oceanic", "Veo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4359}, + {"Oceanic", "Veo 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x435A}, + {"Tusa", "Zen (IQ-900)", DC_FAMILY_OCEANIC_ATOM2, 0x4441}, + {"Tusa", "Zen Air (IQ-950)", DC_FAMILY_OCEANIC_ATOM2, 0x4442}, + {"Aeris", "Atmos AI 2", DC_FAMILY_OCEANIC_ATOM2, 0x4443}, + {"Oceanic", "Pro Plus 2.1", DC_FAMILY_OCEANIC_ATOM2, 0x4444}, + {"Oceanic", "Geo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4446}, + {"Oceanic", "VT4", DC_FAMILY_OCEANIC_ATOM2, 0x4447}, + {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4449}, + {"Beuchat", "Voyager 2G", DC_FAMILY_OCEANIC_ATOM2, 0x444B}, + {"Oceanic", "Atom 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x444C}, + {"Hollis", "DG03", DC_FAMILY_OCEANIC_ATOM2, 0x444D}, + {"Oceanic", "OCS", DC_FAMILY_OCEANIC_ATOM2, 0x4450}, + {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4451}, + {"Oceanic", "VT 4.1", DC_FAMILY_OCEANIC_ATOM2, 0x4452}, + {"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4453}, + {"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4455}, + {"Oceanic", "Atom 3.1", DC_FAMILY_OCEANIC_ATOM2, 0x4456}, + {"Aeris", "A300 AI", DC_FAMILY_OCEANIC_ATOM2, 0x4457}, + {"Sherwood", "Wisdom 3", DC_FAMILY_OCEANIC_ATOM2, 0x4458}, + {"Aeris", "A300", DC_FAMILY_OCEANIC_ATOM2, 0x445A}, + {"Hollis", "TX1", DC_FAMILY_OCEANIC_ATOM2, 0x4542}, + {"Beuchat", "Mundial 2", DC_FAMILY_OCEANIC_ATOM2, 0x4543}, + {"Sherwood", "Amphos", DC_FAMILY_OCEANIC_ATOM2, 0x4545}, + {"Sherwood", "Amphos Air", DC_FAMILY_OCEANIC_ATOM2, 0x4546}, + {"Oceanic", "Pro Plus 3", DC_FAMILY_OCEANIC_ATOM2, 0x4548}, + {"Aeris", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4549}, + {"Oceanic", "OCi", DC_FAMILY_OCEANIC_ATOM2, 0x454B}, + {"Aeris", "A300CS", DC_FAMILY_OCEANIC_ATOM2, 0x454C}, + {"Beuchat", "Mundial 3", DC_FAMILY_OCEANIC_ATOM2, 0x4550}, + {"Oceanic", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x4553}, + {"Oceanic", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4554}, + {"Subgear", "XP-Air", DC_FAMILY_OCEANIC_ATOM2, 0x4555}, + {"Sherwood", "Vision", DC_FAMILY_OCEANIC_ATOM2, 0x4556}, + {"Oceanic", "VTX", DC_FAMILY_OCEANIC_ATOM2, 0x4557}, + {"Aqualung", "i300", DC_FAMILY_OCEANIC_ATOM2, 0x4559}, + {"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A}, + {"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641}, + {"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642}, + {"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646}, /* Mares Nemo */ {"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0}, {"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0}, @@ -252,22 +252,22 @@ static const dc_descriptor_t g_descriptors[] = { {"Mares", "Quad Air", DC_FAMILY_MARES_ICONHD , 0x23}, {"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29}, /* Heinrichs Weikamp */ - {"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0}, // FTDI - {"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1}, // FTDI - {"Heinrichs Weikamp", "OSTC 2N", DC_FAMILY_HW_OSTC, 2}, // FTDI - {"Heinrichs Weikamp", "OSTC 2C", DC_FAMILY_HW_OSTC, 3}, // FTDI - {"Heinrichs Weikamp", "Frog", DC_FAMILY_HW_FROG, 0}, // FTDI - {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x11}, // FTDI - {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x13}, // FTDI - {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x1B}, // FTDI - {"Heinrichs Weikamp", "OSTC 3", DC_FAMILY_HW_OSTC3, 0x0A}, // FTDI - {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x13}, // FTDI // BT - {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x1A}, // FTDI // BT - {"Heinrichs Weikamp", "OSTC 4", DC_FAMILY_HW_OSTC3, 0x3B}, // BT - {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x05}, // FTDI - {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x07}, // FTDI - {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x12}, // FTDI // BT - {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x13}, // FTDI // BT + {"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0}, + {"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1}, + {"Heinrichs Weikamp", "OSTC 2N", DC_FAMILY_HW_OSTC, 2}, + {"Heinrichs Weikamp", "OSTC 2C", DC_FAMILY_HW_OSTC, 3}, + {"Heinrichs Weikamp", "Frog", DC_FAMILY_HW_FROG, 0}, + {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x11}, + {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x13}, + {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x1B}, + {"Heinrichs Weikamp", "OSTC 3", DC_FAMILY_HW_OSTC3, 0x0A}, + {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x13}, + {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x1A}, + {"Heinrichs Weikamp", "OSTC 4", DC_FAMILY_HW_OSTC3, 0x3B}, + {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x05}, + {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x07}, + {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x12}, + {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x13}, /* Cressi Edy */ {"Tusa", "IQ-700", DC_FAMILY_CRESSI_EDY, 0x05}, {"Cressi", "Edy", DC_FAMILY_CRESSI_EDY, 0x08}, @@ -287,14 +287,14 @@ static const dc_descriptor_t g_descriptors[] = { {"Atomic Aquatics", "Cobalt 2", DC_FAMILY_ATOMICS_COBALT, 2}, #endif /* Shearwater Predator */ - {"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2}, // BT + {"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2}, /* Shearwater Petrel family */ - {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3}, // BT // BLE - {"Shearwater", "Petrel 2", DC_FAMILY_SHEARWATER_PETREL, 3}, // BT // BLE - {"Shearwater", "Nerd", DC_FAMILY_SHEARWATER_PETREL, 4}, // BT - {"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5}, // BT // BLE - {"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6}, // BLE - {"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7}, // BLE + {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3}, + {"Shearwater", "Petrel 2", DC_FAMILY_SHEARWATER_PETREL, 3}, + {"Shearwater", "Nerd", DC_FAMILY_SHEARWATER_PETREL, 4}, + {"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5}, + {"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6}, + {"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7}, /* Dive Rite NiTek Q */ {"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0}, /* Citizen Hyper Aqualand */ @@ -323,12 +323,12 @@ static const dc_descriptor_t g_descriptors[] = { {"Ratio", "iDive Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x45}, {"Seac", "Jack", DC_FAMILY_DIVESYSTEM_IDIVE, 0x1000}, /* Cochran Commander */ - {"Cochran", "Commander TM", DC_FAMILY_COCHRAN_COMMANDER, 0}, // FTDI - {"Cochran", "Commander I", DC_FAMILY_COCHRAN_COMMANDER, 1}, // FTDI - {"Cochran", "Commander II", DC_FAMILY_COCHRAN_COMMANDER, 2}, // FTDI - {"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 3}, // FTDI - {"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 4}, // FTDI - {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5}, // FTDI + {"Cochran", "Commander TM", DC_FAMILY_COCHRAN_COMMANDER, 0}, + {"Cochran", "Commander I", DC_FAMILY_COCHRAN_COMMANDER, 1}, + {"Cochran", "Commander II", DC_FAMILY_COCHRAN_COMMANDER, 2}, + {"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 3}, + {"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 4}, + {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5}, }; typedef struct dc_descriptor_iterator_t {