diff --git a/configure.ac b/configure.ac index cf0a56d..21a95f7 100644 --- a/configure.ac +++ b/configure.ac @@ -166,6 +166,7 @@ AC_CHECK_HEADERS([IOKit/serial/ioss.h]) AC_CHECK_HEADERS([getopt.h]) AC_CHECK_HEADERS([sys/param.h]) AC_CHECK_HEADERS([pthread.h]) +AC_CHECK_HEADERS([mach/mach_time.h]) # Checks for global variable declarations. AC_CHECK_DECLS([optreset]) @@ -178,6 +179,7 @@ AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[ # Checks for library functions. AC_FUNC_STRERROR_R AC_CHECK_FUNCS([localtime_r gmtime_r timegm _mkgmtime]) +AC_CHECK_FUNCS([clock_gettime mach_absolute_time]) AC_CHECK_FUNCS([getopt_long]) # Checks for supported compiler options. @@ -187,12 +189,16 @@ AX_APPEND_COMPILE_FLAGS([ \ -Wrestrict \ -Wformat=2 \ -Wwrite-strings \ - -Wcast-qual \ -Wpointer-arith \ -Wstrict-prototypes \ -Wmissing-prototypes \ -Wmissing-declarations \ -Wno-unused-parameter \ + -Wno-unused-function \ + -Wno-unused-variable \ + -Wno-unused-but-set-variable \ + -Wno-pointer-sign \ + -Wno-shadow \ ]) # Windows specific compiler options. diff --git a/include/libdivecomputer/custom_io.h b/include/libdivecomputer/custom_io.h index 1e310b9..0e0ab7c 100644 --- a/include/libdivecomputer/custom_io.h +++ b/include/libdivecomputer/custom_io.h @@ -49,7 +49,6 @@ typedef struct dc_custom_io_t dc_status_t (*serial_configure) (struct dc_custom_io_t *io, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol); dc_status_t (*serial_set_dtr) (struct dc_custom_io_t *io, int level); dc_status_t (*serial_set_rts) (struct dc_custom_io_t *io, int level); - dc_status_t (*serial_set_halfduplex) (struct dc_custom_io_t *io, unsigned int value); dc_status_t (*serial_set_break) (struct dc_custom_io_t *io, unsigned int level); //dc_serial_set_latency (dc_serial_t *device, unsigned int milliseconds) - Unused //dc_serial_get_lines (dc_serial_t *device, unsigned int *value) - Unused diff --git a/include/libdivecomputer/descriptor.h b/include/libdivecomputer/descriptor.h index 8601b0f..a78b600 100644 --- a/include/libdivecomputer/descriptor.h +++ b/include/libdivecomputer/descriptor.h @@ -58,9 +58,6 @@ dc_descriptor_get_type (dc_descriptor_t *descriptor); unsigned int dc_descriptor_get_model (dc_descriptor_t *descriptor); -unsigned int -dc_descriptor_get_serial (dc_descriptor_t *descriptor); - dc_transport_t dc_descriptor_get_transport (dc_descriptor_t *descriptor); diff --git a/include/libdivecomputer/iostream.h b/include/libdivecomputer/iostream.h index dab5daf..832680d 100644 --- a/include/libdivecomputer/iostream.h +++ b/include/libdivecomputer/iostream.h @@ -129,17 +129,6 @@ dc_iostream_set_timeout (dc_iostream_t *iostream, int timeout); dc_status_t dc_iostream_set_latency (dc_iostream_t *iostream, unsigned int value); -/** - * Set the state of the half duplex emulation. - * - * @param[in] iostream A valid I/O stream. - * @param[in] value The half duplex state. - * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code - * on failure. - */ -dc_status_t -dc_iostream_set_halfduplex (dc_iostream_t *iostream, unsigned int value); - /** * Set the state of the break condition. * diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index c54c3d7..1b1cd96 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -482,6 +482,10 @@ RelativePath="..\src\suunto_vyper_parser.c" > + + @@ -820,6 +824,10 @@ RelativePath="..\src\suunto_vyper2.h" > + + diff --git a/src/Makefile.am b/src/Makefile.am index 49ae4e7..f72f38d 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 \ @@ -24,6 +24,7 @@ libdivecomputer_la_SOURCES = \ device-private.h device.c \ parser-private.h parser.c \ datetime.c \ + timer.h timer.c \ suunto_common.h suunto_common.c \ suunto_common2.h suunto_common2.c \ suunto_solution.h suunto_solution.c suunto_solution_parser.c \ diff --git a/src/bluetooth.c b/src/bluetooth.c index 1c3211e..772101f 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -24,6 +24,7 @@ #endif #include // malloc, free +#include #include "socket.h" @@ -50,6 +51,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,12 +67,38 @@ #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 */ dc_socket_set_latency, /* set_latency */ - dc_socket_set_halfduplex, /* set_halfduplex */ dc_socket_set_break, /* set_break */ dc_socket_set_dtr, /* set_dtr */ dc_socket_set_rts, /* set_rts */ @@ -194,8 +223,275 @@ error: #endif #endif +char * +dc_bluetooth_addr2str(dc_bluetooth_address_t address, char *str, size_t size) +{ + if (str == NULL || size < DC_BLUETOOTH_SIZE) + return NULL; + + int n = snprintf(str, size, "%02X:%02X:%02X:%02X:%02X:%02X", + (unsigned char)((address >> 40) & 0xFF), + (unsigned char)((address >> 32) & 0xFF), + (unsigned char)((address >> 24) & 0xFF), + (unsigned char)((address >> 16) & 0xFF), + (unsigned char)((address >> 8) & 0xFF), + (unsigned char)((address >> 0) & 0xFF)); + if (n < 0 || (size_t) n >= size) + return NULL; + + return str; +} + +dc_bluetooth_address_t +dc_bluetooth_str2addr(const char *str) +{ + dc_bluetooth_address_t address = 0; + + if (str == NULL) + return 0; + + unsigned char c = 0; + while ((c = *str++) != '\0') { + if (c == ':') { + continue; + } else if (c >= '0' && c <= '9') { + c -= '0'; + } else if (c >= 'A' && c <= 'F') { + c -= 'A' - 10; + } else if (c >= 'a' && c <= 'f') { + c -= 'a' - 10; + } else { + return 0; /* Invalid character! */ + } + + address <<= 4; + address |= c; + } + + return address; +} + +dc_bluetooth_address_t +dc_bluetooth_device_get_address (dc_bluetooth_device_t *device) +{ + 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_open (dc_iostream_t **out, dc_context_t *context) +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, dc_bluetooth_address_t address, unsigned int port) { #ifdef BLUETOOTH dc_status_t status = DC_STATUS_SUCCESS; @@ -204,6 +500,8 @@ dc_bluetooth_open (dc_iostream_t **out, dc_context_t *context) if (out == NULL) return DC_STATUS_INVALIDARGS; + INFO (context, "Open: address=" DC_ADDRESS_FORMAT ", port=%u", address, port); + // Allocate memory. device = (dc_socket_t *) dc_iostream_allocate (context, &dc_bluetooth_vtable); if (device == NULL) { @@ -221,164 +519,6 @@ dc_bluetooth_open (dc_iostream_t **out, dc_context_t *context) goto error_free; } - *out = (dc_iostream_t *) device; - - return DC_STATUS_SUCCESS; - -error_free: - dc_iostream_deallocate ((dc_iostream_t *) device); - return status; -#else - return DC_STATUS_UNSUPPORTED; -#endif -} - -dc_status_t -dc_bluetooth_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) -{ -#ifdef BLUETOOTH - dc_socket_t *device = (dc_socket_t *) abstract; - - if (!ISINSTANCE (abstract)) - return DC_STATUS_INVALIDARGS; - - INFO (abstract->context, "Connect: address=" DC_ADDRESS_FORMAT ", port=%d", address, port); - #ifdef _WIN32 SOCKADDR_BTH sa; sa.addressFamily = AF_BTH; @@ -394,16 +534,29 @@ dc_bluetooth_connect (dc_iostream_t *abstract, dc_bluetooth_address_t address, u sa.rc_family = AF_BLUETOOTH; dc_address_set (&sa.rc_bdaddr, address); if (port == 0) { - dc_status_t rc = dc_bluetooth_sdp (&sa.rc_channel, abstract->context, &sa.rc_bdaddr); - if (rc != DC_STATUS_SUCCESS) { - return rc; + status = dc_bluetooth_sdp (&sa.rc_channel, context, &sa.rc_bdaddr); + if (status != DC_STATUS_SUCCESS) { + goto error_close; } } else { sa.rc_channel = port; } #endif - return dc_socket_connect (&device->base, (struct sockaddr *) &sa, sizeof (sa)); + status = dc_socket_connect (&device->base, (struct sockaddr *) &sa, sizeof (sa)); + if (status != DC_STATUS_SUCCESS) { + goto error_close; + } + + *out = (dc_iostream_t *) device; + + return DC_STATUS_SUCCESS; + +error_close: + dc_socket_close (&device->base); +error_free: + dc_iostream_deallocate ((dc_iostream_t *) device); + return status; #else return DC_STATUS_UNSUPPORTED; #endif diff --git a/src/bluetooth.h b/src/bluetooth.h index 1a8e947..5b443a4 100644 --- a/src/bluetooth.h +++ b/src/bluetooth.h @@ -25,11 +25,19 @@ #include #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ +/** + * The minimum number of bytes (including the terminating null byte) for + * formatting a bluetooth address as a string. + */ +#define DC_BLUETOOTH_SIZE 18 + /** * Bluetooth address (48 bits). */ @@ -40,48 +48,86 @@ typedef unsigned long long dc_bluetooth_address_t; #endif /** - * Bluetooth enumeration callback. + * Convert a bluetooth address to a string. * - * @param[in] address The bluetooth device address. - * @param[in] name The bluetooth device name. - * @param[in] userdata The user data pointer. + * The bluetooth address is formatted as XX:XX:XX:XX:XX:XX, where each + * XX is a hexadecimal number specifying an octet of the 48-bit address. + * The minimum size for the buffer is #DC_BLUETOOTH_SIZE bytes. + * + * @param[in] address A bluetooth address. + * @param[in] str The memory buffer to store the result. + * @param[in] size The size of the memory buffer. + * @returns The null-terminated string on success, or NULL on failure. */ -typedef void (*dc_bluetooth_callback_t) (dc_bluetooth_address_t address, const char *name, void *userdata); +char * +dc_bluetooth_addr2str(dc_bluetooth_address_t address, char *str, size_t size); + +/** + * Convert a string to a bluetooth address. + * + * The string is expected to be in the format XX:XX:XX:XX:XX:XX, where + * each XX is a hexadecimal number specifying an octet of the 48-bit + * address. + * + * @param[in] address A null-terminated string. + * @returns The bluetooth address on success, or zero on failure. + */ +dc_bluetooth_address_t +dc_bluetooth_str2addr(const char *address); + +/** + * Opaque object representing a bluetooth device. + */ +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. * * @param[out] iostream A location to store the bluetooth connection. * @param[in] context A valid context object. - * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code - * on failure. - */ -dc_status_t -dc_bluetooth_open (dc_iostream_t **iostream, dc_context_t *context); - -/** - * 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. - * - * @param[in] iostream A valid bluetooth connection. * @param[in] address The bluetooth device address. * @param[in] port The bluetooth port number. * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code * on failure. */ dc_status_t -dc_bluetooth_connect (dc_iostream_t *iostream, dc_bluetooth_address_t address, unsigned int port); +dc_bluetooth_open (dc_iostream_t **iostream, dc_context_t *context, dc_bluetooth_address_t address, unsigned int port); #ifdef __cplusplus } diff --git a/src/context.c b/src/context.c index f731184..d662cd9 100644 --- a/src/context.c +++ b/src/context.c @@ -27,11 +27,11 @@ #ifdef _WIN32 #define NOGDI #include -#else -#include #endif #include "context-private.h" +#include "timer.h" + #include struct dc_context_t { @@ -40,11 +40,7 @@ struct dc_context_t { void *userdata; #ifdef ENABLE_LOGGING char msg[8192 + 32]; -#ifdef _WIN32 - LARGE_INTEGER timestamp, frequency; -#else - struct timeval timestamp; -#endif + dc_timer_t *timer; #endif dc_custom_io_t *custom_io; dc_user_device_t *user_device; @@ -137,23 +133,11 @@ logfunc (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsign { const char *loglevels[] = {"NONE", "ERROR", "WARNING", "INFO", "DEBUG", "ALL"}; - unsigned long seconds = 0, microseconds = 0; + dc_usecs_t now = 0; + dc_timer_now (context->timer, &now); -#ifdef _WIN32 - LARGE_INTEGER now, delta; - QueryPerformanceCounter(&now); - delta.QuadPart = now.QuadPart - context->timestamp.QuadPart; - delta.QuadPart *= 1000000; - delta.QuadPart /= context->frequency.QuadPart; - seconds = delta.QuadPart / 1000000; - microseconds = delta.QuadPart % 1000000; -#else - struct timeval now, delta; - gettimeofday (&now, NULL); - timersub (&now, &context->timestamp, &delta); - seconds = delta.tv_sec; - microseconds = delta.tv_usec; -#endif + unsigned long seconds = now / 1000000; + unsigned long microseconds = now % 1000000; if (loglevel == DC_LOGLEVEL_ERROR || loglevel == DC_LOGLEVEL_WARNING) { fprintf (stderr, "[%li.%06li] %s: %s [in %s:%d (%s)]\n", @@ -190,12 +174,8 @@ dc_context_new (dc_context_t **out) #ifdef ENABLE_LOGGING memset (context->msg, 0, sizeof (context->msg)); -#ifdef _WIN32 - QueryPerformanceFrequency(&context->frequency); - QueryPerformanceCounter(&context->timestamp); -#else - gettimeofday (&context->timestamp, NULL); -#endif + context->timer = NULL; + dc_timer_new (&context->timer); #endif context->custom_io = NULL; @@ -208,6 +188,10 @@ dc_context_new (dc_context_t **out) dc_status_t dc_context_free (dc_context_t *context) { + if (context == NULL) + return DC_STATUS_SUCCESS; + + dc_timer_free (context->timer); free (context); return DC_STATUS_SUCCESS; diff --git a/src/custom.c b/src/custom.c index e96ba5f..519a39e 100644 --- a/src/custom.c +++ b/src/custom.c @@ -29,7 +29,6 @@ static dc_status_t dc_custom_set_timeout (dc_iostream_t *abstract, int timeout); static dc_status_t dc_custom_set_latency (dc_iostream_t *abstract, unsigned int value); -static dc_status_t dc_custom_set_halfduplex (dc_iostream_t *abstract, unsigned int value); static dc_status_t dc_custom_set_break (dc_iostream_t *abstract, unsigned int value); static dc_status_t dc_custom_set_dtr (dc_iostream_t *abstract, unsigned int value); static dc_status_t dc_custom_set_rts (dc_iostream_t *abstract, unsigned int value); @@ -55,7 +54,6 @@ static const dc_iostream_vtable_t dc_custom_vtable = { sizeof(dc_custom_t), dc_custom_set_timeout, /* set_timeout */ dc_custom_set_latency, /* set_latency */ - dc_custom_set_halfduplex, /* set_halfduplex */ dc_custom_set_break, /* set_break */ dc_custom_set_dtr, /* set_dtr */ dc_custom_set_rts, /* set_rts */ @@ -117,17 +115,6 @@ dc_custom_set_latency (dc_iostream_t *abstract, unsigned int value) return custom->callbacks.set_latency (custom->userdata, value); } -static dc_status_t -dc_custom_set_halfduplex (dc_iostream_t *abstract, unsigned int value) -{ - dc_custom_t *custom = (dc_custom_t *) abstract; - - if (custom->callbacks.set_halfduplex == NULL) - return DC_STATUS_UNSUPPORTED; - - return custom->callbacks.set_halfduplex (custom->userdata, value); -} - static dc_status_t dc_custom_set_break (dc_iostream_t *abstract, unsigned int value) { diff --git a/src/custom.h b/src/custom.h index f062cd3..f1ab5da 100644 --- a/src/custom.h +++ b/src/custom.h @@ -33,7 +33,6 @@ extern "C" { typedef struct dc_custom_cbs_t { dc_status_t (*set_timeout) (void *userdata, int timeout); dc_status_t (*set_latency) (void *userdata, unsigned int value); - dc_status_t (*set_halfduplex) (void *userdata, unsigned int value); dc_status_t (*set_break) (void *userdata, unsigned int value); dc_status_t (*set_dtr) (void *userdata, unsigned int value); dc_status_t (*set_rts) (void *userdata, unsigned int value); diff --git a/src/custom_io.c b/src/custom_io.c index dd1adfa..7b47c7c 100644 --- a/src/custom_io.c +++ b/src/custom_io.c @@ -56,18 +56,6 @@ dc_custom_set_latency (dc_iostream_t *abstract, unsigned int value) return DC_STATUS_SUCCESS; } -static dc_status_t -dc_custom_set_halfduplex (dc_iostream_t *abstract, unsigned int value) -{ - dc_custom_t *custom = (dc_custom_t *) abstract; - dc_custom_io_t *io = _dc_context_custom_io(custom->context); - - if (!io->serial_set_halfduplex) - return DC_STATUS_SUCCESS; - - return io->serial_set_halfduplex(io, value); -} - static dc_status_t dc_custom_set_break (dc_iostream_t *abstract, unsigned int value) { @@ -198,7 +186,6 @@ static const dc_iostream_vtable_t dc_custom_vtable = { sizeof(dc_custom_t), dc_custom_set_timeout, /* set_timeout */ dc_custom_set_latency, /* set_latency */ - dc_custom_set_halfduplex, /* set_halfduplex */ dc_custom_set_break, /* set_break */ dc_custom_set_dtr, /* set_dtr */ dc_custom_set_rts, /* set_rts */ 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 c549f7a..c562317 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -41,19 +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; - unsigned int serial; + 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, }; /* @@ -65,285 +84,369 @@ struct dc_descriptor_t { static const dc_descriptor_t g_descriptors[] = { /* Suunto Solution */ - {"Suunto", "Solution", DC_FAMILY_SUUNTO_SOLUTION, 0}, // FTDI + {"Suunto", "Solution", DC_FAMILY_SUUNTO_SOLUTION, 0, NULL}, /* Suunto Eon */ - {"Suunto", "Eon", DC_FAMILY_SUUNTO_EON, 0}, // FTDI - {"Suunto", "Solution Alpha", DC_FAMILY_SUUNTO_EON, 0}, // FTDI - {"Suunto", "Solution Nitrox", DC_FAMILY_SUUNTO_EON, 0}, // FTDI + {"Suunto", "Eon", DC_FAMILY_SUUNTO_EON, 0, 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}, // FTDI - {"Suunto", "Stinger", DC_FAMILY_SUUNTO_VYPER, 0x03}, // FTDI - {"Suunto", "Mosquito", DC_FAMILY_SUUNTO_VYPER, 0x04}, // FTDI - {"Suunto", "D3", DC_FAMILY_SUUNTO_VYPER, 0x05}, // FTDI - {"Suunto", "Vyper", DC_FAMILY_SUUNTO_VYPER, 0x0A}, // FTDI - {"Suunto", "Vytec", DC_FAMILY_SUUNTO_VYPER, 0X0B}, // FTDI - {"Suunto", "Cobra", DC_FAMILY_SUUNTO_VYPER, 0X0C}, // FTDI - {"Suunto", "Gekko", DC_FAMILY_SUUNTO_VYPER, 0X0D}, // FTDI - {"Suunto", "Zoop", DC_FAMILY_SUUNTO_VYPER, 0x16}, // FTDI + {"Suunto", "Spyder", DC_FAMILY_SUUNTO_VYPER, 0x01, 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}, // FTDI - {"Suunto", "Cobra 2", DC_FAMILY_SUUNTO_VYPER2, 0x11}, // FTDI - {"Suunto", "Vyper Air", DC_FAMILY_SUUNTO_VYPER2, 0x13}, // FTDI - {"Suunto", "Cobra 3", DC_FAMILY_SUUNTO_VYPER2, 0x14}, // FTDI - {"Suunto", "HelO2", DC_FAMILY_SUUNTO_VYPER2, 0x15}, // FTDI + {"Suunto", "Vyper 2", DC_FAMILY_SUUNTO_VYPER2, 0x10, 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}, // FTDI - {"Suunto", "D6", DC_FAMILY_SUUNTO_D9, 0x0F}, // FTDI - {"Suunto", "D4", DC_FAMILY_SUUNTO_D9, 0x12}, // FTDI - {"Suunto", "D4i", DC_FAMILY_SUUNTO_D9, 0x19}, // FTDI - {"Suunto", "D6i", DC_FAMILY_SUUNTO_D9, 0x1A}, // FTDI - {"Suunto", "D9tx", DC_FAMILY_SUUNTO_D9, 0x1B}, // FTDI - {"Suunto", "DX", DC_FAMILY_SUUNTO_D9, 0x1C}, // FTDI - {"Suunto", "Vyper Novo", DC_FAMILY_SUUNTO_D9, 0x1D}, // FTDI - {"Suunto", "Zoop Novo", DC_FAMILY_SUUNTO_D9, 0x1E}, // FTDI - {"Suunto", "D4f", DC_FAMILY_SUUNTO_D9, 0x20}, // FTDI + {"Suunto", "D9", DC_FAMILY_SUUNTO_D9, 0x0E, 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 */ #if defined(USBHID) || defined(ENABLE_BLE) - {"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0}, // BLE - {"Suunto", "EON Core", DC_FAMILY_SUUNTO_EONSTEEL, 1}, // BLE + {"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0, 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}, // FTDI - {"Uwatec", "Aladin Sport Plus", DC_FAMILY_UWATEC_ALADIN, 0x3E}, // FTDI - {"Uwatec", "Aladin Pro", DC_FAMILY_UWATEC_ALADIN, 0x3F}, // FTDI - {"Uwatec", "Aladin Air Z", DC_FAMILY_UWATEC_ALADIN, 0x44}, // FTDI - {"Uwatec", "Aladin Air Z O2", DC_FAMILY_UWATEC_ALADIN, 0xA4}, // FTDI - {"Uwatec", "Aladin Air Z Nitrox", DC_FAMILY_UWATEC_ALADIN, 0xF4}, // FTDI - {"Uwatec", "Aladin Pro Ultra", DC_FAMILY_UWATEC_ALADIN, 0xFF}, // FTDI + {"Uwatec", "Aladin Air Twin", DC_FAMILY_UWATEC_ALADIN, 0x1C, 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}, // FTDI + {"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 */ #if defined(USBHID) || defined(ENABLE_BLE) - {"Scubapro", "Aladin Sport Matrix", DC_FAMILY_UWATEC_G2, 0x17}, // BLE - {"Scubapro", "Aladin Square", DC_FAMILY_UWATEC_G2, 0x22}, - {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x32}, // BLE + {"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}, // FTDI - {"Oceanic", "Versa Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4155}, // FTDI - {"Aeris", "Atmos 2", DC_FAMILY_OCEANIC_VTPRO, 0x4158}, // FTDI - {"Oceanic", "Pro Plus 2", DC_FAMILY_OCEANIC_VTPRO, 0x4159}, // FTDI - {"Aeris", "Atmos AI", DC_FAMILY_OCEANIC_VTPRO, 0x4244}, // FTDI - {"Oceanic", "VT Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4245}, // FTDI - {"Sherwood", "Wisdom", DC_FAMILY_OCEANIC_VTPRO, 0x4246}, // FTDI - {"Aeris", "Elite", DC_FAMILY_OCEANIC_VTPRO, 0x424F}, // FTDI + {"Aeris", "500 AI", DC_FAMILY_OCEANIC_VTPRO, 0x4151, 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}, // FTDI - {"Oceanic", "Veo 200", DC_FAMILY_OCEANIC_VEO250, 0x424B}, // FTDI - {"Oceanic", "Veo 250", DC_FAMILY_OCEANIC_VEO250, 0x424C}, // FTDI - {"Seemann", "XP5", DC_FAMILY_OCEANIC_VEO250, 0x4251}, // FTDI - {"Oceanic", "Veo 180", DC_FAMILY_OCEANIC_VEO250, 0x4252}, // FTDI - {"Aeris", "XR-2", DC_FAMILY_OCEANIC_VEO250, 0x4255}, // FTDI - {"Sherwood", "Insight", DC_FAMILY_OCEANIC_VEO250, 0x425A}, // FTDI - {"Hollis", "DG02", DC_FAMILY_OCEANIC_VEO250, 0x4352}, // FTDI + {"Genesis", "React Pro", DC_FAMILY_OCEANIC_VEO250, 0x4247, 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}, // FTDI - {"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4257}, // FTDI - {"Oceanic", "VT3", DC_FAMILY_OCEANIC_ATOM2, 0x4258}, // FTDI - {"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4259}, // FTDI - {"Oceanic", "Atom 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4342}, // FTDI - {"Oceanic", "Geo", DC_FAMILY_OCEANIC_ATOM2, 0x4344}, // FTDI - {"Aeris", "Manta", DC_FAMILY_OCEANIC_ATOM2, 0x4345}, // FTDI - {"Aeris", "XR-1 NX", DC_FAMILY_OCEANIC_ATOM2, 0x4346}, // FTDI - {"Oceanic", "Datamask", DC_FAMILY_OCEANIC_ATOM2, 0x4347}, // FTDI - {"Aeris", "Compumask", DC_FAMILY_OCEANIC_ATOM2, 0x4348}, // FTDI - {"Aeris", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x434D}, // FTDI - {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x434E}, // FTDI - {"Sherwood", "Wisdom 2", DC_FAMILY_OCEANIC_ATOM2, 0x4350}, // FTDI - {"Sherwood", "Insight 2", DC_FAMILY_OCEANIC_ATOM2, 0x4353}, // FTDI - {"Genesis", "React Pro White", DC_FAMILY_OCEANIC_ATOM2, 0x4354}, // FTDI - {"Tusa", "Element II (IQ-750)", DC_FAMILY_OCEANIC_ATOM2, 0x4357}, // FTDI - {"Oceanic", "Veo 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4358}, // FTDI - {"Oceanic", "Veo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4359}, // FTDI - {"Oceanic", "Veo 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x435A}, // FTDI - {"Tusa", "Zen (IQ-900)", DC_FAMILY_OCEANIC_ATOM2, 0x4441}, // FTDI - {"Tusa", "Zen Air (IQ-950)", DC_FAMILY_OCEANIC_ATOM2, 0x4442}, // FTDI - {"Aeris", "Atmos AI 2", DC_FAMILY_OCEANIC_ATOM2, 0x4443}, // FTDI - {"Oceanic", "Pro Plus 2.1", DC_FAMILY_OCEANIC_ATOM2, 0x4444}, // FTDI - {"Oceanic", "Geo 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4446}, // FTDI - {"Oceanic", "VT4", DC_FAMILY_OCEANIC_ATOM2, 0x4447}, // FTDI - {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4449}, // FTDI - {"Beuchat", "Voyager 2G", DC_FAMILY_OCEANIC_ATOM2, 0x444B}, // FTDI - {"Oceanic", "Atom 3.0", DC_FAMILY_OCEANIC_ATOM2, 0x444C}, // FTDI - {"Hollis", "DG03", DC_FAMILY_OCEANIC_ATOM2, 0x444D}, // FTDI - {"Oceanic", "OCS", DC_FAMILY_OCEANIC_ATOM2, 0x4450}, // FTDI - {"Oceanic", "OC1", DC_FAMILY_OCEANIC_ATOM2, 0x4451}, // FTDI - {"Oceanic", "VT 4.1", DC_FAMILY_OCEANIC_ATOM2, 0x4452}, // FTDI - {"Aeris", "Epic", DC_FAMILY_OCEANIC_ATOM2, 0x4453}, // FTDI - {"Aeris", "Elite T3", DC_FAMILY_OCEANIC_ATOM2, 0x4455}, // FTDI - {"Oceanic", "Atom 3.1", DC_FAMILY_OCEANIC_ATOM2, 0x4456}, // FTDI - {"Aeris", "A300 AI", DC_FAMILY_OCEANIC_ATOM2, 0x4457}, // FTDI - {"Sherwood", "Wisdom 3", DC_FAMILY_OCEANIC_ATOM2, 0x4458}, // FTDI - {"Aeris", "A300", DC_FAMILY_OCEANIC_ATOM2, 0x445A}, // FTDI - {"Hollis", "TX1", DC_FAMILY_OCEANIC_ATOM2, 0x4542}, // FTDI - {"Beuchat", "Mundial 2", DC_FAMILY_OCEANIC_ATOM2, 0x4543}, // FTDI - {"Sherwood", "Amphos", DC_FAMILY_OCEANIC_ATOM2, 0x4545}, // FTDI - {"Sherwood", "Amphos Air", DC_FAMILY_OCEANIC_ATOM2, 0x4546}, // FTDI - {"Oceanic", "Pro Plus 3", DC_FAMILY_OCEANIC_ATOM2, 0x4548}, // FTDI - {"Aeris", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4549}, // FTDI - {"Oceanic", "OCi", DC_FAMILY_OCEANIC_ATOM2, 0x454B}, // FTDI - {"Aeris", "A300CS", DC_FAMILY_OCEANIC_ATOM2, 0x454C}, // FTDI - {"Beuchat", "Mundial 3", DC_FAMILY_OCEANIC_ATOM2, 0x4550}, // FTDI - {"Oceanic", "F10", DC_FAMILY_OCEANIC_ATOM2, 0x4553}, // FTDI - {"Oceanic", "F11", DC_FAMILY_OCEANIC_ATOM2, 0x4554}, // FTDI - {"Subgear", "XP-Air", DC_FAMILY_OCEANIC_ATOM2, 0x4555}, // FTDI - {"Sherwood", "Vision", DC_FAMILY_OCEANIC_ATOM2, 0x4556}, // FTDI - {"Oceanic", "VTX", DC_FAMILY_OCEANIC_ATOM2, 0x4557}, // FTDI - {"Aqualung", "i300", DC_FAMILY_OCEANIC_ATOM2, 0x4559}, // FTDI - {"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A}, // FTDI - {"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641}, // FTDI - {"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642}, // FTDI - {"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646}, // FTDI + {"Oceanic", "Atom 1.0", DC_FAMILY_OCEANIC_ATOM2, 0x4250, 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}, // FTDI - {"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1}, // FTDI - {"Heinrichs Weikamp", "OSTC 2N", DC_FAMILY_HW_OSTC, 2}, // FTDI - {"Heinrichs Weikamp", "OSTC 2C", DC_FAMILY_HW_OSTC, 3}, // FTDI - {"Heinrichs Weikamp", "Frog", DC_FAMILY_HW_FROG, 0}, // FTDI - {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x11}, // FTDI - {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x13}, // FTDI - {"Heinrichs Weikamp", "OSTC 2", DC_FAMILY_HW_OSTC3, 0x1B}, // FTDI - {"Heinrichs Weikamp", "OSTC 3", DC_FAMILY_HW_OSTC3, 0x0A}, // FTDI - {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x13}, // FTDI // BT - {"Heinrichs Weikamp", "OSTC Plus", DC_FAMILY_HW_OSTC3, 0x1A}, // FTDI // BT - {"Heinrichs Weikamp", "OSTC 4", DC_FAMILY_HW_OSTC3, 0x3B}, // BT - {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x05}, // FTDI - {"Heinrichs Weikamp", "OSTC cR", DC_FAMILY_HW_OSTC3, 0x07}, // FTDI - {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x12}, // FTDI // BT - {"Heinrichs Weikamp", "OSTC Sport", DC_FAMILY_HW_OSTC3, 0x13}, // FTDI // BT + {"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0, 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}, + {"Heinrichs Weikamp", "OSTC 2 TR", DC_FAMILY_HW_OSTC3, 0x33, 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}, // BT - /* Shearwater Petrel family */ - {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3}, // BT // BLE - {"Shearwater", "Petrel 2", DC_FAMILY_SHEARWATER_PETREL, 3}, // BT // BLE - {"Shearwater", "Nerd", DC_FAMILY_SHEARWATER_PETREL, 4}, // BT - {"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5}, // BT // BLE - {"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6}, // BLE - {"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7}, // BLE + {"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2, dc_filter_shearwater}, + /* Shearwater Petrel */ + {"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}, // FTDI - {"Cochran", "Commander I", DC_FAMILY_COCHRAN_COMMANDER, 1}, // FTDI - {"Cochran", "Commander II", DC_FAMILY_COCHRAN_COMMANDER, 2}, // FTDI - {"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 3}, // FTDI - {"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 4}, // FTDI - {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5}, // FTDI + {"Cochran", "Commander TM", DC_FAMILY_COCHRAN_COMMANDER, 0, 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) @@ -353,11 +456,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; @@ -365,14 +467,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) { @@ -436,15 +530,6 @@ dc_descriptor_get_model (dc_descriptor_t *descriptor) return descriptor->model; } -unsigned int -dc_descriptor_get_serial (dc_descriptor_t *descriptor) -{ - if (descriptor == NULL) - return 0; - - return descriptor->serial; -} - dc_transport_t dc_descriptor_get_transport (dc_descriptor_t *descriptor) { @@ -462,3 +547,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/hw_ostc_parser.c b/src/hw_ostc_parser.c index 6b065a9..f053a21 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -757,6 +757,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call switch (info[i].type) { case 0: // Temperature case 1: // Deco / NDL + case 6: // Tank pressure if (info[i].size != 2) { ERROR(abstract->context, "Unexpected sample size."); return DC_STATUS_DATAFORMAT; @@ -790,6 +791,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call unsigned int time = 0; unsigned int nsamples = 0; + unsigned int tank = parser->initial != UNDEFINED ? parser->initial : 0; unsigned int offset = header; if (version == 0x23 || version == 0x24) @@ -925,6 +927,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call idx--; /* Convert to a zero based index. */ sample.gasmix = idx; if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); + tank = idx; offset++; length--; } @@ -1025,6 +1028,12 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call sample.cns = data[offset] / 100.0; if (callback) callback (DC_SAMPLE_CNS, sample, userdata); break; + case 6: // Tank pressure + value = array_uint16_le (data + offset); + sample.pressure.tank = tank; + sample.pressure.value = value / 10.0; + if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); + break; default: // Not yet used. break; } diff --git a/src/iostream-private.h b/src/iostream-private.h index 7f7be84..b9bd4f1 100644 --- a/src/iostream-private.h +++ b/src/iostream-private.h @@ -44,8 +44,6 @@ struct dc_iostream_vtable_t { dc_status_t (*set_latency) (dc_iostream_t *iostream, unsigned int value); - dc_status_t (*set_halfduplex) (dc_iostream_t *iostream, unsigned int value); - dc_status_t (*set_break) (dc_iostream_t *iostream, unsigned int value); dc_status_t (*set_dtr) (dc_iostream_t *iostream, unsigned int value); diff --git a/src/iostream.c b/src/iostream.c index e6c40ea..c1e3017 100644 --- a/src/iostream.c +++ b/src/iostream.c @@ -85,17 +85,6 @@ dc_iostream_set_latency (dc_iostream_t *iostream, unsigned int value) return iostream->vtable->set_latency (iostream, value); } -dc_status_t -dc_iostream_set_halfduplex (dc_iostream_t *iostream, unsigned int value) -{ - if (iostream == NULL || iostream->vtable->set_halfduplex == NULL) - return DC_STATUS_UNSUPPORTED; - - INFO (iostream->context, "Halfduplex: value=%i", value); - - return iostream->vtable->set_halfduplex (iostream, value); -} - dc_status_t dc_iostream_set_break (dc_iostream_t *iostream, unsigned int value) { diff --git a/src/irda.c b/src/irda.c index b38a502..d1aecfb 100644 --- a/src/irda.c +++ b/src/irda.c @@ -47,17 +47,51 @@ #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 */ dc_socket_set_latency, /* set_latency */ - dc_socket_set_halfduplex, /* set_halfduplex */ dc_socket_set_break, /* set_break */ dc_socket_set_dtr, /* set_dtr */ dc_socket_set_rts, /* set_rts */ @@ -73,73 +107,72 @@ 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_open (dc_iostream_t **out, dc_context_t *context) +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_socket_t *device = NULL; + dc_irda_iterator_t *iterator = NULL; if (out == NULL) return DC_STATUS_INVALIDARGS; - // Allocate memory. - device = (dc_socket_t *) dc_iostream_allocate (context, &dc_irda_vtable); - if (device == NULL) { + iterator = (dc_irda_iterator_t *) dc_iterator_allocate (context, &dc_irda_iterator_vtable); + if (iterator == NULL) { SYSERROR (context, S_ENOMEM); return DC_STATUS_NOMEMORY; } - // Open the socket. - status = dc_socket_open (&device->base, AF_IRDA, SOCK_STREAM, 0); + // Initialize the socket library. + status = dc_socket_init (context); if (status != DC_STATUS_SUCCESS) { goto error_free; } - *out = (dc_iostream_t *) device; - - return DC_STATUS_SUCCESS; - -error_free: - dc_iostream_deallocate ((dc_iostream_t *) device); - return status; -#else - return DC_STATUS_UNSUPPORTED; -#endif -} - -#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; + // 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; - int size = sizeof (data); #else struct irda_device_list *list = (struct irda_device_list *) data; - socklen_t size = sizeof (data); #endif + s_socklen_t size = sizeof (data); int rc = 0; unsigned int nretries = 0; - while ((rc = getsockopt (device->fd, SOL_IRLMP, IRLMP_ENUMDEVICES, (char*) data, &size)) != 0 || + while ((rc = getsockopt (fd, SOL_IRLMP, IRLMP_ENUMDEVICES, (char *) data, &size)) != 0 || #ifdef _WIN32 list->numDevice == 0) #else @@ -153,14 +186,16 @@ dc_irda_discover (dc_iostream_t *abstract, dc_irda_callback_t callback, void *us if (rc != 0) { s_errcode_t errcode = S_ERRNO; if (errcode != S_EAGAIN) { - SYSERROR (abstract->context, errcode); - return dc_socket_syserror(errcode); + 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) - return DC_STATUS_SUCCESS; + if (nretries++ >= DISCOVER_MAX_RETRIES) { + break; + } // Restore the size parameter in case it was // modified by the previous getsockopt call. @@ -173,88 +208,109 @@ dc_irda_discover (dc_iostream_t *abstract, dc_irda_callback_t callback, void *us #endif } - if (callback) { + S_CLOSE (fd); + dc_socket_exit (context); + + dc_filter_t filter = dc_descriptor_get_filter (descriptor); + + unsigned int count = 0; #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; + 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 (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); + 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 (abstract->context, - "Discover: address=%08x, name=%s, charset=%02x, hints=%04x", - address, name, charset, hints); + INFO (context, "Discover: address=%08x, name=%s, charset=%02x, hints=%04x", + address, name, charset, hints); - callback (address, name, charset, hints, userdata); + 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 } -dc_status_t -dc_irda_connect_name (dc_iostream_t *abstract, unsigned int address, const char *name) -{ #ifdef IRDA - dc_socket_t *device = (dc_socket_t *) abstract; +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 (!ISINSTANCE (abstract)) - return DC_STATUS_INVALIDARGS; + if (iterator->current >= iterator->count) + return DC_STATUS_DONE; - INFO (abstract->context, "Connect: address=%08x, name=%s", address, name ? name : ""); - -#ifdef _WIN32 - SOCKADDR_IRDA peer; - peer.irdaAddressFamily = AF_IRDA; - peer.irdaDeviceID[0] = (address ) & 0xFF; - peer.irdaDeviceID[1] = (address >> 8) & 0xFF; - peer.irdaDeviceID[2] = (address >> 16) & 0xFF; - peer.irdaDeviceID[3] = (address >> 24) & 0xFF; - if (name) { - strncpy (peer.irdaServiceName, name, sizeof(peer.irdaServiceName) - 1); - peer.irdaServiceName[sizeof(peer.irdaServiceName) - 1] = '\0'; - } else { - memset (peer.irdaServiceName, 0x00, sizeof(peer.irdaServiceName)); + device = (dc_irda_device_t *) malloc (sizeof(dc_irda_device_t)); + if (device == NULL) { + SYSERROR (abstract->context, S_ENOMEM); + return DC_STATUS_NOMEMORY; } -#else - struct sockaddr_irda peer; - peer.sir_family = AF_IRDA; - peer.sir_addr = address; - if (name) { - strncpy (peer.sir_name, name, sizeof(peer.sir_name) - 1); - peer.sir_name[sizeof(peer.sir_name) - 1] = '\0'; - } else { - memset (peer.sir_name, 0x00, sizeof(peer.sir_name)); - } -#endif - return dc_socket_connect (&device->base, (struct sockaddr *) &peer, sizeof (peer)); -#else - return DC_STATUS_UNSUPPORTED; -#endif + *device = iterator->items[iterator->current++]; + + *(dc_irda_device_t **) out = device; + + return DC_STATUS_SUCCESS; } +#endif dc_status_t -dc_irda_connect_lsap (dc_iostream_t *abstract, unsigned int address, unsigned int lsap) +dc_irda_open (dc_iostream_t **out, dc_context_t *context, unsigned int address, unsigned int lsap) { #ifdef IRDA - dc_socket_t *device = (dc_socket_t *) abstract; + dc_status_t status = DC_STATUS_SUCCESS; + dc_socket_t *device = NULL; - if (!ISINSTANCE (abstract)) + if (out == NULL) return DC_STATUS_INVALIDARGS; - INFO (abstract->context, "Connect: address=%08x, lsap=%u", address, lsap); + INFO (context, "Open: address=%08x, lsap=%u", address, lsap); + + // Allocate memory. + device = (dc_socket_t *) dc_iostream_allocate (context, &dc_irda_vtable); + if (device == NULL) { + SYSERROR (context, S_ENOMEM); + return DC_STATUS_NOMEMORY; + } + + // Open the socket. + status = dc_socket_open (&device->base, AF_IRDA, SOCK_STREAM, 0); + if (status != DC_STATUS_SUCCESS) { + goto error_free; + } #ifdef _WIN32 SOCKADDR_IRDA peer; @@ -272,7 +328,20 @@ dc_irda_connect_lsap (dc_iostream_t *abstract, unsigned int address, unsigned in memset (peer.sir_name, 0x00, sizeof(peer.sir_name)); #endif - return dc_socket_connect (&device->base, (struct sockaddr *) &peer, sizeof (peer)); + status = dc_socket_connect (&device->base, (struct sockaddr *) &peer, sizeof (peer)); + if (status != DC_STATUS_SUCCESS) { + goto error_close; + } + + *out = (dc_iostream_t *) device; + + return DC_STATUS_SUCCESS; + +error_close: + dc_socket_close (&device->base); +error_free: + dc_iostream_deallocate ((dc_iostream_t *) device); + return status; #else return DC_STATUS_UNSUPPORTED; #endif diff --git a/src/irda.h b/src/irda.h index c413963..92e9557 100644 --- a/src/irda.h +++ b/src/irda.h @@ -25,68 +25,66 @@ #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. * * @param[out] iostream A location to store the IrDA connection. * @param[in] context A valid context object. - * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code - * on failure. - */ -dc_status_t -dc_irda_open (dc_iostream_t **iostream, dc_context_t *context); - -/** - * 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. - * - * @param[in] iostream A valid IrDA connection. - * @param[in] address The IrDA device address. - * @param[in] name The IrDA service name. - * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code - * on failure. - */ -dc_status_t -dc_irda_connect_name (dc_iostream_t *iostream, unsigned int address, const char *name); - -/** - * Connect to an IrDA device. - * - * @param[in] iostream A valid IrDA connection. * @param[in] address The IrDA device address. * @param[in] lsap The IrDA LSAP number. * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code * on failure. */ dc_status_t -dc_irda_connect_lsap (dc_iostream_t *iostream, unsigned int address, unsigned int lsap); +dc_irda_open (dc_iostream_t **iostream, dc_context_t *context, unsigned int address, unsigned int lsap); #ifdef __cplusplus } diff --git a/src/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/libdivecomputer.symbols b/src/libdivecomputer.symbols index f272bd6..3f0760d 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -35,7 +35,6 @@ dc_descriptor_get_model dc_descriptor_get_transport dc_iostream_set_timeout -dc_iostream_set_halfduplex dc_iostream_set_latency dc_iostream_set_break dc_iostream_set_dtr diff --git a/src/oceanic_common.c b/src/oceanic_common.c index 1ab491d..9f7341a 100644 --- a/src/oceanic_common.c +++ b/src/oceanic_common.c @@ -32,7 +32,7 @@ #define VTABLE(abstract) ((const oceanic_common_device_vtable_t *) abstract->vtable) -#define RB_LOGBOOK_DISTANCE(a,b,l) ringbuffer_distance (a, b, 0, l->rb_logbook_begin, l->rb_logbook_end) +#define RB_LOGBOOK_DISTANCE(a,b,l) ringbuffer_distance (a, b, 1, l->rb_logbook_begin, l->rb_logbook_end) #define RB_LOGBOOK_INCR(a,b,l) ringbuffer_increment (a, b, l->rb_logbook_begin, l->rb_logbook_end) #define RB_PROFILE_DISTANCE(a,b,l) ringbuffer_distance (a, b, 0, l->rb_profile_begin, l->rb_profile_end) @@ -208,31 +208,37 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr // Get the logbook pointers. unsigned int rb_logbook_first = array_uint16_le (pointers + 4); unsigned int rb_logbook_last = array_uint16_le (pointers + 6); - if (rb_logbook_first < layout->rb_logbook_begin || - rb_logbook_first >= layout->rb_logbook_end || - rb_logbook_last < layout->rb_logbook_begin || + if (rb_logbook_last < layout->rb_logbook_begin || rb_logbook_last >= layout->rb_logbook_end) { - ERROR (abstract->context, "Invalid logbook pointer detected (0x%04x 0x%04x).", - rb_logbook_first, rb_logbook_last); + ERROR (abstract->context, "Invalid logbook end pointer detected (0x%04x).", rb_logbook_last); return DC_STATUS_DATAFORMAT; } - // Calculate the end pointer and the number of bytes. - unsigned int rb_logbook_end, rb_logbook_size; + // Calculate the end pointer. + unsigned int rb_logbook_end = 0; if (layout->pt_mode_global == 0) { rb_logbook_end = RB_LOGBOOK_INCR (rb_logbook_last, layout->rb_logbook_entry_size, layout); - rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_last, layout) + layout->rb_logbook_entry_size; } else { rb_logbook_end = rb_logbook_last; - rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_last, layout); - // In a typical ringbuffer implementation with only two begin/end - // pointers, there is no distinction possible between an empty and - // a full ringbuffer. We always consider the ringbuffer full in - // that case, because an empty ringbuffer can be detected by - // inspecting the logbook entries once they are downloaded. - if (rb_logbook_first == rb_logbook_last) - rb_logbook_size = layout->rb_logbook_end - layout->rb_logbook_begin; + } + + // Calculate the number of bytes. + // In a typical ringbuffer implementation with only two begin/end + // pointers, there is no distinction possible between an empty and a + // full ringbuffer. We always consider the ringbuffer full in that + // case, because an empty ringbuffer can be detected by inspecting + // the logbook entries once they are downloaded. + unsigned int rb_logbook_size = 0; + if (rb_logbook_first < layout->rb_logbook_begin || + rb_logbook_first >= layout->rb_logbook_end) + { + // Fall back to downloading the entire logbook ringbuffer as + // workaround for an invalid logbook begin pointer! + ERROR (abstract->context, "Invalid logbook begin pointer detected (0x%04x).", rb_logbook_first); + rb_logbook_size = layout->rb_logbook_end - layout->rb_logbook_begin; + } else { + rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_end, layout); } // Update and emit a progress event. diff --git a/src/parser.c b/src/parser.c index 4e6071f..f5f55e3 100644 --- a/src/parser.c +++ b/src/parser.c @@ -195,7 +195,7 @@ dc_parser_new2 (dc_parser_t **out, dc_context_t *context, dc_descriptor_t *descr return dc_parser_new_internal (out, context, dc_descriptor_get_type (descriptor), dc_descriptor_get_model (descriptor), - dc_descriptor_get_serial (descriptor), + 0, devtime, systime); } 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 9a9373e..03af81d 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -30,7 +30,6 @@ #include // fcntl #include // tcgetattr, tcsetattr, cfsetispeed, cfsetospeed, tcflush, tcsendbreak #include // ioctl -#include // gettimeofday #include // nanosleep #ifdef HAVE_LINUX_SERIAL_H #include @@ -58,10 +57,17 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" +#include "descriptor-private.h" +#include "timer.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); -static dc_status_t dc_serial_set_halfduplex (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_set_break (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_set_dtr (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_set_rts (dc_iostream_t *iostream, unsigned int value); @@ -75,6 +81,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; /* @@ -82,23 +98,25 @@ typedef struct dc_serial_t { */ int fd; int timeout; + dc_timer_t *timer; /* * Serial port settings are saved into this variable immediately * after the port is opened. These settings are restored when the * serial port is closed. */ struct termios tty; - /* Half-duplex settings */ - int halfduplex; - unsigned int baudrate; - unsigned int nbits; } dc_serial_t; +static const dc_iterator_vtable_t dc_serial_iterator_vtable = { + 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 */ dc_serial_set_latency, /* set_latency */ - dc_serial_set_halfduplex, /* set_halfduplex */ dc_serial_set_break, /* set_break */ dc_serial_set_dtr, /* set_dtr */ dc_serial_set_rts, /* set_rts */ @@ -131,12 +149,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 +217,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; } @@ -200,10 +284,12 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name) // Default to blocking reads. device->timeout = -1; - // Default to full-duplex. - device->halfduplex = 0; - device->baudrate = 0; - device->nbits = 0; + // Create a high resolution timer. + status = dc_timer_new (&device->timer); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to create a high resolution timer."); + goto error_free; + } // Open the device in non-blocking mode, to return immediately // without waiting for the modem connection to complete. @@ -212,7 +298,7 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name) int errcode = errno; SYSERROR (context, errcode); status = syserror (errcode); - goto error_free; + goto error_timer_free; } #ifndef ENABLE_PTY @@ -242,6 +328,8 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name) error_close: close (device->fd); +error_timer_free: + dc_timer_free (device->timer); error_free: dc_iostream_deallocate ((dc_iostream_t *) device); return status; @@ -276,6 +364,8 @@ dc_serial_close (dc_iostream_t *abstract) dc_status_set_error(&status, syserror (errcode)); } + dc_timer_free (device->timer); + return status; } @@ -522,9 +612,6 @@ dc_serial_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned in #endif } - device->baudrate = baudrate; - device->nbits = 1 + databits + stopbits + (parity ? 1 : 0); - return DC_STATUS_SUCCESS; } @@ -538,16 +625,6 @@ dc_serial_set_timeout (dc_iostream_t *abstract, int timeout) return DC_STATUS_SUCCESS; } -static dc_status_t -dc_serial_set_halfduplex (dc_iostream_t *abstract, unsigned int value) -{ - dc_serial_t *device = (dc_serial_t *) abstract; - - device->halfduplex = value; - - return DC_STATUS_SUCCESS; -} - static dc_status_t dc_serial_set_latency (dc_iostream_t *abstract, unsigned int milliseconds) { @@ -597,11 +674,8 @@ dc_serial_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual dc_serial_t *device = (dc_serial_t *) abstract; size_t nbytes = 0; - // The total timeout. - int timeout = device->timeout; - // The absolute target time. - struct timeval tve; + dc_usecs_t target = 0; int init = 1; while (nbytes < size) { @@ -609,35 +683,40 @@ dc_serial_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual FD_ZERO (&fds); FD_SET (device->fd, &fds); - struct timeval tvt; - if (timeout > 0) { - struct timeval now; - if (gettimeofday (&now, NULL) != 0) { - int errcode = errno; - SYSERROR (abstract->context, errcode); - status = syserror (errcode); + struct timeval tv, *ptv = NULL; + if (device->timeout > 0) { + dc_usecs_t timeout = 0; + + dc_usecs_t now = 0; + status = dc_timer_now (device->timer, &now); + if (status != DC_STATUS_SUCCESS) { goto out; } if (init) { // Calculate the initial timeout. - tvt.tv_sec = (timeout / 1000); - tvt.tv_usec = (timeout % 1000) * 1000; + timeout = device->timeout * 1000; // Calculate the target time. - timeradd (&now, &tvt, &tve); + target = now + timeout; + init = 0; } else { // Calculate the remaining timeout. - if (timercmp (&now, &tve, <)) - timersub (&tve, &now, &tvt); - else - timerclear (&tvt); + if (now < target) { + timeout = target - now; + } else { + timeout = 0; + } } - init = 0; - } else if (timeout == 0) { - timerclear (&tvt); + tv.tv_sec = timeout / 1000000; + tv.tv_usec = timeout % 1000000; + ptv = &tv; + } else if (device->timeout == 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + ptv = &tv; } - int rc = select (device->fd + 1, &fds, NULL, NULL, timeout >= 0 ? &tvt : NULL); + int rc = select (device->fd + 1, &fds, NULL, NULL, ptv); if (rc < 0) { int errcode = errno; if (errcode == EINTR) @@ -682,17 +761,6 @@ dc_serial_write (dc_iostream_t *abstract, const void *data, size_t size, size_t dc_serial_t *device = (dc_serial_t *) abstract; size_t nbytes = 0; - struct timeval tve, tvb; - if (device->halfduplex) { - // Get the current time. - if (gettimeofday (&tvb, NULL) != 0) { - int errcode = errno; - SYSERROR (abstract->context, errcode); - status = syserror (errcode); - goto out; - } - } - while (nbytes < size) { fd_set fds; FD_ZERO (&fds); @@ -740,35 +808,6 @@ dc_serial_write (dc_iostream_t *abstract, const void *data, size_t size, size_t } } - if (device->halfduplex) { - // Get the current time. - if (gettimeofday (&tve, NULL) != 0) { - int errcode = errno; - SYSERROR (abstract->context, errcode); - status = syserror (errcode); - goto out; - } - - // Calculate the elapsed time (microseconds). - struct timeval tvt; - timersub (&tve, &tvb, &tvt); - unsigned long elapsed = tvt.tv_sec * 1000000 + tvt.tv_usec; - - // Calculate the expected duration (microseconds). A 2 millisecond fudge - // factor is added because it improves the success rate significantly. - unsigned long expected = 1000000.0 * device->nbits / device->baudrate * size + 0.5 + 2000; - - // Wait for the remaining time. - if (elapsed < expected) { - unsigned long remaining = expected - elapsed; - - // The remaining time is rounded up to the nearest millisecond to - // match the Windows implementation. The higher resolution is - // pointless anyway, since we already added a fudge factor above. - dc_serial_sleep (abstract, (remaining + 999) / 1000); - } - } - out: if (actual) *actual = nbytes; diff --git a/src/serial_win32.c b/src/serial_win32.c index b530c9b..1b3f0a8 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -29,10 +29,14 @@ #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); -static dc_status_t dc_serial_set_halfduplex (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_set_break (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_set_dtr (dc_iostream_t *iostream, unsigned int value); static dc_status_t dc_serial_set_rts (dc_iostream_t *iostream, unsigned int value); @@ -46,6 +50,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; /* @@ -59,17 +75,18 @@ typedef struct dc_serial_t { */ DCB dcb; COMMTIMEOUTS timeouts; - /* Half-duplex settings */ - int halfduplex; - unsigned int baudrate; - unsigned int nbits; } dc_serial_t; +static const dc_iterator_vtable_t dc_serial_iterator_vtable = { + 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 */ dc_serial_set_latency, /* set_latency */ - dc_serial_set_halfduplex, /* set_halfduplex */ dc_serial_set_break, /* set_break */ dc_serial_set_dtr, /* set_dtr */ dc_serial_set_rts, /* set_rts */ @@ -101,37 +118,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 +213,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; } @@ -190,11 +286,6 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name) return DC_STATUS_NOMEMORY; } - // Default to full-duplex. - device->halfduplex = 0; - device->baudrate = 0; - device->nbits = 0; - // Open the device. device->hFile = CreateFileA (devname, GENERIC_READ | GENERIC_WRITE, 0, @@ -359,9 +450,6 @@ dc_serial_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned in return syserror (errcode); } - device->baudrate = baudrate; - device->nbits = 1 + databits + stopbits + (parity ? 1 : 0); - return DC_STATUS_SUCCESS; } @@ -412,16 +500,6 @@ dc_serial_set_timeout (dc_iostream_t *abstract, int timeout) return DC_STATUS_SUCCESS; } -static dc_status_t -dc_serial_set_halfduplex (dc_iostream_t *abstract, unsigned int value) -{ - dc_serial_t *device = (dc_serial_t *) abstract; - - device->halfduplex = value; - - return DC_STATUS_SUCCESS; -} - static dc_status_t dc_serial_set_latency (dc_iostream_t *abstract, unsigned int value) { @@ -460,18 +538,6 @@ dc_serial_write (dc_iostream_t *abstract, const void *data, size_t size, size_t dc_serial_t *device = (dc_serial_t *) abstract; DWORD dwWritten = 0; - LARGE_INTEGER begin, end, freq; - if (device->halfduplex) { - // Get the current time. - if (!QueryPerformanceFrequency(&freq) || - !QueryPerformanceCounter(&begin)) { - DWORD errcode = GetLastError (); - SYSERROR (abstract->context, errcode); - status = syserror (errcode); - goto out; - } - } - if (!WriteFile (device->hFile, data, size, &dwWritten, NULL)) { DWORD errcode = GetLastError (); SYSERROR (abstract->context, errcode); @@ -479,33 +545,6 @@ dc_serial_write (dc_iostream_t *abstract, const void *data, size_t size, size_t goto out; } - if (device->halfduplex) { - // Get the current time. - if (!QueryPerformanceCounter(&end)) { - DWORD errcode = GetLastError (); - SYSERROR (abstract->context, errcode); - status = syserror (errcode); - goto out; - } - - // Calculate the elapsed time (microseconds). - unsigned long elapsed = 1000000.0 * (end.QuadPart - begin.QuadPart) / freq.QuadPart + 0.5; - - // Calculate the expected duration (microseconds). A 2 millisecond fudge - // factor is added because it improves the success rate significantly. - unsigned long expected = 1000000.0 * device->nbits / device->baudrate * size + 0.5 + 2000; - - // Wait for the remaining time. - if (elapsed < expected) { - unsigned long remaining = expected - elapsed; - - // The remaining time is rounded up to the nearest millisecond - // because the Windows Sleep() function doesn't have a higher - // resolution. - dc_serial_sleep (abstract, (remaining + 999) / 1000); - } - } - if (dwWritten != size) { status = DC_STATUS_TIMEOUT; } diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 8c760f0..dda042c 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -494,7 +494,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) nsensors++; } } - if (nsensors == ndefaults) { + if (nsensors && nsensors == ndefaults) { // If all (calibrated) sensors still have their factory default // calibration values (2100), they are probably not calibrated // properly. To avoid returning incorrect ppO2 values to the diff --git a/src/socket.c b/src/socket.c index 5ce392f..1842a75 100644 --- a/src/socket.c +++ b/src/socket.c @@ -169,12 +169,6 @@ dc_socket_set_latency (dc_iostream_t *iostream, unsigned int value) return DC_STATUS_SUCCESS; } -dc_status_t -dc_socket_set_halfduplex (dc_iostream_t *iostream, unsigned int value) -{ - return DC_STATUS_SUCCESS; -} - dc_status_t dc_socket_set_break (dc_iostream_t *iostream, unsigned int value) { diff --git a/src/socket.h b/src/socket.h index d612603..626003c 100644 --- a/src/socket.h +++ b/src/socket.h @@ -107,9 +107,6 @@ dc_socket_set_timeout (dc_iostream_t *iostream, int timeout); dc_status_t dc_socket_set_latency (dc_iostream_t *iostream, unsigned int value); -dc_status_t -dc_socket_set_halfduplex (dc_iostream_t *iostream, unsigned int value); - dc_status_t dc_socket_set_break (dc_iostream_t *iostream, unsigned int value); diff --git a/src/suunto_eonsteel_parser.c b/src/suunto_eonsteel_parser.c index ffdd172..b48b701 100644 --- a/src/suunto_eonsteel_parser.c +++ b/src/suunto_eonsteel_parser.c @@ -1462,6 +1462,8 @@ static int traverse_sample_fields(suunto_eonsteel_parser_t *eon, const struct ty set_depth_field(eon, array_uint16_le(data)); data += 2; continue; + default: + break; } break; } diff --git a/src/suunto_vyper2.c b/src/suunto_vyper2.c index 2591878..596b06e 100644 --- a/src/suunto_vyper2.c +++ b/src/suunto_vyper2.c @@ -28,6 +28,7 @@ #include "serial.h" #include "checksum.h" #include "array.h" +#include "timer.h" #define ISINSTANCE(device) dc_device_isinstance((device), (const dc_device_vtable_t *) &suunto_vyper2_device_vtable) @@ -36,6 +37,7 @@ typedef struct suunto_vyper2_device_t { suunto_common2_device_t base; dc_iostream_t *iostream; + dc_timer_t *timer; } suunto_vyper2_device_t; static dc_status_t suunto_vyper2_device_packet (dc_device_t *abstract, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size); @@ -95,11 +97,18 @@ suunto_vyper2_device_open (dc_device_t **out, dc_context_t *context, const char // Set the default values. device->iostream = NULL; + // Create a high resolution timer. + status = dc_timer_new (&device->timer); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to create a high resolution timer."); + goto error_free; + } + // Open the device. status = dc_serial_open (&device->iostream, context, name); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to open the serial port."); - goto error_free; + goto error_timer_free; } // Set the serial communication protocol (9600 8N1). @@ -133,13 +142,6 @@ suunto_vyper2_device_open (dc_device_t **out, dc_context_t *context, const char goto error_close; } - // Enable half-duplex emulation. - status = dc_iostream_set_halfduplex (device->iostream, 1); - if (status != DC_STATUS_SUCCESS) { - ERROR (context, "Failed to set half duplex."); - goto error_close; - } - // Read the version info. status = suunto_common2_device_version ((dc_device_t *) device, device->base.version, sizeof (device->base.version)); if (status != DC_STATUS_SUCCESS) { @@ -160,6 +162,8 @@ suunto_vyper2_device_open (dc_device_t **out, dc_context_t *context, const char error_close: dc_iostream_close (device->iostream); +error_timer_free: + dc_timer_free (device->timer); error_free: dc_device_deallocate ((dc_device_t *) device); return status; @@ -173,6 +177,8 @@ suunto_vyper2_device_close (dc_device_t *abstract) suunto_vyper2_device_t *device = (suunto_vyper2_device_t*) abstract; dc_status_t rc = DC_STATUS_SUCCESS; + dc_timer_free (device->timer); + // Close the device. rc = dc_iostream_close (device->iostream); if (rc != DC_STATUS_SUCCESS) { @@ -201,6 +207,13 @@ suunto_vyper2_device_packet (dc_device_t *abstract, const unsigned char command[ return status; } + // Get the current timestamp. + dc_usecs_t begin = 0; + status = dc_timer_now (device->timer, &begin); + if (status != DC_STATUS_SUCCESS) { + return status; + } + // Send the command to the dive computer. status = dc_iostream_write (device->iostream, command, csize, NULL); if (status != DC_STATUS_SUCCESS) { @@ -208,6 +221,34 @@ suunto_vyper2_device_packet (dc_device_t *abstract, const unsigned char command[ return status; } + // Get the current timestamp. + dc_usecs_t end = 0; + status = dc_timer_now (device->timer, &end); + if (status != DC_STATUS_SUCCESS) { + return status; + } + + // Calculate the elapsed time. + dc_usecs_t elapsed = end - begin; + + // Calculate the expected duration. A 2 millisecond fudge factor is added + // because it improves the success rate significantly. + unsigned int baudrate = 9600; + unsigned int nbits = 1 + 8 /* databits */ + 1 /* stopbits */ + 0 /* parity */; + dc_usecs_t expected = (dc_usecs_t) csize * 1000000 * nbits / baudrate + 2000; + + // Wait for the remaining time. + if (elapsed < expected) { + dc_usecs_t remaining = expected - elapsed; + + // The remaining time is rounded up to the nearest millisecond + // because the sleep function doesn't support a higher + // resolution on all platforms. The higher resolution is + // pointless anyway, since we already added a fudge factor + // above. + dc_iostream_sleep (device->iostream, (remaining + 999) / 1000); + } + // Clear RTS to receive the reply. status = dc_iostream_set_rts (device->iostream, 0); if (status != DC_STATUS_SUCCESS) { diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000..36d1f8f --- /dev/null +++ b/src/timer.c @@ -0,0 +1,161 @@ +/* + * libdivecomputer + * + * Copyright (C) 2018 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifdef _WIN32 +#define NOGDI +#include +#else +#include +#include +#ifdef HAVE_MACH_MACH_TIME_H +#include +#endif +#endif + +#include "timer.h" + +struct dc_timer_t { +#if defined (_WIN32) + LARGE_INTEGER timestamp; + LARGE_INTEGER frequency; +#elif defined (HAVE_CLOCK_GETTIME) + struct timespec timestamp; +#elif defined (HAVE_MACH_ABSOLUTE_TIME) + uint64_t timestamp; + mach_timebase_info_data_t info; +#else + struct timeval timestamp; +#endif +}; + +dc_status_t +dc_timer_new (dc_timer_t **out) +{ + dc_timer_t *timer = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + timer = (dc_timer_t *) malloc (sizeof (dc_timer_t)); + if (timer == NULL) { + return DC_STATUS_NOMEMORY; + } + +#if defined (_WIN32) + if (!QueryPerformanceFrequency(&timer->frequency) || + !QueryPerformanceCounter(&timer->timestamp)) { + free(timer); + return DC_STATUS_IO; + } +#elif defined (HAVE_CLOCK_GETTIME) + if (clock_gettime(CLOCK_MONOTONIC, &timer->timestamp) != 0) { + free(timer); + return DC_STATUS_IO; + } +#elif defined (HAVE_MACH_ABSOLUTE_TIME) + if (mach_timebase_info(&timer->info) != KERN_SUCCESS) { + free(timer); + return DC_STATUS_IO; + } + + timer->timestamp = mach_absolute_time(); +#else + if (gettimeofday (&timer->timestamp, NULL) != 0) { + free(timer); + return DC_STATUS_IO; + } +#endif + + *out = timer; + + return DC_STATUS_SUCCESS; +} + +dc_status_t +dc_timer_now (dc_timer_t *timer, dc_usecs_t *usecs) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_usecs_t value = 0; + + if (timer == NULL) { + status = DC_STATUS_INVALIDARGS; + goto out; + } + +#if defined (_WIN32) + LARGE_INTEGER now; + if (!QueryPerformanceCounter(&now)) { + status = DC_STATUS_IO; + goto out; + } + + value = (now.QuadPart - timer->timestamp.QuadPart) * 1000000 / timer->frequency.QuadPart; +#elif defined (HAVE_CLOCK_GETTIME) + struct timespec now, delta; + if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) { + status = DC_STATUS_IO; + goto out; + } + + if (now.tv_nsec < timer->timestamp.tv_nsec) { + delta.tv_nsec = 1000000000 + now.tv_nsec - timer->timestamp.tv_nsec; + delta.tv_sec = now.tv_sec - timer->timestamp.tv_sec - 1; + } else { + delta.tv_nsec = now.tv_nsec - timer->timestamp.tv_nsec; + delta.tv_sec = now.tv_sec - timer->timestamp.tv_sec; + } + + value = (dc_usecs_t) delta.tv_sec * 1000000 + delta.tv_nsec / 1000; +#elif defined (HAVE_MACH_ABSOLUTE_TIME) + uint64_t now = mach_absolute_time(); + value = (now - timer->timestamp) * timer->info.numer / timer->info.denom; +#else + struct timeval now, delta; + if (gettimeofday (&now, NULL) != 0) { + status = DC_STATUS_IO; + goto out; + } + + timersub (&now, &timer->timestamp, &delta); + + value = (dc_usecs_t) delta.tv_sec * 1000000 + delta.tv_usec; +#endif + +out: + if (usecs) + *usecs = value; + + return status; +} + +dc_status_t +dc_timer_free (dc_timer_t *timer) +{ + free (timer); + + return DC_STATUS_SUCCESS; +} diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 0000000..651991f --- /dev/null +++ b/src/timer.h @@ -0,0 +1,51 @@ +/* + * libdivecomputer + * + * Copyright (C) 2018 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef DC_TIMER_H +#define DC_TIMER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if defined (_WIN32) && !defined (__GNUC__) +typedef unsigned __int64 dc_usecs_t; +#else +typedef unsigned long long dc_usecs_t; +#endif + +typedef struct dc_timer_t dc_timer_t; + +dc_status_t +dc_timer_new (dc_timer_t **timer); + +dc_status_t +dc_timer_now (dc_timer_t *timer, dc_usecs_t *usecs); + +dc_status_t +dc_timer_free (dc_timer_t *timer); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_TIMER_H */ diff --git a/src/usbhid.c b/src/usbhid.c index 468c1ab..52adf1f 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,11 +109,16 @@ 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 */ NULL, /* set_latency */ - NULL, /* set_halfduplex */ NULL, /* set_break */ NULL, /* set_dtr */ NULL, /* set_rts */ @@ -286,6 +312,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 0b381da..6ff43d1 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..a3be17d 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,36 +166,44 @@ 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; - // Open the irda socket. - status = dc_irda_open (&device->iostream, context); + // Create the irda device iterator. + status = dc_irda_iterator_new (&iterator, context, NULL); if (status != DC_STATUS_SUCCESS) { - ERROR (context, "Failed to open the irda socket."); + ERROR (context, "Failed to create the irda iterator."); 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; + // 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); } - if (device->address == 0) { - ERROR (context, "No dive computer found."); - status = DC_STATUS_IO; - goto error_close; - } - - // Connect the device. - status = dc_irda_connect_lsap (device->iostream, device->address, 1); + // Open the irda socket. + status = dc_irda_open (&device->iostream, context, dc_irda_device_get_address (dev), 1); if (status != DC_STATUS_SUCCESS) { - ERROR (context, "Failed to connect the device."); - goto error_close; + ERROR (context, "Failed to open the irda socket."); + goto error_device_free; } // Perform the handshaking. @@ -210,6 +219,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;