Merge branch 'discovery'

This commit is contained in:
Jef Driesen 2017-12-22 23:06:40 +01:00
commit 695212ddf8
16 changed files with 1596 additions and 601 deletions

View File

@ -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 \

View File

@ -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)
{

View File

@ -25,6 +25,8 @@
#include <libdivecomputer/common.h>
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/iterator.h>
#include <libdivecomputer/descriptor.h>
#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.
*

44
src/descriptor-private.h Normal file
View File

@ -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 <libdivecomputer/descriptor.h>
#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 */

View File

@ -41,18 +41,38 @@
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <libdivecomputer/descriptor.h>
#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;
}

View File

@ -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)
{

View File

@ -25,21 +25,53 @@
#include <libdivecomputer/common.h>
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/iterator.h>
#include <libdivecomputer/descriptor.h>
#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.
*

View File

@ -22,6 +22,7 @@
#ifndef DC_ITERATOR_PRIVATE_H
#define DC_ITERATOR_PRIVATE_H
#include <libdivecomputer/context.h>
#include <libdivecomputer/iterator.h>
#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 */

View File

@ -21,16 +21,51 @@
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#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;
}

View File

@ -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

View File

@ -25,29 +25,45 @@
#include <libdivecomputer/common.h>
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/iterator.h>
#include <libdivecomputer/descriptor.h>
#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.

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)
{

View File

@ -25,11 +25,54 @@
#include <libdivecomputer/common.h>
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/iterator.h>
#include <libdivecomputer/descriptor.h>
#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.
*

View File

@ -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, &current);
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;