Add an I/O implementation for USB communication
The USB communication is now also implemented as an I/O stream
transport. Unlike most I/O devices, USB communication supports multiple
interfaces and endpoints. This requires some some special care:
In the general case, autodetection isn't really possible without
additional knowledge. Hence the need for the filter parameters to pass
this kind of information.
The implementation assumes two bulk endpoints for the standard
read/write interface. Communication with the control endpoint is
supported through the new DC_IOCTL_USB_CONTROL_{READ,WRITE} ioctl's.
This commit is contained in:
parent
57f0ce6d79
commit
c84bbd93a3
@ -12,6 +12,7 @@ libdivecomputer_HEADERS = \
|
|||||||
bluetooth.h \
|
bluetooth.h \
|
||||||
ble.h \
|
ble.h \
|
||||||
irda.h \
|
irda.h \
|
||||||
|
usb.h \
|
||||||
usbhid.h \
|
usbhid.h \
|
||||||
custom.h \
|
custom.h \
|
||||||
device.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"
|
RelativePath="..\src\timer.c"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\src\usb.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\src\usbhid.c"
|
RelativePath="..\src\usbhid.c"
|
||||||
>
|
>
|
||||||
@ -872,6 +876,10 @@
|
|||||||
RelativePath="..\include\libdivecomputer\units.h"
|
RelativePath="..\include\libdivecomputer\units.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\include\libdivecomputer\usb.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\include\libdivecomputer\usbhid.h"
|
RelativePath="..\include\libdivecomputer\usbhid.h"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -76,6 +76,7 @@ libdivecomputer_la_SOURCES = \
|
|||||||
liquivision_lynx.h liquivision_lynx.c liquivision_lynx_parser.c \
|
liquivision_lynx.h liquivision_lynx.c liquivision_lynx_parser.c \
|
||||||
socket.h socket.c \
|
socket.h socket.c \
|
||||||
irda.c \
|
irda.c \
|
||||||
|
usb.c \
|
||||||
usbhid.c \
|
usbhid.c \
|
||||||
bluetooth.c \
|
bluetooth.c \
|
||||||
custom.c
|
custom.c
|
||||||
|
|||||||
@ -33,6 +33,12 @@ typedef struct dc_usb_desc_t {
|
|||||||
unsigned short pid;
|
unsigned short pid;
|
||||||
} dc_usb_desc_t;
|
} dc_usb_desc_t;
|
||||||
|
|
||||||
|
typedef struct dc_usb_params_t {
|
||||||
|
unsigned int interface;
|
||||||
|
unsigned char endpoint_in;
|
||||||
|
unsigned char endpoint_out;
|
||||||
|
} dc_usb_params_t;
|
||||||
|
|
||||||
int
|
int
|
||||||
dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata, void *params);
|
dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata, void *params);
|
||||||
|
|
||||||
|
|||||||
@ -70,6 +70,12 @@ dc_irda_device_free
|
|||||||
dc_irda_iterator_new
|
dc_irda_iterator_new
|
||||||
dc_irda_open
|
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_vid
|
||||||
dc_usbhid_device_get_pid
|
dc_usbhid_device_get_pid
|
||||||
dc_usbhid_device_free
|
dc_usbhid_device_free
|
||||||
|
|||||||
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
|
||||||
Loading…
x
Reference in New Issue
Block a user