reactos/drivers/usb/usbccgp/fdo.c

721 lines
18 KiB
C
Raw Normal View History

/*
* PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface
* LICENSE: GPL - See COPYING in the top level directory
* FILE: drivers/usb/usbccgp/fdo.c
* PURPOSE: USB device driver.
* PROGRAMMERS:
* Michael Martin (michael.martin@reactos.org)
* Johannes Anderwald (johannes.anderwald@reactos.org)
* Cameron Gutman
*/
#include "usbccgp.h"
NTSTATUS
NTAPI
FDO_QueryCapabilitiesCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
//
// set event
//
KeSetEvent((PRKEVENT)Context, 0, FALSE);
//
// completion is done in the HidClassFDO_QueryCapabilities routine
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
FDO_QueryCapabilities(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PDEVICE_CAPABILITIES Capabilities)
{
PIRP Irp;
KEVENT Event;
NTSTATUS Status;
PIO_STACK_LOCATION IoStack;
PFDO_DEVICE_EXTENSION FDODeviceExtension;
//
// get device extension
//
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT(FDODeviceExtension->Common.IsFDO);
//
// init event
//
KeInitializeEvent(&Event, NotificationEvent, FALSE);
//
// now allocte the irp
//
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
if (!Irp)
{
//
// no memory
//
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// get next stack location
//
IoStack = IoGetNextIrpStackLocation(Irp);
//
// init stack location
//
IoStack->MajorFunction = IRP_MJ_PNP;
IoStack->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
IoStack->Parameters.DeviceCapabilities.Capabilities = Capabilities;
//
// set completion routine
//
IoSetCompletionRoutine(Irp, FDO_QueryCapabilitiesCompletionRoutine, (PVOID)&Event, TRUE, TRUE, TRUE);
//
// init capabilities
//
RtlZeroMemory(Capabilities, sizeof(DEVICE_CAPABILITIES));
Capabilities->Size = sizeof(DEVICE_CAPABILITIES);
Capabilities->Version = 1; // FIXME hardcoded constant
Capabilities->Address = MAXULONG;
Capabilities->UINumber = MAXULONG;
//
// pnp irps have default completion code
//
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
//
// call lower device
//
Status = IoCallDriver(FDODeviceExtension->NextDeviceObject, Irp);
if (Status == STATUS_PENDING)
{
//
// wait for completion
//
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
}
//
// get status
//
Status = Irp->IoStatus.Status;
//
// complete request
//
IoFreeIrp(Irp);
//
// done
//
return Status;
}
NTSTATUS
FDO_DeviceRelations(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
ULONG DeviceCount = 0;
ULONG Index;
PDEVICE_RELATIONS DeviceRelations;
PIO_STACK_LOCATION IoStack;
PFDO_DEVICE_EXTENSION FDODeviceExtension;
//
// get device extension
//
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
//
// get current irp stack location
//
IoStack = IoGetCurrentIrpStackLocation(Irp);
//
// check if relation type is BusRelations
//
if (IoStack->Parameters.QueryDeviceRelations.Type != BusRelations)
{
//
// FDO always only handles bus relations
//
return USBCCGP_SyncForwardIrp(FDODeviceExtension->NextDeviceObject, Irp);
}
//
// go through array and count device objects
//
for(Index = 0; Index < FDODeviceExtension->FunctionDescriptorCount; Index++)
{
if (FDODeviceExtension->ChildPDO[Index])
{
//
// child pdo
//
DeviceCount++;
}
}
//
// allocate device relations
//
DeviceRelations = (PDEVICE_RELATIONS)AllocateItem(PagedPool, sizeof(DEVICE_RELATIONS) + (DeviceCount > 1 ? (DeviceCount-1) * sizeof(PDEVICE_OBJECT) : 0));
if (!DeviceRelations)
{
//
// no memory
//
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// add device objects
//
for(Index = 0; Index < FDODeviceExtension->FunctionDescriptorCount; Index++)
{
if (FDODeviceExtension->ChildPDO[Index])
{
//
// store child pdo
//
DeviceRelations->Objects[DeviceRelations->Count] = FDODeviceExtension->ChildPDO[Index];
//
// add reference
//
ObReferenceObject(FDODeviceExtension->ChildPDO[Index]);
//
// increment count
//
DeviceRelations->Count++;
}
}
//
// store result
//
Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
//
// request completed successfully
//
return STATUS_SUCCESS;
}
NTSTATUS
FDO_CreateChildPdo(
IN PDEVICE_OBJECT DeviceObject)
{
NTSTATUS Status;
PDEVICE_OBJECT PDODeviceObject;
PPDO_DEVICE_EXTENSION PDODeviceExtension;
PFDO_DEVICE_EXTENSION FDODeviceExtension;
ULONG Index;
//
// get device extension
//
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT(FDODeviceExtension->Common.IsFDO);
//
// lets create array for the child PDO
//
FDODeviceExtension->ChildPDO = AllocateItem(NonPagedPool, sizeof(PDEVICE_OBJECT) * FDODeviceExtension->FunctionDescriptorCount);
if (!FDODeviceExtension->ChildPDO)
{
//
// no memory
//
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// create pdo for each function
//
for(Index = 0; Index < FDODeviceExtension->FunctionDescriptorCount; Index++)
{
//
// create the PDO
//
Status = IoCreateDevice(FDODeviceExtension->DriverObject, sizeof(PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_USB, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &PDODeviceObject);
if (!NT_SUCCESS(Status))
{
//
// failed to create device object
//
DPRINT1("IoCreateDevice failed with %x\n", Status);
return Status;
}
//
// store in array
//
FDODeviceExtension->ChildPDO[Index] = PDODeviceObject;
//
// get device extension
//
PDODeviceExtension = (PPDO_DEVICE_EXTENSION)PDODeviceObject->DeviceExtension;
RtlZeroMemory(PDODeviceExtension, sizeof(PDO_DEVICE_EXTENSION));
//
// init device extension
//
PDODeviceExtension->Common.IsFDO = FALSE;
PDODeviceExtension->FunctionDescriptor = &FDODeviceExtension->FunctionDescriptor[Index];
PDODeviceExtension->NextDeviceObject = DeviceObject;
PDODeviceExtension->FunctionIndex = Index;
PDODeviceExtension->FDODeviceExtension = FDODeviceExtension;
PDODeviceExtension->InterfaceList = FDODeviceExtension->InterfaceList;
PDODeviceExtension->InterfaceListCount = FDODeviceExtension->InterfaceListCount;
PDODeviceExtension->ConfigurationHandle = FDODeviceExtension->ConfigurationHandle;
PDODeviceExtension->ConfigurationDescriptor = FDODeviceExtension->ConfigurationDescriptor;
RtlCopyMemory(&PDODeviceExtension->Capabilities, &FDODeviceExtension->Capabilities, sizeof(DEVICE_CAPABILITIES));
RtlCopyMemory(&PDODeviceExtension->DeviceDescriptor, &FDODeviceExtension->DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR));
//
// patch the stack size
//
PDODeviceObject->StackSize = DeviceObject->StackSize + 1;
//
// set device flags
//
PDODeviceObject->Flags |= DO_DIRECT_IO | DO_MAP_IO_BUFFER;
//
// device is initialized
//
PDODeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
}
//
// done
//
return STATUS_SUCCESS;
}
NTSTATUS
FDO_StartDevice(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
NTSTATUS Status;
PFDO_DEVICE_EXTENSION FDODeviceExtension;
//
// get device extension
//
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT(FDODeviceExtension->Common.IsFDO);
//
// first start lower device
//
Status = USBCCGP_SyncForwardIrp(FDODeviceExtension->NextDeviceObject, Irp);
if (!NT_SUCCESS(Status))
{
//
// failed to start lower device
//
DPRINT1("FDO_StartDevice lower device failed to start with %x\n", Status);
return Status;
}
// get descriptors
Status = USBCCGP_GetDescriptors(DeviceObject);
if (!NT_SUCCESS(Status))
{
// failed to start lower device
DPRINT1("FDO_StartDevice failed to get descriptors with %x\n", Status);
return Status;
}
// get capabilities
Status = FDO_QueryCapabilities(DeviceObject, &FDODeviceExtension->Capabilities);
if (!NT_SUCCESS(Status))
{
// failed to start lower device
DPRINT1("FDO_StartDevice failed to get capabilities with %x\n", Status);
return Status;
}
// now select the configuration
Status = USBCCGP_SelectConfiguration(DeviceObject, FDODeviceExtension);
if (!NT_SUCCESS(Status))
{
// failed to select interface
DPRINT1("FDO_StartDevice failed to get capabilities with %x\n", Status);
return Status;
}
// query bus interface
USBCCGP_QueryInterface(FDODeviceExtension->NextDeviceObject, &FDODeviceExtension->BusInterface);
// now enumerate the functions
Status = USBCCGP_EnumerateFunctions(DeviceObject);
if (!NT_SUCCESS(Status))
{
// failed to enumerate functions
DPRINT1("Failed to enumerate functions with %x\n", Status);
return Status;
}
//
// sanity checks
//
ASSERT(FDODeviceExtension->FunctionDescriptorCount);
ASSERT(FDODeviceExtension->FunctionDescriptor);
DumpFunctionDescriptor(FDODeviceExtension->FunctionDescriptor, FDODeviceExtension->FunctionDescriptorCount);
//
// now create the pdo
//
Status = FDO_CreateChildPdo(DeviceObject);
if (!NT_SUCCESS(Status))
{
//
// failed
//
DPRINT1("FDO_CreateChildPdo failed with %x\n", Status);
return Status;
}
//
// inform pnp manager of new device objects
//
IoInvalidateDeviceRelations(FDODeviceExtension->PhysicalDeviceObject, BusRelations);
//
// done
//
DPRINT1("[USBCCGP] FDO initialized successfully\n");
return Status;
}
NTSTATUS
FDO_HandlePnp(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
PFDO_DEVICE_EXTENSION FDODeviceExtension;
// get device extension
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT(FDODeviceExtension->Common.IsFDO);
// get stack location
IoStack = IoGetCurrentIrpStackLocation(Irp);
DPRINT1("[USBCCGP] PnP Minor %x\n", IoStack->MinorFunction);
switch(IoStack->MinorFunction)
{
case IRP_MN_REMOVE_DEVICE:
{
/* Send the IRP down the stack */
Status = USBCCGP_SyncForwardIrp(FDODeviceExtension->NextDeviceObject, Irp);
if (NT_SUCCESS(Status))
{
//
// Detach from the device stack
//
IoDetachDevice(FDODeviceExtension->NextDeviceObject);
//
// Delete the device object
//
IoDeleteDevice(DeviceObject);
}
//
// request completed
//
break;
}
case IRP_MN_START_DEVICE:
{
//
// start the device
//
Status = FDO_StartDevice(DeviceObject, Irp);
break;
}
case IRP_MN_QUERY_DEVICE_RELATIONS:
{
//
// handle device relations
//
Status = FDO_DeviceRelations(DeviceObject, Irp);
break;
}
case IRP_MN_QUERY_CAPABILITIES:
{
//
// copy capabilities
//
RtlCopyMemory(IoStack->Parameters.DeviceCapabilities.Capabilities, &FDODeviceExtension->Capabilities, sizeof(DEVICE_CAPABILITIES));
Status = USBCCGP_SyncForwardIrp(FDODeviceExtension->NextDeviceObject, Irp);
if (NT_SUCCESS(Status))
{
//
// surprise removal ok
//
IoStack->Parameters.DeviceCapabilities.Capabilities->SurpriseRemovalOK = TRUE;
}
break;
}
case IRP_MN_QUERY_REMOVE_DEVICE:
case IRP_MN_QUERY_STOP_DEVICE:
{
//
// sure
//
Irp->IoStatus.Status = STATUS_SUCCESS;
//
// forward irp to next device object
//
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(FDODeviceExtension->NextDeviceObject, Irp);
}
default:
{
//
// forward irp to next device object
//
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(FDODeviceExtension->NextDeviceObject, Irp);
}
}
//
// complete request
//
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
NTSTATUS
FDO_HandleResetCyclePort(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
PFDO_DEVICE_EXTENSION FDODeviceExtension;
PLIST_ENTRY ListHead, Entry;
LIST_ENTRY TempList;
PUCHAR ResetActive;
PIRP ListIrp;
KIRQL OldLevel;
//
// get device extension
//
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT(FDODeviceExtension->Common.IsFDO);
// get stack location
IoStack = IoGetCurrentIrpStackLocation(Irp);
DPRINT1("FDO_HandleResetCyclePort IOCTL %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode);
if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_RESET_PORT)
{
//
// use reset port list
//
ListHead = &FDODeviceExtension->ResetPortListHead;
ResetActive = &FDODeviceExtension->ResetPortActive;
}
else
{
//
// use cycle port list
//
ListHead = &FDODeviceExtension->CyclePortListHead;
ResetActive = &FDODeviceExtension->CyclePortActive;
}
//
// acquire lock
//
KeAcquireSpinLock(&FDODeviceExtension->Lock, &OldLevel);
if (*ResetActive)
{
//
// insert into pending list
//
InsertTailList(ListHead, &Irp->Tail.Overlay.ListEntry);
//
// mark irp pending
//
IoMarkIrpPending(Irp);
Status = STATUS_PENDING;
//
// release lock
//
KeReleaseSpinLock(&FDODeviceExtension->Lock, OldLevel);
}
else
{
//
// mark reset active
//
*ResetActive = TRUE;
//
// release lock
//
KeReleaseSpinLock(&FDODeviceExtension->Lock, OldLevel);
//
// forward request synchronized
//
USBCCGP_SyncForwardIrp(FDODeviceExtension->NextDeviceObject, Irp);
//
// reacquire lock
//
KeAcquireSpinLock(&FDODeviceExtension->Lock, &OldLevel);
//
// mark reset as completed
//
*ResetActive = FALSE;
//
// move all requests into temporary list
//
InitializeListHead(&TempList);
while(!IsListEmpty(ListHead))
{
Entry = RemoveHeadList(ListHead);
InsertTailList(&TempList, Entry);
}
//
// release lock
//
KeReleaseSpinLock(&FDODeviceExtension->Lock, OldLevel);
//
// complete pending irps
//
while(!IsListEmpty(&TempList))
{
Entry = RemoveHeadList(&TempList);
ListIrp = (PIRP)CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
//
// complete request with status success
//
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
//
// status success
//
Status = STATUS_SUCCESS;
}
return Status;
}
NTSTATUS
FDO_HandleInternalDeviceControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
PFDO_DEVICE_EXTENSION FDODeviceExtension;
//
// get device extension
//
FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT(FDODeviceExtension->Common.IsFDO);
// get stack location
IoStack = IoGetCurrentIrpStackLocation(Irp);
if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_RESET_PORT ||
IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_CYCLE_PORT)
{
//
// handle reset / cycle ports
//
Status = FDO_HandleResetCyclePort(DeviceObject, Irp);
DPRINT1("FDO_HandleResetCyclePort Status %x\n", Status);
if (Status != STATUS_PENDING)
{
//
// complete request
//
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
return Status;
}
//
// forward and forget request
//
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(FDODeviceExtension->NextDeviceObject, Irp);
}
NTSTATUS
FDO_Dispatch(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
/* get stack location */
IoStack = IoGetCurrentIrpStackLocation(Irp);
switch(IoStack->MajorFunction)
{
case IRP_MJ_PNP:
return FDO_HandlePnp(DeviceObject, Irp);
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
return FDO_HandleInternalDeviceControl(DeviceObject, Irp);
default:
DPRINT1("FDO_Dispatch Function %x not implemented\n", IoStack->MajorFunction);
ASSERT(FALSE);
Status = Irp->IoStatus.Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
}