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 1c3211e..4581cd7 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -50,6 +50,8 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" +#include "descriptor-private.h" #ifdef _WIN32 #define DC_ADDRESS_FORMAT "%012I64X" @@ -64,7 +66,34 @@ #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; + dc_filter_t filter; +#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 +223,225 @@ 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 + iterator->filter = dc_descriptor_get_filter (descriptor); + + *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 : ""); + + 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); + 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 +481,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/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 49df56d..47fc028 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -41,18 +41,38 @@ #include #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 { const char *vendor; const char *product; dc_family_t type; unsigned int model; + dc_filter_t filter; +}; + +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, }; /* @@ -64,285 +84,368 @@ struct dc_descriptor_t { 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, 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}, - {"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, 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}, - {"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, 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}, - {"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, 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}, - {"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, dc_filter_shearwater}, /* 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, 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}, + {"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}, }; -typedef struct dc_descriptor_iterator_t { - dc_iterator_t base; - size_t current; -} dc_descriptor_iterator_t; +static int +dc_filter_internal_name (const char *name, const char *values[], size_t count) +{ + if (name == NULL) + return 0; -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); + for (size_t i = 0; i < count; ++i) { + if (strcasecmp (name, values[i]) == 0) { + return 1; + } + } -static const dc_iterator_vtable_t dc_descriptor_iterator_vtable = { - dc_descriptor_iterator_free, - dc_descriptor_iterator_next -}; + 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) @@ -352,11 +455,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 +466,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) { @@ -452,3 +546,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 b38a502..da5773f 100644 --- a/src/irda.c +++ b/src/irda.c @@ -47,12 +47,47 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" +#include "descriptor-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 +108,186 @@ 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); + + dc_filter_t filter = dc_descriptor_get_filter (descriptor); + + 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); + + 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; + 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 +323,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/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; } 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 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..c1697d5 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -58,6 +58,13 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" +#include "descriptor-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 +82,16 @@ 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; + dc_filter_t filter; + DIR *dp; +} dc_serial_iterator_t; + typedef struct dc_serial_t { dc_iostream_t base; /* @@ -94,6 +111,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 +154,62 @@ 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; + } + + iterator->filter = dc_descriptor_get_filter (descriptor); + + *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 +222,44 @@ 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; } + + 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); + 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..c9b31e9 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -29,6 +29,11 @@ #include "common-private.h" #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); 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 +51,18 @@ 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; + dc_filter_t filter; + HKEY hKey; + DWORD count; + DWORD current; +} dc_serial_iterator_t; + typedef struct dc_serial_t { dc_iostream_t base; /* @@ -65,6 +82,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 +124,93 @@ 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->filter = dc_descriptor_get_filter (descriptor); + 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 +219,40 @@ 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); + 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); + 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..b2dfa16 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -54,6 +54,8 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "descriptor-private.h" +#include "iterator-private.h" #include "platform.h" #ifdef _WIN32 @@ -66,12 +68,31 @@ 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; + dc_filter_t filter; +#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 +109,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 +238,229 @@ 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 + iterator->filter = dc_descriptor_get_filter (descriptor); + + *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); + } + + 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); + 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; + + 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."); + 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;