/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** \file
This file consists of implementation of class AndroidUsbDeviceObject that
encapsulates an extension for KMDF device (FDO) object.
*/
#pragma data_seg()
#pragma code_seg()
#include "precomp.h"
#include "android_usb_device_object.h"
#include "android_usb_file_object.h"
#include "android_usb_device_file_object.h"
#include "android_usb_pipe_file_object.h"
#include "android_usb_bulk_file_object.h"
#include "android_usb_interrupt_file_object.h"
#pragma data_seg()
/// Buffer for bulk read pipe name
const WCHAR bulk_read_pipe_str[] = L"\\" DEVICE_BULK_READ_PIPE_NAME;
/// Unicode string for bulk read pipe name
UNICODE_STRING bulk_read_pipe_name = {
sizeof(bulk_read_pipe_str) - sizeof(WCHAR),
sizeof(bulk_read_pipe_str) - sizeof(WCHAR),
const_cast<PWSTR>(bulk_read_pipe_str)
};
/// Buffer for bulk write pipe name
const WCHAR bulk_write_pipe_str[] = L"\\" DEVICE_BULK_WRITE_PIPE_NAME;
/// Unicode string for bulk write pipe name
UNICODE_STRING bulk_write_pipe_name = {
sizeof(bulk_write_pipe_str) - sizeof(WCHAR),
sizeof(bulk_write_pipe_str) - sizeof(WCHAR),
const_cast<PWSTR>(bulk_write_pipe_str)
};
/// Buffer for an index-based pipe name prefix
const WCHAR index_pipe_prefix_str[] = L"\\" DEVICE_PIPE_NAME_PREFIX;
/// Unicode string for index-based pipe name prefix
UNICODE_STRING index_pipe_prefix = {
sizeof(index_pipe_prefix_str) - sizeof(WCHAR),
sizeof(index_pipe_prefix_str) - sizeof(WCHAR),
const_cast<PWSTR>(index_pipe_prefix_str)
};
/// GUID that sets class ID for our device
const GUID android_guid = ANDROID_USB_CLASS_ID;
#pragma code_seg("PAGE")
AndroidUsbDeviceObject::AndroidUsbDeviceObject()
: AndroidUsbWdfObject(AndroidUsbWdfObjectTypeDevice),
wdf_target_device_(NULL),
wdf_usb_interface_(NULL),
serial_number_handle_(NULL),
serial_number_char_len_(0),
configured_pipes_num_(0),
bulk_read_pipe_index_(INVALID_UCHAR),
bulk_write_pipe_index_(INVALID_UCHAR),
configuration_descriptor_(NULL) {
ASSERT_IRQL_PASSIVE();
}
#pragma code_seg()
AndroidUsbDeviceObject::~AndroidUsbDeviceObject() {
ASSERT_IRQL_LOW_OR_DISPATCH();
if (NULL != serial_number_handle_)
WdfObjectDelete(serial_number_handle_);
}
#pragma code_seg("PAGE")
NTSTATUS AndroidUsbDeviceObject::CreateFDODevice(PWDFDEVICE_INIT device_init) {
ASSERT_IRQL_PASSIVE();
ASSERT(!IsTaretDeviceCreated());
if (IsTaretDeviceCreated())
return STATUS_INTERNAL_ERROR;
// Initialize our object attributes first
WDF_OBJECT_ATTRIBUTES device_attr;
NTSTATUS status = InitObjectAttributes(&device_attr, NULL);
ASSERT(NT_SUCCESS(status));
if (!NT_SUCCESS(status))
return status;
// Initialize the pnp_power_callbacks structure. Callback events for PnP
// and Power are specified here. If we don't supply any callbacks, the
// KMDF will take appropriate default actions for an FDO device object.
// EvtDevicePrepareHardware and EvtDeviceReleaseHardware are major entry
// points for initializing / cleaning up our device. Probably, we can leave
// the rest to the framework.
WDF_PNPPOWER_EVENT_CALLBACKS pnp_power_callbacks;
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnp_power_callbacks);
pnp_power_callbacks.EvtDevicePrepareHardware =
EvtDevicePrepareHardwareEntry;
pnp_power_callbacks.EvtDeviceReleaseHardware =
EvtDeviceReleaseHardwareEntry;
WdfDeviceInitSetPnpPowerEventCallbacks(device_init, &pnp_power_callbacks);
// Initialize the request attributes to specify the context size and type
// for every request created by framework for this device.
WDF_OBJECT_ATTRIBUTES request_attr;
WDF_OBJECT_ATTRIBUTES_INIT(&request_attr);
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&request_attr, AndroidUsbWdfRequestContext);
WdfDeviceInitSetRequestAttributes(device_init, &request_attr);
// Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the KMDF that we are
// interested in handling Create requests that get genereated when an
// application or another kernel component opens a handle through the device.
// We are not interested in receiving cleanup / close IRPs at this point.
WDF_FILEOBJECT_CONFIG file_config;
WDF_OBJECT_ATTRIBUTES file_attr;
WDF_FILEOBJECT_CONFIG_INIT(&file_config,
EvtDeviceFileCreateEntry,
WDF_NO_EVENT_CALLBACK,
WDF_NO_EVENT_CALLBACK);
WDF_OBJECT_ATTRIBUTES_INIT(&file_attr);
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&file_attr,
AndroidUsbWdfObjectContext);
file_attr.EvtCleanupCallback = AndroidUsbWdfObject::EvtCleanupCallbackEntry;
file_attr.EvtDestroyCallback = AndroidUsbWdfObject::EvtDestroyCallbackEntry;
// We will provide our own synchronization for file access
file_attr.SynchronizationScope = WdfSynchronizationScopeNone;
WdfDeviceInitSetFileObjectConfig(device_init, &file_config, &file_attr);
// I/O type is buffered by default. It could be very inefficient if we have
// large reads / writes through our device.
WdfDeviceInitSetIoType(device_init, WdfDeviceIoDirect);
// DeviceInit is completely initialized. So call the framework
// to create the device and attach it to the lower stack.
WDFDEVICE wdf_dev = NULL;
status = WdfDeviceCreate(&device_init, &device_attr, &wdf_dev);
ASSERT(NT_SUCCESS(status) && (NULL != wdf_dev));
if (!NT_SUCCESS(status))
return status;
// Save handle to the created device
set_wdf_object(wdf_dev);
// Tell the framework to set the SurpriseRemovalOK in the DeviceCaps so
// that we don't get the popup in usermode (on Win2K) when we surprise
// remove the device.
WDF_DEVICE_PNP_CAPABILITIES pnp_caps;
WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnp_caps);
pnp_caps.SurpriseRemovalOK = WdfTrue;
WdfDeviceSetPnpCapabilities(wdf_device(), &pnp_caps);
// Create our default queue object for this device to start receiving I/O
status = CreateDefaultQueue();
ASSERT(NT_SUCCESS(status));
if (!NT_SUCCESS(status))
return status;
// Register a device interface so that app can find our device and talk to it.
status = WdfDeviceCreateDeviceInterface(wdf_device(), &android_guid, NULL);
ASSERT(NT_SUCCESS(status));
if (!NT_SUCCESS(status))
return status;
// Initialize our extension to that device. We will do this at the very end
// so we know that we successfully passed entire device create chain when
// we are called with other callbacks to that device.
status = InitializeContext();
ASSERT(NT_SUCCESS(status));
if (!NT_SUCCESS(status))
return status;
return STATUS_SUCCESS;
}
NTSTATUS AndroidUsbDeviceObject::ResetDevice() {
ASSERT_IRQL_PASSIVE();
if (!IsTaretDeviceCreated())
return STATUS_SUCCESS;
// Reset the device
NTSTATUS status =
status = WdfUsbTargetDeviceResetPortSynchronously(wdf_target_device());
// !!!!! Note that after the call to WdfUsbTargetDeviceResetPortSynchronously
// this object may be no longer valid !!!!!
if (!NT_SUCCESS(status))
GoogleDbgPrint("\n!!!!! AndroidUsbDeviceObject::ResetDevice failed %X", status);
return status;
}
NTSTATUS AndroidUsbDeviceObject::OnEvtDevicePrepareHardware(
WDFCMRESLIST resources_raw,
WDFCMRESLIST resources_translated) {
ASSERT_IRQL_PASSIVE();
// Create a USB device handle so that we can communicate with the underlying
// USB stack. The wdf_target_device_ handle is used to query, configure, and
// manage all aspects of the USB device. These aspects include device
// properties, bus properties, and I/O creation and synchronization. This
// call gets the device and configuration descriptors and stores them in
// wdf_target_device_ object.
NTSTATUS status = WdfUsbTargetDeviceCreate(wdf_device(),
WDF_NO_OBJECT_ATTRIBUTES,
&wdf_target_device_);
ASSERT(NT_SUCCESS(status) && (NULL != wdf_target_device_));
if (!NT_SUCCESS(status))
return status;
// Retrieve USBD version information, port driver capabilites and device
// capabilites such as speed, power, etc.
WDF_USB_DEVICE_INFORMATION_INIT(&usb_device_info_);
status = WdfUsbTargetDeviceRetrieveInformation(wdf_target_device(),
&usb_device_info_);
ASSERT(NT_SUCCESS(status));
if (!NT_SUCCESS(status))
return status;
WdfUsbTargetDeviceGetDeviceDescriptor(wdf_target_device(),
&usb_device_descriptor_);
#if DBG
PrintUsbTargedDeviceInformation(usb_device_info());
PrintUsbDeviceDescriptor(&usb_device_descriptor_);
#endif // DBG
// Save device serial number
status =
WdfUsbTargetDeviceAllocAndQueryString(wdf_target_device(),
WDF_NO_OBJECT_ATTRIBUTES,
&serial_number_handle_,
&serial_number_char_len_,
usb_device_descriptor_.iSerialNumber,
0x0409); // English (US)
if (!NT_SUCCESS(status))
return status;
#if DBG
UNICODE_STRING ser_num;
ser_num.Length = serial_number_byte_len();
ser_num.MaximumLength = ser_num.Length;
ser_num.Buffer = const_cast<WCHAR*>
(serial_number());
GoogleDbgPrint("\n*** Device serial number %wZ", &ser_num);
#endif // DBG
// Configure our device now
status = ConfigureDevice();
ASSERT(NT_SUCCESS(status));
if (!NT_SUCCESS(status))
return status;
// Select device interfaces
status = SelectInterfaces();
if (!NT_SUCCESS(status))
return status;
return status;
}
NTSTATUS AndroidUsbDeviceObject::OnEvtDeviceReleaseHardware(
WDFCMRESLIST resources_translated) {
ASSERT_IRQL_PASSIVE();
// It's possible that Preparehardware failed half way thru. So make
// sure the target device exists.
if (!IsTaretDeviceCreated())
return STATUS_SUCCESS;
// Cancel all the currently queued I/O. This is better than sending an
// explicit USB abort request down because release hardware gets
// called even when the device surprise-removed.
WdfIoTargetStop(WdfUsbTargetDeviceGetIoTarget(wdf_target_device()),
WdfIoTargetCancelSentIo);
// Unselect all selected configurations
WDF_USB_DEVICE_SELECT_CONFIG_PARAMS config_params;
WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_DECONFIG(&config_params);
NTSTATUS status = WdfUsbTargetDeviceSelectConfig(wdf_target_device(),
WDF_NO_OBJECT_ATTRIBUTES,
&config_params);
ASSERT(NT_SUCCESS(status) || (STATUS_DEVICE_NOT_CONNECTED == status));
return status;
}
void AndroidUsbDeviceObject::OnEvtDeviceFileCreate(WDFREQUEST request,
WDFFILEOBJECT wdf_fo) {
ASSERT_IRQL_PASSIVE();
ASSERT(IsInterfaceSelected());
if (!IsInterfaceSelected()) {
WdfRequestComplete(request, STATUS_INVALID_DEVICE_STATE);
return;
}
PUNICODE_STRING file_name = WdfFileObjectGetFileName(wdf_fo);
ASSERT(NULL != file_name);
if (NULL == file_name) {
WdfRequestComplete(request, STATUS_OBJECT_NAME_INVALID);
return;
}
WDFUSBPIPE wdf_pipe_obj = NULL;
WDF_USB_PIPE_INFORMATION pipe_info;
// TODO: Share / access check here?
// Lets see if this is a device open
if (0 != file_name->Length) {
// This is a pipe open. Lets retrieve pipe index from the name
UCHAR pipe_index = GetPipeIndexFromFileName(file_name);
if (INVALID_UCHAR == pipe_index) {
GoogleDbgPrint("\n!!!!! There is no pipe index for file %wZ", file_name);
WdfRequestComplete(request, STATUS_OBJECT_NAME_INVALID);
return;
}
// Make sure that pipe index doesn't exceed number of pipes
if (pipe_index >= configured_pipes_num()) {
WdfRequestComplete(request, STATUS_OBJECT_NAME_NOT_FOUND);
return;
}
// Retrieve the pipe along with the pipe info
WDF_USB_PIPE_INFORMATION_INIT(&pipe_info);
wdf_pipe_obj = WdfUsbInterfaceGetConfiguredPipe(wdf_usb_interface(),
pipe_index,
&pipe_info);
if (NULL == wdf_pipe_obj) {
GoogleDbgPrint("\n!!!!! There is no pipe for index %u for file %wZ",
pipe_index, file_name);
WdfRequestComplete(request, STATUS_OBJECT_NAME_NOT_FOUND);
return;
}
}
// If we're here this must be either device open or pipe open
ASSERT((NULL != wdf_pipe_obj) || (0 == file_name->Length));
// Create our file object extension for this file
AndroidUsbFileObject* wdf_file_ext = NULL;
NTSTATUS status;
if (0 == file_name->Length) {
// This is a device FO. Create wrapper for device FO
ASSERT(NULL == wdf_pipe_obj);
wdf_file_ext = new(NonPagedPool, GANDR_POOL_TAG_DEVICE_FO)
AndroidUsbDeviceFileObject(this, wdf_fo);
ASSERT(NULL != wdf_file_ext);
if (NULL == wdf_file_ext) {
WdfRequestComplete(request, STATUS_INSUFFICIENT_RESOURCES);
return;
}
// Initialize extension
status = wdf_file_ext->Initialize();
if (!NT_SUCCESS(status)) {
delete wdf_file_ext;
WdfRequestComplete(request, status);
return;
}
} else {
// This is a pipe file. Create and initialize appropriate extension for it.
status =
CreatePipeFileObjectExt(wdf_fo, wdf_pipe_obj, &pipe_info, &wdf_file_ext);
ASSERT((NULL != wdf_file_ext) || !NT_SUCCESS(status));
if (!NT_SUCCESS(status)) {
WdfRequestComplete(request, status);
return;
}
}
ASSERT(GetAndroidUsbFileObjectFromHandle(wdf_fo) == wdf_file_ext);
WdfRequestComplete(request, STATUS_SUCCESS);
}
NTSTATUS AndroidUsbDeviceObject::EvtDevicePrepareHardwareEntry(
WDFDEVICE wdf_dev,
WDFCMRESLIST resources_raw,
WDFCMRESLIST resources_translated) {
ASSERT_IRQL_PASSIVE();
// Get our wrapper for the device and redirect event to its handler
AndroidUsbDeviceObject* wdf_device_ext =
GetAndroidUsbDeviceObjectFromHandle(wdf_dev);
ASSERT(NULL != wdf_device_ext);
return (NULL != wdf_device_ext) ?
wdf_device_ext->OnEvtDevicePrepareHardware(resources_raw,
resources_translated) :
STATUS_INVALID_DEVICE_REQUEST;
}
NTSTATUS AndroidUsbDeviceObject::EvtDeviceReleaseHardwareEntry(
WDFDEVICE wdf_dev,
WDFCMRESLIST resources_translated) {
ASSERT_IRQL_PASSIVE();
// Get our wrapper for the device and redirect event to its handler
AndroidUsbDeviceObject* wdf_device_ext =
GetAndroidUsbDeviceObjectFromHandle(wdf_dev);
ASSERT(NULL != wdf_device_ext);
return (NULL != wdf_device_ext) ?
wdf_device_ext->OnEvtDeviceReleaseHardware(resources_translated) :
STATUS_INVALID_DEVICE_REQUEST;
}
void AndroidUsbDeviceObject::EvtDeviceFileCreateEntry(
WDFDEVICE wdf_dev,
WDFREQUEST request,
WDFFILEOBJECT wdf_fo) {
ASSERT_IRQL_PASSIVE();
ASSERT(NULL != wdf_fo);
if (NULL == wdf_fo) {
WdfRequestComplete(request, STATUS_INVALID_PARAMETER);
return;
}
// Get our wrapper for the device and redirect event to its handler
AndroidUsbDeviceObject* wdf_device_ext =
GetAndroidUsbDeviceObjectFromHandle(wdf_dev);
ASSERT(NULL != wdf_device_ext);
if (NULL != wdf_device_ext) {
wdf_device_ext->OnEvtDeviceFileCreate(request, wdf_fo);
} else {
WdfRequestComplete(request, STATUS_INVALID_DEVICE_REQUEST);
}
}
#pragma code_seg()
void AndroidUsbDeviceObject::OnEvtIoRead(WDFREQUEST request,
size_t length) {
ASSERT_IRQL_LOW_OR_DISPATCH();
ASSERT(IsInterfaceSelected());
if (!IsInterfaceSelected()) {
WdfRequestComplete(request, STATUS_INVALID_DEVICE_STATE);
return;
}
// Get our file extension and dispatch this event to its handler
AndroidUsbFileObject* wdf_file_ext =
GetAndroidUsbFileObjectForRequest(request);
ASSERT(NULL != wdf_file_ext);
if (NULL != wdf_file_ext) {
wdf_file_ext->OnEvtIoRead(request, length);
} else {
WdfRequestComplete(request, STATUS_INVALID_DEVICE_REQUEST);
}
}
void AndroidUsbDeviceObject::OnEvtIoWrite(WDFREQUEST request,
size_t length) {
ASSERT_IRQL_LOW_OR_DISPATCH();
ASSERT(IsInterfaceSelected());
if (!IsInterfaceSelected()) {
WdfRequestComplete(request, STATUS_INVALID_DEVICE_STATE);
return;
}
// Get our file extension and dispatch this event to its handler
AndroidUsbFileObject* wdf_file_ext =
GetAndroidUsbFileObjectForRequest(request);
ASSERT(NULL != wdf_file_ext);
if (NULL != wdf_file_ext) {
wdf_file_ext->OnEvtIoWrite(request, length);
} else {
WdfRequestComplete(request, STATUS_INVALID_DEVICE_REQUEST);
}
}
void AndroidUsbDeviceObject::OnEvtIoDeviceControl(WDFREQUEST request,
size_t output_buf_len,
size_t input_buf_len,
ULONG ioctl_code) {
ASSERT_IRQL_LOW_OR_DISPATCH();
ASSERT(IsInterfaceSelected());
if (!IsInterfaceSelected()) {
WdfRequestComplete(request, STATUS_INVALID_DEVICE_STATE);
return;
}
// Get our file extension and dispatch this event to its handler
AndroidUsbFileObject* wdf_file_ext =
GetAndroidUsbFileObjectForRequest(request);
ASSERT(NULL != wdf_file_ext);
if (NULL != wdf_file_ext) {
wdf_file_ext->OnEvtIoDeviceControl(request,
output_buf_len,
input_buf_len,
ioctl_code);
} else {
WdfRequestComplete(request, STATUS_INVALID_DEVICE_REQUEST);
}
}
void AndroidUsbDeviceObject::EvtIoReadEntry(WDFQUEUE queue,
WDFREQUEST request,
size_t length) {
ASSERT_IRQL_LOW_OR_DISPATCH();
// Get our file extension and dispatch this event to the appropriate handler
// inside our device extension.
AndroidUsbFileObject* wdf_file_ext =
GetAndroidUsbFileObjectForRequest(request);
ASSERT(NULL != wdf_file_ext);
if (NULL != wdf_file_ext) {
wdf_file_ext->device_object()->OnEvtIoRead(request, length);
} else {
WdfRequestComplete(request, STATUS_INVALID_DEVICE_REQUEST);
}
}
void AndroidUsbDeviceObject::EvtIoWriteEntry(WDFQUEUE queue,
WDFREQUEST request,
size_t length) {
ASSERT_IRQL_LOW_OR_DISPATCH();
// Get our file extension and dispatch this event to the appropriate handler
// inside our device extension.
AndroidUsbFileObject* wdf_file_ext =
GetAndroidUsbFileObjectForRequest(request);
ASSERT(NULL != wdf_file_ext);
if (NULL != wdf_file_ext) {
wdf_file_ext->device_object()->OnEvtIoWrite(request, length);
} else {
WdfRequestComplete(request, STATUS_INVALID_DEVICE_REQUEST);
}
}
void AndroidUsbDeviceObject::EvtIoDeviceControlEntry(WDFQUEUE queue,
WDFREQUEST request,
size_t output_buf_len,
size_t input_buf_len,
ULONG ioctl_code) {
ASSERT_IRQL_LOW_OR_DISPATCH();
// Get our file extension and dispatch this event to the appropriate handler
// inside our device extension.
AndroidUsbFileObject* wdf_file_ext =
GetAndroidUsbFileObjectForRequest(request);
ASSERT(NULL != wdf_file_ext);
if (NULL != wdf_file_ext) {
wdf_file_ext->device_object()->OnEvtIoDeviceControl(request,
output_buf_len,
input_buf_len,
ioctl_code);
} else {
WdfRequestComplete(request, STATUS_INVALID_DEVICE_REQUEST);
}
}
void AndroidUsbDeviceObject::OnGetUsbDeviceDescriptorCtl(WDFREQUEST request,
size_t output_buf_len) {
ASSERT_IRQL_LOW_OR_DISPATCH();
// Check the buffer first
if (output_buf_len >= sizeof(USB_DEVICE_DESCRIPTOR)) {
// Get the output buffer
NTSTATUS status;
void* ret_info = OutAddress(request, &status);
ASSERT(NT_SUCCESS(status) && (NULL != ret_info));
if (NT_SUCCESS(status)) {
// Copy requested info into output buffer and complete request
RtlCopyMemory(ret_info,
usb_device_descriptor(),
sizeof(USB_DEVICE_DESCRIPTOR));
WdfRequestCompleteWithInformation(request,
STATUS_SUCCESS,
sizeof(USB_DEVICE_DESCRIPTOR));
} else {
WdfRequestComplete(request, status);
}
} else {
WdfRequestCompleteWithInformation(request,
STATUS_BUFFER_TOO_SMALL,
sizeof(USB_DEVICE_DESCRIPTOR));
}
}
void AndroidUsbDeviceObject::OnGetUsbConfigDescriptorCtl(WDFREQUEST request,
size_t output_buf_len) {
ASSERT_IRQL_LOW_OR_DISPATCH();
if (NULL != configuration_descriptor()) {
// Check the buffer first
if (output_buf_len >= sizeof(USB_CONFIGURATION_DESCRIPTOR)) {
// Get the output buffer
NTSTATUS status;
void* ret_info = OutAddress(request, &status);
ASSERT(NT_SUCCESS(status) && (NULL != ret_info));
if (NT_SUCCESS(status)) {
// Copy requested info into output buffer and complete request
RtlCopyMemory(ret_info,
configuration_descriptor(),
sizeof(USB_CONFIGURATION_DESCRIPTOR));
WdfRequestCompleteWithInformation(request,
STATUS_SUCCESS,
sizeof(USB_CONFIGURATION_DESCRIPTOR));
} else {
WdfRequestComplete(request, status);
}
} else {
WdfRequestCompleteWithInformation(request,
STATUS_BUFFER_TOO_SMALL,
sizeof(USB_CONFIGURATION_DESCRIPTOR));
}
} else {
WdfRequestComplete(request, STATUS_INVALID_DEVICE_REQUEST);
}
}
void AndroidUsbDeviceObject::OnGetUsbInterfaceDescriptorCtl(WDFREQUEST request,
size_t output_buf_len) {
ASSERT_IRQL_LOW_OR_DISPATCH();
// Check the buffer first
if (output_buf_len >= sizeof(USB_INTERFACE_DESCRIPTOR)) {
// Get the output buffer
NTSTATUS status;
void* ret_info = OutAddress(request, &status);
ASSERT(NT_SUCCESS(status) && (NULL != ret_info));
if (NT_SUCCESS(status)) {
// Copy requested info into output buffer and complete request
RtlCopyMemory(ret_info,
interface_descriptor(),
sizeof(USB_INTERFACE_DESCRIPTOR));
WdfRequestCompleteWithInformation(request,
STATUS_SUCCESS,
sizeof(USB_INTERFACE_DESCRIPTOR));
} else {
WdfRequestComplete(request, status);
}
} else {
WdfRequestCompleteWithInformation(request,
STATUS_BUFFER_TOO_SMALL,
sizeof(USB_INTERFACE_DESCRIPTOR));
}
}
void AndroidUsbDeviceObject::OnGetEndpointInformationCtl(
WDFREQUEST request,
size_t input_buf_len,
size_t output_buf_len) {
ASSERT_IRQL_LOW_OR_DISPATCH();
// Check the buffers first
if (input_buf_len < sizeof(AdbQueryEndpointInformation)) {
WdfRequestComplete(request, STATUS_INVALID_BUFFER_SIZE);
return;
}
if (output_buf_len < sizeof(AdbEndpointInformation)) {
WdfRequestCompleteWithInformation(request,
STATUS_BUFFER_TOO_SMALL,
sizeof(AdbEndpointInformation));
return;
}
// Get the output buffer
NTSTATUS status;
AdbEndpointInformation* ret_info = reinterpret_cast<AdbEndpointInformation*>
(OutAddress(request, &status));
ASSERT(NT_SUCCESS(status) && (NULL != ret_info));
if (!NT_SUCCESS(status)) {
WdfRequestComplete(request, status);
return;
}
// Get the input buffer
AdbQueryEndpointInformation* in = reinterpret_cast<AdbQueryEndpointInformation*>
(InAddress(request, &status));
ASSERT(NT_SUCCESS(status) && (NULL != in));
if (!NT_SUCCESS(status)) {
WdfRequestComplete(request, status);
return;
}
// Lets see what exactly is queried
UCHAR endpoint_index = in->endpoint_index;
if (ADB_QUERY_BULK_WRITE_ENDPOINT_INDEX == endpoint_index)
endpoint_index = bulk_write_pipe_index();
else if (ADB_QUERY_BULK_READ_ENDPOINT_INDEX == endpoint_index)
endpoint_index = bulk_read_pipe_index();
// Make sure index is valid and within interface range
if ((INVALID_UCHAR == endpoint_index) ||
(endpoint_index >= configured_pipes_num())) {
WdfRequestComplete(request, STATUS_NOT_FOUND);
return;
}
// Get endpoint information
WDF_USB_PIPE_INFORMATION pipe_info;
WDF_USB_PIPE_INFORMATION_INIT(&pipe_info);
WDFUSBPIPE wdf_pipe_obj =
WdfUsbInterfaceGetConfiguredPipe(wdf_usb_interface(), endpoint_index, &pipe_info);
if (NULL == wdf_pipe_obj) {
WdfRequestComplete(request, STATUS_NOT_FOUND);
return;
}
// Copy endpoint info to the output
ret_info->max_packet_size = pipe_info.MaximumPacketSize;
ret_info->endpoint_address = pipe_info.EndpointAddress;
ret_info->polling_interval = pipe_info.Interval;
ret_info->setting_index = pipe_info.SettingIndex;
ret_info->endpoint_type = static_cast<AdbEndpointType>(pipe_info.PipeType);
ret_info->max_transfer_size = pipe_info.MaximumTransferSize;
WdfRequestCompleteWithInformation(request,
STATUS_SUCCESS,
sizeof(AdbEndpointInformation));
}
void AndroidUsbDeviceObject::OnGetSerialNumberCtl(WDFREQUEST request,
size_t output_buf_len) {
ASSERT_IRQL_LOW();
if (NULL == serial_number()) {
// There is no serial number saved for this device!
WdfRequestComplete(request, STATUS_INTERNAL_ERROR);
return;
}
size_t expected_len = serial_number_byte_len() + sizeof(WCHAR);
// Check the buffer first
if (output_buf_len >= expected_len) {
// Get the output buffer
NTSTATUS status;
WCHAR* ret_info = reinterpret_cast<WCHAR*>(OutAddress(request, &status));
ASSERT(NT_SUCCESS(status) && (NULL != ret_info));
if (NT_SUCCESS(status)) {
// Copy serial number
RtlCopyMemory(ret_info, serial_number(), serial_number_byte_len());
ret_info[serial_number_char_len()] = L'\0';
WdfRequestCompleteWithInformation(request, STATUS_SUCCESS, expected_len);
} else {
WdfRequestComplete(request, status);
}
} else {
WdfRequestCompleteWithInformation(request,
STATUS_BUFFER_TOO_SMALL,
sizeof(expected_len));
}
}
#pragma code_seg("PAGE")
NTSTATUS AndroidUsbDeviceObject::CreateDefaultQueue() {
ASSERT_IRQL_PASSIVE();
// Register I/O callbacks to tell the framework that we are interested
// in handling WdfRequestTypeRead, WdfRequestTypeWrite, and
// WdfRequestTypeDeviceControl requests. WdfIoQueueDispatchParallel means
// that we are capable of handling all the I/O request simultaneously and we
// are responsible for protecting data that could be accessed by these
// callbacks simultaneously. This queue will be, by default, automanaged by
// the framework with respect to PnP and Power events. That is, framework
// will take care of queuing, failing, dispatching incoming requests based
// on the current PnP / Power state of the device. We also need to register
// a EvtIoStop handler so that we can acknowledge requests that are pending
// at the target driver.
WDF_IO_QUEUE_CONFIG io_queue_config;
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&io_queue_config,
WdfIoQueueDispatchParallel);
io_queue_config.EvtIoDeviceControl = EvtIoDeviceControlEntry;
io_queue_config.EvtIoRead = EvtIoReadEntry;
io_queue_config.EvtIoWrite = EvtIoWriteEntry;
io_queue_config.AllowZeroLengthRequests = TRUE;
// By default KMDF will take care of the power management of this queue
io_queue_config.PowerManaged = WdfUseDefault;
// Create queue object
WDFQUEUE wdf_queue_obj = NULL;
NTSTATUS status = WdfIoQueueCreate(wdf_device(),
&io_queue_config,
WDF_NO_OBJECT_ATTRIBUTES,
&wdf_queue_obj);
ASSERT(NT_SUCCESS(status) && (NULL != wdf_queue_obj));
if (!NT_SUCCESS(status))
return status;
return STATUS_SUCCESS;
}
NTSTATUS AndroidUsbDeviceObject::ConfigureDevice() {
ASSERT_IRQL_PASSIVE();
ASSERT(IsTaretDeviceCreated());
if (!IsTaretDeviceCreated())
return STATUS_INTERNAL_ERROR;
// In order to get the configuration descriptor we must first query for its
// size (by supplying NULL for the descriptor's address), allocate enough
// memory and then retrieve the descriptor.
USHORT size = 0;
// Query descriptor size first
NTSTATUS status =
WdfUsbTargetDeviceRetrieveConfigDescriptor(wdf_target_device(),
WDF_NO_HANDLE,
&size);
ASSERT((status == STATUS_BUFFER_TOO_SMALL) || !NT_SUCCESS(status));
if (status != STATUS_BUFFER_TOO_SMALL)
return status;
// Create a memory object and specify our device as the parent so that
// it will be freed automatically along with our device.
WDFMEMORY memory = NULL;
WDF_OBJECT_ATTRIBUTES attributes;
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = wdf_device();
status = WdfMemoryCreate(&attributes,
NonPagedPool,
GANDR_POOL_TAG_DEV_CFG_DESC,
size,
&memory,
reinterpret_cast<PVOID*>(&configuration_descriptor_));
ASSERT(NT_SUCCESS(status));
if (!NT_SUCCESS(status))
return status;
// Now retrieve configuration descriptor
status =
WdfUsbTargetDeviceRetrieveConfigDescriptor(wdf_target_device(),
configuration_descriptor_,
&size);
ASSERT(NT_SUCCESS(status) && (NULL != configuration_descriptor_));
if (!NT_SUCCESS(status))
return status;
#if DBG
PrintConfigDescriptor(configuration_descriptor(), size);
#endif // DBG
return status;
}
NTSTATUS AndroidUsbDeviceObject::SelectInterfaces() {
ASSERT_IRQL_PASSIVE();
ASSERT(IsDeviceConfigured());
if (!IsDeviceConfigured())
return STATUS_INTERNAL_ERROR;
WDF_USB_DEVICE_SELECT_CONFIG_PARAMS config_params;
PWDF_USB_INTERFACE_SETTING_PAIR pairs = NULL;
// TODO: We need to find a way (possibly by looking at each
// interface descriptor) to get index of the ADB interface in multiinterface
// configuration.
UCHAR adb_interface_index = 0;
if (IsSingleInterfaceDevice()) {
// Our device has only one interface, so we don't have to bother with
// multiple interfaces at all.
GoogleDbgPrint("\n********** Device reports single interface");
// Select single interface configuration
WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_SINGLE_INTERFACE(&config_params);
} else {
// Configure multiple interfaces
ULONG num_interf = GetInterfaceCount();
GoogleDbgPrint("\n********** Device reports %u interfaces",
num_interf);
// Allocate pairs for each interface
pairs = new(PagedPool, GANDR_POOL_TAG_INTERF_PAIRS)
WDF_USB_INTERFACE_SETTING_PAIR[num_interf];
ASSERT(NULL != pairs);
if (NULL == pairs)
return STATUS_INSUFFICIENT_RESOURCES;
adb_interface_index = 1;
// Initialize each interface pair
for (UCHAR pair = 0; pair < num_interf; pair++) {
pairs[pair].SettingIndex = 0;
pairs[pair].UsbInterface =
WdfUsbTargetDeviceGetInterface(wdf_target_device(), pair);
ASSERT(NULL != pairs[pair].UsbInterface);
if (NULL == pairs[pair].UsbInterface) {
delete[] pairs;
return STATUS_INTERNAL_ERROR;
}
}
// Select multiinterface configuration
WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_MULTIPLE_INTERFACES(&config_params,
(UCHAR)num_interf,
pairs);
}
NTSTATUS status =
WdfUsbTargetDeviceSelectConfig(wdf_target_device(),
WDF_NO_OBJECT_ATTRIBUTES,
&config_params);
if (NULL != pairs)
delete[] pairs;
// ASSERT(NT_SUCCESS(status));
if (!NT_SUCCESS(status))
return status;
#if DBG
PrintSelectedConfig(&config_params);
#endif // DBG
wdf_usb_interface_ =
WdfUsbTargetDeviceGetInterface(wdf_target_device(), adb_interface_index);
ASSERT(NULL != wdf_usb_interface_);
if (NULL == wdf_usb_interface_)
return STATUS_INTERNAL_ERROR;
configured_pipes_num_ = WdfUsbInterfaceGetNumEndpoints(wdf_usb_interface(), 0);
ASSERT(0 != configured_pipes_num_);
// Cache selected interface descriptor
BYTE setting_index =
WdfUsbInterfaceGetConfiguredSettingIndex(wdf_usb_interface());
WdfUsbInterfaceGetDescriptor(wdf_usb_interface(),
setting_index,
&interface_descriptor_);
#if DBG
PrintInterfaceDescriptor(interface_descriptor());
#endif // DBG
// Iterate over pipes, decoding and saving info about bulk r/w pipes for
// easier and faster addressing later on when they get opened
for (UCHAR pipe = 0; pipe < configured_pipes_num(); pipe++) {
WDF_USB_PIPE_INFORMATION pipe_info;
WDF_USB_PIPE_INFORMATION_INIT(&pipe_info);
WDFUSBPIPE wdf_pipe_obj =
WdfUsbInterfaceGetConfiguredPipe(wdf_usb_interface(), pipe, &pipe_info);
ASSERT(NULL != wdf_pipe_obj);
if (NULL != wdf_pipe_obj) {
if ((WdfUsbPipeTypeBulk == pipe_info.PipeType) &&
WDF_USB_PIPE_DIRECTION_IN(pipe_info.EndpointAddress)) {
// This is a bulk read pipe
ASSERT(!IsBulkReadPipeKnown());
bulk_read_pipe_index_ = pipe;
} else {
ASSERT(!IsBulkWritePipeKnown());
bulk_write_pipe_index_ = pipe;
}
}
#if DBG
PrintPipeInformation(&pipe_info, pipe);
#endif // DBG
}
// At the end we must have calculated indexes for both,
// bulk read and write pipes
ASSERT(!NT_SUCCESS(status) || (IsBulkReadPipeKnown() &&
IsBulkWritePipeKnown()));
return status;
}
UCHAR AndroidUsbDeviceObject::GetPipeIndexFromFileName(
PUNICODE_STRING file_path) {
ASSERT_IRQL_PASSIVE();
ASSERT((NULL != file_path) && (0 != file_path->Length) && (NULL != file_path->Buffer));
if ((NULL == file_path) ||
(0 == file_path->Length) ||
(NULL == file_path->Buffer)) {
return INVALID_UCHAR;
}
// Lets check for explicit r/w pipe names
if (0 == RtlCompareUnicodeString(file_path, &bulk_read_pipe_name, TRUE))
return bulk_read_pipe_index();
if (0 == RtlCompareUnicodeString(file_path, &bulk_write_pipe_name, TRUE))
return bulk_write_pipe_index();
// Lets check path format
if (file_path->Length <= index_pipe_prefix.Length) {
GoogleDbgPrint("\n!!!!! Bad format for pipe name: %wZ", file_path);
return INVALID_UCHAR;
}
// Now when whe know that file_path->Length is sufficient lets match this
// path with the prefix
UNICODE_STRING prefix_match = *file_path;
prefix_match.Length = index_pipe_prefix.Length;
prefix_match.MaximumLength = prefix_match.Length;
if (0 != RtlCompareUnicodeString(&prefix_match, &index_pipe_prefix, TRUE)) {
GoogleDbgPrint("\n!!!!! Bad format for pipe name: %wZ", file_path);
return INVALID_UCHAR;
}
// Prefix matches. Make sure that remaining chars are all decimal digits.
// Pipe index begins right after the prefix ends.
const ULONG index_begins_at = WcharLen(index_pipe_prefix.Length);
const ULONG name_len = WcharLen(file_path->Length);
for (ULONG index = index_begins_at; index < name_len; index++) {
if ((file_path->Buffer[index] > L'9') ||
(file_path->Buffer[index] < L'0')) {
GoogleDbgPrint("\n!!!!! Bad format for pipe name: %wZ", file_path);
return INVALID_UCHAR;
}
}
// Parse the pipe#
ULONG uval = 0;
ULONG umultiplier = 1;
// traversing least to most significant digits.
for (ULONG index = name_len - 1; index >= index_begins_at; index--) {
uval += (umultiplier * static_cast<ULONG>(file_path->Buffer[index] - L'0'));
umultiplier *= 10;
}
return static_cast<UCHAR>(uval);
}
NTSTATUS AndroidUsbDeviceObject::CreatePipeFileObjectExt(
WDFFILEOBJECT wdf_fo,
WDFUSBPIPE wdf_pipe_obj,
const WDF_USB_PIPE_INFORMATION* pipe_info,
AndroidUsbFileObject** wdf_file_ext) {
ASSERT_IRQL_PASSIVE();
ASSERT((NULL != wdf_fo) && (NULL != wdf_pipe_obj) && (NULL != pipe_info) && (NULL != wdf_file_ext));
if ((NULL == wdf_fo) || (NULL == wdf_pipe_obj) || (NULL == pipe_info) || (NULL == wdf_file_ext)) {
return STATUS_INTERNAL_ERROR;
}
*wdf_file_ext = NULL;
AndroidUsbPipeFileObject* wdf_pipe_file_ext = NULL;
// We support only WdfUsbPipeTypeBulk and WdfUsbPipeTypeInterrupt files
// at this point.
switch (pipe_info->PipeType) {
case WdfUsbPipeTypeBulk:
wdf_pipe_file_ext = new(NonPagedPool, GANDR_POOL_TAG_BULK_FILE)
AndroidUsbBulkPipeFileObject(this, wdf_fo, wdf_pipe_obj);
break;
case WdfUsbPipeTypeInterrupt:
wdf_pipe_file_ext = new(NonPagedPool, GANDR_POOL_TAG_INTERRUPT_FILE)
AndroidUsbInterruptPipeFileObject(this, wdf_fo, wdf_pipe_obj);
break;;
case WdfUsbPipeTypeIsochronous:
case WdfUsbPipeTypeControl:
case WdfUsbPipeTypeInvalid:
default:
return STATUS_OBJECT_TYPE_MISMATCH;
}
// If we reached here instance of a file wrapper must be created.
ASSERT(NULL != wdf_pipe_file_ext);
if (NULL == wdf_pipe_file_ext)
return STATUS_INSUFFICIENT_RESOURCES;
// Initialize the wrapper.
NTSTATUS status = wdf_pipe_file_ext->InitializePipe(pipe_info);
ASSERT(NT_SUCCESS(status));
if (NT_SUCCESS(status)) {
*wdf_file_ext = wdf_pipe_file_ext;
} else {
delete wdf_pipe_file_ext;
}
return STATUS_SUCCESS;
}
#if DBG
#pragma code_seg()
void AndroidUsbDeviceObject::PrintUsbDeviceDescriptor(
const USB_DEVICE_DESCRIPTOR* desc) {
GoogleDbgPrint("\n***** USB_DEVICE_DESCRIPTOR %p for device %p", desc, this);
GoogleDbgPrint("\n bDescriptorType = %u", desc->bDescriptorType);
GoogleDbgPrint("\n bcdUSB = x%02X", desc->bcdUSB);
GoogleDbgPrint("\n bDeviceClass = x%02X", desc->bDeviceClass);
GoogleDbgPrint("\n bDeviceSubClass = x%02X", desc->bDeviceSubClass);
GoogleDbgPrint("\n bDeviceProtocol = x%02X", desc->bDeviceProtocol);
GoogleDbgPrint("\n bMaxPacketSize = %u", desc->bMaxPacketSize0);
GoogleDbgPrint("\n idVendor = x%04X", desc->idVendor);
GoogleDbgPrint("\n idProduct = x%04X", desc->idProduct);
GoogleDbgPrint("\n bcdDevice = x%02X", desc->bcdDevice);
GoogleDbgPrint("\n iManufacturer = %u", desc->iManufacturer);
GoogleDbgPrint("\n iProduct = %u", desc->iProduct);
GoogleDbgPrint("\n iSerialNumber = %u", desc->iSerialNumber);
GoogleDbgPrint("\n bNumConfigurations = %u", desc->bNumConfigurations);
}
void AndroidUsbDeviceObject::PrintUsbTargedDeviceInformation(
const WDF_USB_DEVICE_INFORMATION* info) {
GoogleDbgPrint("\n***** WDF_USB_DEVICE_INFORMATION %p for device %p", info, this);
GoogleDbgPrint("\n HcdPortCapabilities = x%08X", info->HcdPortCapabilities);
GoogleDbgPrint("\n Traits = x%08X", info->Traits);
GoogleDbgPrint("\n VersionInfo.USBDI_Version = x%08X",
info->UsbdVersionInformation.USBDI_Version);
GoogleDbgPrint("\n VersionInfo.Supported_USB_Version = x%08X",
info->UsbdVersionInformation.Supported_USB_Version);
}
void AndroidUsbDeviceObject::PrintConfigDescriptor(
const USB_CONFIGURATION_DESCRIPTOR* desc,
ULONG size) {
GoogleDbgPrint("\n***** USB_CONFIGURATION_DESCRIPTOR %p for device %p size %u",
desc, this, size);
GoogleDbgPrint("\n bDescriptorType = %u", desc->bDescriptorType);
GoogleDbgPrint("\n wTotalLength = %u", desc->wTotalLength);
GoogleDbgPrint("\n bNumInterfaces = %u", desc->bNumInterfaces);
GoogleDbgPrint("\n bConfigurationValue = %u", desc->bConfigurationValue);
GoogleDbgPrint("\n iConfiguration = %u", desc->iConfiguration);
GoogleDbgPrint("\n bmAttributes = %u", desc->bmAttributes);
GoogleDbgPrint("\n MaxPower = %u", desc->MaxPower);
}
void AndroidUsbDeviceObject::PrintSelectedConfig(
const WDF_USB_DEVICE_SELECT_CONFIG_PARAMS* config) {
GoogleDbgPrint("\n***** WDF_USB_DEVICE_SELECT_CONFIG_PARAMS %p for device %p", config, this);
GoogleDbgPrint("\n Type = %u", config->Type);
switch (config->Type) {
case WdfUsbTargetDeviceSelectConfigTypeSingleInterface:
GoogleDbgPrint("\n SingleInterface:");
GoogleDbgPrint("\n NumberConfiguredPipes = %u",
config->Types.SingleInterface.NumberConfiguredPipes);
GoogleDbgPrint("\n ConfiguredUsbInterface = %p",
config->Types.SingleInterface.ConfiguredUsbInterface);
break;
case WdfUsbTargetDeviceSelectConfigTypeMultiInterface:
GoogleDbgPrint("\n MultiInterface:");
GoogleDbgPrint("\n NumberInterfaces = %u",
config->Types.MultiInterface.NumberInterfaces);
GoogleDbgPrint("\n NumberOfConfiguredInterfaces = %u",
config->Types.MultiInterface.NumberOfConfiguredInterfaces);
GoogleDbgPrint("\n Pairs = %p",
config->Types.MultiInterface.Pairs);
break;
case WdfUsbTargetDeviceSelectConfigTypeInterfacesDescriptor:
GoogleDbgPrint("\n Descriptor:");
GoogleDbgPrint("\n NumInterfaceDescriptors = %u",
config->Types.Descriptor.NumInterfaceDescriptors);
GoogleDbgPrint("\n ConfigurationDescriptor = %p",
config->Types.Descriptor.ConfigurationDescriptor);
GoogleDbgPrint("\n InterfaceDescriptors = %p",
config->Types.Descriptor.InterfaceDescriptors);
break;
case WdfUsbTargetDeviceSelectConfigTypeUrb:
GoogleDbgPrint("\n Urb:");
GoogleDbgPrint("\n Urb = %p",
config->Types.Urb.Urb);
break;
case WdfUsbTargetDeviceSelectConfigTypeInterfacesPairs:
case WdfUsbTargetDeviceSelectConfigTypeInvalid:
case WdfUsbTargetDeviceSelectConfigTypeDeconfig:
default:
GoogleDbgPrint("\n Config type is unknown or invalid or not printable.");
break;
}
}
void AndroidUsbDeviceObject::PrintInterfaceDescriptor(
const USB_INTERFACE_DESCRIPTOR* desc) {
GoogleDbgPrint("\n***** USB_INTERFACE_DESCRIPTOR %p for device %p",
desc, this);
GoogleDbgPrint("\n bLength = %u", desc->bLength);
GoogleDbgPrint("\n bDescriptorType = %u", desc->bDescriptorType);
GoogleDbgPrint("\n bInterfaceNumber = %u", desc->bInterfaceNumber);
GoogleDbgPrint("\n bAlternateSetting = %u", desc->bAlternateSetting);
GoogleDbgPrint("\n bNumEndpoints = %u", desc->bNumEndpoints);
GoogleDbgPrint("\n bInterfaceClass = x%02X", desc->bInterfaceClass);
GoogleDbgPrint("\n bInterfaceSubClass = x%02X", desc->bInterfaceSubClass);
GoogleDbgPrint("\n bInterfaceProtocol = x%02X", desc->bInterfaceProtocol);
GoogleDbgPrint("\n iInterface = %u", desc->iInterface);
}
void AndroidUsbDeviceObject::PrintPipeInformation(
const WDF_USB_PIPE_INFORMATION* info,
UCHAR pipe_index) {
GoogleDbgPrint("\n***** WDF_USB_PIPE_INFORMATION[%u] %p for device %p",
pipe_index, info, this);
GoogleDbgPrint("\n Size = %u", info->Size);
GoogleDbgPrint("\n MaximumPacketSize = %u", info->MaximumPacketSize);
GoogleDbgPrint("\n EndpointAddress = x%02X", info->EndpointAddress);
GoogleDbgPrint("\n Interval = %u", info->Interval);
GoogleDbgPrint("\n SettingIndex = %u", info->SettingIndex);
GoogleDbgPrint("\n PipeType = %u", info->PipeType);
GoogleDbgPrint("\n MaximumTransferSize = %u", info->MaximumTransferSize);
}
#endif // DBG
#pragma data_seg()
#pragma code_seg()