2025-04-27 07:49:33 -04:00

790 lines
23 KiB
C

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
USB.C
Abstract:
This module contains code to execute USB
specific functions
Author:
jdunn
Environment:
kernel mode only
Notes:
Revision History:
05-13-98 : created
--*/
#include <wdm.h>
#include <initguid.h>
#include <wdmguid.h>
#include "stdarg.h"
#include "stdio.h"
#include "dbcusb.h"
#define UsbBuildVendorClassUrb(\
pUrb,\
pDeviceData,\
wFunction,\
ulTransferFlags,\
bRequestType,\
bRequest,\
wFeatureSelector,\
wPort,\
ulTransferBufferLength,\
pTransferBuffer)\
{\
(pUrb)->UrbHeader.Length = (USHORT) sizeof( struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);\
(pUrb)->UrbHeader.Function = wFunction;\
(pUrb)->UrbHeader.UsbdDeviceHandle = pDeviceData;\
(pUrb)->UrbControlVendorClassRequest.TransferFlags = ulTransferFlags;\
(pUrb)->UrbControlVendorClassRequest.TransferBufferLength = ulTransferBufferLength;\
(pUrb)->UrbControlVendorClassRequest.TransferBuffer = pTransferBuffer;\
(pUrb)->UrbControlVendorClassRequest.TransferBufferMDL = NULL;\
(pUrb)->UrbControlVendorClassRequest.RequestTypeReservedBits = bRequestType;\
(pUrb)->UrbControlVendorClassRequest.Request = bRequest;\
(pUrb)->UrbControlVendorClassRequest.Value = wFeatureSelector;\
(pUrb)->UrbControlVendorClassRequest.Index = wPort;\
(pUrb)->UrbControlVendorClassRequest.UrbLink = NULL;\
}
#ifdef ALLOC_PRAGMA
// pagable functions
#pragma alloc_text(PAGE, DBCUSB_SyncUsbCall)
#endif
NTSTATUS
DBCUSB_SyncUsbCall(
IN PDEVICE_OBJECT DeviceObject,
IN PURB Urb
)
/*++
Routine Description:
Called to send a request to the Pdo
Arguments:
Return Value:
NT Status of the operation
--*/
{
IO_STATUS_BLOCK ioStatus;
KEVENT event;
NTSTATUS status;
PIRP irp;
PIO_STACK_LOCATION nextStack;
PDEVICE_EXTENSION deviceExtension;
PAGED_CODE();
deviceExtension = DeviceObject->DeviceExtension;
//
// Initialize an event to wait on
//
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
//
// Build the request
//
irp = IoBuildDeviceIoControlRequest(
IOCTL_INTERNAL_USB_SUBMIT_URB,
deviceExtension->TopOfStackDeviceObject,
NULL,
0,
NULL,
0,
TRUE, /* INTERNAL */
&event,
&ioStatus);
if (!irp) {
DBCUSB_KdPrint ((0, "'failed to allocate Irp\n"));
TRAP();
return STATUS_INSUFFICIENT_RESOURCES;
}
nextStack = IoGetNextIrpStackLocation(irp);
ASSERT(nextStack != NULL);
//
// pass the URB to the USB driver stack
//
nextStack->Parameters.Others.Argument1 = Urb;
//
// Pass request to Pdo, always wait for completion routine
//
status = IoCallDriver(deviceExtension->TopOfStackDeviceObject, irp);
if (status == STATUS_PENDING) {
//
// Wait for the irp to be completed, then grab the real status code
//
KeWaitForSingleObject(
&event,
Executive,
KernelMode,
FALSE,
NULL
);
status = ioStatus.Status;
}
//
// Done
//
DBCUSB_KdPrint((2, "'DBCUSB_SyncUsbCall(%x)\n", status));
return status;
}
NTSTATUS
DBCUSB_ConfigureDevice(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Initializes a given instance of the device on the USB.
All we do here is get the device descriptor and store it
Arguments:
DeviceObject - pointer to the device object for this instance of a
USB DBC
Return Value:
NT status code
--*/
{
PDEVICE_EXTENSION deviceExtension;
PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor;
PUSBD_INTERFACE_INFORMATION interface;
NTSTATUS ntStatus = STATUS_SUCCESS;
PUSB_DEVICE_DESCRIPTOR deviceDescriptor = NULL;
PURB urb;
USHORT siz;
UCHAR interfaceNumber, alternateSetting;
deviceExtension = DeviceObject->DeviceExtension;
urb = ExAllocatePool(NonPagedPool,
sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
if (urb) {
deviceDescriptor = &deviceExtension->DeviceDescriptor;
UsbBuildGetDescriptorRequest(urb,
(USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
USB_DEVICE_DESCRIPTOR_TYPE,
0,
0,
deviceDescriptor,
NULL,
sizeof(*deviceDescriptor),
NULL);
ntStatus = DBCUSB_SyncUsbCall(DeviceObject, urb);
if (NT_SUCCESS(ntStatus)) {
DBCUSB_KdPrint ((2, "Device Descriptor = %x, len %x\n",
deviceDescriptor,
urb->UrbControlDescriptorRequest.TransferBufferLength));
DBCUSB_KdPrint ((2, "'DBCUSB Device Descriptor:\n"));
DBCUSB_KdPrint ((2, "'-------------------------\n"));
DBCUSB_KdPrint ((2, "'bLength %d\n", deviceDescriptor->bLength));
DBCUSB_KdPrint ((2, "'bDescriptorType 0x%x\n", deviceDescriptor->bDescriptorType));
DBCUSB_KdPrint ((2, "'bcdUSB 0x%x\n", deviceDescriptor->bcdUSB));
DBCUSB_KdPrint ((2, "'bDeviceClass 0x%x\n", deviceDescriptor->bDeviceClass));
DBCUSB_KdPrint ((2, "'bDeviceSubClass 0x%x\n", deviceDescriptor->bDeviceSubClass));
DBCUSB_KdPrint ((2, "'bDeviceProtocol 0x%x\n", deviceDescriptor->bDeviceProtocol));
DBCUSB_KdPrint ((2, "'bMaxPacketSize0 0x%x\n", deviceDescriptor->bMaxPacketSize0));
DBCUSB_KdPrint ((2, "'idVendor 0x%x\n", deviceDescriptor->idVendor));
DBCUSB_KdPrint ((2, "'idProduct 0x%x\n", deviceDescriptor->idProduct));
DBCUSB_KdPrint ((2, "'bcdDevice 0x%x\n", deviceDescriptor->bcdDevice));
DBCUSB_KdPrint ((2, "'iManufacturer 0x%x\n", deviceDescriptor->iManufacturer));
DBCUSB_KdPrint ((2, "'iProduct 0x%x\n", deviceDescriptor->iProduct));
DBCUSB_KdPrint ((2, "'iSerialNumber 0x%x\n", deviceDescriptor->iSerialNumber));
DBCUSB_KdPrint ((2, "'bNumConfigurations 0x%x\n", deviceDescriptor->bNumConfigurations));
}
ExFreePool(urb);
} else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
if (NT_SUCCESS(ntStatus)) {
PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = NULL;
urb = ExAllocatePool(NonPagedPool,
sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
if (urb) {
siz = sizeof(USB_CONFIGURATION_DESCRIPTOR)+256;
get_config_descriptor_retry:
configurationDescriptor = ExAllocatePool(NonPagedPool,
siz);
if (configurationDescriptor) {
UsbBuildGetDescriptorRequest(urb,
(USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
USB_CONFIGURATION_DESCRIPTOR_TYPE,
0,
0,
configurationDescriptor,
NULL,
siz,
NULL);
ntStatus = DBCUSB_SyncUsbCall(DeviceObject, urb);
DBCUSB_KdPrint((2, "'Configuration Descriptor = %x, len %x\n",
configurationDescriptor,
urb->UrbControlDescriptorRequest.TransferBufferLength));
} else {
// no mem for config descriptor
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
//
// if we got some data see if it was enough.
//
// NOTE: we may get an error in URB because of buffer overrun
if (urb->UrbControlDescriptorRequest.TransferBufferLength>0 &&
configurationDescriptor->wTotalLength > siz) {
siz = configurationDescriptor->wTotalLength;
ExFreePool(configurationDescriptor);
configurationDescriptor = NULL;
goto get_config_descriptor_retry;
}
ExFreePool(urb);
} else {
// no mem for urb
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
if (configurationDescriptor) {
DBCUSB_KdPrint((0, "'configurationDescriptor(%x)\n", configurationDescriptor));
//
// We have the configuration descriptor for the configuration
// we want.
//
// Now we issue the select configuration command to get
// the pipes associated with this configuration.
//
//
// DBCACPI driver only supports one interface
//
urb = USBD_CreateConfigurationRequest(configurationDescriptor, &siz);
if (urb) {
//
// search thru all the interfaces
// and find any we are interested in
//
interfaceNumber = 0;
alternateSetting = 0;
interfaceDescriptor =
USBD_ParseConfigurationDescriptor(configurationDescriptor,
interfaceNumber,
alternateSetting);
// dbc only has one interface
interface = &urb->UrbSelectConfiguration.Interface;
// dbc always has one pipe
DBCUSB_ASSERT(interface->NumberOfPipes == 1);
//
// perform any pipe initialization here
//
interface->Pipes[0].MaximumTransferSize = 1024;
UsbBuildSelectConfigurationRequest(urb,
(USHORT) siz,
configurationDescriptor);
ntStatus = DBCUSB_SyncUsbCall(DeviceObject, urb);
deviceExtension->ConfigurationHandle =
urb->UrbSelectConfiguration.ConfigurationHandle;
deviceExtension->InterruptPipeHandle =
interface->Pipes[0].PipeHandle;
DBCUSB_ASSERT(interface->Pipes[0].MaximumPacketSize < 255);
deviceExtension->InterruptDataBufferLength = (UCHAR)
interface->Pipes[0].MaximumPacketSize;
} else {
// no mem for urb
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
if (NT_SUCCESS(ntStatus)) {
PUSB_COMMON_DESCRIPTOR usbDescriptor;
// extract the class specific descriptors from the config descriptor
usbDescriptor = USBD_ParseDescriptors(
configurationDescriptor,
configurationDescriptor->wTotalLength,
configurationDescriptor,
DBC_SUSBSYSTEM_DESCRIPTOR_TYPE);
if (usbDescriptor && usbDescriptor->bLength ==
sizeof(deviceExtension->DbcSubsystemDescriptor)) {
PUCHAR p;
ULONG i;
RtlCopyMemory(&deviceExtension->DbcSubsystemDescriptor,
usbDescriptor,
sizeof(deviceExtension->DbcSubsystemDescriptor));
// copy the bay descriptors
p = (PUCHAR) usbDescriptor;
usbDescriptor =
(PUSB_COMMON_DESCRIPTOR) (p+usbDescriptor->bLength);
for (i=0;
i < deviceExtension->DbcSubsystemDescriptor.bmAttributes.BayCount;
i++) {
RtlCopyMemory(&deviceExtension->BayDescriptor[i],
usbDescriptor,
sizeof(DBC_BAY_DESCRIPTOR));
p = (PUCHAR) usbDescriptor;
usbDescriptor =
(PUSB_COMMON_DESCRIPTOR) (p+usbDescriptor->bLength);
}
} else {
ntStatus = STATUS_UNSUCCESSFUL;
}
}
ExFreePool(configurationDescriptor);
} else {
// no mem for config descriptor
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
DBCUSB_KdPrint((2, "'DBCUSB_ConfigureDevice(%x)\n", ntStatus));
return ntStatus;
}
NTSTATUS
DBCUSB_ChangeIndication(
IN PDEVICE_OBJECT PNull,
IN PIRP Irp,
IN PVOID Context)
/* ++
*
* Description:
*
* This is a call back when the interrupt transfer completes.
*
* Arguments:
*
* Return:
*
* NTSTATUS
*
* -- */
{
PURB urb; // the Urb assocaited with this Irp
PDEVICE_EXTENSION deviceExtension;
deviceExtension = (PDEVICE_EXTENSION) Context; // the context is
DBCUSB_DecrementIoCount(deviceExtension->FdoDeviceObject);
urb = (PURB) &deviceExtension->InterruptUrb;
deviceExtension->Flags &= ~DBCUSB_FLAG_INTERUPT_XFER_PENDING;
DBCUSB_KdPrint((1,"'ChangeIndication Irp status %x URB status = %x\n",
Irp->IoStatus.Status, urb->UrbHeader.Status));
MD_TEST_TRAP();
// complete the class drivers change request
DBCUSB_CompleteChangeRequest(deviceExtension->FdoDeviceObject,
urb->UrbBulkOrInterruptTransfer.TransferBuffer,
urb->UrbBulkOrInterruptTransfer.TransferBufferLength,
Irp->IoStatus.Status);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
DBCUSB_GetPortStatus(
IN PDEVICE_OBJECT DeviceObject,
IN PULONG PortStatus
)
/*++
Routine Description:
Passes a URB to the USBD class driver
Arguments:
DeviceExtension - pointer to the device extension for this instance of an
USB HID device
Urb - pointer to Urb request block
Return Value:
STATUS_SUCCESS if successful,
STATUS_UNSUCCESSFUL otherwise
--*/
{
NTSTATUS ntStatus;
PIRP irp;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
PIO_STACK_LOCATION nextStack;
PDEVICE_EXTENSION deviceExtension;
*PortStatus = 0;
deviceExtension = DeviceObject->DeviceExtension;
//
// issue a synchronous request
//
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(
IOCTL_INTERNAL_USB_GET_PORT_STATUS,
deviceExtension->TopOfStackDeviceObject,
NULL,
0,
NULL,
0,
TRUE, /* INTERNAL */
&event,
&ioStatus);
if(irp)
{
//
// Call the class driver to perform the operation. If the returned status
// is PENDING, wait for the request to complete.
//
nextStack = IoGetNextIrpStackLocation(irp);
ASSERT(nextStack != NULL);
nextStack->Parameters.Others.Argument1 = PortStatus;
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, irp);
if (ntStatus == STATUS_PENDING)
{
ntStatus = KeWaitForSingleObject(
&event,
Suspended,
KernelMode,
FALSE,
NULL);
}
else
{
ioStatus.Status = ntStatus;
}
//
// USBD maps the error code for us
//
ntStatus = ioStatus.Status;
}
else
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
DBCUSB_KdPrint((1, "'>>Port status = %x ntStatus %x \n", *PortStatus, ntStatus));
return ntStatus;
}
NTSTATUS
DBCUSB_SubmitInterruptTransfer(
IN PDEVICE_OBJECT DeviceObject
)
/* ++
*
* Description:
*
* PPu an interrupt transfer down to listen for a change indication
*
* Arguments:
*
* Return:
*
* NTSTATUS
*
* -- */
{
NTSTATUS ntStatus;
PIO_STACK_LOCATION nextStack; // next stack of the Irp
PIRP irp;
PURB urb;
CHAR stackSize;
PDEVICE_EXTENSION deviceExtension;
DBCUSB_KdPrint((0,"'Submit Interrupt Transfer\n"));
deviceExtension = DeviceObject->DeviceExtension;
DBCUSB_ASSERT(
(deviceExtension->Flags & DBCUSB_FLAG_INTERUPT_XFER_PENDING) == 0);
deviceExtension->Flags |= DBCUSB_FLAG_INTERUPT_XFER_PENDING;
irp = deviceExtension->InterruptIrp;
urb = (PURB) &deviceExtension->InterruptUrb;
DBCUSB_ASSERT(NULL != irp);
DBCUSB_ASSERT(NULL != urb);
DBCUSB_ASSERT(sizeof(*urb) >= sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
//
// Fill in Urb header
//
//LOGENTRY(LOG_PNP, "Int>", DeviceObject, urb, irp);
urb->UrbHeader.Length = (USHORT) sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
urb->UrbHeader.Function =
URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
//
// Fill in Urb body
//
urb->UrbBulkOrInterruptTransfer.PipeHandle = deviceExtension->InterruptPipeHandle;
urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_SHORT_TRANSFER_OK;
urb->UrbBulkOrInterruptTransfer.TransferBufferLength =
deviceExtension->InterruptDataBufferLength;
urb->UrbBulkOrInterruptTransfer.TransferBuffer =
&deviceExtension->InterruptDataBuffer[0];
urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;
urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
stackSize = deviceExtension->TopOfStackDeviceObject->StackSize;
IoInitializeIrp(irp,
(USHORT) (sizeof(IRP) + stackSize * sizeof(IO_STACK_LOCATION)),
(CCHAR) stackSize);
nextStack = IoGetNextIrpStackLocation(irp);
nextStack->Parameters.Others.Argument1 = urb;
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
IoSetCompletionRoutine(irp, // Irp
DBCUSB_ChangeIndication,
deviceExtension, // context
TRUE, // invoke on success
TRUE, // invoke on error
TRUE); // invoke on cancel
//
// Call the USB stack
//
DBCUSB_IncrementIoCount(DeviceObject);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, irp);
//
// completion routine will handle errors.
//
DBCUSB_KdPrint((2,"'DBCUSB_SubmitInterruptTransfer %x\n", ntStatus));
return ntStatus;
}
NTSTATUS
DBCUSB_Transact(
IN PDEVICE_OBJECT DeviceObject,
IN PUCHAR DataBuffer,
IN ULONG DataBufferLength,
IN BOOLEAN DataOutput,
IN USHORT Function,
IN UCHAR RequestType,
IN UCHAR Request,
IN USHORT Feature,
IN USHORT Port,
OUT PULONG BytesTransferred
)
/* ++
*
* Description:
*
* Arguments:
*
* Return:
*
* NTSTATUS
*
* -- */
{
NTSTATUS ntStatus;
PURB urb;
PUCHAR transferBuffer;
ULONG transferFlags;
PAGED_CODE();
DBCUSB_KdPrint((2,"'Enter DBCUSB_Transact\n"));
//
// Allocate a transaction buffer and Urb from the non-paged pool
//
transferBuffer = ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST) +
DataBufferLength);
if (transferBuffer) {
urb = (PURB) (transferBuffer + DataBufferLength);
DBCUSB_KdPrint((2,"'Transact transfer buffer = %x urb = %x\n",
transferBuffer, urb));
transferFlags = 0;
if (DataOutput) {
// copy output data to transfer buffer
if (DataBufferLength) {
RtlCopyMemory(transferBuffer,
DataBuffer,
DataBufferLength);
}
transferFlags = USBD_TRANSFER_DIRECTION_OUT;
} else {
// zero the input buffer
if (DataBufferLength) {
RtlZeroMemory(DataBuffer,
DataBufferLength);
}
transferFlags = USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK;
}
UsbBuildVendorClassUrb(urb,
NULL,
Function,
transferFlags,
RequestType,
Request,
Feature,
Port,
DataBufferLength,
DataBufferLength ? transferBuffer : NULL);
//
// pass the URB to the USBD 'class driver'
//
ntStatus = DBCUSB_SyncUsbCall(DeviceObject,
urb);
DataBufferLength =
urb->UrbControlVendorClassRequest.TransferBufferLength;
if (!DataOutput && DataBufferLength) {
RtlCopyMemory(DataBuffer,
transferBuffer,
DataBufferLength);
}
if (BytesTransferred) {
*BytesTransferred = DataBufferLength;
}
ExFreePool(transferBuffer);
} else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
//LOGENTRY(LOG_PNP, "Xact", DeviceExtensionHub, 0, ntStatus);
DBCUSB_KdPrint((2,"'Exit DBCUSB_Transact %x\n", ntStatus));
return ntStatus;
}