|
|
/*++
Copyright (c) 1995,1996 Microsoft Corporation :ts=4
Module Name:
hub.c
Abstract:
The UHC driver for USB, this module contains the root hub interface code.
Environment:
kernel mode only
Notes:
Revision History:
2-08-96 : created
--*/
#include "wdm.h"
#include "stdarg.h"
#include "stdio.h"
#include "usbdi.h"
#include "hcdi.h"
#include "uhcd.h"
typedef struct _ROOT_HUB_TIMER { KTIMER Timer; KDPC Dpc; PDEVICE_OBJECT DeviceObject; PROOTHUB_TIMER_ROUTINE TimerRoutine; PVOID Context; } ROOT_HUB_TIMER, *PROOT_HUB_TIMER;
NTSTATUS UHCD_RootHub_OpenEndpoint( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PHCD_URB Urb ) /*++
Routine Description:
This function is called at Dispatch level to process commands bound for the root hub.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Urb - urb for this request
Return Value:
NT status code.
--*/ { NTSTATUS ntStatus = STATUS_SUCCESS; PDEVICE_EXTENSION deviceExtension; PUHCD_ENDPOINT endpoint; PUSB_ENDPOINT_DESCRIPTOR endpointDescriptor;
UHCD_KdPrint((2, "'enter UHCD_RootHub_OpenEndpoint\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
LOGENTRY(LOG_MISC, 'rhOE', deviceExtension, 0, 0);
endpoint = (PUHCD_ENDPOINT) GETHEAP(NonPagedPool, sizeof(UHCD_ENDPOINT)); if (endpoint) {
//
// initialize endpoint structures, state variables.
//
// start endpoint initialization
RtlZeroMemory(endpoint, sizeof(*endpoint)); endpoint->Sig = SIG_EP; SET_EPFLAG(endpoint, EPFLAG_ROOT_HUB);
endpointDescriptor = Urb->HcdUrbOpenEndpoint.EndpointDescriptor; endpoint->MaxRequests = MAX_REQUESTS(endpointDescriptor, endpoint->EndpointFlags);
ntStatus = UHCD_FinishInitializeEndpoint(DeviceObject, endpoint, endpointDescriptor, Urb);
if (NT_SUCCESS(ntStatus)) {
Urb->HcdUrbOpenEndpoint.HcdEndpoint = endpoint; URB_HEADER(Urb).Status = USBD_STATUS_SUCCESS;
//
// if this is the interrupt endpoint start a timer Dpc to be called
// based on the endpoint polling interval.
//
if (endpoint->Type == USB_ENDPOINT_TYPE_INTERRUPT) { LARGE_INTEGER dueTime; LONG period;
endpoint->Interval = endpointDescriptor->bInterval; UHCD_ASSERT(endpoint->Interval != 0);
deviceExtension->RootHubInterruptEndpoint = endpoint; KeInitializeTimer(&deviceExtension->RootHubPollTimer); KeInitializeDpc(&deviceExtension->RootHubPollDpc, UHCD_RootHubPollDpc, DeviceObject);
deviceExtension->RootHubPollTimerInitialized = TRUE;
dueTime.QuadPart = -10000 * endpoint->Interval; period = 100; //every 100 ms
UHCD_KdPrint((2, "'UHCD Poll Interval = (0x%x) %x %x\n", endpoint->Interval, dueTime.LowPart, dueTime.HighPart));
KeSetTimerEx(&deviceExtension->RootHubPollTimer, dueTime, period, &deviceExtension->RootHubPollDpc);
} } else { RETHEAP(endpoint); } }
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
UHCD_KdPrint((2, "'exit UHCD_RootHub_OpenEndpoint (%x)\n", ntStatus));
return ntStatus; }
NTSTATUS UHCD_RootHub_CloseEndpoint( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PHCD_URB Urb ) /*++
Routine Description:
This function is called at Dispatch level to process commands bound for the root hub.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Urb - urb for this request
Return Value:
NT status code.
--*/ { NTSTATUS ntStatus = STATUS_SUCCESS; PDEVICE_EXTENSION deviceExtension; PUHCD_ENDPOINT endpoint;
UHCD_KdPrint((2, "'enter UHCD_RootHub_CloseEndpoint\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
endpoint = Urb->HcdUrbCloseEndpoint.HcdEndpoint; ASSERT_ENDPOINT(endpoint);
if (endpoint->ActiveTransfers[0]) { URB_HEADER(Urb).Status = USBD_STATUS_ERROR_BUSY; } else {
if (endpoint == deviceExtension->RootHubInterruptEndpoint) { // closing the interrupt endpoint,
// this means the root hub is stopped
deviceExtension->RootHubDeviceAddress = USB_DEFAULT_DEVICE_ADDRESS;
// if the timer fires before we cancel it then this
// will stop the polling process
deviceExtension->RootHubInterruptEndpoint = NULL; // if this is the interrupt endpoint kill the timer here
KeCancelTimer(&deviceExtension->RootHubPollTimer); }
RETHEAP(endpoint);
URB_HEADER(Urb).Status = USBD_STATUS_SUCCESS; }
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
UHCD_KdPrint((2, "'exit UHCD_RootHub_CloseEndpoint (%x)\n", ntStatus));
return ntStatus; }
NTSTATUS UHCD_RootHub_ControlTransfer( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PHCD_URB Urb ) /*++
Routine Description:
This function is called at Dispatch level to process commands bound for the root hub.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Urb - urb for this request
Return Value:
NT status code.
--*/ { NTSTATUS ntStatus = STATUS_SUCCESS; PDEVICE_EXTENSION deviceExtension; PUHCD_ENDPOINT endpoint; RHSTATUS rootHubReturnCode; KIRQL irql; PVOID mappedSystemVa;
UHCD_KdPrint((2, "'enter UHCD_RootHub_ControlTransfer\n"));
INCREMENT_PENDING_URB_COUNT(Irp);
IoAcquireCancelSpinLock(&irql); if (Irp->Cancel) { TEST_TRAP(); //BUGBUG Irp was canceled
IoReleaseCancelSpinLock(irql); UHCD_CompleteIrp(DeviceObject, Irp, STATUS_CANCELLED, 0, Urb); goto UHCD_RootHub_ControlTransfer_Done;
} else { IoSetCancelRoutine(Irp, NULL); IoReleaseCancelSpinLock(irql); }
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
endpoint = HCD_AREA(Urb).HcdEndpoint; ASSERT_ENDPOINT(endpoint);
// NOTE:
// should not get control transfers for anything but
// the default pipe
//
UHCD_ASSERT(endpoint->EndpointAddress == 0);
//BUGBUG no support for linked URBs yet
UHCD_ASSERT(Urb->HcdUrbCommonTransfer.UrbLink == NULL);
if (endpoint->EndpointAddress != 0) { URB_HEADER(Urb).Status = USBD_STATUS_INVALID_PARAMETER; } else {
//
// convert transfer buffer from MDL
//
mappedSystemVa = Urb->HcdUrbCommonTransfer.TransferBufferLength ? Urb->HcdUrbCommonTransfer.TransferBufferMDL->MappedSystemVa : NULL;
//UHCD_KdPrint((2, "' Mapped systemVa = 0x%x \n", mappedSystemVa));
rootHubReturnCode = RootHub_Endpoint0(deviceExtension->RootHub, (PRH_SETUP)Urb->HcdUrbCommonTransfer.Extension.u.SetupPacket, (PUCHAR) &deviceExtension->RootHubDeviceAddress, mappedSystemVa, &Urb->HcdUrbCommonTransfer.TransferBufferLength);
switch (rootHubReturnCode) { case RH_SUCCESS: URB_HEADER(Urb).Status = USBD_STATUS_SUCCESS; #if DBG
// restore the original transfer buffer address
Urb->HcdUrbCommonTransfer.TransferBuffer = mappedSystemVa; #endif
break; case RH_NAK: case RH_STALL: // return stall error and set the endpoint state to
// stalled on the host side
if (endpoint->EndpointFlags & EPFLAG_NO_HALT) { URB_HEADER(Urb).Status = USBD_STATUS(USBD_STATUS_STALL_PID) | USBD_STATUS_ERROR; } else { SET_EPFLAG(endpoint, EPFLAG_HOST_HALTED); URB_HEADER(Urb).Status = USBD_STATUS_STALL_PID; } //
// if we get here it is probably a bug in the root hub
// code or the hub driver.
//
UHCD_KdTrap(("Root hub stalled request\n")); } }
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, Urb);
UHCD_RootHub_ControlTransfer_Done:
UHCD_KdPrint((2, "'exit UHCD_RootHub_ControlTransfer (%x)\n", ntStatus));
return ntStatus; }
NTSTATUS UHCD_RootHub_InterruptTransfer( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PHCD_URB Urb ) /*++
Routine Description:
This function is called at Dispatch level to process commands bound for the root hub.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Urb - urb for this request
Return Value:
NT status code.
--*/ { NTSTATUS ntStatus = STATUS_SUCCESS; PDEVICE_EXTENSION deviceExtension; PUHCD_ENDPOINT endpoint; KIRQL irql; PHCD_EXTENSION urbWork;
UHCD_KdPrint((2, "'enter UHCD_RootHub_InterruptTransfer\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
INCREMENT_PENDING_URB_COUNT(Irp); endpoint = HCD_AREA(Urb).HcdEndpoint; ASSERT_ENDPOINT(endpoint); ASSERT(HCD_AREA(Urb).HcdExtension != NULL);
urbWork = HCD_AREA(Urb).HcdExtension; // set interrupt transfer to active.
urbWork->Flags |= UHCD_TRANSFER_ACTIVE;
//
// BUGBUG
// we only allow one transfer outstanding
// at a time here.
//
if (endpoint->ActiveTransfers[0] != NULL) { TEST_TRAP();
URB_HEADER(Urb).Status = USBD_STATUS_REQUEST_FAILED; UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, Urb);
} else if (Urb->HcdUrbCommonTransfer.UrbLink != NULL) { TEST_TRAP();
URB_HEADER(Urb).Status = USBD_STATUS_INVALID_PARAMETER; UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, Urb); } else { endpoint->ActiveTransfers[0] = Urb; URB_HEADER(Urb).Status = UHCD_STATUS_PENDING_CURRENT;
//
// set up a cancel routine
//
IoAcquireCancelSpinLock(&irql); if (Irp->Cancel) { TEST_TRAP(); //BUGBUG Irp was canceled
IoReleaseCancelSpinLock(irql);
// call cancel routine
UHCD_RootHub_InterruptTransferCancel(DeviceObject, Irp);
goto UHCD_RootHub_InterruptTransfer_Done;
} else { IoSetCancelRoutine(Irp, UHCD_RootHub_InterruptTransferCancel); IoReleaseCancelSpinLock(irql); }
ntStatus = STATUS_PENDING;
UHCD_KdPrint((2, "'Pending transfer for root hub\n"));
Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoMarkIrpPending(Irp); }
UHCD_RootHub_InterruptTransfer_Done:
UHCD_KdPrint((2, "'exit UHCD_RootHub_InterruptTransfer (%x)\n", ntStatus));
return ntStatus; }
VOID UHCD_RootHubPoll( IN PDEVICE_OBJECT DeviceObject, IN PUHCD_ENDPOINT Endpoint ) /*++
Routine Description:
This function is called at DPC level from the ISR DPC routine to process transfers queued to for the root hub.
Arguments:
DeviceObject - pointer to a device object
Return Value:
NT status code.
--*/ { PHCD_URB urb; PDEVICE_EXTENSION deviceExtension; PIRP irp; RHSTATUS rootHubReturnCode; BOOLEAN completeIt = TRUE; KIRQL irql; PVOID mappedSystemVa; NTSTATUS status = STATUS_SUCCESS;
//UHCD_KdPrint((2, "'enter UHCD_RootHub_Poll\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
//
// See if we need to poll an endpoint for the
// root hub.
//
// if the current PM state is not 'ON' we will not poll the endpoint
// it means the HC has not fully resumed yet.
//
if (Endpoint && deviceExtension->CurrentDevicePowerState == PowerDeviceD0) {
// Yes, poll
ASSERT_ENDPOINT(Endpoint);
urb = Endpoint->ActiveTransfers[0];
// see if we have an abort request
if ((Endpoint->EndpointFlags & EPFLAG_ABORT_PENDING_TRANSFERS) && urb) { CLR_EPFLAG(Endpoint, EPFLAG_ABORT_PENDING_TRANSFERS); urb->HcdUrbCommonTransfer.Status = USBD_STATUS_CANCELED; completeIt = TRUE; // BUGBUG do we need the error bit set here?
} else if (urb) { //
// convert transfer buffer from MDL
//
if (urb->HcdUrbCommonTransfer.TransferBufferLength != 0) { urb->HcdUrbCommonTransfer.TransferBufferMDL->MdlFlags |= MDL_MAPPING_CAN_FAIL;
mappedSystemVa = MmGetSystemAddressForMdl(urb->HcdUrbCommonTransfer .TransferBufferMDL);
urb->HcdUrbCommonTransfer.TransferBufferMDL->MdlFlags &= ~MDL_MAPPING_CAN_FAIL;
if (mappedSystemVa == NULL) { rootHubReturnCode = RH_STALL; status = STATUS_INSUFFICIENT_RESOURCES; goto UHCD_RootHubPollError; } } else{ mappedSystemVa = NULL; }
//UHCD_KdPrint((2, "' Mapped systemVa = 0x%x \n", mappedSystemVa));
rootHubReturnCode = RootHub_Endpoint1(deviceExtension->RootHub, mappedSystemVa, &urb->HcdUrbCommonTransfer .TransferBufferLength); //
// set urb error code if necessary
//
switch (rootHubReturnCode) { case RH_SUCCESS: urb->HcdUrbCommonTransfer.Status = USBD_STATUS_SUCCESS; #if DBG
// restore the original transfer buffer address
urb->HcdUrbCommonTransfer.TransferBuffer = mappedSystemVa; #endif
break;
case RH_STALL: if (Endpoint->EndpointFlags & EPFLAG_NO_HALT) { // if we dont halt clear th ehalt bit on
// the error code
URB_HEADER(urb).Status = USBD_STATUS(USBD_STATUS_STALL_PID) | USBD_STATUS_ERROR; } else { SET_EPFLAG(Endpoint, EPFLAG_HOST_HALTED); URB_HEADER(urb).Status = USBD_STATUS_STALL_PID; } //
// if we get here it is probably a bug in the root hub code
//
UHCD_KdTrap(("root hub stalled request\n")); break; case RH_NAK: // NAK, don't complete request
completeIt = FALSE; break; default: UHCD_KdTrap(("bogus return code from RootHub_Endpoint1\n")); }
} else { // no transfer, same as NAK
completeIt = FALSE; }
UHCD_RootHubPollError:; if (completeIt) { UHCD_ASSERT(urb != NULL);
//
// Remove the Irp from the cancelable state
// before passing it to the Root Hub Code.
//
irp = HCD_AREA(urb).HcdIrp;
IoAcquireCancelSpinLock(&irql); if (irp->Cancel) { TEST_TRAP(); //BUGBUG Irp was canceled
IoReleaseCancelSpinLock(irql);
UHCD_RootHub_InterruptTransferCancel(DeviceObject, irp);
} else { IoSetCancelRoutine(irp, NULL); IoReleaseCancelSpinLock(irql); //
// have data or error, complete the irp
//
// BUGBUG we are not supporting queued
// transfers for the hub driver.
Endpoint->ActiveTransfers[0] = NULL;
UHCD_ASSERT(irp != NULL); UHCD_CompleteIrp(DeviceObject, irp, status, 0, urb); } } }
//UHCD_KdPrint((2, "'exit UHCD_RootHub_Poll\n"));
return; }
//
// Root Hub Services
//
// These services are provided to the root
// hub code by UHCD.
//
USHORT UHCD_RootHub_ReadPort( IN PROOTHUB_PORT HubPort ) /*++
Routine Description:
Arguments:
Return Value:
returns the value of the requested hub register for a given controller identified by HcdPtr.
--*/ { PDEVICE_EXTENSION deviceExtension; USHORT dataVal;
deviceExtension = HubPort->DeviceObject->DeviceExtension;
dataVal = READ_PORT_USHORT( (PUSHORT) (deviceExtension->DeviceRegisters[0] + HubPort->Address));
// UHCD_KdPrint((2, "'RooHub -- read port %x\n", dataVal));
return dataVal; }
//BUGBUG need a return code here
VOID UHCD_RootHub_Timer( IN PVOID HcdPtr, IN LONG WaitTime, IN PROOTHUB_TIMER_ROUTINE RootHubTimerRoutine, IN PVOID TimerContext ) /*++
Routine Description:
This Routine can only be called at passive level, it iniializes a timer object and starts the timer for the root hub code.
Arguments:
HcdPtr - pointer passed to the root hub code during initialization.
WaitTime - time to wait before signaling (in ms)
RootHubTimerRoutine - root hub function to call when timer expires.
TimerContext - context pointer passed to RootHubTimerRoutine when timer expires.
Return Value:
None
--*/ { PDEVICE_OBJECT deviceObject = HcdPtr; PDEVICE_EXTENSION deviceExtension; LARGE_INTEGER dueTime; PROOT_HUB_TIMER rootHubTimer; ULONG timerIncerent;
deviceExtension = deviceObject->DeviceExtension;
rootHubTimer = GETHEAP(NonPagedPool, sizeof(ROOT_HUB_TIMER));
// compute timeout value (convert millisec to nanosec)
UHCD_ASSERT(WaitTime == 10); // BUGBUG root hub should always use 10ms
timerIncerent = KeQueryTimeIncrement() - 1;
dueTime.HighPart = -1; // round up to the next highest timer increment
dueTime.LowPart = -1 * (10000 * WaitTime + timerIncerent);
if (rootHubTimer) {
UHCD_KdPrint((2, "'roothub timer set %d (%x %x)\n", WaitTime, dueTime.LowPart, dueTime.HighPart)); rootHubTimer->DeviceObject = deviceObject; rootHubTimer->TimerRoutine = RootHubTimerRoutine; rootHubTimer->Context = TimerContext; deviceExtension->RootHubTimersActive++;
//BUGBUG Timer DPCs not working with NTKERN
//#ifdef NTKERN
//#pragma message ("warning: using workaround for bugs in ntkern")
// (VOID) KeDelayExecutionThread(KernelMode,
// FALSE,
// &dueTime);
// UHCD_RootHubTimerDpc(&rootHubTimer->Dpc,
// rootHubTimer,
// NULL,
// NULL);
//#else
KeInitializeTimer(&rootHubTimer->Timer); KeInitializeDpc(&rootHubTimer->Dpc, UHCD_RootHubTimerDpc, rootHubTimer);
KeSetTimer(&rootHubTimer->Timer, dueTime, &rootHubTimer->Dpc);
//#endif
}
}
VOID UHCD_RootHubTimerDpc( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 )
/*++
Routine Description:
This routine runs at DISPATCH_LEVEL IRQL.
Arguments:
Dpc - Pointer to the DPC object.
DeferredContext - supplies the RootHubTimer.
SystemArgument1 - not used.
SystemArgument2 - not used.
Return Value:
None.
--*/ { PROOT_HUB_TIMER rootHubTimer = DeferredContext; PDEVICE_EXTENSION deviceExtension;
UHCD_KdPrint((2, "'enter roothub timer Dpc\n"));
deviceExtension = rootHubTimer->DeviceObject->DeviceExtension;
//
// call the root hub code callback
//
rootHubTimer->TimerRoutine(rootHubTimer->Context);
UHCD_ASSERT(deviceExtension->RootHubTimersActive != 0);
deviceExtension->RootHubTimersActive--;
RETHEAP(rootHubTimer); }
VOID UHCD_RootHub_WritePort( IN PROOTHUB_PORT HubPort, IN USHORT DataVal ) /*++
Routine Description:
This routine is called by the root hub code to perform writes to a specific HC register.
Arguments:
Return Value:
None.
--*/ { PDEVICE_EXTENSION deviceExtension;
deviceExtension = HubPort->DeviceObject->DeviceExtension;
//
// mask off bits 13:15 (see UHCI design guide)
//
DataVal &= 0x1fff;
// UHCD_KdPrint((2, "'RooHub -- write port %x\n", DataVal));
WRITE_PORT_USHORT( (PUSHORT) (deviceExtension->DeviceRegisters[0] + HubPort->Address), DataVal); }
VOID UHCD_RootHub_InterruptTransferCancel( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
This function is called to cancel at interrupt transfer request pending in the root hub code .
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Return Value:
NT status code.
--*/ {
PHCD_URB urb; PUHCD_ENDPOINT endpoint;
UHCD_KdPrint((2, "'enter UHCD_RootHub_InterruptTransferCancel\n"));
IoReleaseCancelSpinLock(Irp->CancelIrql);
urb = (PHCD_URB) URB_FROM_IRP(Irp);
// BUGBUG we are not supporting queued
// transfers for the hub driver.
endpoint = HCD_AREA(urb).HcdEndpoint; ASSERT_ENDPOINT(endpoint); endpoint->ActiveTransfers[0] = NULL;
UHCD_CompleteIrp(DeviceObject, Irp, STATUS_CANCELLED, 0, urb);
UHCD_KdPrint((2, "'exit UHCD_RootHub_InterruptTransferCancel\n"));
}
VOID UHCD_RootHubPollDpc( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 )
/*++
Routine Description:
This routine runs at DISPATCH_LEVEL IRQL.
Arguments:
Dpc - Pointer to the DPC object.
DeferredContext - supplies the device object.
SystemArgument1 - not used.
SystemArgument2 - not used.
Return Value:
None.
--*/ { PDEVICE_OBJECT deviceObject = DeferredContext; PUHCD_ENDPOINT endpoint; PDEVICE_EXTENSION deviceExtension;
deviceExtension = deviceObject->DeviceExtension; endpoint = deviceExtension->RootHubInterruptEndpoint;
// LOGENTRY(LOG_MISC, 'rhpX', deviceExtension, 0, 0);
if (deviceExtension->InterruptObject) { UHCD_IsrDpc( NULL, DeferredContext, NULL, NULL); }
UHCD_CheckIdle(deviceObject);
if (endpoint == NULL || (deviceExtension->HcFlags & HCFLAG_HCD_STOPPED)) { // root hub has been closed, no more polling
// please.
LOGENTRY(LOG_MISC, 'rhpS', deviceExtension, 0, deviceExtension->HcFlags);
return; }
ASSERT_ENDPOINT(endpoint);
UHCD_RootHubPoll(deviceObject, endpoint);
}
|