123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856 |
- /*++
- Copyright (c) 2013 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- usbhost.c
- Abstract:
- This module implements USB host controller support routines.
- Author:
- Evan Green 16-Jan-2013
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/driver.h>
- #include "usbcore.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- VOID
- UsbpDestroyEndpoint (
- PUSB_DEVICE Device,
- PUSB_ENDPOINT Endpoint
- );
- VOID
- UsbpPortStatusChangeWorker (
- PVOID Parameter
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ------------------------------------------------------------------ Functions
- //
- USB_API
- KSTATUS
- UsbHostRegisterController (
- PUSB_HOST_CONTROLLER_INTERFACE ControllerInterface,
- PHANDLE ControllerHandle
- )
- /*++
- Routine Description:
- This routine registers a new host controller instance with the USB core.
- This routine must be called at low level.
- Arguments:
- ControllerInterface - Supplies a pointer to the interface that the USB
- core will use to call back into the host controller. This contents of
- this memory will be copied during this call, so the caller can pass
- a pointer to a stack-allocated buffer here.
- ControllerHandle - Supplies a pointer where a handle will be returned
- representing this controller instance. When calls are made to the USB
- core regarding a specific controller, pass this handle.
- Return Value:
- STATUS_SUCCESS on success. A handle will also be returned on success.
- STATUS_NOT_SUPPORTED if an unsupported version was supplied with the
- controller interface.
- Other error codes on other failures.
- --*/
- {
- UCHAR Address;
- PUSB_TRANSFER_COMPLETION_QUEUE CompletionQueue;
- PUSB_HOST_CONTROLLER Controller;
- KSTATUS Status;
- //
- // Surely DriverEntry must have been called to initialize this list head.
- //
- ASSERT(UsbHostControllerList.Next != NULL);
- Controller = NULL;
- if (UsbHostControllerListLock == NULL) {
- Status = STATUS_NOT_READY;
- goto HostRegisterControllerEnd;
- }
- //
- // Validate parameters.
- //
- if ((ControllerInterface == NULL) || (ControllerHandle == NULL)) {
- Status = STATUS_INVALID_PARAMETER;
- goto HostRegisterControllerEnd;
- }
- if (ControllerInterface->Version < USB_HOST_CONTROLLER_INTERFACE_VERSION) {
- Status = STATUS_NOT_SUPPORTED;
- goto HostRegisterControllerEnd;
- }
- if ((ControllerInterface->DriverObject == NULL) ||
- (ControllerInterface->DeviceObject == NULL) ||
- (ControllerInterface->CreateEndpoint == NULL) ||
- (ControllerInterface->DestroyEndpoint == NULL) ||
- (ControllerInterface->CreateTransfer == NULL) ||
- (ControllerInterface->DestroyTransfer == NULL) ||
- (ControllerInterface->SubmitTransfer == NULL) ||
- (ControllerInterface->CancelTransfer == NULL) ||
- (ControllerInterface->GetRootHubStatus == NULL) ||
- (ControllerInterface->SetRootHubStatus == NULL) ||
- (ControllerInterface->Speed == UsbDeviceSpeedInvalid) ||
- (ControllerInterface->RootHubPortCount == 0)) {
- Status = STATUS_INVALID_PARAMETER;
- goto HostRegisterControllerEnd;
- }
- //
- // The endpoint flush routine is required if polled I/O is supported.
- //
- if ((ControllerInterface->SubmitPolledTransfer != NULL) &&
- (ControllerInterface->FlushEndpoint == NULL)) {
- Status = STATUS_INVALID_PARAMETER;
- goto HostRegisterControllerEnd;
- }
- //
- // Create a controller structure.
- //
- Controller = MmAllocateNonPagedPool(sizeof(USB_HOST_CONTROLLER),
- USB_CORE_ALLOCATION_TAG);
- if (Controller == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto HostRegisterControllerEnd;
- }
- RtlZeroMemory(Controller, sizeof(USB_HOST_CONTROLLER));
- RtlCopyMemory(&(Controller->Device),
- ControllerInterface,
- sizeof(USB_HOST_CONTROLLER_INTERFACE));
- Controller->Lock = KeCreateQueuedLock();
- if (Controller->Lock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto HostRegisterControllerEnd;
- }
- Controller->AddressLock = KeCreateQueuedLock();
- if (Controller->AddressLock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto HostRegisterControllerEnd;
- }
- //
- // Initialize the completed transfers queue. It uses the USB core's work
- // queue.
- //
- CompletionQueue = &(Controller->TransferCompletionQueue);
- Status = UsbpInitializeTransferCompletionQueue(CompletionQueue, FALSE);
- if (!KSUCCESS(Status)) {
- goto HostRegisterControllerEnd;
- }
- //
- // Allocate a work item for handling root hub port change notifications.
- //
- Controller->PortStatusWorkItem = KeCreateWorkItem(
- UsbCoreWorkQueue,
- WorkPriorityNormal,
- UsbpPortStatusChangeWorker,
- Controller,
- USB_CORE_ALLOCATION_TAG);
- if (Controller->PortStatusWorkItem == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto HostRegisterControllerEnd;
- }
- ASSERT(Controller->PortStatusWorkItemQueued == FALSE);
- //
- // If the debugger handoff data refers to this controller, set it in the
- // host controller.
- //
- if ((UsbDebugHandoffData != NULL) &&
- (ControllerInterface->DebugPortSubType ==
- UsbDebugHandoffData->PortSubType) &&
- (ControllerInterface->Identifier == UsbDebugHandoffData->Identifier)) {
- if ((UsbDebugFlags & USB_DEBUG_DEBUGGER_HANDOFF) != 0) {
- RtlDebugPrint("USB: Handoff data matches host 0x%x\n", Controller);
- }
- Controller->HandoffData = UsbDebugHandoffData;
- //
- // Reserve the debugger device and hub address if they're valid.
- //
- Address = Controller->HandoffData->U.Usb.DeviceAddress;
- if (Address != 0) {
- Status = UsbpReserveDeviceAddress(Controller, NULL, Address);
- if (!KSUCCESS(Status)) {
- goto HostRegisterControllerEnd;
- }
- }
- Address = Controller->HandoffData->U.Usb.HubAddress;
- if (Address != 0) {
- Status = UsbpReserveDeviceAddress(Controller, NULL, Address);
- if (!KSUCCESS(Status)) {
- goto HostRegisterControllerEnd;
- }
- }
- }
- //
- // Add the controller to the master list and return successfully.
- //
- ASSERT(KeGetRunLevel() == RunLevelLow);
- KeAcquireQueuedLock(UsbHostControllerListLock);
- INSERT_BEFORE(&(Controller->ListEntry), &UsbHostControllerList);
- KeReleaseQueuedLock(UsbHostControllerListLock);
- Status = STATUS_SUCCESS;
- HostRegisterControllerEnd:
- if (!KSUCCESS(Status)) {
- if (Controller != NULL) {
- UsbHostDestroyControllerState((HANDLE)Controller);
- Controller = NULL;
- }
- *ControllerHandle = INVALID_HANDLE;
- } else {
- *ControllerHandle = (HANDLE)Controller;
- }
- return Status;
- }
- USB_API
- VOID
- UsbHostDestroyControllerState (
- HANDLE ControllerHandle
- )
- /*++
- Routine Description:
- This routine destroys the state of a USB host controller that was created
- during registration.
- Arguments:
- ControllerHandle - Supplies a handle to a controller instance.
- Return Value:
- None.
- --*/
- {
- PUSB_HOST_CONTROLLER Controller;
- ASSERT(ControllerHandle != INVALID_HANDLE);
- Controller = (PUSB_HOST_CONTROLLER)ControllerHandle;
- if (Controller->PortStatusWorkItem != NULL) {
- KeDestroyWorkItem(Controller->PortStatusWorkItem);
- }
- UsbpDestroyTransferCompletionQueue(&(Controller->TransferCompletionQueue));
- if (Controller->AddressLock != NULL) {
- KeDestroyQueuedLock(Controller->AddressLock);
- }
- if (Controller->Lock != NULL) {
- KeDestroyQueuedLock(Controller->Lock);
- }
- MmFreeNonPagedPool(Controller);
- return;
- }
- USB_API
- VOID
- UsbHostProcessCompletedTransfer (
- PUSB_TRANSFER_INTERNAL Transfer
- )
- /*++
- Routine Description:
- This routine is called by the USB host controller when the host controller
- is done with a transfer. This routine must be called if the transfer is
- completed successfully, failed, or was cancelled.
- This routine must be called while the host controller holds its controller
- lock. This is expected to be done at dispatch level.
- Arguments:
- Transfer - Supplies a pointer to the transfer that has completed.
- Return Value:
- None.
- --*/
- {
- ASSERT(KeGetRunLevel() == RunLevelDispatch);
- ASSERT(Transfer->Public.LengthTransferred <= Transfer->Public.Length);
- if (((UsbDebugFlags & USB_DEBUG_TRANSFER_COMPLETION) != 0) ||
- ((!KSUCCESS(Transfer->Public.Status)) &&
- ((UsbDebugFlags & USB_DEBUG_ERRORS) != 0))) {
- if ((UsbDebugDeviceAddress == 0) ||
- (UsbDebugDeviceAddress == Transfer->DeviceAddress)) {
- ASSERT(Transfer->Public.Error < UsbErrorCount);
- RtlDebugPrint(
- "USB: Transfer (0x%08x) %s dev %d EP%x status %d (%s), "
- "len 0x%x of 0x%x\n",
- Transfer,
- UsbTransferDirectionStrings[Transfer->Public.Direction],
- Transfer->DeviceAddress,
- Transfer->EndpointNumber,
- Transfer->Public.Status,
- UsbErrorStrings[Transfer->Public.Error],
- Transfer->Public.LengthTransferred,
- Transfer->Public.Length);
- }
- }
- //
- // Forward this on for the transfer code to handle.
- //
- UsbpProcessCompletedTransfer(Transfer);
- return;
- }
- USB_API
- VOID
- UsbHostNotifyPortChange (
- HANDLE ControllerHandle
- )
- /*++
- Routine Description:
- This routine notifies the USB core that the USB host controller detected a
- port change.
- Arguments:
- ControllerHandle - Supplies a handle to the USB core instance that needs to
- be notified that a host port changed status.
- Return Value:
- None.
- --*/
- {
- PUSB_HOST_CONTROLLER Controller;
- BOOL OldValue;
- KSTATUS Status;
- ASSERT(KeGetRunLevel() == RunLevelDispatch);
- ASSERT(ControllerHandle != INVALID_HANDLE);
- //
- // Do nothing if the root hub is not yet initialized.
- //
- Controller = (PUSB_HOST_CONTROLLER)ControllerHandle;
- if (Controller->RootHub == NULL) {
- goto HostNotifyPortChangeEnd;
- }
- //
- // Queue a work item to handle the actual processing since this is running
- // at dispatch. But be sure not to queue the work item if it is already
- // on the queue.
- //
- OldValue = RtlAtomicCompareExchange32(
- &(Controller->PortStatusWorkItemQueued),
- TRUE,
- FALSE);
- if (OldValue != FALSE) {
- goto HostNotifyPortChangeEnd;
- }
- Status = KeQueueWorkItem(Controller->PortStatusWorkItem);
- ASSERT(KSUCCESS(Status));
- HostNotifyPortChangeEnd:
- return;
- }
- KSTATUS
- UsbpCreateEndpoint (
- PUSB_DEVICE Device,
- UCHAR Number,
- USB_TRANSFER_DIRECTION Direction,
- USB_TRANSFER_TYPE Type,
- ULONG MaxPacketSize,
- ULONG PollRate,
- PUSB_ENDPOINT *CreatedEndpoint
- )
- /*++
- Routine Description:
- This routine creates the accounting structures associated with a new USB
- endpoint.
- Arguments:
- Device - Supplies a pointer to the device that will own the endpoint.
- Number - Supplies the endpoint number of the new endpoint.
- Direction - Supplies the direction of the endpoint.
- Type - Supplies the type of the endpoint.
- MaxPacketSize - Supplies the maximum packet size for the endpoint, in bytes.
- PollRate - Supplies the polling rate of the endpoint.
- CreatedEndpoint - Supplies a pointer where the newly minted endpoint will
- be returned.
- Return Value:
- Status code.
- --*/
- {
- PVOID Context;
- PUSB_HOST_CREATE_ENDPOINT CreateEndpoint;
- PUSB_ENDPOINT Endpoint;
- USB_HOST_ENDPOINT_CREATION_REQUEST Request;
- KSTATUS Status;
- //
- // Convert the supplied poll rate into (micro)frames. For isochronous high
- // and full speed endpoints and high speed interrupt endpoints, the
- // supplied poll rate value is (x), between 1 and 16, where the
- // (micro)frame period is calculated by the forumla 2^(x-1).
- //
- // For all other combinations, the poll rate is a value between 1 and 255.
- // For full and low-speed interrupts this indicates the frame rate. For
- // high-speed control and bulk transfers, this indicates the maximum NAK
- // rate.
- //
- if ((PollRate != 0) &&
- (((Type == UsbTransferTypeInterrupt) &&
- (Device->Speed == UsbDeviceSpeedHigh)) ||
- ((Type == UsbTransferTypeIsochronous) &&
- ((Device->Speed == UsbDeviceSpeedFull) ||
- (Device->Speed == UsbDeviceSpeedHigh))))) {
- PollRate = 1 << (PollRate - 1);
- }
- //
- // Allocate and initialize the endpoint structures.
- //
- Endpoint = MmAllocateNonPagedPool(sizeof(USB_ENDPOINT),
- USB_CORE_ALLOCATION_TAG);
- if (Endpoint == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateEndpointEnd;
- }
- RtlZeroMemory(Endpoint, sizeof(USB_ENDPOINT));
- Endpoint->Type = Type;
- Endpoint->Direction = Direction;
- Endpoint->MaxPacketSize = MaxPacketSize;
- Endpoint->PollRate = PollRate;
- Endpoint->Number = Number;
- Endpoint->ReferenceCount = 1;
- //
- // Fill out the endpoint creation request.
- //
- RtlZeroMemory(&Request, sizeof(USB_HOST_ENDPOINT_CREATION_REQUEST));
- Request.Version = USB_HOST_ENDPOINT_CREATION_REQUEST_VERSION;
- Request.Type = Type;
- Request.Direction = Direction;
- Request.Speed = Device->Speed;
- Request.MaxPacketSize = Endpoint->MaxPacketSize;
- Request.PollRate = Endpoint->PollRate;
- Request.EndpointNumber = Endpoint->Number;
- Request.HubPortNumber = Device->PortNumber;
- if (Device->Parent != NULL) {
- Request.HubAddress = Device->Parent->BusAddress;
- }
- //
- // Call the host controller to create any needed endpoint structures on its
- // end, and save the context pointer it returns.
- //
- CreateEndpoint = Device->Controller->Device.CreateEndpoint;
- Context = Device->Controller->Device.HostControllerContext;
- Status = CreateEndpoint(Context,
- &Request,
- &(Endpoint->HostControllerContext));
- if (!KSUCCESS(Status)) {
- goto CreateEndpointEnd;
- }
- CreateEndpointEnd:
- if (!KSUCCESS(Status)) {
- if (Endpoint != NULL) {
- MmFreeNonPagedPool(Endpoint);
- Endpoint = NULL;
- }
- }
- *CreatedEndpoint = Endpoint;
- return Status;
- }
- VOID
- UsbpResetEndpoint (
- PUSB_DEVICE Device,
- PUSB_ENDPOINT Endpoint
- )
- /*++
- Routine Description:
- This routine resets a USB endpoint.
- Arguments:
- Device - Supplies a pointer to the device to which the endpoint belongs.
- Endpoint - Supplies a pointer to the USB endpoint.
- Return Value:
- None.
- --*/
- {
- PVOID Context;
- PUSB_HOST_RESET_ENDPOINT ResetEndpoint;
- ResetEndpoint = Device->Controller->Device.ResetEndpoint;
- Context = Device->Controller->Device.HostControllerContext;
- ResetEndpoint(Context,
- Endpoint->HostControllerContext,
- Endpoint->MaxPacketSize);
- return;
- }
- KSTATUS
- UsbpFlushEndpoint (
- PUSB_DEVICE Device,
- PUSB_ENDPOINT Endpoint,
- PULONG TransferCount
- )
- /*++
- Routine Description:
- This routine flushes the given endpoint for the given USB device. This
- includes busily waiting for all active transfers to complete. This is only
- meant to be used at high run level when preparing to write a crash dump
- file using USB Mass Storage.
- Arguments:
- Device - Supplies a pointer to the device to which the endpoint belongs.
- Endpoint - Supplies a pointer to the USB endpoint.
- TransferCount - Supplies a pointer that receives the total number of
- transfers that were flushed.
- Return Value:
- Status code.
- --*/
- {
- PVOID Context;
- PUSB_HOST_FLUSH_ENDPOINT FlushEndpoint;
- KSTATUS Status;
- FlushEndpoint = Device->Controller->Device.FlushEndpoint;
- if (FlushEndpoint == NULL) {
- return STATUS_NOT_SUPPORTED;
- }
- Context = Device->Controller->Device.HostControllerContext;
- Status = FlushEndpoint(Context,
- Endpoint->HostControllerContext,
- TransferCount);
- return Status;
- }
- VOID
- UsbpEndpointAddReference (
- PUSB_ENDPOINT Endpoint
- )
- /*++
- Routine Description:
- This routine increments the reference count on the given endpoint.
- Arguments:
- Endpoint - Supplies a pointer to the endpoint whose reference count should
- be incremented.
- Return Value:
- None.
- --*/
- {
- ULONG OldReferenceCount;
- OldReferenceCount = RtlAtomicAdd32(&(Endpoint->ReferenceCount), 1);
- ASSERT((OldReferenceCount != 0) && (OldReferenceCount < 0x1000));
- return;
- }
- VOID
- UsbpEndpointReleaseReference (
- PUSB_DEVICE Device,
- PUSB_ENDPOINT Endpoint
- )
- /*++
- Routine Description:
- This routine decrements the reference count on the given endpoint, and
- destroys it if it hits zero.
- Arguments:
- Device - Supplies a pointer to the device that owns the endpoint.
- Endpoint - Supplies a pointer to the endpoint whose reference count should
- be decremented.
- Return Value:
- None.
- --*/
- {
- ULONG OldReferenceCount;
- OldReferenceCount = RtlAtomicAdd32(&(Endpoint->ReferenceCount), -1);
- ASSERT((OldReferenceCount != 0) && (OldReferenceCount < 0x1000));
- if (OldReferenceCount == 1) {
- UsbpDestroyEndpoint(Device, Endpoint);
- }
- return;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- VOID
- UsbpDestroyEndpoint (
- PUSB_DEVICE Device,
- PUSB_ENDPOINT Endpoint
- )
- /*++
- Routine Description:
- This routine destroys a created USB endpoint.
- Arguments:
- Device - Supplies a pointer to the device that owns the endpoint.
- Endpoint - Supplies a pointer to the endpoint.
- Return Value:
- None.
- --*/
- {
- PVOID ControllerContext;
- PUSB_HOST_DESTROY_ENDPOINT DestroyEndpoint;
- if (Endpoint->ListEntry.Next != NULL) {
- LIST_REMOVE(&(Endpoint->ListEntry));
- }
- DestroyEndpoint = Device->Controller->Device.DestroyEndpoint;
- ControllerContext = Device->Controller->Device.HostControllerContext;
- DestroyEndpoint(ControllerContext, Endpoint->HostControllerContext);
- MmFreeNonPagedPool(Endpoint);
- return;
- }
- VOID
- UsbpPortStatusChangeWorker (
- PVOID Parameter
- )
- /*++
- Routine Description:
- This routine processes a port status change notification for the host
- controller.
- Arguments:
- Parameter - Supplies a pointer to the USB host controller.
- Return Value:
- None.
- --*/
- {
- PUSB_HOST_CONTROLLER Controller;
- BOOL OldValue;
- Controller = (PUSB_HOST_CONTROLLER)Parameter;
- //
- // Only the hub module can accurately handle this.
- //
- ASSERT(Controller->RootHub != NULL);
- UsbpNotifyRootHubStatusChange(Controller->RootHub);
- //
- // The above call collected the port status and cleared the hardware change
- // bits. Now allow another item to queue. This is done after the hub
- // notification to prevent the host controller from queuing a second work
- // item based on the same change information.
- //
- ASSERT(Controller->PortStatusWorkItemQueued != FALSE);
- OldValue = RtlAtomicCompareExchange32(
- &(Controller->PortStatusWorkItemQueued),
- FALSE,
- TRUE);
- ASSERT(OldValue != FALSE);
- return;
- }
|