/* * 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" #define NDEBUG #include 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 */ DPRINT("[USBCCGP] FDO initialized successfully\n"); return Status; } NTSTATUS FDO_CloseConfiguration( IN PDEVICE_OBJECT DeviceObject) { NTSTATUS Status; PURB Urb; PFDO_DEVICE_EXTENSION FDODeviceExtension; /* Get device extension */ FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; ASSERT(FDODeviceExtension->Common.IsFDO); /* Now allocate the urb */ Urb = USBD_CreateConfigurationRequestEx(FDODeviceExtension->ConfigurationDescriptor, FDODeviceExtension->InterfaceList); if (!Urb) { /* No memory */ return STATUS_INSUFFICIENT_RESOURCES; } /* Clear configuration descriptor to make it an unconfigure request */ Urb->UrbSelectConfiguration.ConfigurationDescriptor = NULL; /* Submit urb */ Status = USBCCGP_SyncUrbRequest(FDODeviceExtension->NextDeviceObject, Urb); if (!NT_SUCCESS(Status)) { /* Failed to set configuration */ DPRINT1("USBCCGP_SyncUrbRequest failed to unconfigure device\n", Status); } ExFreePool(Urb); 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); DPRINT("[USBCCGP] PnP Minor %x\n", IoStack->MinorFunction); switch(IoStack->MinorFunction) { case IRP_MN_REMOVE_DEVICE: { // Unconfigure device */ DPRINT1("[USBCCGP] FDO IRP_MN_REMOVE\n"); FDO_CloseConfiguration(DeviceObject); /* 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); DPRINT("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 */ ListIrp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(ListIrp, 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); DPRINT("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_HandleSystemControl( PDEVICE_OBJECT DeviceObject, PIRP Irp) { PFDO_DEVICE_EXTENSION FDODeviceExtension; /* Get device extension */ FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; ASSERT(FDODeviceExtension->Common.IsFDO); /* 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; PFDO_DEVICE_EXTENSION FDODeviceExtension; /* Get device extension */ FDODeviceExtension = DeviceObject->DeviceExtension; ASSERT(FDODeviceExtension->Common.IsFDO); /* 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); case IRP_MJ_POWER: PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); return PoCallDriver(FDODeviceExtension->NextDeviceObject, Irp); case IRP_MJ_SYSTEM_CONTROL: return FDO_HandleSystemControl(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; } }