1258 lines
32 KiB
C
1258 lines
32 KiB
C
/* libusb-win32, Generic Windows USB Library
|
|
* Copyright (c) 2002-2005 Stephan Meyer <ste_meyer@web.de>
|
|
* Copyright (c) 2000-2005 Johannes Erdfelt <johannes@erdfelt.com>
|
|
*
|
|
* 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <windows.h>
|
|
#include <winioctl.h>
|
|
#include <setupapi.h>
|
|
|
|
#include "usb.h"
|
|
#include "error.h"
|
|
#include "usbi.h"
|
|
#include "driver_api.h"
|
|
#include "registry.h"
|
|
#include "libusb-win32_version.h"
|
|
|
|
#define LIBUSB_WIN32_DLL_LARGE_TRANSFER_SUPPORT
|
|
|
|
#define LIBUSB_DEFAULT_TIMEOUT 5000
|
|
#define LIBUSB_DEVICE_NAME "\\\\.\\libusb0-"
|
|
#define LIBUSB_BUS_NAME "bus-0"
|
|
#define LIBUSB_MAX_DEVICES 256
|
|
|
|
typedef struct
|
|
{
|
|
usb_dev_handle *dev;
|
|
libusb_request req;
|
|
char *bytes;
|
|
int size;
|
|
DWORD control_code;
|
|
OVERLAPPED ol;
|
|
} usb_context_t;
|
|
|
|
|
|
static struct usb_version _usb_version =
|
|
{
|
|
{ VERSION_MAJOR,
|
|
VERSION_MINOR,
|
|
VERSION_MICRO,
|
|
VERSION_NANO },
|
|
{ -1, -1, -1, -1 }
|
|
};
|
|
|
|
|
|
static int _usb_setup_async(usb_dev_handle *dev, void **context,
|
|
DWORD control_code,
|
|
unsigned char ep, int pktsize);
|
|
static int _usb_transfer_sync(usb_dev_handle *dev, int control_code,
|
|
int ep, int pktsize, char *bytes, int size,
|
|
int timeout);
|
|
|
|
static int usb_get_configuration(usb_dev_handle *dev, bool_t cached);
|
|
static int _usb_cancel_io(usb_context_t *context);
|
|
static int _usb_abort_ep(usb_dev_handle *dev, unsigned int ep);
|
|
|
|
static int _usb_io_sync(HANDLE dev, unsigned int code, void *in, int in_size,
|
|
void *out, int out_size, int *ret);
|
|
static int _usb_reap_async(void *context, int timeout, int cancel);
|
|
static int _usb_add_virtual_hub(struct usb_bus *bus);
|
|
|
|
static void _usb_free_bus_list(struct usb_bus *bus);
|
|
static void _usb_free_dev_list(struct usb_device *dev);
|
|
static void _usb_deinit(void);
|
|
|
|
/* DLL main entry point */
|
|
BOOL WINAPI DllMain(HANDLE module, DWORD reason, LPVOID reserved)
|
|
{
|
|
switch (reason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
break;
|
|
case DLL_PROCESS_DETACH:
|
|
_usb_deinit();
|
|
break;
|
|
case DLL_THREAD_ATTACH:
|
|
break;
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static int usb_get_configuration(usb_dev_handle *dev, bool_t cached)
|
|
{
|
|
int ret;
|
|
char config;
|
|
libusb_request request;
|
|
|
|
if (cached)
|
|
{
|
|
memset(&request, 0, sizeof(request));
|
|
request.timeout = LIBUSB_DEFAULT_TIMEOUT;
|
|
|
|
if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_GET_CACHED_CONFIGURATION,
|
|
&request, sizeof(request), &request, sizeof(request), &ret))
|
|
{
|
|
USBERR("sending get cached configuration ioctl failed, win error: %s\n", usb_win_error_to_string());
|
|
ret = -usb_win_error_to_errno();
|
|
}
|
|
|
|
if (ret < 1)
|
|
ret = -EINVAL;
|
|
else
|
|
config = *((char*)&request);
|
|
}
|
|
else
|
|
{
|
|
ret = usb_control_msg(dev, USB_RECIP_DEVICE | USB_ENDPOINT_IN,
|
|
USB_REQ_GET_CONFIGURATION, 0, 0, &config, 1,
|
|
LIBUSB_DEFAULT_TIMEOUT);
|
|
}
|
|
|
|
if(ret < 0)
|
|
return ret;
|
|
|
|
return config;
|
|
}
|
|
|
|
int usb_os_open(usb_dev_handle *dev)
|
|
{
|
|
char dev_name[LIBUSB_PATH_MAX];
|
|
char *p;
|
|
int config;
|
|
if (!dev)
|
|
{
|
|
USBERR("invalid device handle %p", dev);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev->impl_info = INVALID_HANDLE_VALUE;
|
|
dev->config = 0;
|
|
dev->interface = -1;
|
|
dev->altsetting = -1;
|
|
|
|
if (!dev->device->filename)
|
|
{
|
|
USBERR0("invalid file name\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* build the Windows file name from the unique device name */
|
|
strcpy(dev_name, dev->device->filename);
|
|
|
|
p = strstr(dev_name, "--");
|
|
|
|
if (!p)
|
|
{
|
|
USBERR("invalid file name %s\n", dev->device->filename);
|
|
return -ENOENT;
|
|
}
|
|
|
|
*p = 0;
|
|
|
|
dev->impl_info = CreateFile(dev_name, 0, 0, NULL, OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED, NULL);
|
|
|
|
if (dev->impl_info == INVALID_HANDLE_VALUE)
|
|
{
|
|
USBERR("failed to open %s: win error: %s",
|
|
dev->device->filename, usb_win_error_to_string());
|
|
return -ENOENT;
|
|
}
|
|
|
|
// get the cached configuration (no device i/o)
|
|
config = usb_get_configuration(dev, TRUE);
|
|
if (config > 0)
|
|
{
|
|
dev->config = config;
|
|
dev->interface = -1;
|
|
dev->altsetting = -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int usb_os_close(usb_dev_handle *dev)
|
|
{
|
|
if (dev->impl_info != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (dev->interface >= 0)
|
|
{
|
|
usb_release_interface(dev, dev->interface);
|
|
}
|
|
|
|
CloseHandle(dev->impl_info);
|
|
dev->impl_info = INVALID_HANDLE_VALUE;
|
|
dev->interface = -1;
|
|
dev->altsetting = -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int usb_set_configuration(usb_dev_handle *dev, int configuration)
|
|
{
|
|
libusb_request req;
|
|
|
|
if (dev->impl_info == INVALID_HANDLE_VALUE)
|
|
{
|
|
USBERR0("error: device not open\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (dev->config == configuration)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (dev->interface >= 0)
|
|
{
|
|
USBERR0("can't change configuration, an interface is still in use (claimed)\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
req.configuration.configuration = configuration;
|
|
req.timeout = LIBUSB_DEFAULT_TIMEOUT;
|
|
|
|
if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_SET_CONFIGURATION,
|
|
&req, sizeof(libusb_request), NULL, 0, NULL))
|
|
{
|
|
USBERR("could not set config %d: "
|
|
"win error: %s", configuration, usb_win_error_to_string());
|
|
return -usb_win_error_to_errno();
|
|
}
|
|
|
|
dev->config = configuration;
|
|
dev->interface = -1;
|
|
dev->altsetting = -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int usb_claim_interface(usb_dev_handle *dev, int interface)
|
|
{
|
|
libusb_request req;
|
|
|
|
if (dev->impl_info == INVALID_HANDLE_VALUE)
|
|
{
|
|
USBERR0("device not open\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!dev->config)
|
|
{
|
|
USBERR("could not claim interface %d, invalid configuration %d\n", interface, dev->config);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (dev->interface == interface)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
req.interface.interface = interface;
|
|
|
|
if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_CLAIM_INTERFACE,
|
|
&req, sizeof(libusb_request), NULL, 0, NULL))
|
|
{
|
|
USBERR("could not claim interface %d, "
|
|
"win error: %s", interface, usb_win_error_to_string());
|
|
return -usb_win_error_to_errno();
|
|
}
|
|
else
|
|
{
|
|
dev->interface = interface;
|
|
dev->altsetting = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int usb_release_interface(usb_dev_handle *dev, int interface)
|
|
{
|
|
libusb_request req;
|
|
|
|
if (dev->impl_info == INVALID_HANDLE_VALUE)
|
|
{
|
|
USBERR0("device not open\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!dev->config)
|
|
{
|
|
USBERR("could not release interface %d, invalid configuration %d\n", interface, dev->config);
|
|
return -EINVAL;
|
|
}
|
|
|
|
req.interface.interface = interface;
|
|
|
|
if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RELEASE_INTERFACE,
|
|
&req, sizeof(libusb_request), NULL, 0, NULL))
|
|
{
|
|
USBERR("could not release interface %d, "
|
|
"win error: %s", interface, usb_win_error_to_string());
|
|
return -usb_win_error_to_errno();
|
|
}
|
|
else
|
|
{
|
|
dev->interface = -1;
|
|
dev->altsetting = -1;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int usb_set_altinterface(usb_dev_handle *dev, int alternate)
|
|
{
|
|
libusb_request req;
|
|
|
|
if (dev->impl_info == INVALID_HANDLE_VALUE)
|
|
{
|
|
USBERR0("device not open\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (dev->config <= 0)
|
|
{
|
|
USBERR("could not set alt interface %d: invalid configuration %d\n", alternate, dev->config);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (dev->interface < 0)
|
|
{
|
|
USBERR("could not set alt interface %d: no interface claimed\n", alternate);
|
|
return -EINVAL;
|
|
}
|
|
|
|
req.interface.interface = dev->interface;
|
|
req.interface.altsetting = alternate;
|
|
req.timeout = LIBUSB_DEFAULT_TIMEOUT;
|
|
|
|
if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_SET_INTERFACE,
|
|
&req, sizeof(libusb_request),
|
|
NULL, 0, NULL))
|
|
{
|
|
USBERR("could not set alt interface "
|
|
"%d/%d: win error: %s",
|
|
dev->interface, alternate, usb_win_error_to_string());
|
|
return -usb_win_error_to_errno();
|
|
}
|
|
|
|
dev->altsetting = alternate;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _usb_setup_async(usb_dev_handle *dev, void **context,
|
|
DWORD control_code,
|
|
unsigned char ep, int pktsize)
|
|
{
|
|
usb_context_t **c = (usb_context_t **)context;
|
|
|
|
if (((control_code == LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE)
|
|
|| (control_code == LIBUSB_IOCTL_ISOCHRONOUS_WRITE))
|
|
&& (ep & USB_ENDPOINT_IN))
|
|
{
|
|
USBERR("invalid endpoint 0x%02x", ep);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (((control_code == LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ)
|
|
|| (control_code == LIBUSB_IOCTL_ISOCHRONOUS_READ))
|
|
&& !(ep & USB_ENDPOINT_IN))
|
|
{
|
|
USBERR("invalid endpoint 0x%02x\n", ep);
|
|
return -EINVAL;
|
|
}
|
|
|
|
*c = malloc(sizeof(usb_context_t));
|
|
|
|
if (!*c)
|
|
{
|
|
USBERR0("memory allocation error\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memset(*c, 0, sizeof(usb_context_t));
|
|
|
|
(*c)->dev = dev;
|
|
(*c)->req.endpoint.endpoint = ep;
|
|
(*c)->req.endpoint.packet_size = pktsize;
|
|
(*c)->control_code = control_code;
|
|
|
|
(*c)->ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if (!(*c)->ol.hEvent)
|
|
{
|
|
free(*c);
|
|
*c = NULL;
|
|
USBERR("creating event failed: win error: %s",
|
|
usb_win_error_to_string());
|
|
return -usb_win_error_to_errno();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int usb_submit_async(void *context, char *bytes, int size)
|
|
{
|
|
usb_context_t *c = (usb_context_t *)context;
|
|
|
|
if (!c)
|
|
{
|
|
USBERR0("invalid context");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (c->dev->impl_info == INVALID_HANDLE_VALUE)
|
|
{
|
|
USBERR0("device not open\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (c->dev->config <= 0)
|
|
{
|
|
USBERR("invalid configuration %d\n", c->dev->config);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (c->dev->interface < 0)
|
|
{
|
|
USBERR("invalid interface %d\n", c->dev->interface);
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
c->ol.Offset = 0;
|
|
c->ol.OffsetHigh = 0;
|
|
c->bytes = bytes;
|
|
c->size = size;
|
|
|
|
ResetEvent(c->ol.hEvent);
|
|
|
|
if (!DeviceIoControl(c->dev->impl_info,
|
|
c->control_code,
|
|
&c->req, sizeof(libusb_request),
|
|
c->bytes,
|
|
c->size, NULL, &c->ol))
|
|
{
|
|
if (GetLastError() != ERROR_IO_PENDING)
|
|
{
|
|
USBERR("submitting request failed, "
|
|
"win error: %s", usb_win_error_to_string());
|
|
return -usb_win_error_to_errno();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _usb_reap_async(void *context, int timeout, int cancel)
|
|
{
|
|
usb_context_t *c = (usb_context_t *)context;
|
|
ULONG ret = 0;
|
|
|
|
if (!c)
|
|
{
|
|
USBERR0("invalid context\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (WaitForSingleObject(c->ol.hEvent, timeout) == WAIT_TIMEOUT)
|
|
{
|
|
/* request timed out */
|
|
if (cancel)
|
|
{
|
|
_usb_cancel_io(c);
|
|
}
|
|
|
|
USBERR0("timeout error\n");
|
|
return -ETRANSFER_TIMEDOUT;
|
|
}
|
|
|
|
if (!GetOverlappedResult(c->dev->impl_info, &c->ol, &ret, TRUE))
|
|
{
|
|
USBERR("reaping request failed, win error: %s\n",usb_win_error_to_string());
|
|
return -usb_win_error_to_errno();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int usb_reap_async(void *context, int timeout)
|
|
{
|
|
return _usb_reap_async(context, timeout, TRUE);
|
|
}
|
|
|
|
int usb_reap_async_nocancel(void *context, int timeout)
|
|
{
|
|
return _usb_reap_async(context, timeout, FALSE);
|
|
}
|
|
|
|
|
|
int usb_cancel_async(void *context)
|
|
{
|
|
/* NOTE that this function will cancel all pending URBs */
|
|
/* on the same endpoint as this particular context, or even */
|
|
/* all pending URBs for this particular device. */
|
|
|
|
usb_context_t *c = (usb_context_t *)context;
|
|
|
|
if (!c)
|
|
{
|
|
USBERR0("invalid context\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (c->dev->impl_info == INVALID_HANDLE_VALUE)
|
|
{
|
|
USBERR0("device not open\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
_usb_cancel_io(c);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int usb_free_async(void **context)
|
|
{
|
|
usb_context_t **c = (usb_context_t **)context;
|
|
|
|
if (!*c)
|
|
{
|
|
USBERR0("invalid context\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
CloseHandle((*c)->ol.hEvent);
|
|
|
|
free(*c);
|
|
*c = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _usb_transfer_sync(usb_dev_handle *dev, int control_code,
|
|
int ep, int pktsize, char *bytes, int size,
|
|
int timeout)
|
|
{
|
|
void *context = NULL;
|
|
int transmitted = 0;
|
|
int ret;
|
|
int requested;
|
|
|
|
ret = _usb_setup_async(dev, &context, control_code, (unsigned char )ep,
|
|
pktsize);
|
|
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
do
|
|
{
|
|
#ifdef LIBUSB_WIN32_DLL_LARGE_TRANSFER_SUPPORT
|
|
requested = size > LIBUSB_MAX_READ_WRITE ? LIBUSB_MAX_READ_WRITE : size;
|
|
#else
|
|
requested = size;
|
|
#endif
|
|
ret = usb_submit_async(context, bytes, requested);
|
|
|
|
if (ret < 0)
|
|
{
|
|
transmitted = ret;
|
|
break;
|
|
}
|
|
|
|
ret = usb_reap_async(context, timeout);
|
|
|
|
if (ret < 0)
|
|
{
|
|
transmitted = ret;
|
|
break;
|
|
}
|
|
|
|
transmitted += ret;
|
|
bytes += ret;
|
|
size -= ret;
|
|
}
|
|
while (size > 0 && ret == requested);
|
|
|
|
usb_free_async(&context);
|
|
|
|
return transmitted;
|
|
}
|
|
|
|
int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size,
|
|
int timeout)
|
|
{
|
|
return _usb_transfer_sync(dev, LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE,
|
|
ep, 0, bytes, size, timeout);
|
|
}
|
|
|
|
int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size,
|
|
int timeout)
|
|
{
|
|
return _usb_transfer_sync(dev, LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ,
|
|
ep, 0, bytes, size, timeout);
|
|
}
|
|
|
|
int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size,
|
|
int timeout)
|
|
{
|
|
return _usb_transfer_sync(dev, LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE,
|
|
ep, 0, bytes, size, timeout);
|
|
}
|
|
|
|
int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size,
|
|
int timeout)
|
|
{
|
|
return _usb_transfer_sync(dev, LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ,
|
|
ep, 0, bytes, size, timeout);
|
|
}
|
|
|
|
int usb_isochronous_setup_async(usb_dev_handle *dev, void **context,
|
|
unsigned char ep, int pktsize)
|
|
{
|
|
if (ep & 0x80)
|
|
return _usb_setup_async(dev, context, LIBUSB_IOCTL_ISOCHRONOUS_READ,
|
|
ep, pktsize);
|
|
else
|
|
return _usb_setup_async(dev, context, LIBUSB_IOCTL_ISOCHRONOUS_WRITE,
|
|
ep, pktsize);
|
|
}
|
|
|
|
int usb_bulk_setup_async(usb_dev_handle *dev, void **context, unsigned char ep)
|
|
{
|
|
if (ep & 0x80)
|
|
return _usb_setup_async(dev, context, LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ,
|
|
ep, 0);
|
|
else
|
|
return _usb_setup_async(dev, context, LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE,
|
|
ep, 0);
|
|
}
|
|
|
|
int usb_interrupt_setup_async(usb_dev_handle *dev, void **context,
|
|
unsigned char ep)
|
|
{
|
|
if (ep & 0x80)
|
|
return _usb_setup_async(dev, context, LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ,
|
|
ep, 0);
|
|
else
|
|
return _usb_setup_async(dev, context, LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE,
|
|
ep, 0);
|
|
}
|
|
|
|
int usb_control_msg(usb_dev_handle *dev, int requesttype, int request,
|
|
int value, int index, char *bytes, int size, int timeout)
|
|
{
|
|
int read = 0;
|
|
libusb_request req;
|
|
void *out = &req;
|
|
int out_size = sizeof(libusb_request);
|
|
void *in = bytes;
|
|
int in_size = size;
|
|
int code;
|
|
|
|
if (dev->impl_info == INVALID_HANDLE_VALUE)
|
|
{
|
|
USBERR0("device not open\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
req.timeout = timeout;
|
|
|
|
/* windows doesn't support generic control messages, so it needs to be */
|
|
/* split up */
|
|
switch (requesttype & (0x03 << 5))
|
|
{
|
|
case USB_TYPE_STANDARD:
|
|
switch (request)
|
|
{
|
|
case USB_REQ_GET_STATUS:
|
|
req.status.recipient = requesttype & 0x1F;
|
|
req.status.index = index;
|
|
code = LIBUSB_IOCTL_GET_STATUS;
|
|
break;
|
|
|
|
case USB_REQ_CLEAR_FEATURE:
|
|
req.feature.recipient = requesttype & 0x1F;
|
|
req.feature.feature = value;
|
|
req.feature.index = index;
|
|
code = LIBUSB_IOCTL_CLEAR_FEATURE;
|
|
break;
|
|
|
|
case USB_REQ_SET_FEATURE:
|
|
req.feature.recipient = requesttype & 0x1F;
|
|
req.feature.feature = value;
|
|
req.feature.index = index;
|
|
code = LIBUSB_IOCTL_SET_FEATURE;
|
|
break;
|
|
|
|
case USB_REQ_GET_DESCRIPTOR:
|
|
req.descriptor.recipient = requesttype & 0x1F;
|
|
req.descriptor.type = (value >> 8) & 0xFF;
|
|
req.descriptor.index = value & 0xFF;
|
|
req.descriptor.language_id = index;
|
|
code = LIBUSB_IOCTL_GET_DESCRIPTOR;
|
|
break;
|
|
|
|
case USB_REQ_SET_DESCRIPTOR:
|
|
req.descriptor.recipient = requesttype & 0x1F;
|
|
req.descriptor.type = (value >> 8) & 0xFF;
|
|
req.descriptor.index = value & 0xFF;
|
|
req.descriptor.language_id = index;
|
|
code = LIBUSB_IOCTL_SET_DESCRIPTOR;
|
|
break;
|
|
|
|
case USB_REQ_GET_CONFIGURATION:
|
|
code = LIBUSB_IOCTL_GET_CONFIGURATION;
|
|
break;
|
|
|
|
case USB_REQ_SET_CONFIGURATION:
|
|
req.configuration.configuration = value;
|
|
code = LIBUSB_IOCTL_SET_CONFIGURATION;
|
|
break;
|
|
|
|
case USB_REQ_GET_INTERFACE:
|
|
req.interface.interface = index;
|
|
code = LIBUSB_IOCTL_GET_INTERFACE;
|
|
break;
|
|
|
|
case USB_REQ_SET_INTERFACE:
|
|
req.interface.interface = index;
|
|
req.interface.altsetting = value;
|
|
code = LIBUSB_IOCTL_SET_INTERFACE;
|
|
break;
|
|
|
|
default:
|
|
USBERR("invalid request 0x%x", request);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
|
|
case USB_TYPE_VENDOR:
|
|
case USB_TYPE_CLASS:
|
|
|
|
req.vendor.type = (requesttype >> 5) & 0x03;
|
|
req.vendor.recipient = requesttype & 0x1F;
|
|
req.vendor.request = request;
|
|
req.vendor.value = value;
|
|
req.vendor.index = index;
|
|
|
|
if (requesttype & 0x80)
|
|
code = LIBUSB_IOCTL_VENDOR_READ;
|
|
else
|
|
code = LIBUSB_IOCTL_VENDOR_WRITE;
|
|
break;
|
|
|
|
case USB_TYPE_RESERVED:
|
|
default:
|
|
USBERR("invalid or unsupported request type: %x",
|
|
requesttype);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* out request? */
|
|
if (!(requesttype & USB_ENDPOINT_IN))
|
|
{
|
|
if (!(out = malloc(sizeof(libusb_request) + size)))
|
|
{
|
|
USBERR0("memory allocation failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memcpy(out, &req, sizeof(libusb_request));
|
|
memcpy((char *)out + sizeof(libusb_request), bytes, size);
|
|
out_size = sizeof(libusb_request) + size;
|
|
in = NULL;
|
|
in_size = 0;
|
|
}
|
|
|
|
if (!_usb_io_sync(dev->impl_info, code, out, out_size, in, in_size, &read))
|
|
{
|
|
USBERR("sending control message failed, win error: %s\n", usb_win_error_to_string());
|
|
if (!(requesttype & USB_ENDPOINT_IN))
|
|
{
|
|
free(out);
|
|
}
|
|
return -usb_win_error_to_errno();
|
|
}
|
|
|
|
/* out request? */
|
|
if (!(requesttype & USB_ENDPOINT_IN))
|
|
{
|
|
free(out);
|
|
return size;
|
|
}
|
|
else
|
|
return read;
|
|
}
|
|
|
|
|
|
int usb_os_find_busses(struct usb_bus **busses)
|
|
{
|
|
struct usb_bus *bus = NULL;
|
|
|
|
/* create one 'virtual' bus */
|
|
|
|
bus = malloc(sizeof(struct usb_bus));
|
|
|
|
if (!bus)
|
|
{
|
|
USBERR0("memory allocation failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memset(bus, 0, sizeof(*bus));
|
|
strcpy(bus->dirname, LIBUSB_BUS_NAME);
|
|
|
|
USBMSG("found %s\n", bus->dirname);
|
|
|
|
*busses = bus;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices)
|
|
{
|
|
int i;
|
|
struct usb_device *dev, *fdev = NULL;
|
|
char dev_name[LIBUSB_PATH_MAX];
|
|
int ret;
|
|
HANDLE handle;
|
|
libusb_request req;
|
|
|
|
for (i = 1; i < LIBUSB_MAX_DEVICES; i++)
|
|
{
|
|
ret = 0;
|
|
|
|
_snprintf(dev_name, sizeof(dev_name) - 1,"%s%04d",
|
|
LIBUSB_DEVICE_NAME, i);
|
|
|
|
if (!(dev = malloc(sizeof(*dev))))
|
|
{
|
|
USBERR0("memory allocation failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memset(dev, 0, sizeof(*dev));
|
|
dev->bus = bus;
|
|
dev->devnum = (unsigned char)i;
|
|
|
|
handle = CreateFile(dev_name, 0, 0, NULL, OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED, NULL);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
free(dev);
|
|
continue;
|
|
}
|
|
|
|
/* retrieve device descriptor */
|
|
req.descriptor.type = USB_DT_DEVICE;
|
|
req.descriptor.recipient = USB_RECIP_DEVICE;
|
|
req.descriptor.index = 0;
|
|
req.descriptor.language_id = 0;
|
|
req.timeout = LIBUSB_DEFAULT_TIMEOUT;
|
|
|
|
_usb_io_sync(handle, LIBUSB_IOCTL_GET_DESCRIPTOR,
|
|
&req, sizeof(libusb_request),
|
|
&dev->descriptor, USB_DT_DEVICE_SIZE, &ret);
|
|
|
|
if (ret < USB_DT_DEVICE_SIZE)
|
|
{
|
|
USBERR0("couldn't read device descriptor\n");
|
|
free(dev);
|
|
CloseHandle(handle);
|
|
continue;
|
|
}
|
|
|
|
_snprintf(dev->filename, LIBUSB_PATH_MAX - 1, "%s--0x%04x-0x%04x",
|
|
dev_name, dev->descriptor.idVendor, dev->descriptor.idProduct);
|
|
|
|
CloseHandle(handle);
|
|
|
|
LIST_ADD(fdev, dev);
|
|
|
|
USBMSG("found %s on %s\n", dev->filename, bus->dirname);
|
|
}
|
|
|
|
*devices = fdev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void usb_os_init(void)
|
|
{
|
|
HANDLE dev;
|
|
libusb_request req;
|
|
int i;
|
|
int ret;
|
|
char dev_name[LIBUSB_PATH_MAX];
|
|
|
|
USBMSG("dll version: %d.%d.%d.%d\n",
|
|
VERSION_MAJOR, VERSION_MINOR,
|
|
VERSION_MICRO, VERSION_NANO);
|
|
|
|
|
|
for (i = 1; i < LIBUSB_MAX_DEVICES; i++)
|
|
{
|
|
/* build the Windows file name */
|
|
_snprintf(dev_name, sizeof(dev_name) - 1,"%s%04d",
|
|
LIBUSB_DEVICE_NAME, i);
|
|
|
|
dev = CreateFile(dev_name, 0, 0, NULL, OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED, NULL);
|
|
|
|
if (dev == INVALID_HANDLE_VALUE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!_usb_io_sync(dev, LIBUSB_IOCTL_GET_VERSION,
|
|
&req, sizeof(libusb_request),
|
|
&req, sizeof(libusb_request), &ret)
|
|
|| (ret < sizeof(libusb_request)))
|
|
{
|
|
USBERR0("getting driver version failed\n");
|
|
CloseHandle(dev);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
_usb_version.driver.major = req.version.major;
|
|
_usb_version.driver.minor = req.version.minor;
|
|
_usb_version.driver.micro = req.version.micro;
|
|
_usb_version.driver.nano = req.version.nano;
|
|
|
|
USBMSG("driver version: %d.%d.%d.%d\n",
|
|
req.version.major, req.version.minor,
|
|
req.version.micro, req.version.nano);
|
|
|
|
/* set debug level */
|
|
req.timeout = 0;
|
|
req.debug.level = usb_log_get_level();
|
|
|
|
if (!_usb_io_sync(dev, LIBUSB_IOCTL_SET_DEBUG_LEVEL,
|
|
&req, sizeof(libusb_request),
|
|
NULL, 0, NULL))
|
|
{
|
|
USBERR0("setting debug level failed");
|
|
}
|
|
|
|
CloseHandle(dev);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int usb_resetep(usb_dev_handle *dev, unsigned int ep)
|
|
{
|
|
libusb_request req;
|
|
|
|
if (dev->impl_info == INVALID_HANDLE_VALUE)
|
|
{
|
|
USBERR0("device not open\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
req.endpoint.endpoint = (int)ep;
|
|
req.timeout = LIBUSB_DEFAULT_TIMEOUT;
|
|
|
|
if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_ABORT_ENDPOINT, &req,
|
|
sizeof(libusb_request), NULL, 0, NULL))
|
|
{
|
|
USBERR("could not abort ep 0x%02x, win error: %s\n", ep, usb_win_error_to_string());
|
|
return -usb_win_error_to_errno();
|
|
}
|
|
|
|
if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RESET_ENDPOINT, &req,
|
|
sizeof(libusb_request), NULL, 0, NULL))
|
|
{
|
|
USBERR("could not reset ep 0x%02x, win error: %s\n", ep, usb_win_error_to_string());
|
|
return -usb_win_error_to_errno();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int usb_clear_halt(usb_dev_handle *dev, unsigned int ep)
|
|
{
|
|
libusb_request req;
|
|
|
|
if (dev->impl_info == INVALID_HANDLE_VALUE)
|
|
{
|
|
USBERR0("device not open\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
req.endpoint.endpoint = (int)ep;
|
|
req.timeout = LIBUSB_DEFAULT_TIMEOUT;
|
|
|
|
if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RESET_ENDPOINT, &req,
|
|
sizeof(libusb_request), NULL, 0, NULL))
|
|
{
|
|
USBERR("could not clear halt, ep 0x%02x, "
|
|
"win error: %s", ep, usb_win_error_to_string());
|
|
return -usb_win_error_to_errno();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int usb_reset(usb_dev_handle *dev)
|
|
{
|
|
libusb_request req;
|
|
|
|
if (dev->impl_info == INVALID_HANDLE_VALUE)
|
|
{
|
|
USBERR0("device not open\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
req.timeout = LIBUSB_DEFAULT_TIMEOUT;
|
|
|
|
if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_RESET_DEVICE,
|
|
&req, sizeof(libusb_request), NULL, 0, NULL))
|
|
{
|
|
USBERR("could not reset device, win error: %s\n", usb_win_error_to_string());
|
|
return -usb_win_error_to_errno();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const struct usb_version *usb_get_version(void)
|
|
{
|
|
return &_usb_version;
|
|
}
|
|
|
|
void usb_set_debug(int level)
|
|
{
|
|
HANDLE dev;
|
|
libusb_request req;
|
|
int i;
|
|
char dev_name[LIBUSB_PATH_MAX];
|
|
|
|
if (usb_log_get_level() || level)
|
|
{
|
|
USBMSG("setting debugging level to %d (%s)\n",
|
|
level, level ? "on" : "off");
|
|
}
|
|
|
|
usb_log_set_level(level);
|
|
|
|
/* find a valid device */
|
|
for (i = 1; i < LIBUSB_MAX_DEVICES; i++)
|
|
{
|
|
/* build the Windows file name */
|
|
_snprintf(dev_name, sizeof(dev_name) - 1,"%s%04d",
|
|
LIBUSB_DEVICE_NAME, i);
|
|
|
|
dev = CreateFile(dev_name, 0, 0, NULL, OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED, NULL);
|
|
|
|
if (dev == INVALID_HANDLE_VALUE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* set debug level */
|
|
req.timeout = 0;
|
|
req.debug.level = usb_log_get_level();
|
|
|
|
if (!_usb_io_sync(dev, LIBUSB_IOCTL_SET_DEBUG_LEVEL,
|
|
&req, sizeof(libusb_request),
|
|
NULL, 0, NULL))
|
|
{
|
|
USBERR0("setting debug level failed\n");
|
|
}
|
|
|
|
CloseHandle(dev);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
int usb_os_determine_children(struct usb_bus *bus)
|
|
{
|
|
struct usb_device *dev;
|
|
int i = 0;
|
|
|
|
/* add a virtual hub to the bus to emulate this feature */
|
|
if (_usb_add_virtual_hub(bus))
|
|
{
|
|
if (bus->root_dev->children)
|
|
{
|
|
free(bus->root_dev->children);
|
|
}
|
|
|
|
bus->root_dev->num_children = 0;
|
|
for (dev = bus->devices; dev; dev = dev->next)
|
|
bus->root_dev->num_children++;
|
|
|
|
bus->root_dev->children
|
|
= malloc(sizeof(struct usb_device *) * bus->root_dev->num_children);
|
|
|
|
for (dev = bus->devices; dev; dev = dev->next)
|
|
bus->root_dev->children[i++] = dev;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _usb_cancel_io(usb_context_t *context)
|
|
{
|
|
int ret;
|
|
ret = _usb_abort_ep(context->dev, context->req.endpoint.endpoint);
|
|
WaitForSingleObject(context->ol.hEvent, 0);
|
|
return ret;
|
|
}
|
|
|
|
static int _usb_abort_ep(usb_dev_handle *dev, unsigned int ep)
|
|
{
|
|
libusb_request req;
|
|
|
|
if (dev->impl_info == INVALID_HANDLE_VALUE)
|
|
{
|
|
USBERR0("device not open");
|
|
return -EINVAL;
|
|
}
|
|
|
|
req.endpoint.endpoint = (int)ep;
|
|
req.timeout = LIBUSB_DEFAULT_TIMEOUT;
|
|
|
|
if (!_usb_io_sync(dev->impl_info, LIBUSB_IOCTL_ABORT_ENDPOINT, &req,
|
|
sizeof(libusb_request), NULL, 0, NULL))
|
|
{
|
|
USBERR("could not abort ep 0x%02x, win error: %s\n", ep, usb_win_error_to_string());
|
|
return -usb_win_error_to_errno();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _usb_io_sync(HANDLE dev, unsigned int code, void *out, int out_size,
|
|
void *in, int in_size, int *ret)
|
|
{
|
|
OVERLAPPED ol;
|
|
DWORD _ret;
|
|
|
|
memset(&ol, 0, sizeof(ol));
|
|
|
|
if (ret)
|
|
*ret = 0;
|
|
|
|
ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if (!ol.hEvent)
|
|
return FALSE;
|
|
|
|
if (!DeviceIoControl(dev, code, out, out_size, in, in_size, NULL, &ol))
|
|
{
|
|
if (GetLastError() != ERROR_IO_PENDING)
|
|
{
|
|
CloseHandle(ol.hEvent);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (GetOverlappedResult(dev, &ol, &_ret, TRUE))
|
|
{
|
|
if (ret)
|
|
*ret = (int)_ret;
|
|
CloseHandle(ol.hEvent);
|
|
return TRUE;
|
|
}
|
|
|
|
CloseHandle(ol.hEvent);
|
|
return FALSE;
|
|
}
|
|
|
|
static int _usb_add_virtual_hub(struct usb_bus *bus)
|
|
{
|
|
struct usb_device *dev;
|
|
|
|
if (!bus->root_dev)
|
|
{
|
|
if (!(dev = malloc(sizeof(*dev))))
|
|
return FALSE;
|
|
|
|
memset(dev, 0, sizeof(*dev));
|
|
strcpy(dev->filename, "virtual-hub");
|
|
dev->bus = bus;
|
|
|
|
dev->descriptor.bLength = USB_DT_DEVICE_SIZE;
|
|
dev->descriptor.bDescriptorType = USB_DT_DEVICE;
|
|
dev->descriptor.bcdUSB = 0x0200;
|
|
dev->descriptor.bDeviceClass = USB_CLASS_HUB;
|
|
dev->descriptor.bDeviceSubClass = 0;
|
|
dev->descriptor.bDeviceProtocol = 0;
|
|
dev->descriptor.bMaxPacketSize0 = 64;
|
|
dev->descriptor.idVendor = 0;
|
|
dev->descriptor.idProduct = 0;
|
|
dev->descriptor.bcdDevice = 0x100;
|
|
dev->descriptor.iManufacturer = 0;
|
|
dev->descriptor.iProduct = 0;
|
|
dev->descriptor.iSerialNumber = 0;
|
|
dev->descriptor.bNumConfigurations = 0;
|
|
|
|
bus->root_dev = dev;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void _usb_free_bus_list(struct usb_bus *bus)
|
|
{
|
|
if (bus)
|
|
{
|
|
_usb_free_bus_list(bus->next);
|
|
if (bus->root_dev)
|
|
usb_free_dev(bus->root_dev);
|
|
_usb_free_dev_list(bus->devices);
|
|
usb_free_bus(bus);
|
|
}
|
|
}
|
|
|
|
static void _usb_free_dev_list(struct usb_device *dev)
|
|
{
|
|
if (dev)
|
|
{
|
|
_usb_free_dev_list(dev->next);
|
|
usb_free_dev(dev);
|
|
}
|
|
}
|
|
|
|
static void _usb_deinit(void)
|
|
{
|
|
_usb_free_bus_list(usb_get_busses());
|
|
}
|