Merge branch 'iostream-usb'
This commit is contained in:
commit
33a20bd2b8
@ -31,6 +31,7 @@
|
||||
#include <libdivecomputer/serial.h>
|
||||
#include <libdivecomputer/bluetooth.h>
|
||||
#include <libdivecomputer/irda.h>
|
||||
#include <libdivecomputer/usb.h>
|
||||
#include <libdivecomputer/usbhid.h>
|
||||
|
||||
#include "common.h"
|
||||
@ -398,6 +399,41 @@ dctool_file_read (const char *filename)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dctool_usb_open (dc_iostream_t **out, dc_context_t *context, dc_descriptor_t *descriptor)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_iostream_t *iostream = NULL;
|
||||
|
||||
// Discover the usb device.
|
||||
dc_iterator_t *iterator = NULL;
|
||||
dc_usb_device_t *device = NULL;
|
||||
dc_usb_iterator_new (&iterator, context, descriptor);
|
||||
while (dc_iterator_next (iterator, &device) == DC_STATUS_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
dc_iterator_free (iterator);
|
||||
|
||||
if (device == NULL) {
|
||||
ERROR ("No dive computer found.");
|
||||
status = DC_STATUS_NODEVICE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Open the usb device.
|
||||
status = dc_usb_open (&iostream, context, device);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR ("Failed to open the usb device.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*out = iostream;
|
||||
|
||||
cleanup:
|
||||
dc_usb_device_free (device);
|
||||
return status;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dctool_usbhid_open (dc_iostream_t **out, dc_context_t *context, dc_descriptor_t *descriptor)
|
||||
{
|
||||
@ -532,7 +568,7 @@ dctool_iostream_open (dc_iostream_t **iostream, dc_context_t *context, dc_descri
|
||||
case DC_TRANSPORT_SERIAL:
|
||||
return dc_serial_open (iostream, context, devname);
|
||||
case DC_TRANSPORT_USB:
|
||||
return DC_STATUS_SUCCESS;
|
||||
return dctool_usb_open(iostream, context, descriptor);
|
||||
case DC_TRANSPORT_USBHID:
|
||||
return dctool_usbhid_open(iostream, context, descriptor);
|
||||
case DC_TRANSPORT_IRDA:
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
#include <libdivecomputer/serial.h>
|
||||
#include <libdivecomputer/irda.h>
|
||||
#include <libdivecomputer/bluetooth.h>
|
||||
#include <libdivecomputer/usb.h>
|
||||
#include <libdivecomputer/usbhid.h>
|
||||
|
||||
#include "dctool.h"
|
||||
@ -59,6 +60,9 @@ scan (dc_context_t *context, dc_descriptor_t *descriptor, dc_transport_t transpo
|
||||
case DC_TRANSPORT_BLUETOOTH:
|
||||
status = dc_bluetooth_iterator_new (&iterator, context, descriptor);
|
||||
break;
|
||||
case DC_TRANSPORT_USB:
|
||||
status = dc_usb_iterator_new (&iterator, context, descriptor);
|
||||
break;
|
||||
case DC_TRANSPORT_USBHID:
|
||||
status = dc_usbhid_iterator_new (&iterator, context, descriptor);
|
||||
break;
|
||||
@ -90,6 +94,10 @@ scan (dc_context_t *context, dc_descriptor_t *descriptor, dc_transport_t transpo
|
||||
dc_bluetooth_device_get_name (device));
|
||||
dc_bluetooth_device_free (device);
|
||||
break;
|
||||
case DC_TRANSPORT_USB:
|
||||
printf ("%04x:%04x\n", dc_usb_device_get_vid (device), dc_usb_device_get_pid (device));
|
||||
dc_usb_device_free (device);
|
||||
break;
|
||||
case DC_TRANSPORT_USBHID:
|
||||
printf ("%04x:%04x\n", dc_usbhid_device_get_vid (device), dc_usbhid_device_get_pid (device));
|
||||
dc_usbhid_device_free (device);
|
||||
|
||||
@ -12,6 +12,7 @@ libdivecomputer_HEADERS = \
|
||||
bluetooth.h \
|
||||
ble.h \
|
||||
irda.h \
|
||||
usb.h \
|
||||
usbhid.h \
|
||||
custom.h \
|
||||
device.h \
|
||||
|
||||
145
include/libdivecomputer/usb.h
Normal file
145
include/libdivecomputer/usb.h
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2020 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_USB_H
|
||||
#define DC_USB_H
|
||||
|
||||
#include "common.h"
|
||||
#include "context.h"
|
||||
#include "iostream.h"
|
||||
#include "iterator.h"
|
||||
#include "descriptor.h"
|
||||
#include "ioctl.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**
|
||||
* Perform a USB control transfer.
|
||||
*
|
||||
* The parameters for the control transfer are specified in the
|
||||
* #dc_usb_control_t data structure. If the control transfer requires
|
||||
* additional data as in- or output, the buffer must be located
|
||||
* immediately after the #dc_usb_control_t data structure, and the
|
||||
* length of the buffer must be indicated in the #wLength field. The
|
||||
* size of the ioctl request is the total size, including the size of
|
||||
* the #dc_usb_control_t structure.
|
||||
*/
|
||||
#define DC_IOCTL_USB_CONTROL_READ DC_IOCTL_IOR('u', 0, DC_IOCTL_SIZE_VARIABLE)
|
||||
#define DC_IOCTL_USB_CONTROL_WRITE DC_IOCTL_IOW('u', 0, DC_IOCTL_SIZE_VARIABLE)
|
||||
|
||||
/**
|
||||
* USB control transfer.
|
||||
*/
|
||||
typedef struct dc_usb_control_t {
|
||||
unsigned char bmRequestType;
|
||||
unsigned char bRequest;
|
||||
unsigned short wValue;
|
||||
unsigned short wIndex;
|
||||
unsigned short wLength;
|
||||
} dc_usb_control_t;
|
||||
|
||||
/**
|
||||
* Endpoint direction bits of the USB control transfer.
|
||||
*/
|
||||
typedef enum dc_usb_endpoint_t {
|
||||
DC_USB_ENDPOINT_OUT = 0x00,
|
||||
DC_USB_ENDPOINT_IN = 0x80
|
||||
} dc_usb_endpoint_t;
|
||||
|
||||
/**
|
||||
* Request type bits of the USB control transfer.
|
||||
*/
|
||||
typedef enum dc_usb_request_t {
|
||||
DC_USB_REQUEST_STANDARD = 0x00,
|
||||
DC_USB_REQUEST_CLASS = 0x20,
|
||||
DC_USB_REQUEST_VENDOR = 0x40,
|
||||
DC_USB_REQUEST_RESERVED = 0x60
|
||||
} dc_usb_request_t;
|
||||
|
||||
/**
|
||||
* Recipient bits of the USB control transfer.
|
||||
*/
|
||||
typedef enum dc_usb_recipient_t {
|
||||
DC_USB_RECIPIENT_DEVICE = 0x00,
|
||||
DC_USB_RECIPIENT_INTERFACE = 0x01,
|
||||
DC_USB_RECIPIENT_ENDPOINT = 0x02,
|
||||
DC_USB_RECIPIENT_OTHER = 0x03,
|
||||
} dc_usb_recipient_t;
|
||||
|
||||
/**
|
||||
* Opaque object representing a USB device.
|
||||
*/
|
||||
typedef struct dc_usb_device_t dc_usb_device_t;
|
||||
|
||||
/**
|
||||
* Get the vendor id (VID) of the USB device.
|
||||
*
|
||||
* @param[in] device A valid USB device.
|
||||
*/
|
||||
unsigned int
|
||||
dc_usb_device_get_vid (dc_usb_device_t *device);
|
||||
|
||||
/**
|
||||
* Get the product id (PID) of the USB device.
|
||||
*
|
||||
* @param[in] device A valid USB device.
|
||||
*/
|
||||
unsigned int
|
||||
dc_usb_device_get_pid (dc_usb_device_t *device);
|
||||
|
||||
/**
|
||||
* Destroy the USB device and free all resources.
|
||||
*
|
||||
* @param[in] device A valid USB device.
|
||||
*/
|
||||
void
|
||||
dc_usb_device_free(dc_usb_device_t *device);
|
||||
|
||||
/**
|
||||
* Create an iterator to enumerate the USB 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_usb_iterator_new (dc_iterator_t **iterator, dc_context_t *context, dc_descriptor_t *descriptor);
|
||||
|
||||
/**
|
||||
* Open a USB connection.
|
||||
*
|
||||
* @param[out] iostream A location to store the USB connection.
|
||||
* @param[in] context A valid context object.
|
||||
* @param[in] device A valid USB device.
|
||||
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
|
||||
* on failure.
|
||||
*/
|
||||
dc_status_t
|
||||
dc_usb_open (dc_iostream_t **iostream, dc_context_t *context, dc_usb_device_t *device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* DC_USB_H */
|
||||
@ -518,6 +518,10 @@
|
||||
RelativePath="..\src\timer.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\usb.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\usbhid.c"
|
||||
>
|
||||
@ -872,6 +876,10 @@
|
||||
RelativePath="..\include\libdivecomputer\units.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\include\libdivecomputer\usb.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\include\libdivecomputer\usbhid.h"
|
||||
>
|
||||
|
||||
@ -76,6 +76,7 @@ libdivecomputer_la_SOURCES = \
|
||||
liquivision_lynx.h liquivision_lynx.c liquivision_lynx_parser.c \
|
||||
socket.h socket.c \
|
||||
irda.c \
|
||||
usb.c \
|
||||
usbhid.c \
|
||||
bluetooth.c \
|
||||
custom.c
|
||||
|
||||
@ -19,19 +19,10 @@
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h> // memcmp, memcpy
|
||||
#include <stdlib.h> // malloc, free
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
#ifdef _WIN32
|
||||
#define NOGDI
|
||||
#endif
|
||||
#include <libusb.h>
|
||||
#endif
|
||||
#include <libdivecomputer/usb.h>
|
||||
|
||||
#include "atomics_cobalt.h"
|
||||
#include "context-private.h"
|
||||
@ -41,8 +32,6 @@
|
||||
|
||||
#define ISINSTANCE(device) dc_device_isinstance((device), &atomics_cobalt_device_vtable)
|
||||
|
||||
#define EXITCODE(rc) (rc == LIBUSB_ERROR_TIMEOUT ? DC_STATUS_TIMEOUT : DC_STATUS_IO)
|
||||
|
||||
#define COBALT1 0
|
||||
#define COBALT2 2
|
||||
|
||||
@ -58,10 +47,7 @@
|
||||
|
||||
typedef struct atomics_cobalt_device_t {
|
||||
dc_device_t base;
|
||||
#ifdef HAVE_LIBUSB
|
||||
libusb_context *context;
|
||||
libusb_device_handle *handle;
|
||||
#endif
|
||||
dc_iostream_t *iostream;
|
||||
unsigned int simulation;
|
||||
unsigned char fingerprint[6];
|
||||
unsigned char version[SZ_VERSION];
|
||||
@ -69,7 +55,6 @@ typedef struct atomics_cobalt_device_t {
|
||||
|
||||
static dc_status_t atomics_cobalt_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
|
||||
static dc_status_t atomics_cobalt_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
|
||||
static dc_status_t atomics_cobalt_device_close (dc_device_t *abstract);
|
||||
|
||||
static const dc_device_vtable_t atomics_cobalt_device_vtable = {
|
||||
sizeof(atomics_cobalt_device_t),
|
||||
@ -80,22 +65,19 @@ static const dc_device_vtable_t atomics_cobalt_device_vtable = {
|
||||
NULL, /* dump */
|
||||
atomics_cobalt_device_foreach, /* foreach */
|
||||
NULL, /* timesync */
|
||||
atomics_cobalt_device_close /* close */
|
||||
NULL /* close */
|
||||
};
|
||||
|
||||
|
||||
dc_status_t
|
||||
atomics_cobalt_device_open (dc_device_t **out, dc_context_t *context)
|
||||
atomics_cobalt_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
|
||||
{
|
||||
#ifdef HAVE_LIBUSB
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
atomics_cobalt_device_t *device = NULL;
|
||||
#endif
|
||||
|
||||
if (out == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
// Allocate memory.
|
||||
device = (atomics_cobalt_device_t *) dc_device_allocate (context, &atomics_cobalt_device_vtable);
|
||||
if (device == NULL) {
|
||||
@ -104,67 +86,30 @@ atomics_cobalt_device_open (dc_device_t **out, dc_context_t *context)
|
||||
}
|
||||
|
||||
// Set the default values.
|
||||
device->context = NULL;
|
||||
device->handle = NULL;
|
||||
device->iostream = iostream;
|
||||
device->simulation = 0;
|
||||
memset (device->fingerprint, 0, sizeof (device->fingerprint));
|
||||
|
||||
int rc = libusb_init (&device->context);
|
||||
if (rc < 0) {
|
||||
ERROR (context, "Failed to initialize usb support.");
|
||||
status = DC_STATUS_IO;
|
||||
// Set the timeout for receiving data (2000 ms).
|
||||
status = dc_iostream_set_timeout (device->iostream, TIMEOUT);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (context, "Failed to set the timeout.");
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
device->handle = libusb_open_device_with_vid_pid (device->context, VID, PID);
|
||||
if (device->handle == NULL) {
|
||||
ERROR (context, "Failed to open the usb device.");
|
||||
status = DC_STATUS_IO;
|
||||
goto error_usb_exit;
|
||||
}
|
||||
|
||||
rc = libusb_claim_interface (device->handle, 0);
|
||||
if (rc < 0) {
|
||||
ERROR (context, "Failed to claim the usb interface.");
|
||||
status = DC_STATUS_IO;
|
||||
goto error_usb_close;
|
||||
}
|
||||
|
||||
status = atomics_cobalt_device_version ((dc_device_t *) device, device->version, sizeof (device->version));
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (context, "Failed to identify the dive computer.");
|
||||
goto error_usb_close;
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
*out = (dc_device_t*) device;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
error_usb_close:
|
||||
libusb_close (device->handle);
|
||||
error_usb_exit:
|
||||
libusb_exit (device->context);
|
||||
error_free:
|
||||
dc_device_deallocate ((dc_device_t *) device);
|
||||
return status;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
atomics_cobalt_device_close (dc_device_t *abstract)
|
||||
{
|
||||
atomics_cobalt_device_t *device = (atomics_cobalt_device_t *) abstract;
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
libusb_release_interface(device->handle, 0);
|
||||
libusb_close (device->handle);
|
||||
libusb_exit (device->context);
|
||||
#endif
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@ -202,6 +147,7 @@ atomics_cobalt_device_set_simulation (dc_device_t *abstract, unsigned int simula
|
||||
dc_status_t
|
||||
atomics_cobalt_device_version (dc_device_t *abstract, unsigned char data[], unsigned int size)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
atomics_cobalt_device_t *device = (atomics_cobalt_device_t *) abstract;
|
||||
|
||||
if (!ISINSTANCE (abstract))
|
||||
@ -210,31 +156,31 @@ atomics_cobalt_device_version (dc_device_t *abstract, unsigned char data[], unsi
|
||||
if (size < SZ_VERSION)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
// Send the command to the dive computer.
|
||||
uint8_t bRequest = 0x01;
|
||||
int rc = libusb_control_transfer (device->handle,
|
||||
LIBUSB_RECIPIENT_DEVICE | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT,
|
||||
bRequest, 0, 0, NULL, 0, TIMEOUT);
|
||||
if (rc != LIBUSB_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to send the command.");
|
||||
return EXITCODE(rc);
|
||||
}
|
||||
unsigned char bRequest = 0x01;
|
||||
dc_usb_control_t control = {
|
||||
DC_USB_REQUEST_VENDOR | DC_USB_RECIPIENT_DEVICE | DC_USB_ENDPOINT_OUT, /* bmRequestType */
|
||||
bRequest, /* bRequest */
|
||||
0, /* wValue */
|
||||
0, /* wIndex */
|
||||
0, /* wLength */
|
||||
};
|
||||
|
||||
HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Write", &bRequest, 1);
|
||||
status = dc_iostream_ioctl (device->iostream, DC_IOCTL_USB_CONTROL_WRITE, &control, sizeof(control));
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to send the command.");
|
||||
return status;
|
||||
}
|
||||
|
||||
// Receive the answer from the dive computer.
|
||||
int length = 0;
|
||||
size_t length = 0;
|
||||
unsigned char packet[SZ_VERSION + 2] = {0};
|
||||
rc = libusb_bulk_transfer (device->handle, 0x82,
|
||||
packet, sizeof (packet), &length, TIMEOUT);
|
||||
if (rc != LIBUSB_SUCCESS || length != sizeof (packet)) {
|
||||
status = dc_iostream_read (device->iostream, packet, sizeof(packet), &length);
|
||||
if (status != DC_STATUS_SUCCESS || length != sizeof (packet)) {
|
||||
ERROR (abstract->context, "Failed to receive the answer.");
|
||||
return EXITCODE(rc);
|
||||
return status;
|
||||
}
|
||||
|
||||
HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Read", packet, length);
|
||||
|
||||
// Verify the checksum of the packet.
|
||||
unsigned short crc = array_uint16_le (packet + SZ_VERSION);
|
||||
unsigned short ccrc = checksum_add_uint16 (packet, SZ_VERSION, 0x0);
|
||||
@ -246,16 +192,13 @@ atomics_cobalt_device_version (dc_device_t *abstract, unsigned char data[], unsi
|
||||
memcpy (data, packet, SZ_VERSION);
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
atomics_cobalt_read_dive (dc_device_t *abstract, dc_buffer_t *buffer, int init, dc_event_progress_t *progress)
|
||||
{
|
||||
#ifdef HAVE_LIBUSB
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
atomics_cobalt_device_t *device = (atomics_cobalt_device_t *) abstract;
|
||||
|
||||
if (device_is_cancelled (abstract))
|
||||
@ -268,35 +211,37 @@ atomics_cobalt_read_dive (dc_device_t *abstract, dc_buffer_t *buffer, int init,
|
||||
}
|
||||
|
||||
// Send the command to the dive computer.
|
||||
uint8_t bRequest = 0;
|
||||
unsigned char bRequest = 0;
|
||||
if (device->simulation)
|
||||
bRequest = init ? 0x02 : 0x03;
|
||||
else
|
||||
bRequest = init ? 0x09 : 0x0A;
|
||||
int rc = libusb_control_transfer (device->handle,
|
||||
LIBUSB_RECIPIENT_DEVICE | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT,
|
||||
bRequest, 0, 0, NULL, 0, TIMEOUT);
|
||||
if (rc != LIBUSB_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to send the command.");
|
||||
return EXITCODE(rc);
|
||||
}
|
||||
|
||||
HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Write", &bRequest, 1);
|
||||
dc_usb_control_t control = {
|
||||
DC_USB_REQUEST_VENDOR | DC_USB_RECIPIENT_DEVICE | DC_USB_ENDPOINT_OUT, /* bmRequestType */
|
||||
bRequest, /* bRequest */
|
||||
0, /* wValue */
|
||||
0, /* wIndex */
|
||||
0, /* wLength */
|
||||
};
|
||||
|
||||
status = dc_iostream_ioctl (device->iostream, DC_IOCTL_USB_CONTROL_WRITE, &control, sizeof(control));
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to send the command.");
|
||||
return status;
|
||||
}
|
||||
|
||||
unsigned int nbytes = 0;
|
||||
while (1) {
|
||||
// Receive the answer from the dive computer.
|
||||
int length = 0;
|
||||
size_t length = 0;
|
||||
unsigned char packet[8 * 1024] = {0};
|
||||
rc = libusb_bulk_transfer (device->handle, 0x82,
|
||||
packet, sizeof (packet), &length, TIMEOUT);
|
||||
if (rc != LIBUSB_SUCCESS && rc != LIBUSB_ERROR_TIMEOUT) {
|
||||
status = dc_iostream_read (device->iostream, packet, sizeof(packet), &length);
|
||||
if (status != DC_STATUS_SUCCESS && status != DC_STATUS_TIMEOUT) {
|
||||
ERROR (abstract->context, "Failed to receive the answer.");
|
||||
return EXITCODE(rc);
|
||||
return status;
|
||||
}
|
||||
|
||||
HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Read", packet, length);
|
||||
|
||||
// Update and emit a progress event.
|
||||
if (progress) {
|
||||
progress->current += length;
|
||||
@ -343,9 +288,6 @@ atomics_cobalt_read_dive (dc_device_t *abstract, dc_buffer_t *buffer, int init,
|
||||
dc_buffer_slice (buffer, 0, nbytes - 2);
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
#define ATOMICS_COBALT_H
|
||||
|
||||
#include <libdivecomputer/context.h>
|
||||
#include <libdivecomputer/iostream.h>
|
||||
#include <libdivecomputer/device.h>
|
||||
#include <libdivecomputer/parser.h>
|
||||
#include <libdivecomputer/atomics_cobalt.h>
|
||||
@ -32,7 +33,7 @@ extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
dc_status_t
|
||||
atomics_cobalt_device_open (dc_device_t **device, dc_context_t *context);
|
||||
atomics_cobalt_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
|
||||
|
||||
dc_status_t
|
||||
atomics_cobalt_parser_create (dc_parser_t **parser, dc_context_t *context);
|
||||
|
||||
@ -79,7 +79,7 @@ static dc_status_t dc_bluetooth_iterator_free (dc_iterator_t *iterator);
|
||||
|
||||
typedef struct dc_bluetooth_iterator_t {
|
||||
dc_iterator_t base;
|
||||
dc_filter_t filter;
|
||||
dc_descriptor_t *descriptor;
|
||||
#ifdef _WIN32
|
||||
HANDLE hLookup;
|
||||
#else
|
||||
@ -376,7 +376,7 @@ dc_bluetooth_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descri
|
||||
iterator->count = ndevices;
|
||||
iterator->current = 0;
|
||||
#endif
|
||||
iterator->filter = dc_descriptor_get_filter (descriptor);
|
||||
iterator->descriptor = descriptor;
|
||||
|
||||
*out = (dc_iterator_t *) iterator;
|
||||
|
||||
@ -456,7 +456,7 @@ dc_bluetooth_iterator_next (dc_iterator_t *abstract, void *out)
|
||||
INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s",
|
||||
address, name ? name : "");
|
||||
|
||||
if (iterator->filter && !iterator->filter (DC_TRANSPORT_BLUETOOTH, name)) {
|
||||
if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_BLUETOOTH, name, NULL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@ -33,10 +33,14 @@ typedef struct dc_usb_desc_t {
|
||||
unsigned short pid;
|
||||
} dc_usb_desc_t;
|
||||
|
||||
typedef int (*dc_filter_t) (dc_transport_t transport, const void *userdata);
|
||||
typedef struct dc_usb_params_t {
|
||||
unsigned int interface;
|
||||
unsigned char endpoint_in;
|
||||
unsigned char endpoint_out;
|
||||
} dc_usb_params_t;
|
||||
|
||||
dc_filter_t
|
||||
dc_descriptor_get_filter (dc_descriptor_t *descriptor);
|
||||
int
|
||||
dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata, void *params);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -36,19 +36,32 @@
|
||||
values, \
|
||||
C_ARRAY_SIZE(values) - isnullterminated, \
|
||||
C_ARRAY_ITEMSIZE(values), \
|
||||
match)
|
||||
match, \
|
||||
NULL, NULL, 0)
|
||||
|
||||
#define DC_FILTER_INTERNAL_WITH_PARAMS(key, values, isnullterminated, match, params_dst, params_src) \
|
||||
dc_filter_internal( \
|
||||
key, \
|
||||
values, \
|
||||
C_ARRAY_SIZE(values) - isnullterminated, \
|
||||
C_ARRAY_ITEMSIZE(values), \
|
||||
match, \
|
||||
params_dst, params_src, sizeof *(params_src))
|
||||
|
||||
typedef int (*dc_match_t)(const void *, const void *);
|
||||
|
||||
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 int dc_filter_tecdiving (dc_transport_t transport, const void *userdata);
|
||||
static int dc_filter_mares (dc_transport_t transport, const void *userdata);
|
||||
static int dc_filter_divesystem (dc_transport_t transport, const void *userdata);
|
||||
static int dc_filter_oceanic (dc_transport_t transport, const void *userdata);
|
||||
static int dc_filter_mclean (dc_transport_t transport, const void *userdata);
|
||||
typedef int (*dc_filter_t) (dc_transport_t transport, const void *userdata, void *params);
|
||||
|
||||
static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_suunto (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_hw (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_mares (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_divesystem (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_mclean (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_atomic (dc_transport_t transport, const void *userdata, void *params);
|
||||
|
||||
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
|
||||
|
||||
@ -314,8 +327,8 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"Dive Rite", "NiTek Trio", DC_FAMILY_ZEAGLE_N2ITION3, 0, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Scubapro", "XTender 5", DC_FAMILY_ZEAGLE_N2ITION3, 0, DC_TRANSPORT_SERIAL, NULL},
|
||||
/* Atomic Aquatics Cobalt */
|
||||
{"Atomic Aquatics", "Cobalt", DC_FAMILY_ATOMICS_COBALT, 0, DC_TRANSPORT_USB, NULL},
|
||||
{"Atomic Aquatics", "Cobalt 2", DC_FAMILY_ATOMICS_COBALT, 2, DC_TRANSPORT_USB, NULL},
|
||||
{"Atomic Aquatics", "Cobalt", DC_FAMILY_ATOMICS_COBALT, 0, DC_TRANSPORT_USB, dc_filter_atomic},
|
||||
{"Atomic Aquatics", "Cobalt 2", DC_FAMILY_ATOMICS_COBALT, 2, DC_TRANSPORT_USB, dc_filter_atomic},
|
||||
/* Shearwater Predator */
|
||||
{"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_shearwater},
|
||||
/* Shearwater Petrel */
|
||||
@ -461,13 +474,16 @@ dc_match_oceanic (const void *key, const void *value)
|
||||
}
|
||||
|
||||
static int
|
||||
dc_filter_internal (const void *key, const void *values, size_t count, size_t size, dc_match_t match)
|
||||
dc_filter_internal (const void *key, const void *values, size_t count, size_t size, dc_match_t match, void *params_dst, const void *params_src, size_t params_size)
|
||||
{
|
||||
if (key == NULL)
|
||||
return 0;
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (match (key, (const unsigned char *) values + i * size)) {
|
||||
if (params_src && params_dst) {
|
||||
memcpy (params_dst, params_src, params_size);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -482,7 +498,7 @@ static const char * const rfcomm[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static int dc_filter_uwatec (dc_transport_t transport, const void *userdata)
|
||||
static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const char * const irda[] = {
|
||||
"Aladin Smart Com",
|
||||
@ -517,7 +533,7 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dc_filter_suunto (dc_transport_t transport, const void *userdata)
|
||||
static int dc_filter_suunto (dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const dc_usb_desc_t usbhid[] = {
|
||||
{0x1493, 0x0030}, // Eon Steel
|
||||
@ -539,7 +555,7 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dc_filter_hw (dc_transport_t transport, const void *userdata)
|
||||
static int dc_filter_hw (dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const char * const bluetooth[] = {
|
||||
"OSTC",
|
||||
@ -555,7 +571,7 @@ static int dc_filter_hw (dc_transport_t transport, const void *userdata)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata)
|
||||
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const char * const bluetooth[] = {
|
||||
"Predator",
|
||||
@ -575,7 +591,7 @@ static int dc_filter_shearwater (dc_transport_t transport, const void *userdata)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata)
|
||||
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const char * const bluetooth[] = {
|
||||
"DiveComputer",
|
||||
@ -590,7 +606,7 @@ static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dc_filter_mares (dc_transport_t transport, const void *userdata)
|
||||
static int dc_filter_mares (dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const char * const bluetooth[] = {
|
||||
"Mares bluelink pro",
|
||||
@ -604,7 +620,7 @@ static int dc_filter_mares (dc_transport_t transport, const void *userdata)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dc_filter_divesystem (dc_transport_t transport, const void *userdata)
|
||||
static int dc_filter_divesystem (dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const char * const bluetooth[] = {
|
||||
"DS",
|
||||
@ -617,7 +633,7 @@ static int dc_filter_divesystem (dc_transport_t transport, const void *userdata)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dc_filter_oceanic (dc_transport_t transport, const void *userdata)
|
||||
static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const unsigned int model[] = {
|
||||
0x4552, // Oceanic Pro Plus X
|
||||
@ -639,7 +655,7 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dc_filter_mclean(dc_transport_t transport, const void *userdata)
|
||||
static int dc_filter_mclean(dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const char * const bluetooth[] = {
|
||||
"McLean Extreme",
|
||||
@ -654,6 +670,23 @@ static int dc_filter_mclean(dc_transport_t transport, const void *userdata)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dc_filter_atomic (dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const dc_usb_desc_t usb[] = {
|
||||
{0x0471, 0x0888}, // Atomic Aquatics Cobalt
|
||||
};
|
||||
|
||||
static const dc_usb_params_t usb_params = {
|
||||
0, 0x82, 0x02
|
||||
};
|
||||
|
||||
if (transport == DC_TRANSPORT_USB) {
|
||||
return DC_FILTER_INTERNAL_WITH_PARAMS (userdata, usb, 0, dc_match_usb, params, &usb_params);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_descriptor_iterator (dc_iterator_t **out)
|
||||
{
|
||||
@ -745,11 +778,11 @@ dc_descriptor_get_transports (dc_descriptor_t *descriptor)
|
||||
return descriptor->transports;
|
||||
}
|
||||
|
||||
dc_filter_t
|
||||
dc_descriptor_get_filter (dc_descriptor_t *descriptor)
|
||||
int
|
||||
dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
if (descriptor == NULL)
|
||||
return NULL;
|
||||
if (descriptor == NULL || descriptor->filter == NULL)
|
||||
return 1;
|
||||
|
||||
return descriptor->filter;
|
||||
return descriptor->filter (transport, userdata, params);
|
||||
}
|
||||
|
||||
@ -190,7 +190,7 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
|
||||
rc = zeagle_n2ition3_device_open (&device, context, iostream);
|
||||
break;
|
||||
case DC_FAMILY_ATOMICS_COBALT:
|
||||
rc = atomics_cobalt_device_open (&device, context);
|
||||
rc = atomics_cobalt_device_open (&device, context, iostream);
|
||||
break;
|
||||
case DC_FAMILY_SHEARWATER_PREDATOR:
|
||||
rc = shearwater_predator_device_open (&device, context, iostream);
|
||||
|
||||
@ -212,8 +212,6 @@ dc_irda_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_
|
||||
S_CLOSE (fd);
|
||||
dc_socket_exit (context);
|
||||
|
||||
dc_filter_t filter = dc_descriptor_get_filter (descriptor);
|
||||
|
||||
unsigned int count = 0;
|
||||
#ifdef _WIN32
|
||||
for (size_t i = 0; i < list->numDevice; ++i) {
|
||||
@ -233,7 +231,7 @@ dc_irda_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_
|
||||
INFO (context, "Discover: address=%08x, name=%s, charset=%02x, hints=%04x",
|
||||
address, name, charset, hints);
|
||||
|
||||
if (filter && !filter (DC_TRANSPORT_IRDA, name)) {
|
||||
if (!dc_descriptor_filter (descriptor, DC_TRANSPORT_IRDA, name, NULL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@ -70,6 +70,12 @@ dc_irda_device_free
|
||||
dc_irda_iterator_new
|
||||
dc_irda_open
|
||||
|
||||
dc_usb_device_get_vid
|
||||
dc_usb_device_get_pid
|
||||
dc_usb_device_free
|
||||
dc_usb_iterator_new
|
||||
dc_usb_open
|
||||
|
||||
dc_usbhid_device_get_vid
|
||||
dc_usbhid_device_get_pid
|
||||
dc_usbhid_device_free
|
||||
|
||||
@ -88,7 +88,7 @@ struct dc_serial_device_t {
|
||||
|
||||
typedef struct dc_serial_iterator_t {
|
||||
dc_iterator_t base;
|
||||
dc_filter_t filter;
|
||||
dc_descriptor_t *descriptor;
|
||||
DIR *dp;
|
||||
} dc_serial_iterator_t;
|
||||
|
||||
@ -189,7 +189,7 @@ dc_serial_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descripto
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
iterator->filter = dc_descriptor_get_filter (descriptor);
|
||||
iterator->descriptor = descriptor;
|
||||
|
||||
*out = (dc_iterator_t *) iterator;
|
||||
|
||||
@ -230,7 +230,7 @@ dc_serial_iterator_next (dc_iterator_t *abstract, void *out)
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
if (iterator->filter && !iterator->filter (DC_TRANSPORT_SERIAL, filename)) {
|
||||
if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_SERIAL, filename, NULL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@ -57,7 +57,7 @@ struct dc_serial_device_t {
|
||||
|
||||
typedef struct dc_serial_iterator_t {
|
||||
dc_iterator_t base;
|
||||
dc_filter_t filter;
|
||||
dc_descriptor_t *descriptor;
|
||||
HKEY hKey;
|
||||
DWORD count;
|
||||
DWORD current;
|
||||
@ -180,7 +180,7 @@ dc_serial_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descripto
|
||||
}
|
||||
}
|
||||
|
||||
iterator->filter = dc_descriptor_get_filter (descriptor);
|
||||
iterator->descriptor = descriptor;
|
||||
iterator->hKey = hKey;
|
||||
iterator->count = count;
|
||||
iterator->current = 0;
|
||||
@ -226,7 +226,7 @@ dc_serial_iterator_next (dc_iterator_t *abstract, void *out)
|
||||
// Null terminate the string.
|
||||
data[data_len] = 0;
|
||||
|
||||
if (iterator->filter && !iterator->filter (DC_TRANSPORT_SERIAL, data)) {
|
||||
if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_SERIAL, data, NULL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
585
src/usb.c
Normal file
585
src/usb.c
Normal file
@ -0,0 +1,585 @@
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2020 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
#ifdef _WIN32
|
||||
#define NOGDI
|
||||
#endif
|
||||
#include <libusb.h>
|
||||
#endif
|
||||
|
||||
#include <libdivecomputer/usb.h>
|
||||
|
||||
#include "common-private.h"
|
||||
#include "context-private.h"
|
||||
#include "iostream-private.h"
|
||||
#include "descriptor-private.h"
|
||||
#include "iterator-private.h"
|
||||
#include "platform.h"
|
||||
|
||||
#define ISINSTANCE(device) dc_iostream_isinstance((device), &dc_usb_vtable)
|
||||
|
||||
typedef struct dc_usb_session_t {
|
||||
size_t refcount;
|
||||
#ifdef HAVE_LIBUSB
|
||||
libusb_context *handle;
|
||||
#endif
|
||||
} dc_usb_session_t;
|
||||
|
||||
struct dc_usb_device_t {
|
||||
unsigned short vid, pid;
|
||||
dc_usb_session_t *session;
|
||||
#ifdef HAVE_LIBUSB
|
||||
struct libusb_device *handle;
|
||||
int interface;
|
||||
unsigned char endpoint_in;
|
||||
unsigned char endpoint_out;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
static dc_status_t dc_usb_iterator_next (dc_iterator_t *iterator, void *item);
|
||||
static dc_status_t dc_usb_iterator_free (dc_iterator_t *iterator);
|
||||
|
||||
static dc_status_t dc_usb_set_timeout (dc_iostream_t *iostream, int timeout);
|
||||
static dc_status_t dc_usb_poll (dc_iostream_t *iostream, int timeout);
|
||||
static dc_status_t dc_usb_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual);
|
||||
static dc_status_t dc_usb_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual);
|
||||
static dc_status_t dc_usb_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size);
|
||||
static dc_status_t dc_usb_close (dc_iostream_t *iostream);
|
||||
|
||||
typedef struct dc_usb_iterator_t {
|
||||
dc_iterator_t base;
|
||||
dc_descriptor_t *descriptor;
|
||||
dc_usb_session_t *session;
|
||||
struct libusb_device **devices;
|
||||
size_t count;
|
||||
size_t current;
|
||||
} dc_usb_iterator_t;
|
||||
|
||||
typedef struct dc_usb_t {
|
||||
/* Base class. */
|
||||
dc_iostream_t base;
|
||||
/* Internal state. */
|
||||
dc_usb_session_t *session;
|
||||
libusb_device_handle *handle;
|
||||
int interface;
|
||||
unsigned char endpoint_in;
|
||||
unsigned char endpoint_out;
|
||||
unsigned int timeout;
|
||||
} dc_usb_t;
|
||||
|
||||
static const dc_iterator_vtable_t dc_usb_iterator_vtable = {
|
||||
sizeof(dc_usb_iterator_t),
|
||||
dc_usb_iterator_next,
|
||||
dc_usb_iterator_free,
|
||||
};
|
||||
|
||||
static const dc_iostream_vtable_t dc_usb_vtable = {
|
||||
sizeof(dc_usb_t),
|
||||
dc_usb_set_timeout, /* set_timeout */
|
||||
NULL, /* set_break */
|
||||
NULL, /* set_dtr */
|
||||
NULL, /* set_rts */
|
||||
NULL, /* get_lines */
|
||||
NULL, /* get_available */
|
||||
NULL, /* configure */
|
||||
dc_usb_poll, /* poll */
|
||||
dc_usb_read, /* read */
|
||||
dc_usb_write, /* write */
|
||||
dc_usb_ioctl, /* ioctl */
|
||||
NULL, /* flush */
|
||||
NULL, /* purge */
|
||||
NULL, /* sleep */
|
||||
dc_usb_close, /* close */
|
||||
};
|
||||
|
||||
static dc_status_t
|
||||
syserror(int errcode)
|
||||
{
|
||||
switch (errcode) {
|
||||
case LIBUSB_ERROR_INVALID_PARAM:
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
case LIBUSB_ERROR_NO_MEM:
|
||||
return DC_STATUS_NOMEMORY;
|
||||
case LIBUSB_ERROR_NO_DEVICE:
|
||||
case LIBUSB_ERROR_NOT_FOUND:
|
||||
return DC_STATUS_NODEVICE;
|
||||
case LIBUSB_ERROR_ACCESS:
|
||||
case LIBUSB_ERROR_BUSY:
|
||||
return DC_STATUS_NOACCESS;
|
||||
case LIBUSB_ERROR_TIMEOUT:
|
||||
return DC_STATUS_TIMEOUT;
|
||||
default:
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_usb_session_new (dc_usb_session_t **out, dc_context_t *context)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_usb_session_t *session = NULL;
|
||||
|
||||
if (out == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
session = (dc_usb_session_t *) malloc (sizeof(dc_usb_session_t));
|
||||
if (session == NULL) {
|
||||
ERROR (context, "Failed to allocate memory.");
|
||||
status = DC_STATUS_NOMEMORY;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
session->refcount = 1;
|
||||
|
||||
int rc = libusb_init (&session->handle);
|
||||
if (rc != LIBUSB_SUCCESS) {
|
||||
ERROR (context, "Failed to initialize usb support (%s).",
|
||||
libusb_error_name (rc));
|
||||
status = syserror (rc);
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
*out = session;
|
||||
|
||||
return status;
|
||||
|
||||
error_free:
|
||||
free (session);
|
||||
error_unlock:
|
||||
return status;
|
||||
}
|
||||
|
||||
static dc_usb_session_t *
|
||||
dc_usb_session_ref (dc_usb_session_t *session)
|
||||
{
|
||||
if (session == NULL)
|
||||
return NULL;
|
||||
|
||||
session->refcount++;
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_usb_session_unref (dc_usb_session_t *session)
|
||||
{
|
||||
if (session == NULL)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
if (--session->refcount == 0) {
|
||||
libusb_exit (session->handle);
|
||||
free (session);
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int
|
||||
dc_usb_device_get_vid (dc_usb_device_t *device)
|
||||
{
|
||||
if (device == NULL)
|
||||
return 0;
|
||||
|
||||
return device->vid;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
dc_usb_device_get_pid (dc_usb_device_t *device)
|
||||
{
|
||||
if (device == NULL)
|
||||
return 0;
|
||||
|
||||
return device->pid;
|
||||
}
|
||||
|
||||
void
|
||||
dc_usb_device_free(dc_usb_device_t *device)
|
||||
{
|
||||
if (device == NULL)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
libusb_unref_device (device->handle);
|
||||
dc_usb_session_unref (device->session);
|
||||
#endif
|
||||
free (device);
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_usb_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_t *descriptor)
|
||||
{
|
||||
#ifdef HAVE_LIBUSB
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_usb_iterator_t *iterator = NULL;
|
||||
|
||||
if (out == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
iterator = (dc_usb_iterator_t *) dc_iterator_allocate (context, &dc_usb_iterator_vtable);
|
||||
if (iterator == NULL) {
|
||||
ERROR (context, "Failed to allocate memory.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
// Initialize the usb library.
|
||||
status = dc_usb_session_new (&iterator->session, context);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
// Enumerate the USB devices.
|
||||
struct libusb_device **devices = NULL;
|
||||
ssize_t ndevices = libusb_get_device_list (iterator->session->handle, &devices);
|
||||
if (ndevices < 0) {
|
||||
ERROR (context, "Failed to enumerate the usb devices (%s).",
|
||||
libusb_error_name (ndevices));
|
||||
status = syserror (ndevices);
|
||||
goto error_session_unref;
|
||||
}
|
||||
|
||||
iterator->devices = devices;
|
||||
iterator->count = ndevices;
|
||||
iterator->current = 0;
|
||||
iterator->descriptor = descriptor;
|
||||
|
||||
*out = (dc_iterator_t *) iterator;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
error_session_unref:
|
||||
dc_usb_session_unref (iterator->session);
|
||||
error_free:
|
||||
dc_iterator_deallocate ((dc_iterator_t *) iterator);
|
||||
return status;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
static dc_status_t
|
||||
dc_usb_iterator_next (dc_iterator_t *abstract, void *out)
|
||||
{
|
||||
dc_usb_iterator_t *iterator = (dc_usb_iterator_t *) abstract;
|
||||
dc_usb_device_t *device = NULL;
|
||||
|
||||
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};
|
||||
dc_usb_params_t params = {0, 0, 0};
|
||||
if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_USB, &usb, ¶ms)) {
|
||||
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 matching 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 (interface == NULL && desc->bInterfaceNumber == params.interface) {
|
||||
interface = desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (interface == NULL) {
|
||||
libusb_free_config_descriptor (config);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the first matching input and output bulk 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_BULK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ep_in == NULL && direction == LIBUSB_ENDPOINT_IN &&
|
||||
(params.endpoint_in == 0 || params.endpoint_in == desc->bEndpointAddress)) {
|
||||
ep_in = desc;
|
||||
}
|
||||
|
||||
if (ep_out == NULL && direction == LIBUSB_ENDPOINT_OUT &&
|
||||
(params.endpoint_out == 0 || params.endpoint_out == desc->bEndpointAddress)) {
|
||||
ep_out = desc;
|
||||
}
|
||||
}
|
||||
|
||||
if (ep_in == NULL || ep_out == NULL) {
|
||||
libusb_free_config_descriptor (config);
|
||||
continue;
|
||||
}
|
||||
|
||||
device = (dc_usb_device_t *) malloc (sizeof(dc_usb_device_t));
|
||||
if (device == NULL) {
|
||||
ERROR (abstract->context, "Failed to allocate memory.");
|
||||
libusb_free_config_descriptor (config);
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
device->session = dc_usb_session_ref (iterator->session);
|
||||
device->vid = dev.idVendor;
|
||||
device->pid = dev.idProduct;
|
||||
device->handle = libusb_ref_device (current);
|
||||
device->interface = interface->bInterfaceNumber;
|
||||
device->endpoint_in = ep_in->bEndpointAddress;
|
||||
device->endpoint_out = ep_out->bEndpointAddress;
|
||||
|
||||
*(dc_usb_device_t **) out = device;
|
||||
|
||||
libusb_free_config_descriptor (config);
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return DC_STATUS_DONE;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_usb_iterator_free (dc_iterator_t *abstract)
|
||||
{
|
||||
dc_usb_iterator_t *iterator = (dc_usb_iterator_t *) abstract;
|
||||
|
||||
libusb_free_device_list (iterator->devices, 1);
|
||||
dc_usb_session_unref (iterator->session);
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
dc_status_t
|
||||
dc_usb_open (dc_iostream_t **out, dc_context_t *context, dc_usb_device_t *device)
|
||||
{
|
||||
#ifdef HAVE_LIBUSB
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_usb_t *usb = NULL;
|
||||
|
||||
if (out == NULL || device == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
INFO (context, "Open: vid=%04x, pid=%04x, interface=%u, endpoints=%02x,%02x",
|
||||
device->vid, device->pid, device->interface, device->endpoint_in, device->endpoint_out);
|
||||
|
||||
// Allocate memory.
|
||||
usb = (dc_usb_t *) dc_iostream_allocate (context, &dc_usb_vtable, DC_TRANSPORT_USB);
|
||||
if (usb == NULL) {
|
||||
ERROR (context, "Out of memory.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
// Initialize the usb library.
|
||||
usb->session = dc_usb_session_ref (device->session);
|
||||
if (usb->session == NULL) {
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
// Open the USB device.
|
||||
int rc = libusb_open (device->handle, &usb->handle);
|
||||
if (rc != LIBUSB_SUCCESS) {
|
||||
ERROR (context, "Failed to open the usb device (%s).",
|
||||
libusb_error_name (rc));
|
||||
status = syserror (rc);
|
||||
goto error_session_unref;
|
||||
}
|
||||
|
||||
#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)
|
||||
libusb_set_auto_detach_kernel_driver (usb->handle, 1);
|
||||
#endif
|
||||
|
||||
// Claim the interface.
|
||||
rc = libusb_claim_interface (usb->handle, device->interface);
|
||||
if (rc != LIBUSB_SUCCESS) {
|
||||
ERROR (context, "Failed to claim the usb interface (%s).",
|
||||
libusb_error_name (rc));
|
||||
status = syserror (rc);
|
||||
goto error_usb_close;
|
||||
}
|
||||
|
||||
usb->interface = device->interface;
|
||||
usb->endpoint_in = device->endpoint_in;
|
||||
usb->endpoint_out = device->endpoint_out;
|
||||
usb->timeout = 0;
|
||||
|
||||
*out = (dc_iostream_t *) usb;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
error_usb_close:
|
||||
libusb_close (usb->handle);
|
||||
error_session_unref:
|
||||
dc_usb_session_unref (usb->session);
|
||||
error_free:
|
||||
dc_iostream_deallocate ((dc_iostream_t *) usb);
|
||||
return status;
|
||||
#else
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
static dc_status_t
|
||||
dc_usb_close (dc_iostream_t *abstract)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_usb_t *usb = (dc_usb_t *) abstract;
|
||||
|
||||
libusb_release_interface (usb->handle, usb->interface);
|
||||
libusb_close (usb->handle);
|
||||
dc_usb_session_unref (usb->session);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_usb_set_timeout (dc_iostream_t *abstract, int timeout)
|
||||
{
|
||||
dc_usb_t *usb = (dc_usb_t *) abstract;
|
||||
|
||||
if (timeout < 0) {
|
||||
usb->timeout = 0;
|
||||
} else if (timeout == 0) {
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
} else {
|
||||
usb->timeout = timeout;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_usb_poll (dc_iostream_t *abstract, int timeout)
|
||||
{
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_usb_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_usb_t *usb = (dc_usb_t *) abstract;
|
||||
int nbytes = 0;
|
||||
|
||||
int rc = libusb_bulk_transfer (usb->handle, usb->endpoint_in, data, size, &nbytes, usb->timeout);
|
||||
if (rc != LIBUSB_SUCCESS) {
|
||||
ERROR (abstract->context, "Usb read bulk transfer failed (%s).",
|
||||
libusb_error_name (rc));
|
||||
status = syserror (rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (actual)
|
||||
*actual = nbytes;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_usb_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_usb_t *usb = (dc_usb_t *) abstract;
|
||||
int nbytes = 0;
|
||||
|
||||
int rc = libusb_bulk_transfer (usb->handle, usb->endpoint_out, (void *) data, size, &nbytes, 0);
|
||||
if (rc != LIBUSB_SUCCESS) {
|
||||
ERROR (abstract->context, "Usb write bulk transfer failed (%s).",
|
||||
libusb_error_name (rc));
|
||||
status = syserror (rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (actual)
|
||||
*actual = nbytes;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_usb_ioctl_control (dc_iostream_t *abstract, void *data, size_t size)
|
||||
{
|
||||
dc_usb_t *usb = (dc_usb_t *) abstract;
|
||||
const dc_usb_control_t *control = (const dc_usb_control_t *) data;
|
||||
|
||||
if (size < sizeof(control) || control->wLength > size - sizeof(control)) {
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
int rc = libusb_control_transfer (usb->handle,
|
||||
control->bmRequestType, control->bRequest, control->wValue, control->wIndex,
|
||||
(unsigned char *) data + sizeof(control), control->wLength, usb->timeout);
|
||||
if (rc != LIBUSB_SUCCESS) {
|
||||
ERROR (abstract->context, "Usb control transfer failed (%s).",
|
||||
libusb_error_name (rc));
|
||||
return syserror (rc);
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
dc_usb_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size)
|
||||
{
|
||||
switch (request) {
|
||||
case DC_IOCTL_USB_CONTROL_READ:
|
||||
case DC_IOCTL_USB_CONTROL_WRITE:
|
||||
return dc_usb_ioctl_control (abstract, data, size);
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -102,7 +102,7 @@ static dc_status_t dc_usbhid_close (dc_iostream_t *iostream);
|
||||
|
||||
typedef struct dc_usbhid_iterator_t {
|
||||
dc_iterator_t base;
|
||||
dc_filter_t filter;
|
||||
dc_descriptor_t *descriptor;
|
||||
dc_usbhid_session_t *session;
|
||||
#if defined(USE_LIBUSB)
|
||||
struct libusb_device **devices;
|
||||
@ -398,7 +398,7 @@ dc_usbhid_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descripto
|
||||
iterator->devices = devices;
|
||||
iterator->current = devices;
|
||||
#endif
|
||||
iterator->filter = dc_descriptor_get_filter (descriptor);
|
||||
iterator->descriptor = descriptor;
|
||||
|
||||
*out = (dc_iterator_t *) iterator;
|
||||
|
||||
@ -435,7 +435,7 @@ dc_usbhid_iterator_next (dc_iterator_t *abstract, void *out)
|
||||
}
|
||||
|
||||
dc_usb_desc_t usb = {dev.idVendor, dev.idProduct};
|
||||
if (iterator->filter && !iterator->filter (DC_TRANSPORT_USBHID, &usb)) {
|
||||
if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_USBHID, &usb, NULL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -518,7 +518,7 @@ dc_usbhid_iterator_next (dc_iterator_t *abstract, void *out)
|
||||
iterator->current = current->next;
|
||||
|
||||
dc_usb_desc_t usb = {current->vendor_id, current->product_id};
|
||||
if (iterator->filter && !iterator->filter (DC_TRANSPORT_USBHID, &usb)) {
|
||||
if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_USBHID, &usb)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user