summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--examples/xusb.c215
-rw-r--r--libusb/os/windows_compat.c14
-rw-r--r--libusb/os/windows_compat.h11
-rw-r--r--libusb/os/windows_usb.c39
5 files changed, 212 insertions, 68 deletions
diff --git a/configure.ac b/configure.ac
index 6802458..01e0984 100644
--- a/configure.ac
+++ b/configure.ac
@@ -33,7 +33,6 @@ case $host in
*-mingw*)
AC_DEFINE(OS_WINDOWS, [], [Windows backend])
AC_SUBST(OS_WINDOWS)
- AC_DEFINE(USBI_OS_HANDLES_TIMEOUT, [], [Backend handles timeout])
AC_MSG_RESULT([Windows])
backend="windows"
LIBS="-lpthread -lsetupapi -lole32"
diff --git a/examples/xusb.c b/examples/xusb.c
index 0341d7f..0157f24 100644
--- a/examples/xusb.c
+++ b/examples/xusb.c
@@ -43,6 +43,7 @@
#define ERR_EXIT(errcode) do { perr(" libusb error: %d\n", errcode); return -1; } while (0)
#define CALL_CHECK(fcall) do { r=fcall; if (r < 0) ERR_EXIT(r); } while (0);
#define B(x) (((x)!=0)?1:0)
+#define be_to_int32(buf) (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|(buf)[3])
// HID Class-Specific Requests values. See section 7.2 of the HID specifications
#define HID_GET_REPORT 0x01
@@ -78,6 +79,25 @@ struct command_status_wrapper {
uint8_t bCSWStatus;
};
+static uint8_t cdb_length[256] = {
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 06,06,06,06,06,06,06,06,06,06,06,06,06,06,06,06, // 0
+ 06,06,06,06,06,06,06,06,06,06,06,06,06,06,06,06, // 1
+ 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 2
+ 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 3
+ 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 4
+ 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 5
+ 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00, // 6
+ 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00, // 7
+ 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, // 8
+ 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, // 9
+ 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, // A
+ 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, // B
+ 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00, // C
+ 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00, // D
+ 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00, // E
+ 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00, // F
+};
enum test_type {
USE_XBOX,
@@ -117,33 +137,123 @@ int set_xbox_actuators(libusb_device_handle *handle, uint8_t left, uint8_t right
printf("Writing XBox Controller Output Report...\n");
- memset(output_report, 0, 6);
- output_report[1] = 6;
+ memset(output_report, 0, sizeof(output_report));
+ output_report[1] = sizeof(output_report);
output_report[3] = left;
output_report[5] = right;
CALL_CHECK(libusb_control_transfer(handle, LIBUSB_ENDPOINT_OUT|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE,
- HID_SET_REPORT, (HID_REPORT_TYPE_OUTPUT<<8)|0x00, 0, output_report, 6, 1000));
+ HID_SET_REPORT, (HID_REPORT_TYPE_OUTPUT<<8)|0x00, 0, output_report,06, 1000));
return 0;
}
-// Mass Storage device to test bulk transfers (/!\ destructive test /!\)
-int test_mass_storage(libusb_device_handle *handle)
+int send_mass_storage_command(libusb_device_handle *handle, uint8_t endpoint, uint8_t lun,
+ uint8_t *cdb, uint8_t direction, int data_length, uint32_t *ret_tag)
{
+ static uint32_t tag = 1;
+ uint8_t cdb_len;
int r, size;
- unsigned char lun;
struct command_block_wrapper cbw;
+
+ if (cdb == NULL) {
+ return -1;
+ }
+
+ if (endpoint & LIBUSB_ENDPOINT_IN) {
+ perr("send_mass_storage_command: cannot send command on IN endpoint\n");
+ return -1;
+ }
+
+ cdb_len = cdb_length[cdb[0]];
+ if ((cdb_len == 0) || (cdb_len > sizeof(cbw.CBWCB))) {
+ perr("send_mass_storage_command: don't know how to handle this command (%02X, length %d)\n",
+ cdb[0], cdb_len);
+ return -1;
+ }
+
+ memset(&cbw, 0, sizeof(cbw));
+ cbw.dCBWSignature[0] = 'U';
+ cbw.dCBWSignature[1] = 'S';
+ cbw.dCBWSignature[2] = 'B';
+ cbw.dCBWSignature[3] = 'C';
+ *ret_tag = tag;
+ cbw.dCBWTag = tag++;
+ cbw.dCBWDataTransferLength = data_length;
+ cbw.bmCBWFlags = direction;
+ cbw.bCBWLUN = lun;
+ cbw.bCBWCBLength = cdb_len;
+ memcpy(cbw.CBWCB, cdb, cdb_len);
+
+ CALL_CHECK(libusb_bulk_transfer(handle, endpoint, (unsigned char*)&cbw, 15+cdb_len, &size, 1000));
+ printf("sent %d bytes (confirmed %d)\n", 15+cdb_len, size);
+ return 0;
+}
+
+int get_mass_storage_status(libusb_device_handle *handle, uint8_t endpoint, uint32_t expected_tag)
+{
+ int r, size;
struct command_status_wrapper csw;
+
+ CALL_CHECK(libusb_bulk_transfer(handle, endpoint, (unsigned char*)&csw, 13, &size, 1000));
+ if (size != 13) {
+ perr("get_mass_storage_status: received %d bytes (expected 13)\n", size);
+ return -1;
+ }
+ if (csw.dCSWTag != expected_tag) {
+ perr("get_mass_storage_status: mismatched tags (expected %08X, received %08X)\n",
+ expected_tag, csw.dCSWTag);
+ return -1;
+ }
+ printf("Mass Storage Status: %02X (%s)\n", csw.bCSWStatus, csw.bCSWStatus?"FAILED":"Success");
+ if (csw.dCSWTag != expected_tag)
+ return -1;
+ if (csw.bCSWStatus)
+ return -2; // request Get Sense
+
+ return 0;
+}
+
+void get_sense(libusb_device_handle *handle, uint8_t endpoint_in, uint8_t endpoint_out)
+{
+ uint8_t cdb[16]; // SCSI Command Descriptor Block
+ uint8_t sense[18];
+ uint32_t expected_tag;
+ int size;
+
+ // Request Sense
+ printf("Request Sense...\n");
+ memset(sense, 0, sizeof(sense));
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = 0x03; // Request Sense
+ cdb[4] = 0x12;
+
+ send_mass_storage_command(handle, endpoint_out, 0, cdb, LIBUSB_ENDPOINT_IN, 0x12, &expected_tag);
+ libusb_bulk_transfer(handle, endpoint_in, (unsigned char*)&sense, 0x12, &size, 1000);
+ printf("received %d bytes\n", size);
+
+ if ((sense[0] != 0x70) && (sense[0] != 0x71)) {
+ perr("ERROR No sense data\n");
+ } else {
+ perr("ERROR Sense: %02X %02X %02X\n", sense[2]&0x0F, sense[12], sense[13]);
+ }
+ get_mass_storage_status(handle, endpoint_in, expected_tag);
+}
+
+// Mass Storage device to test bulk transfers (non destructive test)
+int test_mass_storage(libusb_device_handle *handle)
+{
+ int r, i, size;
+ uint8_t lun;
+ uint32_t expected_tag;
+ uint32_t max_lba, block_size;
+ double device_size;
+ uint8_t cdb[16]; // SCSI Command Descriptor Block
uint8_t buffer[512];
if (buffer == NULL) {
perr("failed to allocate mass storage test buffer\n");
return -1;
}
- // This reset doesn't seem to work...
- printf("Resetting device...\n");
- CALL_CHECK(libusb_reset_device(handle));
-
printf("Sending Mass Storage Reset...\n");
CALL_CHECK(libusb_control_transfer(handle, LIBUSB_ENDPOINT_OUT|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE,
BOMS_RESET, 0, 0, NULL, 0, 1000));
@@ -152,41 +262,62 @@ int test_mass_storage(libusb_device_handle *handle)
BOMS_GET_MAX_LUN, 0, 0, &lun, 1, 1000));
printf(" Max LUN = %d\n", lun);
- cbw.dCBWSignature[0] = 'U';
- cbw.dCBWSignature[1] = 'S';
- cbw.dCBWSignature[2] = 'B';
- cbw.dCBWSignature[3] = 'C';
- cbw.dCBWTag = 0x01020304;
- cbw.dCBWDataTransferLength = 0;
- cbw.bmCBWFlags = 0;
- cbw.bCBWLUN = 0;
- cbw.bCBWCBLength = 1;
-
- CALL_CHECK(libusb_bulk_transfer(handle, 0x01, (unsigned char*)&cbw, 16, &size, 1000));
- printf("sent %d bytes\n", size);
- CALL_CHECK(libusb_bulk_transfer(handle, 0x81, (unsigned char*)&csw, 13, &size, 1000));
- printf("received %d bytes\n", size);
- printf("Tag = %08X\n", csw.dCSWTag);
- printf("Status = %02X\n", csw.bCSWStatus);
-
// Send Inquiry
- cbw.dCBWSignature[0] = 'U';
- cbw.dCBWSignature[1] = 'S';
- cbw.dCBWSignature[2] = 'B';
- cbw.dCBWSignature[3] = 'C';
- cbw.dCBWTag = 0x01234567;
- cbw.dCBWDataTransferLength = 0x60;
- cbw.bmCBWFlags = 0x80;
- cbw.bCBWLUN = 0;
- cbw.bCBWCBLength = 6;
- cbw.CBWCB[0] = 0x12; // Inquiry
- cbw.CBWCB[4] = 0x60; // Inquiry data size
-
- CALL_CHECK(libusb_bulk_transfer(handle, 0x01, (unsigned char*)&cbw, 22, &size, 100));
- printf("sent %d bytes\n", size);
- CALL_CHECK(libusb_bulk_transfer(handle, 0x81, (unsigned char*)&buffer, 0x60, &size, 100));
+ printf("Sending Inquiry...\n");
+ memset(buffer, 0, sizeof(buffer));
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = 0x12; // Inquiry
+ cdb[4] = 0x60; // Inquiry data size
+
+ send_mass_storage_command(handle, 0x01, 0, cdb, LIBUSB_ENDPOINT_IN, 0x60, &expected_tag);
+ CALL_CHECK(libusb_bulk_transfer(handle, 0x81, (unsigned char*)&buffer, 0x60, &size, 1000));
printf("received %d bytes\n", size);
printf("VID:PID:REV:SPE %s:%s:%s:%s\n", &buffer[8], &buffer[16], &buffer[32], &buffer[38]);
+ if (get_mass_storage_status(handle, 0x81, expected_tag) == -2) {
+ get_sense(handle, 0x81, 0x01);
+ }
+
+
+ // Read capacity
+ printf("Reading Capacity...\n");
+ memset(buffer, 0, sizeof(buffer));
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = 0x25; // Read Capacity
+
+ send_mass_storage_command(handle, 0x01, 0, cdb, LIBUSB_ENDPOINT_IN, 0x08, &expected_tag);
+ CALL_CHECK(libusb_bulk_transfer(handle, 0x81, (unsigned char*)&buffer, 0x08, &size, 1000));
+ printf("received %d bytes\n", size);
+ max_lba = be_to_int32(&buffer[0]);
+ block_size = be_to_int32(&buffer[4]);
+ device_size = ((double)(max_lba+1))*block_size/(1024*1024*1024);
+ printf("Max LBA: %08X, Block Size: %08X (%.2f GB)\n", max_lba, block_size, device_size);
+ if (get_mass_storage_status(handle, 0x81, expected_tag) == -2) {
+ get_sense(handle, 0x81, 0x01);
+ }
+
+ // Send Read
+ printf("Try to read 512 bytes...\n");
+ memset(buffer, 0, sizeof(buffer));
+ memset(cdb, 0, sizeof(cdb));
+
+// cdb[0] = 0x28; // Read(10)
+// cdb[7] = 0x02; // 0x200 bytes
+
+ cdb[0] = 0xA8; // Read(12)
+ cdb[8] = 0x02; // 0x200 bytes
+
+ send_mass_storage_command(handle, 0x01, 0, cdb, LIBUSB_ENDPOINT_IN, 0x200, &expected_tag);
+ libusb_bulk_transfer(handle, 0x81, (unsigned char*)&buffer, 0x200, &size, 5000);
+ printf("READ: received %d bytes\n", size);
+ if (get_mass_storage_status(handle, 0x81, expected_tag) == -2) {
+ get_sense(handle, 0x81, 0x01);
+ } else {
+ for(i=0; i<0x10; i++) {
+ printf(" %02X", buffer[i]);
+ }
+ printf("\n");
+ }
+
return 0;
}
diff --git a/libusb/os/windows_compat.c b/libusb/os/windows_compat.c
index 6259cbb..ea2455d 100644
--- a/libusb/os/windows_compat.c
+++ b/libusb/os/windows_compat.c
@@ -505,8 +505,10 @@ int poll(struct pollfd *fds, unsigned int nfds, int timeout)
|| (poll_fd[index].handle == 0) || (poll_fd[index].overlapped == NULL)) {
fds[i].revents |= POLLNVAL | POLLERR;
errno = EBADF;
+ if (index >= 0) {
+ pthread_mutex_unlock(&_poll_fd[index].mutex);
+ }
printb("poll: invalid fd\n");
- pthread_mutex_unlock(&_poll_fd[index].mutex);
return -1;
}
@@ -527,7 +529,7 @@ int poll(struct pollfd *fds, unsigned int nfds, int timeout)
return -1;
}
- printb("windows_poll: fd[%d]=%d (overlapped = %p) got events %04X\n", i, poll_fd[index].fd, poll_fd[index].overlapped, fds[i].events);
+ printb("poll: fd[%d]=%d (overlapped = %p) got events %04X\n", i, poll_fd[index].fd, poll_fd[index].overlapped, fds[i].events);
// The following macro only works if overlapped I/O was reported pending
if (HasOverlappedIoCompleted(poll_fd[index].overlapped)) {
@@ -558,7 +560,9 @@ int poll(struct pollfd *fds, unsigned int nfds, int timeout)
index = _fd_to_index_and_lock(fds[i].fd);
fds[i].revents = fds[i].events;
triggered++;
- pthread_mutex_unlock(&_poll_fd[index].mutex);
+ if (index >= 0) {
+ pthread_mutex_unlock(&_poll_fd[index].mutex);
+ }
} else if (ret == WAIT_TIMEOUT) {
printb(" timed out\n");
return 0; // 0 = timeout
@@ -624,7 +628,9 @@ ssize_t write_for_poll(int fd, const void *buf, size_t count)
if ( (index < 0) || (poll_fd[index].overlapped == NULL)
|| (poll_fd[index].rw != RW_WRITE) ) {
errno = EBADF;
- pthread_mutex_unlock(&_poll_fd[index].mutex);
+ if (index >= 0) {
+ pthread_mutex_unlock(&_poll_fd[index].mutex);
+ }
return -1;
}
diff --git a/libusb/os/windows_compat.h b/libusb/os/windows_compat.h
index de4ed5d..37ac3b9 100644
--- a/libusb/os/windows_compat.h
+++ b/libusb/os/windows_compat.h
@@ -83,3 +83,14 @@ struct winfd overlapped_to_winfd(OVERLAPPED* overlapped);
(tv)->tv_usec = (ts)->tv_nsec / 1000; \
}
#endif
+#ifndef timersub
+# define timersub(a, b, result) \
+do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+ if ((result)->tv_usec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_usec += 1000000; \
+ } \
+} while (0)
+#endif
diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c
index 9e00d3a..307cce0 100644
--- a/libusb/os/windows_usb.c
+++ b/libusb/os/windows_usb.c
@@ -1343,6 +1343,14 @@ static void windows_destroy_device(struct libusb_device *dev)
windows_device_priv_release(priv, dev->num_configurations);
}
+static void windows_clear_transfer_priv(struct usbi_transfer *itransfer)
+{
+ struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+
+ usbi_remove_pollfd(ITRANSFER_CTX(itransfer), transfer_priv->pollable_fd.fd);
+ free_fd_for_poll(transfer_priv->pollable_fd.fd);
+}
+
static int submit_bulk_transfer(struct usbi_transfer *itransfer)
{
struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
@@ -1442,6 +1450,8 @@ static int windows_cancel_transfer(struct usbi_transfer *itransfer)
{
struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ windows_clear_transfer_priv(itransfer); // Cancel polling
+
switch (transfer->type) {
case LIBUSB_TRANSFER_TYPE_CONTROL:
return windows_abort_control(itransfer);
@@ -1455,12 +1465,6 @@ static int windows_cancel_transfer(struct usbi_transfer *itransfer)
}
}
-static void windows_clear_transfer_priv(struct usbi_transfer *itransfer)
-{
- struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
- free_fd_for_poll(transfer_priv->pollable_fd.fd);
-}
-
static void windows_transfer_callback (struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
{
int status;
@@ -1473,7 +1477,7 @@ static void windows_transfer_callback (struct usbi_transfer *itransfer, uint32_t
itransfer->transferred += io_size;
break;
case ERROR_GEN_FAILURE:
- usbi_dbg("unsupported I/O request");
+ usbi_dbg("detected endpoint stall");
status = LIBUSB_TRANSFER_STALL;
break;
default:
@@ -2038,13 +2042,7 @@ static int winusb_clear_halt(struct libusb_device_handle *dev_handle, unsigned c
*/
static int winusb_abort_control(struct usbi_transfer *itransfer)
{
- struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-
- CHECK_WINUSB_AVAILABLE;
-
- // The best we can do is cancel the control I/O
- free_fd_for_poll(transfer_priv->pollable_fd.fd);
-
+ // Cancelling of the I/O is done in the parent
return LIBUSB_SUCCESS;
}
@@ -2053,16 +2051,12 @@ static int winusb_abort_transfers(struct usbi_transfer *itransfer)
struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)transfer->dev_handle->os_priv;
- struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev);
HANDLE winusb_handle;
int current_interface;
CHECK_WINUSB_AVAILABLE;
- // Cancel the I/O
- free_fd_for_poll(transfer_priv->pollable_fd.fd);
-
current_interface = winusb_interface_by_endpoint(priv, handle_priv, transfer->endpoint);
if (current_interface < 0) {
usbi_err(ctx, "unable to match endpoint to an open interface - cancelling abort");
@@ -2103,10 +2097,13 @@ static int winusb_reset_device(struct libusb_device_handle *dev_handle)
// Reset any available pipe (except control)
for (i=0; i<USB_MAXINTERFACES; i++) {
winusb_handle = handle_priv->interface_handle[i].winusb;
- do {
+ for (wfd = handle_to_winfd(winusb_handle); wfd.fd > 0;)
+ {
+ // Cancel any pollable I/O
+ usbi_remove_pollfd(ctx, wfd.fd);
+ free_fd_for_poll(wfd.fd);
wfd = handle_to_winfd(winusb_handle);
- free_fd_for_poll(wfd.fd); // Cancel any pending I/O
- } while (wfd.fd > 0);
+ }
if ( (winusb_handle != 0) && (winusb_handle != INVALID_HANDLE_VALUE)) {
for (j=0; j<priv->interface[i].nb_endpoints; j++) {