static int wince_submit_control_or_bulk_transfer(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 wince_transfer_priv *transfer_priv = (struct wince_transfer_priv*)usbi_transfer_get_os_priv(itransfer); struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev); BOOL direction_in, ret; struct winfd wfd; DWORD flags; HANDLE eventHandle; PUKW_CONTROL_HEADER setup = NULL; const BOOL control_transfer = transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL; transfer_priv->pollable_fd = INVALID_WINFD; if (control_transfer) { setup = (PUKW_CONTROL_HEADER) transfer->buffer; direction_in = setup->bmRequestType & LIBUSB_ENDPOINT_IN; } else { direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN; } flags = direction_in ? UKW_TF_IN_TRANSFER : UKW_TF_OUT_TRANSFER; flags |= UKW_TF_SHORT_TRANSFER_OK; eventHandle = CreateEvent(NULL, FALSE, FALSE, NULL); if (eventHandle == NULL) { usbi_err(ctx, "Failed to create event for async transfer"); return LIBUSB_ERROR_NO_MEM; } wfd = usbi_create_fd(eventHandle, direction_in ? RW_READ : RW_WRITE, itransfer, &wince_cancel_transfer); if (wfd.fd < 0) { CloseHandle(eventHandle); return LIBUSB_ERROR_NO_MEM; } transfer_priv->pollable_fd = wfd; if (control_transfer) { // Split out control setup header and data buffer DWORD bufLen = transfer->length - sizeof(UKW_CONTROL_HEADER); PVOID buf = (PVOID) &transfer->buffer[sizeof(UKW_CONTROL_HEADER)]; ret = UkwIssueControlTransfer(priv->dev, flags, setup, buf, bufLen, &transfer->actual_length, wfd.overlapped); } else { ret = UkwIssueBulkTransfer(priv->dev, flags, transfer->endpoint, transfer->buffer, transfer->length, &transfer->actual_length, wfd.overlapped); } if (!ret) { int libusbErr = translate_driver_error(GetLastError()); usbi_err(ctx, "UkwIssue%sTransfer failed: error %d", control_transfer ? "Control" : "Bulk", GetLastError()); wince_clear_transfer_priv(itransfer); return libusbErr; } usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, direction_in ? POLLIN : POLLOUT); itransfer->flags |= USBI_TRANSFER_UPDATED_FDS; return LIBUSB_SUCCESS; }
static void wince_transfer_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct wince_transfer_priv *transfer_priv = (struct wince_transfer_priv*)usbi_transfer_get_os_priv(itransfer); struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev); int status; usbi_dbg("handling I/O completion with errcode %d", io_result); if (io_result == ERROR_NOT_SUPPORTED && transfer->type != LIBUSB_TRANSFER_TYPE_CONTROL) { /* For functional stalls, the WinCE USB layer (and therefore the USB Kernel Wrapper * Driver) will report USB_ERROR_STALL/ERROR_NOT_SUPPORTED in situations where the * endpoint isn't actually stalled. * * One example of this is that some devices will occasionally fail to reply to an IN * token. The WinCE USB layer carries on with the transaction until it is completed * (or cancelled) but then completes it with USB_ERROR_STALL. * * This code therefore needs to confirm that there really is a stall error, by both * checking the pipe status and requesting the endpoint status from the device. */ BOOL halted = FALSE; usbi_dbg("checking I/O completion with errcode ERROR_NOT_SUPPORTED is really a stall"); if (UkwIsPipeHalted(priv->dev, transfer->endpoint, &halted)) { /* Pipe status retrieved, so now request endpoint status by sending a GET_STATUS * control request to the device. This is done synchronously, which is a bit * naughty, but this is a special corner case. */ WORD wStatus = 0; DWORD written = 0; UKW_CONTROL_HEADER ctrlHeader; ctrlHeader.bmRequestType = LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_ENDPOINT; ctrlHeader.bRequest = LIBUSB_REQUEST_GET_STATUS; ctrlHeader.wValue = 0; ctrlHeader.wIndex = transfer->endpoint; ctrlHeader.wLength = sizeof(wStatus); if (UkwIssueControlTransfer(priv->dev, UKW_TF_IN_TRANSFER | UKW_TF_SEND_TO_ENDPOINT, &ctrlHeader, &wStatus, sizeof(wStatus), &written, NULL)) { if (written == sizeof(wStatus) && (wStatus & STATUS_HALT_FLAG) == 0) { if (!halted || UkwClearHaltHost(priv->dev, transfer->endpoint)) { usbi_dbg("Endpoint doesn't appear to be stalled, overriding error with success"); io_result = ERROR_SUCCESS; } else { usbi_dbg("Endpoint doesn't appear to be stalled, but the host is halted, changing error"); io_result = ERROR_IO_DEVICE; } } } } } switch(io_result) { case ERROR_SUCCESS: itransfer->transferred += io_size; status = LIBUSB_TRANSFER_COMPLETED; break; case ERROR_CANCELLED: usbi_dbg("detected transfer cancel"); status = LIBUSB_TRANSFER_CANCELLED; break; case ERROR_NOT_SUPPORTED: case ERROR_GEN_FAILURE: usbi_dbg("detected endpoint stall"); status = LIBUSB_TRANSFER_STALL; break; case ERROR_SEM_TIMEOUT: usbi_dbg("detected semaphore timeout"); status = LIBUSB_TRANSFER_TIMED_OUT; break; case ERROR_OPERATION_ABORTED: if (itransfer->flags & USBI_TRANSFER_TIMED_OUT) { usbi_dbg("detected timeout"); status = LIBUSB_TRANSFER_TIMED_OUT; } else { usbi_dbg("detected operation aborted"); status = LIBUSB_TRANSFER_CANCELLED; } break; default: usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error: %s", windows_error_str(io_result)); status = LIBUSB_TRANSFER_ERROR; break; } wince_clear_transfer_priv(itransfer); if (status == LIBUSB_TRANSFER_CANCELLED) { usbi_handle_transfer_cancellation(itransfer); } else { usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status); } }
// Attempts to request a device switches into Android Accessory Protocol mode. // See: http://developer.android.com/guide/topics/usb/adk.html#accessory-protocol static void sendAapRequest(char line[]) { // Parse the device index DWORD devIdx = 0; line = parseNumber(line, devIdx); if (!line) { printf("Please provide a decimal device number following the command\n"); return; } if (devIdx >= gDeviceListSize || devIdx < 0) { printf("Invalid device index '%d' provided\n", devIdx); return; } // Input validated so form the request UKW_DEVICE device = gDeviceList[devIdx]; UKW_CONTROL_HEADER header; OVERLAPPED overlapped; memset(&overlapped, 0, sizeof(overlapped)); overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (overlapped.hEvent == NULL) { printf("Failed to create event for asynchronous request.\n"); return; } // send control request "Get Protocol" (51) -- gets version of AAP supported by device (if any) unsigned char ioBuffer[2]; DWORD flags = UKW_TF_IN_TRANSFER; header.bmRequestType = 0xC0; header.bRequest = 0x33; header.wValue = 0x0000; header.wIndex = 0x0000; header.wLength = 0x0002; // Now perform the request DWORD transferred = -1; BOOL status = UkwIssueControlTransfer(device, flags, &header, ioBuffer, 2, &transferred, &overlapped); if (!waitForTransferCompletion(status, device, overlapped, transferred, 2)) goto out; DWORD protocolVersion = (ioBuffer[1] << 8) | ioBuffer[0]; if (protocolVersion != 1) { printf("Device does not support the required Android Accessory Protocol version (supports %d)\n", protocolVersion); goto out; } printf("Device supports protocol version: %d\n", protocolVersion); // Send the identity strings char* identityStrings[] = { "RealVNC", // Manufacturer "AAPTest", // Model name "RealVNC AAP Bearer", // Description "0.1", // Version "http://www.realvnc.com", // URI "0123456789", // Device serial 'number' }; flags = UKW_TF_OUT_TRANSFER; header.bmRequestType = 0x40; header.bRequest = 0x34; header.wValue = 0x0000; for(DWORD i = 0; i < (sizeof(identityStrings) / sizeof(char*)) ; ++i) { DWORD length = strlen(identityStrings[i]); header.wIndex = static_cast<UINT16>(i); header.wLength = static_cast<UINT16>(length); status = UkwIssueControlTransfer(device, flags, &header, identityStrings[i], length, &transferred, &overlapped); if (!waitForTransferCompletion(status, device, overlapped, transferred, length)) goto out; } // Finally request that the device goes into AAP mode printf("Requesting device switches to AAP mode\n"); flags = UKW_TF_OUT_TRANSFER; header.bmRequestType = 0x40; header.bRequest = 0x35; header.wValue = 0x0000; header.wIndex = 0x0000; header.wLength = 0x0000; status = UkwIssueControlTransfer(device, flags, &header, NULL, 0, &transferred, &overlapped); if (!waitForTransferCompletion(status, device, overlapped, transferred, 0)) goto out; out: CloseHandle(overlapped.hEvent); return; }
static void sendControlRequest(char line[]) { // Parse the device index DWORD devIdx = 0; line = parseNumber(line, devIdx); if (!line) { printf("Please provide a decimal device number following the command\n"); return; } if (devIdx >= gDeviceListSize || devIdx < 0) { printf("Invalid device index '%d' provided\n", devIdx); return; } // Parse the option async parameter BOOL async; DWORD asyncval; line = parseNumber(line, asyncval); if (line && asyncval != 0) { printf("Performing control request asynchronously.\n"); async = TRUE; } else { printf("Performing control request synchronously.\n"); async = FALSE; } // Input validated so form the request UKW_DEVICE device = gDeviceList[devIdx]; UKW_CONTROL_HEADER header; OVERLAPPED overlapped; if (async) { memset(&overlapped, 0, sizeof(overlapped)); overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (overlapped.hEvent == NULL) { printf("Failed to create event for asynchronous request.\n"); return; } } DWORD flags = UKW_TF_OUT_TRANSFER; header.bmRequestType = 0x40; header.bRequest = 0xF0; header.wValue = 0x0001; header.wIndex = 0x0000; header.wLength = 0x0000; // Now perform the request DWORD transferred = -1; BOOL status = UkwIssueControlTransfer(device, flags, &header, NULL, 0, &transferred, async ? &overlapped : NULL); if (!status) { printf("Control transfer failed with %d.\n", GetLastError()); return; } if (async) { if (!waitForOverlapped(overlapped)) { // Attempt to cancel it if (!UkwCancelTransfer(device, &overlapped, 0)) { printf("Attempt to cancel timed out transfer failed with %d\n", GetLastError()); goto out; } printf("Cancelled transfer due to timeout\n"); if (!waitForOverlapped(overlapped)) { printf("Timeout out waiting for cancel to complete\n"); goto out; } printf("Transfer cancel completed with: Internal: %d InternalHigh: %d Offset: %d OffsetHigh %d\n", overlapped.Internal, overlapped.InternalHigh, overlapped.Offset, overlapped.OffsetHigh); goto out; } // Check for the overlapped members being as expected if (overlapped.Internal != 0 || overlapped.InternalHigh != 0 || overlapped.Offset != 0 || overlapped.OffsetHigh != 0) { printf("Overlapped not as expected. Internal: %d InternalHigh: %d Offset: %d OffsetHigh %d\n", overlapped.Internal, overlapped.InternalHigh, overlapped.Offset, overlapped.OffsetHigh); goto out; } } if (transferred != 0) { printf("Transferred data length not updated, was %d\n", transferred); } out: if (async) CloseHandle(overlapped.hEvent); }