diff options
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | examples/xusb.c | 215 | ||||
-rw-r--r-- | libusb/os/windows_compat.c | 14 | ||||
-rw-r--r-- | libusb/os/windows_compat.h | 11 | ||||
-rw-r--r-- | libusb/os/windows_usb.c | 39 |
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++) { |