diff --git a/src/bluetooth.c b/src/bluetooth.c index 1c3211e..337f1a9 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -50,6 +50,7 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" #ifdef _WIN32 #define DC_ADDRESS_FORMAT "%012I64X" @@ -64,7 +65,33 @@ #define ISINSTANCE(device) dc_iostream_isinstance((device), &dc_bluetooth_vtable) +struct dc_bluetooth_device_t { + dc_bluetooth_address_t address; + char name[248]; +}; + #ifdef BLUETOOTH +static dc_status_t dc_bluetooth_iterator_next (dc_iterator_t *iterator, void *item); +static dc_status_t dc_bluetooth_iterator_free (dc_iterator_t *iterator); + +typedef struct dc_bluetooth_iterator_t { + dc_iterator_t base; +#ifdef _WIN32 + HANDLE hLookup; +#else + int fd; + inquiry_info *devices; + size_t count; + size_t current; +#endif +} dc_bluetooth_iterator_t; + +static const dc_iterator_vtable_t dc_bluetooth_iterator_vtable = { + sizeof(dc_bluetooth_iterator_t), + dc_bluetooth_iterator_next, + dc_bluetooth_iterator_free, +}; + static const dc_iostream_vtable_t dc_bluetooth_vtable = { sizeof(dc_socket_t), dc_socket_set_timeout, /* set_timeout */ @@ -194,6 +221,220 @@ error: #endif #endif +dc_bluetooth_address_t +dc_bluetooth_device_get_address (dc_bluetooth_device_t *device) +{ + if (device == NULL) + return 0; + + return device->address; +} + +const char * +dc_bluetooth_device_get_name (dc_bluetooth_device_t *device) +{ + if (device == NULL || device->name[0] == '\0') + return NULL; + + return device->name; +} + +void +dc_bluetooth_device_free (dc_bluetooth_device_t *device) +{ + free (device); +} + +dc_status_t +dc_bluetooth_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_t *descriptor) +{ +#ifdef BLUETOOTH + dc_status_t status = DC_STATUS_SUCCESS; + dc_bluetooth_iterator_t *iterator = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + iterator = (dc_bluetooth_iterator_t *) dc_iterator_allocate (context, &dc_bluetooth_iterator_vtable); + if (iterator == NULL) { + SYSERROR (context, S_ENOMEM); + return DC_STATUS_NOMEMORY; + } + +#ifdef _WIN32 + WSAQUERYSET wsaq; + memset(&wsaq, 0, sizeof (wsaq)); + wsaq.dwSize = sizeof (wsaq); + wsaq.dwNameSpace = NS_BTH; + wsaq.lpcsaBuffer = NULL; + + HANDLE hLookup = NULL; + if (WSALookupServiceBegin(&wsaq, LUP_CONTAINERS | LUP_FLUSHCACHE, &hLookup) != 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == WSASERVICE_NOT_FOUND) { + // No remote bluetooth devices found. + hLookup = NULL; + } else { + SYSERROR (context, errcode); + status = dc_socket_syserror(errcode); + goto error_free; + } + } + + iterator->hLookup = hLookup; +#else + // Get the resource number for the first available bluetooth adapter. + int dev = hci_get_route (NULL); + if (dev < 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (context, errcode); + status = dc_socket_syserror(errcode); + goto error_free; + } + + // Open a socket to the bluetooth adapter. + int fd = hci_open_dev (dev); + if (fd < 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (context, errcode); + status = dc_socket_syserror(errcode); + goto error_free; + } + + // Perform the bluetooth device discovery. The inquiry lasts for at + // most MAX_PERIODS * 1.28 seconds, and at most MAX_DEVICES devices + // will be returned. + inquiry_info *devices = NULL; + int ndevices = hci_inquiry (dev, MAX_PERIODS, MAX_DEVICES, NULL, &devices, IREQ_CACHE_FLUSH); + if (ndevices < 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (context, errcode); + status = dc_socket_syserror(errcode); + goto error_close; + } + + iterator->fd = fd; + iterator->devices = devices; + iterator->count = ndevices; + iterator->current = 0; +#endif + + *out = (dc_iterator_t *) iterator; + + return DC_STATUS_SUCCESS; + +#ifndef _WIN32 +error_close: + hci_close_dev(fd); +#endif +error_free: + dc_iterator_deallocate ((dc_iterator_t *) iterator); + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +#ifdef BLUETOOTH +static dc_status_t +dc_bluetooth_iterator_next (dc_iterator_t *abstract, void *out) +{ + dc_bluetooth_iterator_t *iterator = (dc_bluetooth_iterator_t *) abstract; + dc_bluetooth_device_t *device = NULL; + +#ifdef _WIN32 + if (iterator->hLookup == NULL) { + return DC_STATUS_DONE; + } + + unsigned char buf[4096]; + LPWSAQUERYSET pwsaResults = (LPWSAQUERYSET) buf; + memset(pwsaResults, 0, sizeof(WSAQUERYSET)); + pwsaResults->dwSize = sizeof(WSAQUERYSET); + pwsaResults->dwNameSpace = NS_BTH; + pwsaResults->lpBlob = NULL; + + while (1) { + DWORD dwSize = sizeof(buf); + if (WSALookupServiceNext (iterator->hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &dwSize, pwsaResults) != 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == WSA_E_NO_MORE || errcode == WSAENOMORE) { + break; // No more results. + } + SYSERROR (abstract->context, errcode); + return dc_socket_syserror(errcode); + } + + if (pwsaResults->dwNumberOfCsAddrs == 0 || + pwsaResults->lpcsaBuffer == NULL || + pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr == NULL) { + ERROR (abstract->context, "Invalid results returned"); + return DC_STATUS_IO; + } + + SOCKADDR_BTH *sa = (SOCKADDR_BTH *) pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr; + dc_bluetooth_address_t address = sa->btAddr; + const char *name = (char *) pwsaResults->lpszServiceInstanceName; +#else + while (iterator->current < iterator->count) { + inquiry_info *dev = &iterator->devices[iterator->current++]; + + dc_bluetooth_address_t address = dc_address_get (&dev->bdaddr); + + // Get the user friendly name. + char buf[HCI_MAX_NAME_LENGTH], *name = buf; + int rc = hci_read_remote_name (iterator->fd, &dev->bdaddr, sizeof(buf), buf, 0); + if (rc < 0) { + name = NULL; + } + + // Null terminate the string. + buf[sizeof(buf) - 1] = '\0'; +#endif + + INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", + address, name ? name : ""); + + device = (dc_bluetooth_device_t *) malloc (sizeof(dc_bluetooth_device_t)); + if (device == NULL) { + SYSERROR (abstract->context, S_ENOMEM); + return DC_STATUS_NOMEMORY; + } + + device->address = address; + if (name) { + strncpy(device->name, name, sizeof(device->name) - 1); + device->name[sizeof(device->name) - 1] = '\0'; + } else { + memset(device->name, 0, sizeof(device->name)); + } + + *(dc_bluetooth_device_t **) out = device; + + return DC_STATUS_SUCCESS; + } + + return DC_STATUS_DONE; +} + +static dc_status_t +dc_bluetooth_iterator_free (dc_iterator_t *abstract) +{ + dc_bluetooth_iterator_t *iterator = (dc_bluetooth_iterator_t *) abstract; + +#ifdef _WIN32 + if (iterator->hLookup) { + WSALookupServiceEnd (iterator->hLookup); + } +#else + bt_free(iterator->devices); + hci_close_dev(iterator->fd); +#endif + + return DC_STATUS_SUCCESS; +} +#endif + dc_status_t dc_bluetooth_open (dc_iostream_t **out, dc_context_t *context) { @@ -233,141 +474,6 @@ error_free: #endif } -dc_status_t -dc_bluetooth_discover (dc_iostream_t *abstract, dc_bluetooth_callback_t callback, void *userdata) -{ -#ifdef BLUETOOTH - dc_status_t status = DC_STATUS_SUCCESS; - - if (!ISINSTANCE (abstract)) - return DC_STATUS_INVALIDARGS; - -#ifdef _WIN32 - WSAQUERYSET wsaq; - memset(&wsaq, 0, sizeof (wsaq)); - wsaq.dwSize = sizeof (wsaq); - wsaq.dwNameSpace = NS_BTH; - wsaq.lpcsaBuffer = NULL; - - HANDLE hLookup; - if (WSALookupServiceBegin(&wsaq, LUP_CONTAINERS | LUP_FLUSHCACHE, &hLookup) != 0) { - s_errcode_t errcode = S_ERRNO; - if (errcode == WSASERVICE_NOT_FOUND) { - // No remote bluetooth devices found. - status = DC_STATUS_SUCCESS; - } else { - SYSERROR (abstract->context, errcode); - status = dc_socket_syserror(errcode); - } - goto error_exit; - } - - unsigned char buf[4096]; - LPWSAQUERYSET pwsaResults = (LPWSAQUERYSET) buf; - memset(pwsaResults, 0, sizeof(WSAQUERYSET)); - pwsaResults->dwSize = sizeof(WSAQUERYSET); - pwsaResults->dwNameSpace = NS_BTH; - pwsaResults->lpBlob = NULL; - - while (1) { - DWORD dwSize = sizeof(buf); - if (WSALookupServiceNext (hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &dwSize, pwsaResults) != 0) { - s_errcode_t errcode = S_ERRNO; - if (errcode == WSA_E_NO_MORE || errcode == WSAENOMORE) { - break; // No more results. - } - SYSERROR (abstract->context, errcode); - status = dc_socket_syserror(errcode); - goto error_close; - } - - if (pwsaResults->dwNumberOfCsAddrs == 0 || - pwsaResults->lpcsaBuffer == NULL || - pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr == NULL) { - ERROR (abstract->context, "Invalid results returned"); - status = DC_STATUS_IO; - goto error_close; - } - - SOCKADDR_BTH *sa = (SOCKADDR_BTH *) pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr; - dc_bluetooth_address_t address = sa->btAddr; - const char *name = (char *) pwsaResults->lpszServiceInstanceName; - - INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", address, name); - - if (callback) callback (address, name, userdata); - - } - -error_close: - WSALookupServiceEnd (hLookup); -#else - // Get the resource number for the first available bluetooth adapter. - int dev = hci_get_route (NULL); - if (dev < 0) { - s_errcode_t errcode = S_ERRNO; - SYSERROR (abstract->context, errcode); - status = dc_socket_syserror(errcode); - goto error_exit; - } - - // Open a socket to the bluetooth adapter. - int fd = hci_open_dev (dev); - if (fd < 0) { - s_errcode_t errcode = S_ERRNO; - SYSERROR (abstract->context, errcode); - status = dc_socket_syserror(errcode); - goto error_exit; - } - - // Allocate a buffer to store the results of the discovery. - inquiry_info *devices = (inquiry_info *) malloc (MAX_DEVICES * sizeof(inquiry_info)); - if (devices == NULL) { - s_errcode_t errcode = S_ERRNO; - SYSERROR (abstract->context, errcode); - status = dc_socket_syserror(errcode); - goto error_close; - } - - // Perform the bluetooth device discovery. The inquiry lasts for at - // most MAX_PERIODS * 1.28 seconds, and at most MAX_DEVICES devices - // will be returned. - int ndevices = hci_inquiry (dev, MAX_PERIODS, MAX_DEVICES, NULL, &devices, IREQ_CACHE_FLUSH); - if (ndevices < 0) { - s_errcode_t errcode = S_ERRNO; - SYSERROR (abstract->context, errcode); - status = dc_socket_syserror(errcode); - goto error_free; - } - - for (unsigned int i = 0; i < ndevices; ++i) { - dc_bluetooth_address_t address = dc_address_get (&devices[i].bdaddr); - - // Get the user friendly name. - char buf[HCI_MAX_NAME_LENGTH], *name = buf; - int rc = hci_read_remote_name (fd, &devices[i].bdaddr, sizeof(buf), buf, 0); - if (rc < 0) { - name = NULL; - } - - INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", address, name); - - if (callback) callback (address, name, userdata); - } - -error_free: - free(devices); -error_close: - hci_close_dev(fd); -#endif - -error_exit: - return status; -#else - return DC_STATUS_UNSUPPORTED; -#endif -} - dc_status_t dc_bluetooth_connect (dc_iostream_t *abstract, dc_bluetooth_address_t address, unsigned int port) { diff --git a/src/bluetooth.h b/src/bluetooth.h index 1a8e947..264e833 100644 --- a/src/bluetooth.h +++ b/src/bluetooth.h @@ -25,6 +25,8 @@ #include #include #include +#include +#include #ifdef __cplusplus extern "C" { @@ -40,13 +42,45 @@ typedef unsigned long long dc_bluetooth_address_t; #endif /** - * Bluetooth enumeration callback. - * - * @param[in] address The bluetooth device address. - * @param[in] name The bluetooth device name. - * @param[in] userdata The user data pointer. + * Opaque object representing a bluetooth device. */ -typedef void (*dc_bluetooth_callback_t) (dc_bluetooth_address_t address, const char *name, void *userdata); +typedef struct dc_bluetooth_device_t dc_bluetooth_device_t; + +/** + * Get the address of the bluetooth device. + * + * @param[in] device A valid bluetooth device. + */ +dc_bluetooth_address_t +dc_bluetooth_device_get_address (dc_bluetooth_device_t *device); + +/** + * Get the name of the bluetooth device. + * + * @param[in] device A valid bluetooth device. + */ +const char * +dc_bluetooth_device_get_name (dc_bluetooth_device_t *device); + +/** + * Destroy the bluetooth device and free all resources. + * + * @param[in] device A valid bluetooth device. + */ +void +dc_bluetooth_device_free (dc_bluetooth_device_t *device); + +/** + * Create an iterator to enumerate the bluetooth devices. + * + * @param[out] iterator A location to store the iterator. + * @param[in] context A valid context object. + * @param[in] descriptor A valid device descriptor or NULL. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_iterator_new (dc_iterator_t **iterator, dc_context_t *context, dc_descriptor_t *descriptor); /** * Open an bluetooth connection. @@ -59,18 +93,6 @@ typedef void (*dc_bluetooth_callback_t) (dc_bluetooth_address_t address, const c dc_status_t dc_bluetooth_open (dc_iostream_t **iostream, dc_context_t *context); -/** - * Enumerate the bluetooth devices. - * - * @param[in] iostream A valid bluetooth connection. - * @param[in] callback The callback function to call. - * @param[in] userdata User data to pass to the callback function. - * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code - * on failure. - */ -dc_status_t -dc_bluetooth_discover (dc_iostream_t *iostream, dc_bluetooth_callback_t callback, void *userdata); - /** * Connect to an bluetooth device. * diff --git a/src/irda.c b/src/irda.c index b38a502..7106dbc 100644 --- a/src/irda.c +++ b/src/irda.c @@ -47,12 +47,46 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" #include "array.h" #include "platform.h" #define ISINSTANCE(device) dc_iostream_isinstance((device), &dc_irda_vtable) +#define DISCOVER_MAX_DEVICES 16 // Maximum number of devices. +#define DISCOVER_MAX_RETRIES 4 // Maximum number of retries. + +#ifdef _WIN32 +#define DISCOVER_BUFSIZE sizeof (DEVICELIST) + \ + sizeof (IRDA_DEVICE_INFO) * (DISCOVER_MAX_DEVICES - 1) +#else +#define DISCOVER_BUFSIZE sizeof (struct irda_device_list) + \ + sizeof (struct irda_device_info) * (DISCOVER_MAX_DEVICES - 1) +#endif + +struct dc_irda_device_t { + unsigned int address; + unsigned int charset; + unsigned int hints; + char name[22]; +}; + #ifdef IRDA +static dc_status_t dc_irda_iterator_next (dc_iterator_t *iterator, void *item); + +typedef struct dc_irda_iterator_t { + dc_iterator_t base; + dc_irda_device_t items[DISCOVER_MAX_DEVICES]; + size_t count; + size_t current; +} dc_irda_iterator_t; + +static const dc_iterator_vtable_t dc_irda_iterator_vtable = { + sizeof(dc_irda_iterator_t), + dc_irda_iterator_next, + NULL, +}; + static const dc_iostream_vtable_t dc_irda_vtable = { sizeof(dc_socket_t), dc_socket_set_timeout, /* set_timeout */ @@ -73,6 +107,180 @@ static const dc_iostream_vtable_t dc_irda_vtable = { }; #endif +unsigned int +dc_irda_device_get_address (dc_irda_device_t *device) +{ + if (device == NULL) + return 0; + + return device->address; +} + +const char * +dc_irda_device_get_name (dc_irda_device_t *device) +{ + if (device == NULL || device->name[0] == '\0') + return NULL; + + return device->name; +} + +void +dc_irda_device_free (dc_irda_device_t *device) +{ + free (device); +} + +dc_status_t +dc_irda_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_t *descriptor) +{ +#ifdef IRDA + dc_status_t status = DC_STATUS_SUCCESS; + dc_irda_iterator_t *iterator = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + iterator = (dc_irda_iterator_t *) dc_iterator_allocate (context, &dc_irda_iterator_vtable); + if (iterator == NULL) { + SYSERROR (context, S_ENOMEM); + return DC_STATUS_NOMEMORY; + } + + // Initialize the socket library. + status = dc_socket_init (context); + if (status != DC_STATUS_SUCCESS) { + goto error_free; + } + + // Open the socket. + int fd = socket (AF_IRDA, SOCK_STREAM, 0); + if (fd == S_INVALID) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (context, errcode); + status = dc_socket_syserror(errcode); + goto error_socket_exit; + } + + unsigned char data[DISCOVER_BUFSIZE] = {0}; +#ifdef _WIN32 + DEVICELIST *list = (DEVICELIST *) data; +#else + struct irda_device_list *list = (struct irda_device_list *) data; +#endif + s_socklen_t size = sizeof (data); + + int rc = 0; + unsigned int nretries = 0; + while ((rc = getsockopt (fd, SOL_IRLMP, IRLMP_ENUMDEVICES, (char *) data, &size)) != 0 || +#ifdef _WIN32 + list->numDevice == 0) +#else + list->len == 0) +#endif + { + // Automatically retry the discovery when no devices were found. + // On Linux, getsockopt fails with EAGAIN when no devices are + // discovered, while on Windows it succeeds and sets the number + // of devices to zero. Both situations are handled the same here. + if (rc != 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode != S_EAGAIN) { + SYSERROR (context, errcode); + status = dc_socket_syserror(errcode); + goto error_socket_close; + } + } + + // Abort if the maximum number of retries is reached. + if (nretries++ >= DISCOVER_MAX_RETRIES) { + break; + } + + // Restore the size parameter in case it was + // modified by the previous getsockopt call. + size = sizeof (data); + +#ifdef _WIN32 + Sleep (1000); +#else + sleep (1); +#endif + } + + S_CLOSE (fd); + dc_socket_exit (context); + + unsigned int count = 0; +#ifdef _WIN32 + for (size_t i = 0; i < list->numDevice; ++i) { + const char *name = list->Device[i].irdaDeviceName; + unsigned int address = array_uint32_le (list->Device[i].irdaDeviceID); + unsigned int charset = list->Device[i].irdaCharSet; + unsigned int hints = (list->Device[i].irdaDeviceHints1 << 8) + + list->Device[i].irdaDeviceHints2; +#else + for (size_t i = 0; i < list->len; ++i) { + const char *name = list->dev[i].info; + unsigned int address = list->dev[i].daddr; + unsigned int charset = list->dev[i].charset; + unsigned int hints = array_uint16_be (list->dev[i].hints); +#endif + + INFO (context, "Discover: address=%08x, name=%s, charset=%02x, hints=%04x", + address, name, charset, hints); + + strncpy(iterator->items[count].name, name, sizeof(iterator->items[count].name) - 1); + iterator->items[count].name[sizeof(iterator->items[count].name) - 1] = '\0'; + iterator->items[count].address = address; + iterator->items[count].charset = charset; + iterator->items[count].hints = hints; + count++; + } + + iterator->current = 0; + iterator->count = count; + + *out = (dc_iterator_t *) iterator; + + return DC_STATUS_SUCCESS; + +error_socket_close: + S_CLOSE (fd); +error_socket_exit: + dc_socket_exit (context); +error_free: + dc_iterator_deallocate ((dc_iterator_t *) iterator); + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +#ifdef IRDA +static dc_status_t +dc_irda_iterator_next (dc_iterator_t *abstract, void *out) +{ + dc_irda_iterator_t *iterator = (dc_irda_iterator_t *) abstract; + dc_irda_device_t *device = NULL; + + if (iterator->current >= iterator->count) + return DC_STATUS_DONE; + + device = (dc_irda_device_t *) malloc (sizeof(dc_irda_device_t)); + if (device == NULL) { + SYSERROR (abstract->context, S_ENOMEM); + return DC_STATUS_NOMEMORY; + } + + *device = iterator->items[iterator->current++]; + + *(dc_irda_device_t **) out = device; + + return DC_STATUS_SUCCESS; +} +#endif + dc_status_t dc_irda_open (dc_iostream_t **out, dc_context_t *context) { @@ -108,101 +316,6 @@ error_free: #endif } -#define DISCOVER_MAX_DEVICES 16 // Maximum number of devices. -#define DISCOVER_MAX_RETRIES 4 // Maximum number of retries. - -#ifdef _WIN32 -#define DISCOVER_BUFSIZE sizeof (DEVICELIST) + \ - sizeof (IRDA_DEVICE_INFO) * (DISCOVER_MAX_DEVICES - 1) -#else -#define DISCOVER_BUFSIZE sizeof (struct irda_device_list) + \ - sizeof (struct irda_device_info) * (DISCOVER_MAX_DEVICES - 1) -#endif - -dc_status_t -dc_irda_discover (dc_iostream_t *abstract, dc_irda_callback_t callback, void *userdata) -{ -#ifdef IRDA - dc_socket_t *device = (dc_socket_t *) abstract; - - if (!ISINSTANCE (abstract)) - return DC_STATUS_INVALIDARGS; - - unsigned char data[DISCOVER_BUFSIZE] = {0}; -#ifdef _WIN32 - DEVICELIST *list = (DEVICELIST *) data; - int size = sizeof (data); -#else - struct irda_device_list *list = (struct irda_device_list *) data; - socklen_t size = sizeof (data); -#endif - - int rc = 0; - unsigned int nretries = 0; - while ((rc = getsockopt (device->fd, SOL_IRLMP, IRLMP_ENUMDEVICES, (char*) data, &size)) != 0 || -#ifdef _WIN32 - list->numDevice == 0) -#else - list->len == 0) -#endif - { - // Automatically retry the discovery when no devices were found. - // On Linux, getsockopt fails with EAGAIN when no devices are - // discovered, while on Windows it succeeds and sets the number - // of devices to zero. Both situations are handled the same here. - if (rc != 0) { - s_errcode_t errcode = S_ERRNO; - if (errcode != S_EAGAIN) { - SYSERROR (abstract->context, errcode); - return dc_socket_syserror(errcode); - } - } - - // Abort if the maximum number of retries is reached. - if (nretries++ >= DISCOVER_MAX_RETRIES) - return DC_STATUS_SUCCESS; - - // Restore the size parameter in case it was - // modified by the previous getsockopt call. - size = sizeof (data); - -#ifdef _WIN32 - Sleep (1000); -#else - sleep (1); -#endif - } - - if (callback) { -#ifdef _WIN32 - for (unsigned int i = 0; i < list->numDevice; ++i) { - const char *name = list->Device[i].irdaDeviceName; - unsigned int address = array_uint32_le (list->Device[i].irdaDeviceID); - unsigned int charset = list->Device[i].irdaCharSet; - unsigned int hints = (list->Device[i].irdaDeviceHints1 << 8) + - list->Device[i].irdaDeviceHints2; -#else - for (unsigned int i = 0; i < list->len; ++i) { - const char *name = list->dev[i].info; - unsigned int address = list->dev[i].daddr; - unsigned int charset = list->dev[i].charset; - unsigned int hints = array_uint16_be (list->dev[i].hints); -#endif - - INFO (abstract->context, - "Discover: address=%08x, name=%s, charset=%02x, hints=%04x", - address, name, charset, hints); - - callback (address, name, charset, hints, userdata); - } - } - - return DC_STATUS_SUCCESS; -#else - return DC_STATUS_UNSUPPORTED; -#endif -} - dc_status_t dc_irda_connect_name (dc_iostream_t *abstract, unsigned int address, const char *name) { diff --git a/src/irda.h b/src/irda.h index c413963..c0ab8d6 100644 --- a/src/irda.h +++ b/src/irda.h @@ -25,21 +25,53 @@ #include #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /** - * IrDA enumeration callback. - * - * @param[in] address The IrDA device address. - * @param[in] name The IrDA device name. - * @param[in] charset The IrDA device character set. - * @param[in] hints The IrDA device hints. - * @param[in] userdata The user data pointer. + * Opaque object representing an IrDA device. */ -typedef void (*dc_irda_callback_t) (unsigned int address, const char *name, unsigned int charset, unsigned int hints, void *userdata); +typedef struct dc_irda_device_t dc_irda_device_t; + +/** + * Get the address of the IrDA device. + * + * @param[in] device A valid IrDA device. + */ +unsigned int +dc_irda_device_get_address (dc_irda_device_t *device); + +/** + * Get the name of the IrDA device. + * + * @param[in] device A valid IrDA device. + */ +const char * +dc_irda_device_get_name (dc_irda_device_t *device); + +/** + * Destroy the IrDA device and free all resources. + * + * @param[in] device A valid IrDA device. + */ +void +dc_irda_device_free (dc_irda_device_t *device); + +/** + * Create an iterator to enumerate the IrDA devices. + * + * @param[out] iterator A location to store the iterator. + * @param[in] context A valid context object. + * @param[in] descriptor A valid device descriptor or NULL. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_irda_iterator_new (dc_iterator_t **iterator, dc_context_t *context, dc_descriptor_t *descriptor); /** * Open an IrDA connection. @@ -52,18 +84,6 @@ typedef void (*dc_irda_callback_t) (unsigned int address, const char *name, unsi dc_status_t dc_irda_open (dc_iostream_t **iostream, dc_context_t *context); -/** - * Enumerate the IrDA devices. - * - * @param[in] iostream A valid IrDA connection. - * @param[in] callback The callback function to call. - * @param[in] userdata User data to pass to the callback function. - * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code - * on failure. - */ -dc_status_t -dc_irda_discover (dc_iostream_t *iostream, dc_irda_callback_t callback, void *userdata); - /** * Connect to an IrDA device. * diff --git a/src/serial.h b/src/serial.h index d6620e0..844c5b2 100644 --- a/src/serial.h +++ b/src/serial.h @@ -25,29 +25,45 @@ #include #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /** - * Serial enumeration callback. - * - * @param[in] name The name of the device node. - * @param[in] userdata The user data pointer. + * Opaque object representing a serial device. */ -typedef void (*dc_serial_callback_t) (const char *name, void *userdata); +typedef struct dc_serial_device_t dc_serial_device_t; /** - * Enumerate the serial ports. + * Get the device node of the serial device. * - * @param[in] callback The callback function to call. - * @param[in] userdata User data to pass to the callback function. + * @param[in] device A valid serial device. + */ +const char * +dc_serial_device_get_name (dc_serial_device_t *device); + +/** + * Destroy the serial device and free all resources. + * + * @param[in] device A valid serial device. + */ +void +dc_serial_device_free (dc_serial_device_t *device); + +/** + * Create an iterator to enumerate the serial devices. + * + * @param[out] iterator A location to store the iterator. + * @param[in] context A valid context object. + * @param[in] descriptor A valid device descriptor or NULL. * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code * on failure. */ dc_status_t -dc_serial_enumerate (dc_serial_callback_t callback, void *userdata); +dc_serial_iterator_new (dc_iterator_t **iterator, dc_context_t *context, dc_descriptor_t *descriptor); /** * Open a serial connection. diff --git a/src/serial_posix.c b/src/serial_posix.c index 356bc77..4108ab8 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -58,6 +58,12 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" + +#define DIRNAME "/dev" + +static dc_status_t dc_serial_iterator_next (dc_iterator_t *iterator, void *item); +static dc_status_t dc_serial_iterator_free (dc_iterator_t *iterator); static dc_status_t dc_serial_set_timeout (dc_iostream_t *iostream, int timeout); static dc_status_t dc_serial_set_latency (dc_iostream_t *iostream, unsigned int value); @@ -75,6 +81,15 @@ static dc_status_t dc_serial_purge (dc_iostream_t *iostream, dc_direction_t dire static dc_status_t dc_serial_sleep (dc_iostream_t *iostream, unsigned int milliseconds); static dc_status_t dc_serial_close (dc_iostream_t *iostream); +struct dc_serial_device_t { + char name[256]; +}; + +typedef struct dc_serial_iterator_t { + dc_iterator_t base; + DIR *dp; +} dc_serial_iterator_t; + typedef struct dc_serial_t { dc_iostream_t base; /* @@ -94,6 +109,12 @@ typedef struct dc_serial_t { unsigned int nbits; } dc_serial_t; +static const dc_iterator_vtable_t dc_serial_iterator_vtable = { + sizeof(dc_serial_iterator_t), + dc_serial_iterator_next, + dc_serial_iterator_free, +}; + static const dc_iostream_vtable_t dc_serial_vtable = { sizeof(dc_serial_t), dc_serial_set_timeout, /* set_timeout */ @@ -131,12 +152,60 @@ syserror(int errcode) } } -dc_status_t -dc_serial_enumerate (dc_serial_callback_t callback, void *userdata) +const char * +dc_serial_device_get_name (dc_serial_device_t *device) { - DIR *dp = NULL; + if (device == NULL || device->name[0] == '\0') + return NULL; + + return device->name; +} + +void +dc_serial_device_free (dc_serial_device_t *device) +{ + free (device); +} + +dc_status_t +dc_serial_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_t *descriptor) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_serial_iterator_t *iterator = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + iterator = (dc_serial_iterator_t *) dc_iterator_allocate (context, &dc_serial_iterator_vtable); + if (iterator == NULL) { + SYSERROR (context, ENOMEM); + return DC_STATUS_NOMEMORY; + } + + iterator->dp = opendir (DIRNAME); + if (iterator->dp == NULL) { + int errcode = errno; + SYSERROR (context, errcode); + status = syserror (errcode); + goto error_free; + } + + *out = (dc_iterator_t *) iterator; + + return DC_STATUS_SUCCESS; + +error_free: + dc_iterator_deallocate ((dc_iterator_t *) iterator); + return status; +} + +static dc_status_t +dc_serial_iterator_next (dc_iterator_t *abstract, void *out) +{ + dc_serial_iterator_t *iterator = (dc_serial_iterator_t *) abstract; + dc_serial_device_t *device = NULL; + struct dirent *ep = NULL; - const char *dirname = "/dev"; const char *patterns[] = { #if defined (__APPLE__) "tty.*", @@ -149,28 +218,40 @@ dc_serial_enumerate (dc_serial_callback_t callback, void *userdata) NULL }; - dp = opendir (dirname); - if (dp == NULL) { - return DC_STATUS_IO; - } - - while ((ep = readdir (dp)) != NULL) { + while ((ep = readdir (iterator->dp)) != NULL) { for (size_t i = 0; patterns[i] != NULL; ++i) { - if (fnmatch (patterns[i], ep->d_name, 0) == 0) { - char filename[1024]; - int n = snprintf (filename, sizeof (filename), "%s/%s", dirname, ep->d_name); - if (n >= sizeof (filename)) { - closedir (dp); - return DC_STATUS_NOMEMORY; - } + if (fnmatch (patterns[i], ep->d_name, 0) != 0) + continue; - callback (filename, userdata); - break; + char filename[sizeof(device->name)]; + int n = snprintf (filename, sizeof (filename), "%s/%s", DIRNAME, ep->d_name); + if (n < 0 || (size_t) n >= sizeof (filename)) { + return DC_STATUS_NOMEMORY; } + + device = (dc_serial_device_t *) malloc (sizeof(dc_serial_device_t)); + if (device == NULL) { + SYSERROR (abstract->context, ENOMEM); + return DC_STATUS_NOMEMORY; + } + + strncpy(device->name, filename, sizeof(device->name)); + + *(dc_serial_device_t **) out = device; + + return DC_STATUS_SUCCESS; } } - closedir (dp); + return DC_STATUS_DONE; +} + +static dc_status_t +dc_serial_iterator_free (dc_iterator_t *abstract) +{ + dc_serial_iterator_t *iterator = (dc_serial_iterator_t *) abstract; + + closedir (iterator->dp); return DC_STATUS_SUCCESS; } diff --git a/src/serial_win32.c b/src/serial_win32.c index 4454a74..3968d3f 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -29,6 +29,10 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" + +static dc_status_t dc_serial_iterator_next (dc_iterator_t *iterator, void *item); +static dc_status_t dc_serial_iterator_free (dc_iterator_t *iterator); static dc_status_t dc_serial_set_timeout (dc_iostream_t *iostream, int timeout); static dc_status_t dc_serial_set_latency (dc_iostream_t *iostream, unsigned int value); @@ -46,6 +50,17 @@ static dc_status_t dc_serial_purge (dc_iostream_t *iostream, dc_direction_t dire static dc_status_t dc_serial_sleep (dc_iostream_t *iostream, unsigned int milliseconds); static dc_status_t dc_serial_close (dc_iostream_t *iostream); +struct dc_serial_device_t { + char name[256]; +}; + +typedef struct dc_serial_iterator_t { + dc_iterator_t base; + HKEY hKey; + DWORD count; + DWORD current; +} dc_serial_iterator_t; + typedef struct dc_serial_t { dc_iostream_t base; /* @@ -65,6 +80,12 @@ typedef struct dc_serial_t { unsigned int nbits; } dc_serial_t; +static const dc_iterator_vtable_t dc_serial_iterator_vtable = { + sizeof(dc_serial_iterator_t), + dc_serial_iterator_next, + dc_serial_iterator_free, +}; + static const dc_iostream_vtable_t dc_serial_vtable = { sizeof(dc_serial_t), dc_serial_set_timeout, /* set_timeout */ @@ -101,37 +122,92 @@ syserror(DWORD errcode) } } -dc_status_t -dc_serial_enumerate (dc_serial_callback_t callback, void *userdata) +const char * +dc_serial_device_get_name (dc_serial_device_t *device) { + if (device == NULL || device->name[0] == '\0') + return NULL; + + return device->name; +} + +void +dc_serial_device_free (dc_serial_device_t *device) +{ + free (device); +} + +dc_status_t +dc_serial_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_t *descriptor) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_serial_iterator_t *iterator = NULL; + HKEY hKey = NULL; + DWORD count = 0; + LONG rc = 0; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + iterator = (dc_serial_iterator_t *) dc_iterator_allocate (context, &dc_serial_iterator_vtable); + if (iterator == NULL) { + SYSERROR (context, ERROR_OUTOFMEMORY); + return DC_STATUS_NOMEMORY; + } + // Open the registry key. - HKEY hKey; - LONG rc = RegOpenKeyExA (HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &hKey); + rc = RegOpenKeyExA (HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &hKey); if (rc != ERROR_SUCCESS) { - if (rc == ERROR_FILE_NOT_FOUND) - return DC_STATUS_SUCCESS; - else - return DC_STATUS_IO; + if (rc == ERROR_FILE_NOT_FOUND) { + hKey = NULL; + } else { + SYSERROR (context, rc); + status = syserror (rc); + goto error_free; + } } // Get the number of values. - DWORD count = 0; - rc = RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL); - if (rc != ERROR_SUCCESS) { - RegCloseKey(hKey); - return DC_STATUS_IO; + if (hKey) { + rc = RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL); + if (rc != ERROR_SUCCESS) { + SYSERROR (context, rc); + status = syserror (rc); + goto error_close; + } } - for (DWORD i = 0; i < count; ++i) { + iterator->hKey = hKey; + iterator->count = count; + iterator->current = 0; + + *out = (dc_iterator_t *) iterator; + + return DC_STATUS_SUCCESS; + +error_close: + RegCloseKey (hKey); +error_free: + dc_iterator_deallocate ((dc_iterator_t *) iterator); + return status; +} + +static dc_status_t +dc_serial_iterator_next (dc_iterator_t *abstract, void *out) +{ + dc_serial_iterator_t *iterator = (dc_serial_iterator_t *) abstract; + dc_serial_device_t *device = NULL; + + while (iterator->current < iterator->count) { // Get the value name, data and type. - char name[512], data[512]; + char name[256], data[sizeof(device->name)]; DWORD name_len = sizeof (name); DWORD data_len = sizeof (data); DWORD type = 0; - rc = RegEnumValueA (hKey, i, name, &name_len, NULL, &type, (LPBYTE) data, &data_len); + LONG rc = RegEnumValueA (iterator->hKey, iterator->current++, name, &name_len, NULL, &type, (LPBYTE) data, &data_len); if (rc != ERROR_SUCCESS) { - RegCloseKey(hKey); - return DC_STATUS_IO; + SYSERROR (abstract->context, rc); + return syserror (rc); } // Ignore non-string values. @@ -140,17 +216,36 @@ dc_serial_enumerate (dc_serial_callback_t callback, void *userdata) // Prevent a possible buffer overflow. if (data_len >= sizeof (data)) { - RegCloseKey(hKey); return DC_STATUS_NOMEMORY; } // Null terminate the string. data[data_len] = 0; - callback (data, userdata); + device = (dc_serial_device_t *) malloc (sizeof(dc_serial_device_t)); + if (device == NULL) { + SYSERROR (abstract->context, ERROR_OUTOFMEMORY); + return DC_STATUS_NOMEMORY; + } + + strncpy(device->name, data, sizeof(device->name)); + + *(dc_serial_device_t **) out = device; + + return DC_STATUS_SUCCESS; } - RegCloseKey(hKey); + return DC_STATUS_DONE; +} + +static dc_status_t +dc_serial_iterator_free (dc_iterator_t *abstract) +{ + dc_serial_iterator_t *iterator = (dc_serial_iterator_t *) abstract; + + if (iterator->hKey) { + RegCloseKey (iterator->hKey); + } return DC_STATUS_SUCCESS; } diff --git a/src/usbhid.c b/src/usbhid.c index dab5efe..ba16b3b 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -54,6 +54,7 @@ #include "common-private.h" #include "context-private.h" #include "iostream-private.h" +#include "iterator-private.h" #include "platform.h" #ifdef _WIN32 @@ -66,12 +67,30 @@ typedef pthread_mutex_t dc_mutex_t; #define ISINSTANCE(device) dc_iostream_isinstance((device), &dc_usbhid_vtable) +struct dc_usbhid_device_t { + unsigned short vid, pid; +}; + #ifdef USBHID +static dc_status_t dc_usbhid_iterator_next (dc_iterator_t *iterator, void *item); +static dc_status_t dc_usbhid_iterator_free (dc_iterator_t *iterator); + static dc_status_t dc_usbhid_set_timeout (dc_iostream_t *iostream, int timeout); static dc_status_t dc_usbhid_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual); static dc_status_t dc_usbhid_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual); static dc_status_t dc_usbhid_close (dc_iostream_t *iostream); +typedef struct dc_usbhid_iterator_t { + dc_iterator_t base; +#if defined(USE_LIBUSB) + struct libusb_device **devices; + size_t count; + size_t current; +#elif defined(USE_HIDAPI) + struct hid_device_info *devices, *current; +#endif +} dc_usbhid_iterator_t; + typedef struct dc_usbhid_t { /* Base class. */ dc_iostream_t base; @@ -88,6 +107,12 @@ typedef struct dc_usbhid_t { #endif } dc_usbhid_t; +static const dc_iterator_vtable_t dc_usbhid_iterator_vtable = { + sizeof(dc_usbhid_iterator_t), + dc_usbhid_iterator_next, + dc_usbhid_iterator_free, +}; + static const dc_iostream_vtable_t dc_usbhid_vtable = { sizeof(dc_usbhid_t), dc_usbhid_set_timeout, /* set_timeout */ @@ -211,6 +236,218 @@ dc_usbhid_exit (void) } #endif +unsigned int +dc_usbhid_device_get_vid (dc_usbhid_device_t *device) +{ + if (device == NULL) + return 0; + + return device->vid; +} + +unsigned int +dc_usbhid_device_get_pid (dc_usbhid_device_t *device) +{ + if (device == NULL) + return 0; + + return device->pid; +} + +void +dc_usbhid_device_free(dc_usbhid_device_t *device) +{ + free (device); +} + +dc_status_t +dc_usbhid_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_t *descriptor) +{ +#ifdef USBHID + dc_status_t status = DC_STATUS_SUCCESS; + dc_usbhid_iterator_t *iterator = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + iterator = (dc_usbhid_iterator_t *) dc_iterator_allocate (context, &dc_usbhid_iterator_vtable); + if (iterator == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Initialize the usb library. + status = dc_usbhid_init (context); + if (status != DC_STATUS_SUCCESS) { + goto error_free; + } + +#if defined(USE_LIBUSB) + // Enumerate the USB devices. + struct libusb_device **devices = NULL; + ssize_t ndevices = libusb_get_device_list (g_usbhid_ctx, &devices); + if (ndevices < 0) { + ERROR (context, "Failed to enumerate the usb devices (%s).", + libusb_error_name (ndevices)); + status = syserror (ndevices); + goto error_usb_exit; + } + + iterator->devices = devices; + iterator->count = ndevices; + iterator->current = 0; +#elif defined(USE_HIDAPI) + struct hid_device_info *devices = hid_enumerate(0x0, 0x0); + if (devices == NULL) { + status = DC_STATUS_IO; + goto error_usb_exit; + } + + iterator->devices = devices; + iterator->current = devices; +#endif + + *out = (dc_iterator_t *) iterator; + + return DC_STATUS_SUCCESS; + +error_usb_exit: + dc_usbhid_exit (); +error_free: + dc_iterator_deallocate ((dc_iterator_t *) iterator); + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +#ifdef USBHID +static dc_status_t +dc_usbhid_iterator_next (dc_iterator_t *abstract, void *out) +{ + dc_usbhid_iterator_t *iterator = (dc_usbhid_iterator_t *) abstract; + dc_usbhid_device_t *device = NULL; + +#if defined(USE_LIBUSB) + while (iterator->current < iterator->count) { + struct libusb_device *current = iterator->devices[iterator->current++]; + + // Get the device descriptor. + struct libusb_device_descriptor dev; + int rc = libusb_get_device_descriptor (current, &dev); + if (rc < 0) { + ERROR (abstract->context, "Failed to get the device descriptor (%s).", + libusb_error_name (rc)); + return syserror (rc); + } + + // Get the active configuration descriptor. + struct libusb_config_descriptor *config = NULL; + rc = libusb_get_active_config_descriptor (current, &config); + if (rc != LIBUSB_SUCCESS) { + ERROR (abstract->context, "Failed to get the configuration descriptor (%s).", + libusb_error_name (rc)); + return syserror (rc); + } + + // Find the first HID interface. + const struct libusb_interface_descriptor *interface = NULL; + for (unsigned int i = 0; i < config->bNumInterfaces; i++) { + const struct libusb_interface *iface = &config->interface[i]; + for (int j = 0; j < iface->num_altsetting; j++) { + const struct libusb_interface_descriptor *desc = &iface->altsetting[j]; + if (desc->bInterfaceClass == LIBUSB_CLASS_HID && interface == NULL) { + interface = desc; + } + } + } + + if (interface == NULL) { + libusb_free_config_descriptor (config); + continue; + } + + // Find the first input and output interrupt endpoints. + const struct libusb_endpoint_descriptor *ep_in = NULL, *ep_out = NULL; + for (unsigned int i = 0; i < interface->bNumEndpoints; i++) { + const struct libusb_endpoint_descriptor *desc = &interface->endpoint[i]; + + unsigned int type = desc->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK; + unsigned int direction = desc->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK; + + if (type != LIBUSB_TRANSFER_TYPE_INTERRUPT) { + continue; + } + + if (direction == LIBUSB_ENDPOINT_IN && ep_in == NULL) { + ep_in = desc; + } + + if (direction == LIBUSB_ENDPOINT_OUT && ep_out == NULL) { + ep_out = desc; + } + } + + if (ep_in == NULL || ep_out == NULL) { + libusb_free_config_descriptor (config); + continue; + } + + device = (dc_usbhid_device_t *) malloc (sizeof(dc_usbhid_device_t)); + if (device == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + libusb_free_config_descriptor (config); + return DC_STATUS_NOMEMORY; + } + + device->vid = dev.idVendor; + device->pid = dev.idProduct; + + *(dc_usbhid_device_t **) out = device; + + libusb_free_config_descriptor (config); + + return DC_STATUS_SUCCESS; + } +#elif defined(USE_HIDAPI) + while (iterator->current) { + struct hid_device_info *current = iterator->current; + iterator->current = current->next; + + device = (dc_usbhid_device_t *) malloc (sizeof(dc_usbhid_device_t)); + if (device == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + device->vid = current->vendor_id; + device->pid = current->product_id; + + *(dc_usbhid_device_t **) out = device; + + return DC_STATUS_SUCCESS; + } +#endif + + return DC_STATUS_DONE; +} + +static dc_status_t +dc_usbhid_iterator_free (dc_iterator_t *abstract) +{ + dc_usbhid_iterator_t *iterator = (dc_usbhid_iterator_t *) abstract; + +#if defined(USE_LIBUSB) + libusb_free_device_list (iterator->devices, 1); +#elif defined(USE_HIDAPI) + hid_free_enumeration (iterator->devices); +#endif + dc_usbhid_exit (); + + return DC_STATUS_SUCCESS; +} +#endif + dc_status_t dc_usbhid_open (dc_iostream_t **out, dc_context_t *context, unsigned int vid, unsigned int pid) { diff --git a/src/usbhid.h b/src/usbhid.h index 1e85f0a..16b302b 100644 --- a/src/usbhid.h +++ b/src/usbhid.h @@ -25,11 +25,54 @@ #include #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ +/** + * Opaque object representing a USB HID device. + */ +typedef struct dc_usbhid_device_t dc_usbhid_device_t; + +/** + * Get the vendor id (VID) of the USB HID device. + * + * @param[in] device A valid USB HID device. + */ +unsigned int +dc_usbhid_device_get_vid (dc_usbhid_device_t *device); + +/** + * Get the product id (PID) of the USB HID device. + * + * @param[in] device A valid USB HID device. + */ +unsigned int +dc_usbhid_device_get_pid (dc_usbhid_device_t *device); + +/** + * Destroy the USB HID device and free all resources. + * + * @param[in] device A valid USB HID device. + */ +void +dc_usbhid_device_free(dc_usbhid_device_t *device); + +/** + * Create an iterator to enumerate the USB HID devices. + * + * @param[out] iterator A location to store the iterator. + * @param[in] context A valid context object. + * @param[in] descriptor A valid device descriptor or NULL. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_usbhid_iterator_new (dc_iterator_t **iterator, dc_context_t *context, dc_descriptor_t *descriptor); + /** * Open a USB HID connection. * diff --git a/src/uwatec_smart.c b/src/uwatec_smart.c index 4581881..f82f3e1 100644 --- a/src/uwatec_smart.c +++ b/src/uwatec_smart.c @@ -36,7 +36,6 @@ typedef struct uwatec_smart_device_t { dc_device_t base; dc_iostream_t *iostream; - unsigned int address; unsigned int timestamp; unsigned int devtime; dc_ticks_t systime; @@ -62,8 +61,8 @@ static const dc_device_vtable_t uwatec_smart_device_vtable = { static dc_status_t uwatec_smart_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata); -static void -uwatec_smart_discovery (unsigned int address, const char *name, unsigned int charset, unsigned int hints, void *userdata) +static int +uwatec_smart_filter (const char *name) { static const char *names[] = { "Aladin Smart Com", @@ -75,16 +74,16 @@ uwatec_smart_discovery (unsigned int address, const char *name, unsigned int cha "UWATEC Galileo Sol", }; - uwatec_smart_device_t *device = (uwatec_smart_device_t*) userdata; - if (device == NULL || name == NULL) - return; + if (name == NULL) + return 0; for (size_t i = 0; i < C_ARRAY_SIZE(names); ++i) { if (strcasecmp(name, names[i]) == 0) { - device->address = address; - return; + return 1; } } + + return 0; } @@ -152,6 +151,8 @@ uwatec_smart_device_open (dc_device_t **out, dc_context_t *context) { dc_status_t status = DC_STATUS_SUCCESS; uwatec_smart_device_t *device = NULL; + dc_iterator_t *iterator = NULL; + dc_irda_device_t *dev = NULL; if (out == NULL) return DC_STATUS_INVALIDARGS; @@ -165,33 +166,48 @@ uwatec_smart_device_open (dc_device_t **out, dc_context_t *context) // Set the default values. device->iostream = NULL; - device->address = 0; device->timestamp = 0; device->systime = (dc_ticks_t) -1; device->devtime = 0; + // Create the irda device iterator. + status = dc_irda_iterator_new (&iterator, context, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to create the irda iterator."); + goto error_free; + } + + // Enumerate the irda devices. + while (1) { + dc_irda_device_t *current = NULL; + status = dc_iterator_next (iterator, ¤t); + if (status != DC_STATUS_SUCCESS) { + if (status == DC_STATUS_DONE) { + ERROR (context, "No dive computer found."); + status = DC_STATUS_NODEVICE; + } else { + ERROR (context, "Failed to enumerate the irda devices."); + } + goto error_iterator_free; + } + + if (uwatec_smart_filter (dc_irda_device_get_name (current))) { + dev = current; + break; + } + + dc_irda_device_free (current); + } + // Open the irda socket. status = dc_irda_open (&device->iostream, context); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to open the irda socket."); - goto error_free; - } - - // Discover the device. - status = dc_irda_discover (device->iostream, uwatec_smart_discovery, device); - if (status != DC_STATUS_SUCCESS) { - ERROR (context, "Failed to discover the device."); - goto error_close; - } - - if (device->address == 0) { - ERROR (context, "No dive computer found."); - status = DC_STATUS_IO; - goto error_close; + goto error_device_free; } // Connect the device. - status = dc_irda_connect_lsap (device->iostream, device->address, 1); + status = dc_irda_connect_lsap (device->iostream, dc_irda_device_get_address (dev), 1); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to connect the device."); goto error_close; @@ -210,6 +226,10 @@ uwatec_smart_device_open (dc_device_t **out, dc_context_t *context) error_close: dc_iostream_close (device->iostream); +error_device_free: + dc_irda_device_free (dev); +error_iterator_free: + dc_iterator_free (iterator); error_free: dc_device_deallocate ((dc_device_t *) device); return status;