/* * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface * LICENSE: GPL - See COPYING in the top level directory * FILE: drivers/usb/usbehci/hcd_controller.cpp * PURPOSE: USB EHCI device driver. * PROGRAMMERS: * Michael Martin (michael.martin@reactos.org) * Johannes Anderwald (johannes.anderwald@reactos.org) */ #define INITGUID #include "usbehci.h" class CHCDController : public IHCDController, public IDispatchIrp { public: STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); STDMETHODIMP_(ULONG) AddRef() { InterlockedIncrement(&m_Ref); return m_Ref; } STDMETHODIMP_(ULONG) Release() { InterlockedDecrement(&m_Ref); if (!m_Ref) { delete this; return 0; } return m_Ref; } // IHCDController interface functions NTSTATUS Initialize(IN PROOTHDCCONTROLLER RootHCDController, IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject); // IDispatchIrp interface functions NTSTATUS HandlePnp(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); NTSTATUS HandlePower(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); NTSTATUS HandleDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); // local functions NTSTATUS CreateFDO(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT * OutDeviceObject); NTSTATUS SetSymbolicLink(BOOLEAN Enable); // constructor / destructor CHCDController(IUnknown *OuterUnknown){} virtual ~CHCDController(){} protected: LONG m_Ref; PROOTHDCCONTROLLER m_RootController; PDRIVER_OBJECT m_DriverObject; PDEVICE_OBJECT m_PhysicalDeviceObject; PDEVICE_OBJECT m_FunctionalDeviceObject; PDEVICE_OBJECT m_NextDeviceObject; PUSBHARDWAREDEVICE m_Hardware; PHUBCONTROLLER m_HubController; ULONG m_FDODeviceNumber; }; //================================================================================================= // COM // NTSTATUS STDMETHODCALLTYPE CHCDController::QueryInterface( IN REFIID refiid, OUT PVOID* Output) { return STATUS_UNSUCCESSFUL; } //------------------------------------------------------------------------------------------------- NTSTATUS CHCDController::Initialize( IN PROOTHDCCONTROLLER RootHCDController, IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject) { NTSTATUS Status; PCOMMON_DEVICE_EXTENSION DeviceExtension; // // create usb hardware // Status = CreateUSBHardware(&m_Hardware); if (!NT_SUCCESS(Status)) { // // failed to create hardware object // DPRINT1("Failed to create hardware object\n"); return STATUS_INSUFFICIENT_RESOURCES; } // // initialize members // m_DriverObject = DriverObject; m_PhysicalDeviceObject = PhysicalDeviceObject; m_RootController = RootHCDController; // // create FDO // Status = CreateFDO(m_DriverObject, &m_FunctionalDeviceObject); if (!NT_SUCCESS(Status)) { // // failed to create PDO // return Status; } // // now attach to device stack // m_NextDeviceObject = IoAttachDeviceToDeviceStack(m_FunctionalDeviceObject, m_PhysicalDeviceObject); if (!m_NextDeviceObject) { // // failed to attach to device stack // IoDeleteDevice(m_FunctionalDeviceObject); m_FunctionalDeviceObject = 0; return STATUS_NO_SUCH_DEVICE; } // // initialize hardware object // Status = m_Hardware->Initialize(m_DriverObject, m_FunctionalDeviceObject, m_PhysicalDeviceObject, m_NextDeviceObject); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to initialize hardware object %x\n", Status); // // failed to initialize hardware object, detach from device stack // IoDetachDevice(m_NextDeviceObject); // // now delete the device // IoDeleteDevice(m_FunctionalDeviceObject); // // nullify pointers :) // m_FunctionalDeviceObject = 0; m_NextDeviceObject = 0; return Status; } // // set device flags // m_FunctionalDeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE; // // get device extension // DeviceExtension = (PCOMMON_DEVICE_EXTENSION)m_FunctionalDeviceObject->DeviceExtension; PC_ASSERT(DeviceExtension); // // initialize device extension // DeviceExtension->IsFDO = TRUE; DeviceExtension->IsHub = FALSE; DeviceExtension->Dispatcher = PDISPATCHIRP(this); // // device is initialized // m_FunctionalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; // // is there a root controller // if (m_RootController) { // // add reference // m_RootController->AddRef(); // // register with controller // m_RootController->RegisterHCD(this); } // // done // return STATUS_SUCCESS; } //------------------------------------------------------------------------------------------------- NTSTATUS CHCDController::HandleDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION IoStack; PCOMMON_DEVICE_EXTENSION DeviceExtension; NTSTATUS Status = STATUS_NOT_IMPLEMENTED; PUSB_HCD_DRIVERKEY_NAME DriverKey; ULONG ResultLength; // // get current stack location // IoStack = IoGetCurrentIrpStackLocation(Irp); // // get device extension // DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; // // sanity check // PC_ASSERT(DeviceExtension->IsFDO); DPRINT1("HandleDeviceControl>Type: IoCtl %x InputBufferLength %lu OutputBufferLength %lu\n", IoStack->Parameters.DeviceIoControl.IoControlCode, IoStack->Parameters.DeviceIoControl.InputBufferLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength); // // perform ioctl for FDO // if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_GET_HCD_DRIVERKEY_NAME) { // // check if sizee is at least >= USB_HCD_DRIVERKEY_NAME // if(IoStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(USB_HCD_DRIVERKEY_NAME)) { // // get device property size // Status = IoGetDeviceProperty(m_PhysicalDeviceObject, DevicePropertyDriverKeyName, 0, NULL, &ResultLength); // // get input buffer // DriverKey = (PUSB_HCD_DRIVERKEY_NAME)Irp->AssociatedIrp.SystemBuffer; // // check result // if (Status == STATUS_BUFFER_TOO_SMALL) { // // does the caller provide enough buffer space // if (IoStack->Parameters.DeviceIoControl.OutputBufferLength >= ResultLength) { // // it does // Status = IoGetDeviceProperty(m_PhysicalDeviceObject, DevicePropertyDriverKeyName, IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG), DriverKey->DriverKeyName, &ResultLength); if (NT_SUCCESS(Status)) { // // informal debug print // DPRINT1("Result %S\n", DriverKey->DriverKeyName); } } // // store result // DriverKey->ActualLength = ResultLength + FIELD_OFFSET(USB_HCD_DRIVERKEY_NAME, DriverKeyName) + sizeof(WCHAR); Irp->IoStatus.Information = IoStack->Parameters.DeviceIoControl.OutputBufferLength; Status = STATUS_SUCCESS; } } else { // // buffer is certainly too small // Status = STATUS_BUFFER_OVERFLOW; Irp->IoStatus.Information = sizeof(USB_HCD_DRIVERKEY_NAME); } } else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_USB_GET_ROOT_HUB_NAME) { // // check if sizee is at least >= USB_HCD_DRIVERKEY_NAME // if(IoStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(USB_HCD_DRIVERKEY_NAME)) { // // sanity check // PC_ASSERT(m_HubController); // // get input buffer // DriverKey = (PUSB_HCD_DRIVERKEY_NAME)Irp->AssociatedIrp.SystemBuffer; // // get symbolic link // Status = m_HubController->GetHubControllerSymbolicLink(IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG), DriverKey->DriverKeyName, &ResultLength); if (NT_SUCCESS(Status)) { // // null terminate it // PC_ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG) - sizeof(WCHAR) >= ResultLength); DriverKey->DriverKeyName[ResultLength / sizeof(WCHAR)] = L'\0'; DPRINT1("Result %S\n", DriverKey->DriverKeyName); } // // store result // DriverKey->ActualLength = ResultLength + FIELD_OFFSET(USB_HCD_DRIVERKEY_NAME, DriverKeyName) + sizeof(WCHAR); Irp->IoStatus.Information = IoStack->Parameters.DeviceIoControl.OutputBufferLength; Status = STATUS_SUCCESS; } else { // // buffer is certainly too small // Status = STATUS_BUFFER_OVERFLOW; Irp->IoStatus.Information = sizeof(USB_HCD_DRIVERKEY_NAME); } } // // complete the request // Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); // // done // return Status; } NTSTATUS CHCDController::HandlePnp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION IoStack; PCOMMON_DEVICE_EXTENSION DeviceExtension; PCM_RESOURCE_LIST RawResourceList; PCM_RESOURCE_LIST TranslatedResourceList; PDEVICE_RELATIONS DeviceRelations; NTSTATUS Status; // // get device extension // DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; // // sanity check // PC_ASSERT(DeviceExtension->IsFDO); // // get current stack location // IoStack = IoGetCurrentIrpStackLocation(Irp); switch(IoStack->MinorFunction) { case IRP_MN_START_DEVICE: { DPRINT1("CHCDController::HandlePnp IRP_MN_START FDO\n"); // // first start lower device object // Status = SyncForwardIrp(m_NextDeviceObject, Irp); if (NT_SUCCESS(Status)) { // // operation succeeded, lets start the device // RawResourceList = IoStack->Parameters.StartDevice.AllocatedResources; TranslatedResourceList = IoStack->Parameters.StartDevice.AllocatedResourcesTranslated; if (m_Hardware) { // // start the hardware // Status = m_Hardware->PnpStart(RawResourceList, TranslatedResourceList); } // // enable symbolic link // Status = SetSymbolicLink(TRUE); } DPRINT1("CHCDController::HandlePnp IRP_MN_START FDO: Status %x\n", Status); break; } case IRP_MN_QUERY_DEVICE_RELATIONS: { DPRINT1("CHCDController::HandlePnp IRP_MN_QUERY_DEVICE_RELATIONS Type %lx\n", IoStack->Parameters.QueryDeviceRelations.Type); if (m_HubController == NULL) { // // create hub controller // Status = CreateHubController(&m_HubController); if (!NT_SUCCESS(Status)) { // // failed to create hub controller // break; } // // initialize hub controller // Status = m_HubController->Initialize(m_DriverObject, PHCDCONTROLLER(this), m_Hardware, TRUE, 0 /* FIXME*/); if (!NT_SUCCESS(Status)) { // // failed to initialize hub controller // break; } // // add reference to prevent it from getting deleting while hub driver adds / removes references // m_HubController->AddRef(); } if (IoStack->Parameters.QueryDeviceRelations.Type == BusRelations) { // // allocate device relations // DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS)); if (!DeviceRelations) { // // no memory // Status = STATUS_INSUFFICIENT_RESOURCES; break; } // // init device relations // DeviceRelations->Count = 1; Status = m_HubController->GetHubControllerDeviceObject(&DeviceRelations->Objects [0]); // // sanity check // PC_ASSERT(Status == STATUS_SUCCESS); ObReferenceObject(DeviceRelations->Objects [0]); // // store result // Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; Status = STATUS_SUCCESS; } else { // // not supported // Status = STATUS_NOT_SUPPORTED; } break; } case IRP_MN_STOP_DEVICE: { DPRINT1("CHCDController::HandlePnp IRP_MN_STOP_DEVICE\n"); if (m_Hardware) { // // stop the hardware // Status = m_Hardware->PnpStop(); } else { // // fake success // Status = STATUS_SUCCESS; } if (NT_SUCCESS(Status)) { // // stop lower device // Status = SyncForwardIrp(m_NextDeviceObject, Irp); } 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(m_NextDeviceObject, Irp); } case IRP_MN_REMOVE_DEVICE: { DPRINT1("CHCDController::HandlePnp IRP_MN_REMOVE_DEVICE FDO\n"); // // delete the symbolic link // SetSymbolicLink(FALSE); // // forward irp to next device object // IoSkipCurrentIrpStackLocation(Irp); IoCallDriver(m_NextDeviceObject, Irp); // // detach device from device stack // IoDetachDevice(m_NextDeviceObject); // // delete device // IoDeleteDevice(m_FunctionalDeviceObject); return STATUS_SUCCESS; } default: { // // forward irp to next device object // IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(m_NextDeviceObject, Irp); } } // // store result and complete request // Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; } NTSTATUS CHCDController::HandlePower( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { UNIMPLEMENTED Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_NOT_IMPLEMENTED; } NTSTATUS CHCDController::CreateFDO( PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT * OutDeviceObject) { WCHAR CharDeviceName[64]; NTSTATUS Status; ULONG UsbDeviceNumber = 0; UNICODE_STRING DeviceName; while (TRUE) { // // construct device name // swprintf(CharDeviceName, L"\\Device\\USBFDO-%d", UsbDeviceNumber); // // initialize device name // RtlInitUnicodeString(&DeviceName, CharDeviceName); // // create device // Status = IoCreateDevice(DriverObject, sizeof(COMMON_DEVICE_EXTENSION), &DeviceName, FILE_DEVICE_CONTROLLER, 0, FALSE, OutDeviceObject); // // check for success // if (NT_SUCCESS(Status)) break; // // is there a device object with that same name // if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION)) { // // Try the next name // UsbDeviceNumber++; continue; } // // bail out on other errors // if (!NT_SUCCESS(Status)) { DPRINT1("CreateFDO: Failed to create %wZ, Status %x\n", &DeviceName, Status); return Status; } } // // store FDO number // m_FDODeviceNumber = UsbDeviceNumber; DPRINT1("CreateFDO: DeviceName %wZ\n", &DeviceName); /* done */ return Status; } NTSTATUS CHCDController::SetSymbolicLink( BOOLEAN Enable) { NTSTATUS Status; WCHAR LinkName[32]; WCHAR FDOName[32]; UNICODE_STRING Link, FDO; if (Enable) { // // create legacy link // swprintf(LinkName, L"\\DosDevices\\HCD%d", m_FDODeviceNumber); swprintf(FDOName, L"\\Device\\USBFDO-%d", m_FDODeviceNumber); RtlInitUnicodeString(&Link, LinkName); RtlInitUnicodeString(&FDO, FDOName); // // create symbolic link // Status = IoCreateSymbolicLink(&Link, &FDO); if (!NT_SUCCESS(Status)) { // // FIXME: handle me // ASSERT(0); } } else { // // create legacy link // swprintf(LinkName, L"\\DosDevices\\HCD%d", m_FDODeviceNumber); RtlInitUnicodeString(&Link, LinkName); // // now delete the symbolic link // Status = IoDeleteSymbolicLink(&Link); if (!NT_SUCCESS(Status)) { // // FIXME: handle me // ASSERT(0); } } // // done // return Status; } NTSTATUS CreateHCDController( PHCDCONTROLLER *OutHcdController) { PHCDCONTROLLER This; // // allocate controller // This = new(NonPagedPool, TAG_USBEHCI) CHCDController(0); if (!This) { // // failed to allocate // return STATUS_INSUFFICIENT_RESOURCES; } // // add reference count // This->AddRef(); // // return result // *OutHcdController = (PHCDCONTROLLER)This; // // done // return STATUS_SUCCESS; }