1169 lines
31 KiB
C
1169 lines
31 KiB
C
/*++
|
|
|
|
Copyright (c) 1995,1996 Microsoft Corporation
|
|
:ts=4
|
|
|
|
Module Name:
|
|
|
|
roothub.c
|
|
|
|
Abstract:
|
|
|
|
The UHC driver for USB, this module contains the root hub
|
|
code.
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
8-13-96 : created
|
|
|
|
--*/
|
|
|
|
#include "wdm.h"
|
|
#include "stdarg.h"
|
|
#include "stdio.h"
|
|
|
|
#include "usbdi.h"
|
|
#include "hcdi.h"
|
|
#include "uhcd.h"
|
|
#include "dbg.h"
|
|
|
|
|
|
#define RH_STANDARD_REQ 0
|
|
#define RH_CLASS_REQ 1
|
|
|
|
#define MIN(x, y) (((x)<(y)) ? (x) : (y))
|
|
|
|
#define RH_CHECK_BUFFER(x, y, z)
|
|
|
|
#define RH_PORT_RESET 0x0200 // Port Reset 1=in reset
|
|
#define RH_PORT_ENABLE 0x0004 // Port Enable/Disable 1=enabled
|
|
#define RH_PORT_CONNECT 0x0001 // Current Connect Status 1=connect
|
|
#define RH_PORT_LS 0x0100 // Low Speed 1=ls device attached
|
|
#define RH_PORT_SUSPEND 0x1000 // Suspend 1=in suspend
|
|
#define RH_PORT_RESUME 0x0040 // resume port
|
|
|
|
#define RH_C_PORT_ENABLE 0x0008 // Port Enabled/Disabled Change
|
|
#define RH_C_PORT_CONNECT 0x0002 // Port Connect Status Change
|
|
|
|
//
|
|
// HUB feature selectors
|
|
//
|
|
#define C_HUB_LOCAL_POWER 0
|
|
#define C_HUB_OVER_CURRENT 1
|
|
#define PORT_CONNECTION 0
|
|
#define PORT_ENABLE 1
|
|
#define PORT_SUSPEND 2
|
|
#define PORT_OVER_CURRENT 3
|
|
#define PORT_RESET 4
|
|
#define PORT_POWER 8
|
|
#define PORT_LOW_SPEED 9
|
|
#define C_PORT_CONNECTION 16
|
|
#define C_PORT_ENABLE 17
|
|
#define C_PORT_SUSPEND 18
|
|
#define C_PORT_OVER_CURRENT 19
|
|
#define C_PORT_RESET 20
|
|
|
|
|
|
#define HUB_REQUEST_GET_STATUS 0
|
|
#define HUB_REQUEST_CLEAR_FEATURE 1
|
|
#define HUB_REQUEST_GET_STATE 2
|
|
#define HUB_REQUEST_SET_FEATURE 3
|
|
#define HUB_REQUEST_GET_DESCRIPTOR 6
|
|
#define HUB_REQUEST_SET_DESCRIPTOR 7
|
|
|
|
|
|
UCHAR RH_DeviceDescriptor[] = {0x12, //bLength
|
|
0x01, //bDescrpitorType
|
|
0x00, 0x01, //bcdUSB
|
|
0x09, //bDeviceClass
|
|
0x01, //bDeviceSubClass
|
|
0x00, //bDeviceProtocol
|
|
0x08, //bMaxPacketSize0
|
|
0x86, 0x80, //idVendor
|
|
0x0B, 0x0B, //idProduct
|
|
0x00, 0x00, //bcdDevice
|
|
0x00, //iManufacturer
|
|
0x00, //iProduct
|
|
0x00, //iSerialNumber
|
|
0x01};//bNumConfigurations
|
|
|
|
UCHAR RH_ConfigurationDescriptor[] =
|
|
/* Config Descriptor */
|
|
{0x09, //bLength
|
|
0x02, //bDescriptorType
|
|
0x19, 0x00, //wTotalLength
|
|
0x01, //bNumInterfaces
|
|
0x23, //iConfigurationValue
|
|
0x00, //iConfiguration
|
|
0x40, //bmAttributes
|
|
0x00, //MaxPower
|
|
|
|
/* Interface Descriptor */
|
|
0x09, //bLength
|
|
0x04, //bDescriptorType
|
|
0x00, //bInterfaceNumber
|
|
0x00, //bAlternateSetting
|
|
0x01, //bNumEndpoints
|
|
0x09, //bInterfaceClass
|
|
0x01, //bInterfaceSubClass
|
|
0x00, //bInterfaceProtocol
|
|
0x00, //iInterface
|
|
|
|
/* Endpoint Descriptor */
|
|
0x07, //bLength
|
|
0x05, //bDescriptorType
|
|
0x81, //bEndpointAddress
|
|
0x03, //bmAttributes
|
|
0x08, 0x00, //wMaxPacketSize
|
|
0x0a};//bInterval
|
|
|
|
UCHAR RH_HubDescriptor[] =
|
|
{0x09, //bLength
|
|
0x00, //bDescriptorType
|
|
0x02, //bNbrPorts
|
|
0x1B, 0x00, //wHubCharacteristics
|
|
// D0,D1 (11) - no power switching
|
|
// D2 (0) - not compund
|
|
// D3,D4 (11) - no overcurrent
|
|
// D5, D15 (0)
|
|
0x01, // bPwrOn2PwrGood
|
|
0x00, // bHubContrCurrent
|
|
0x00, // DeviceRemovable
|
|
0x00}; // PortPwrCtrlMask
|
|
|
|
VOID
|
|
RootHub_ResetTimerHandler(
|
|
IN PVOID TimerContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called as a result of scheduling a timer event for a root hub port
|
|
|
|
This function is called 10ms after the reset bit for the port is set,
|
|
the reset bit is cleared and the enable bit is set.
|
|
|
|
Arguments:
|
|
|
|
TimerContext - supplies hub port structure
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PROOTHUB_PORT hubPort = (PROOTHUB_PORT) TimerContext;
|
|
USHORT reg;
|
|
int i;
|
|
|
|
//
|
|
// BUGBUG this code assumes it is being called as a result
|
|
// of a reset_port command being sent to the hub.
|
|
//
|
|
LOGENTRY(LOG_MISC, 'rRTH', TimerContext, 0, 0);
|
|
|
|
//
|
|
// clear the RESET bit
|
|
//
|
|
reg = UHCD_RootHub_ReadPort(hubPort);
|
|
reg &= (~RH_PORT_RESET);
|
|
UHCD_RootHub_WritePort(hubPort, reg);
|
|
|
|
//
|
|
// Reset is complete, enable the port
|
|
//
|
|
// BUGBUG not sure why we need this loop
|
|
// original code from intel has this
|
|
//
|
|
|
|
for (i=1; i<10; i++) {
|
|
|
|
// Need a delay between clearing the port reset and setting
|
|
// the port enable. VIA suggests delaying 64 USB bit times,
|
|
// or 43us if those are low-speed bit times.
|
|
//
|
|
KeStallExecutionProcessor(50);
|
|
|
|
reg = UHCD_RootHub_ReadPort(hubPort);
|
|
|
|
if (reg & RH_PORT_ENABLE) {
|
|
// port is enabled
|
|
break;
|
|
}
|
|
|
|
// enable the port
|
|
reg |= RH_PORT_ENABLE;
|
|
UHCD_RootHub_WritePort(hubPort, reg);
|
|
}
|
|
|
|
//
|
|
// clear port connect & enable change bits
|
|
//
|
|
|
|
reg |= (RH_C_PORT_CONNECT | RH_C_PORT_ENABLE);
|
|
|
|
UHCD_RootHub_WritePort(hubPort, reg);
|
|
|
|
//
|
|
// Note that we have a change condition
|
|
//
|
|
hubPort->ResetChange = TRUE;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
RootHub_SuspendTimerHandler(
|
|
IN PVOID TimerContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
TimerContext - supplies hub port structure
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PROOTHUB_PORT hubPort = (PROOTHUB_PORT) TimerContext;
|
|
USHORT reg;
|
|
|
|
//
|
|
// BUGBUG this code assumes it is being called as a result
|
|
// of a resume_port command being sent to the hub.
|
|
//
|
|
LOGENTRY(LOG_MISC, 'rSTH', TimerContext, 0, 0);
|
|
|
|
|
|
//
|
|
// clear the SUSPEND bit
|
|
//
|
|
reg = UHCD_RootHub_ReadPort(hubPort);
|
|
reg &= (~RH_PORT_RESUME);
|
|
reg &= (~RH_PORT_SUSPEND);
|
|
UHCD_RootHub_WritePort(hubPort, reg);
|
|
|
|
//
|
|
// Note that we have a change condition
|
|
//
|
|
hubPort->SuspendChange = TRUE;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
PROOTHUB
|
|
RootHub_Initialize(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG NumberOfPorts,
|
|
IN BOOLEAN DoSelectiveSuspend
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the root hub:
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - HCD device object
|
|
|
|
Return Value:
|
|
|
|
ptr to root hub structure.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Perform any Root Hub hardware specific initialization here.
|
|
//
|
|
|
|
UCHAR i;
|
|
USHORT base = RH_PORT_SC_BASE;
|
|
PROOTHUB rootHub;
|
|
#if DBG
|
|
PULONG pDisabledPorts;
|
|
#endif
|
|
|
|
|
|
rootHub = GETHEAP(NonPagedPool, sizeof(ROOTHUB)+
|
|
sizeof(ROOTHUB_PORT)*NumberOfPorts);
|
|
#if DBG
|
|
pDisabledPorts = GETHEAP(NonPagedPool, sizeof(ULONG) * NumberOfPorts);
|
|
#endif
|
|
|
|
if (rootHub) {
|
|
LOGENTRY(LOG_MISC, 'rINI', DeviceObject, rootHub,
|
|
DoSelectiveSuspend);
|
|
|
|
rootHub->Sig = SIG_RH;
|
|
// rootHub->DeviceAddress = 0x00;
|
|
rootHub->DeviceObject = DeviceObject;
|
|
rootHub->NumberOfPorts = (UCHAR) NumberOfPorts;
|
|
rootHub->ConfigurationValue = 0;
|
|
rootHub->DoSelectiveSuspend =
|
|
DoSelectiveSuspend;
|
|
|
|
for (i=0; i<rootHub->NumberOfPorts; i++) {
|
|
rootHub->Port[i].ResetChange = FALSE;
|
|
rootHub->Port[i].SuspendChange = FALSE;
|
|
rootHub->Port[i].DeviceObject = DeviceObject;
|
|
rootHub->Port[i].Address = base;
|
|
base+=2;
|
|
}
|
|
|
|
#if DBG
|
|
rootHub->DisabledPort = pDisabledPorts;
|
|
|
|
if (pDisabledPorts != NULL) {
|
|
RtlZeroMemory(pDisabledPorts, sizeof(ULONG) * NumberOfPorts);
|
|
}
|
|
#else
|
|
rootHub->DisabledPort = NULL;
|
|
#endif
|
|
|
|
}
|
|
|
|
return rootHub;
|
|
}
|
|
|
|
|
|
RHSTATUS
|
|
RootHub_Endpoint0(
|
|
IN PROOTHUB RootHub,
|
|
IN PRH_SETUP SetupPacket,
|
|
IN PUCHAR DeviceAddress,
|
|
IN PUCHAR Buffer,
|
|
IN OUT PULONG BufferLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine should be called anytime there is any control transfer to
|
|
the RootHub endpoint 0. This procedure behaves like a real device would
|
|
in that it parses the command data, performs the requested operation,
|
|
using or modifying the data area as appropriate.
|
|
The return code is equivalent to the 'status stage' of a normal control
|
|
transfer.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
root hub transfer status code
|
|
|
|
--*/
|
|
{
|
|
RHSTATUS rhStatus;
|
|
|
|
ASSERT_RH(RootHub);
|
|
LOGENTRY(LOG_MISC, 'rEP0', RootHub, SetupPacket, DeviceAddress);
|
|
if (SetupPacket->bmRequestType.Type == RH_STANDARD_REQ) {
|
|
rhStatus =
|
|
RootHub_StandardCommand(RootHub,
|
|
SetupPacket,
|
|
DeviceAddress,
|
|
Buffer,
|
|
BufferLength);
|
|
|
|
} else if (SetupPacket->bmRequestType.Type == RH_CLASS_REQ) {
|
|
rhStatus =
|
|
RootHub_ClassCommand(RootHub,
|
|
SetupPacket,
|
|
Buffer,
|
|
BufferLength);
|
|
} else {
|
|
rhStatus = RH_STALL;
|
|
// probably a bug in the hub driver
|
|
TRAP();
|
|
}
|
|
|
|
return rhStatus;
|
|
}
|
|
|
|
|
|
#define RH_DEV_TO_HOST 1
|
|
#define RH_HOST_TO_DEV 0
|
|
|
|
RHSTATUS
|
|
RootHub_StandardCommand(
|
|
IN PROOTHUB RootHub,
|
|
IN PRH_SETUP SetupPacket,
|
|
IN OUT PUCHAR DeviceAddress,
|
|
IN OUT PUCHAR Buffer,
|
|
IN OUT PULONG BufferLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceAddress - pointer to HCD address assigned to the
|
|
root hub
|
|
|
|
SetupPacket - pointer to a SetupPacket packet
|
|
|
|
Return Value:
|
|
|
|
Root Hub status code.
|
|
|
|
--*/
|
|
{
|
|
PVOID descriptor = NULL;
|
|
ULONG length;
|
|
RHSTATUS rhStatus = RH_STALL;
|
|
|
|
ASSERT_RH(RootHub);
|
|
LOGENTRY(LOG_MISC, 'rSCM', RootHub, SetupPacket, DeviceAddress);
|
|
//
|
|
// switch on the command
|
|
//
|
|
switch (SetupPacket->bRequest) {
|
|
case USB_REQUEST_SET_ADDRESS:
|
|
//
|
|
//
|
|
//
|
|
if (SetupPacket->wIndex == 0 &&
|
|
SetupPacket->wLength == 0 &&
|
|
SetupPacket->bmRequestType.Dir == RH_HOST_TO_DEV) {
|
|
*DeviceAddress =
|
|
(UCHAR)SetupPacket->wValue.W;
|
|
|
|
rhStatus = RH_SUCCESS;
|
|
LOGENTRY(LOG_MISC, 'rSAD', *DeviceAddress, 0, 0);
|
|
}
|
|
break;
|
|
|
|
case USB_REQUEST_GET_DESCRIPTOR:
|
|
{
|
|
PVOID descriptor = NULL;
|
|
ULONG siz;
|
|
UCHAR descriptorIndex, descriptorType;
|
|
|
|
descriptorType = (UCHAR) SetupPacket->wValue.hiPart;
|
|
descriptorIndex = (UCHAR) SetupPacket->wValue.lowPart;
|
|
|
|
switch (descriptorType) {
|
|
|
|
case USB_DEVICE_DESCRIPTOR_TYPE:
|
|
if (descriptorIndex == 0 &&
|
|
SetupPacket->bmRequestType.Dir == RH_DEV_TO_HOST) {
|
|
|
|
siz = sizeof(RH_DeviceDescriptor);
|
|
descriptor = RH_DeviceDescriptor;
|
|
LOGENTRY(LOG_MISC, 'rGDS', descriptor, siz, 0);
|
|
}
|
|
break;
|
|
|
|
case USB_CONFIGURATION_DESCRIPTOR_TYPE:
|
|
if (descriptorIndex == 0 &&
|
|
SetupPacket->bmRequestType.Dir == RH_DEV_TO_HOST) {
|
|
siz = sizeof(RH_ConfigurationDescriptor);
|
|
descriptor = RH_ConfigurationDescriptor;
|
|
LOGENTRY(LOG_MISC, 'rGCS', descriptor, siz, 0);
|
|
}
|
|
break;
|
|
|
|
//
|
|
// BUGBUG these descriptor types not handled
|
|
//
|
|
case USB_STRING_DESCRIPTOR_TYPE:
|
|
default:
|
|
TRAP();
|
|
} /* descriptorType */
|
|
|
|
if (descriptor) {
|
|
RH_CHECK_BUFFER(SetupPacket->wLength,
|
|
*BufferLength,
|
|
siz);
|
|
|
|
length = MIN(*BufferLength, siz);
|
|
|
|
RtlCopyMemory(Buffer, descriptor, length);
|
|
*BufferLength = length;
|
|
rhStatus = RH_SUCCESS;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case USB_REQUEST_GET_STATUS:
|
|
//
|
|
// get_device_status
|
|
//
|
|
// report that we are self powered
|
|
//
|
|
// BUGBUG
|
|
// are we self powered?
|
|
// are we a remote wakeup source
|
|
//
|
|
// see section 9.4.5 USB 1.0 spec
|
|
//
|
|
{
|
|
PUSHORT status = (PUSHORT) Buffer;
|
|
|
|
if (SetupPacket->wValue.W == 0 && //mbz
|
|
SetupPacket->wLength == 2 &&
|
|
SetupPacket->wIndex == 0 && //device
|
|
SetupPacket->bmRequestType.Dir == RH_DEV_TO_HOST) {
|
|
|
|
*status = USB_GETSTATUS_SELF_POWERED;
|
|
*BufferLength = sizeof(*status);
|
|
rhStatus = RH_SUCCESS;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case USB_REQUEST_GET_CONFIGURATION:
|
|
//
|
|
// get_device_configuration
|
|
//
|
|
if (SetupPacket->wValue.W == 0 && //mbz
|
|
SetupPacket->wIndex == 0 && //mbz
|
|
SetupPacket->wLength == 1 &&
|
|
SetupPacket->bmRequestType.Dir == RH_DEV_TO_HOST) {
|
|
|
|
TEST_TRAP();
|
|
RH_CHECK_BUFFER(SetupPacket->wLength,
|
|
*BufferLength,
|
|
sizeof(RootHub->Configuration));
|
|
|
|
length = MIN(*BufferLength, sizeof(RootHub->ConfigurationValue));
|
|
RtlCopyMemory(Buffer, &RootHub->ConfigurationValue, length);
|
|
*BufferLength = length;
|
|
rhStatus = RH_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case USB_REQUEST_CLEAR_FEATURE:
|
|
// bugbug, required
|
|
TRAP();
|
|
break;
|
|
case USB_REQUEST_SET_CONFIGURATION:
|
|
{
|
|
PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor =
|
|
(PUSB_CONFIGURATION_DESCRIPTOR) RH_ConfigurationDescriptor;
|
|
|
|
if (SetupPacket->wIndex == 0 && // mbz
|
|
SetupPacket->wLength == 0 && // mbz
|
|
SetupPacket->bmRequestType.Dir == RH_HOST_TO_DEV &&
|
|
(SetupPacket->wValue.W ==
|
|
configurationDescriptor->bConfigurationValue ||
|
|
SetupPacket->wValue.W == 0)) {
|
|
|
|
RootHub->ConfigurationValue = (UCHAR) SetupPacket->wValue.W;
|
|
rhStatus = RH_SUCCESS;
|
|
LOGENTRY(LOG_MISC, 'rSEC', RootHub->ConfigurationValue, 0, 0);
|
|
}
|
|
}
|
|
break;
|
|
case USB_REQUEST_SET_FEATURE:
|
|
// bugbug, required
|
|
TRAP();
|
|
break;
|
|
//
|
|
// these commands are optional for the hub
|
|
//
|
|
case USB_REQUEST_SET_DESCRIPTOR:
|
|
case USB_REQUEST_SET_INTERFACE:
|
|
case USB_REQUEST_GET_INTERFACE:
|
|
case USB_REQUEST_SYNC_FRAME:
|
|
default:
|
|
// bad command, probably a bug in the
|
|
// hub driver
|
|
TRAP();
|
|
break;
|
|
}
|
|
|
|
return rhStatus;
|
|
}
|
|
|
|
|
|
RHSTATUS
|
|
RootHub_ClassCommand(
|
|
IN PROOTHUB RootHub,
|
|
IN PRH_SETUP SetupPacket,
|
|
IN OUT PUCHAR Buffer,
|
|
IN OUT PULONG BufferLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Root Hub status code.
|
|
|
|
--*/
|
|
{
|
|
PVOID descriptor = NULL;
|
|
ULONG length;
|
|
RHSTATUS rhStatus = RH_STALL;
|
|
|
|
ASSERT_RH(RootHub);
|
|
LOGENTRY(LOG_MISC, 'rCCM', RootHub, SetupPacket, 0);
|
|
//
|
|
// switch on the command
|
|
//
|
|
|
|
switch (SetupPacket->bRequest) {
|
|
case HUB_REQUEST_GET_STATUS:
|
|
//
|
|
//
|
|
//
|
|
if (SetupPacket->bmRequestType.Recipient == RECIPIENT_PORT) {
|
|
//
|
|
// get port status
|
|
//
|
|
PRH_PORT_STATUS portStatus;
|
|
//
|
|
// see if we have a valid request
|
|
//
|
|
if (SetupPacket->wIndex > 0 &&
|
|
SetupPacket->wIndex <= RH_NUMBER_OF_PORTS &&
|
|
SetupPacket->wLength >= sizeof(*portStatus)) {
|
|
|
|
USHORT reg;
|
|
PROOTHUB_PORT hubPort =
|
|
&RootHub->Port[SetupPacket->wIndex-1];
|
|
|
|
ASSERT(sizeof(*portStatus) == 4);
|
|
ASSERT(*BufferLength >= sizeof(*portStatus));
|
|
portStatus = (PRH_PORT_STATUS) Buffer;
|
|
RtlZeroMemory(Buffer, sizeof(*portStatus));
|
|
|
|
reg = UHCD_RootHub_ReadPort (hubPort);
|
|
|
|
//
|
|
// get port status bits
|
|
//
|
|
portStatus->Connected = (reg & RH_PORT_CONNECT)
|
|
#if DBG
|
|
&& !(RootHub->DisabledPort[SetupPacket->wIndex - 1]
|
|
& UHCD_FAKE_DISCONNECT)
|
|
#endif
|
|
? 1:0;
|
|
portStatus->Enabled = (reg & RH_PORT_ENABLE) ? 1:0;
|
|
portStatus->Suspended = (reg & RH_PORT_SUSPEND) ? 1:0;
|
|
portStatus->OverCurrent = 0; // never report overcurrent
|
|
portStatus->Reset = (reg & RH_PORT_RESET) ? 1:0;
|
|
portStatus->PowerOn = 1; // always on
|
|
portStatus->LowSpeed = (reg & RH_PORT_LS) ? 1:0;
|
|
|
|
//
|
|
// get port change bits
|
|
//
|
|
portStatus->ConnectChange = (reg & RH_C_PORT_CONNECT)
|
|
#if DBG
|
|
|| (RootHub->DisabledPort[SetupPacket->wIndex - 1]
|
|
& UHCD_FAKE_CONNECT_CHANGE)
|
|
#endif
|
|
? 1:0;
|
|
portStatus->EnableChange = (reg & RH_C_PORT_ENABLE) ? 1:0;
|
|
portStatus->SuspendChange = hubPort->SuspendChange ? 1:0;;
|
|
portStatus->OverCurrentChange = 0;
|
|
portStatus->ResetChange = hubPort->ResetChange ? 1:0;
|
|
|
|
LOGENTRY(LOG_MISC, 'rPST',
|
|
portStatus, *((PULONG) (portStatus)), reg);
|
|
rhStatus = RH_SUCCESS;
|
|
}
|
|
} else {
|
|
//
|
|
// get hub status
|
|
//
|
|
TRAP();
|
|
}
|
|
break;
|
|
|
|
case HUB_REQUEST_CLEAR_FEATURE:
|
|
//
|
|
// Hub/Port Clear Feature
|
|
//
|
|
LOGENTRY(LOG_MISC, 'rCFR',
|
|
SetupPacket->bmRequestType.Recipient, 0, 0);
|
|
if (SetupPacket->bmRequestType.Recipient == RECIPIENT_PORT) {
|
|
//
|
|
// clear port feature
|
|
//
|
|
LOGENTRY(LOG_MISC, 'rCPR', SetupPacket->wValue.W, 0, 0);
|
|
switch(SetupPacket->wValue.W) {
|
|
//
|
|
//
|
|
//
|
|
case PORT_ENABLE:
|
|
// BUGBUG
|
|
// disable the port
|
|
|
|
if (SetupPacket->wIndex > 0 &&
|
|
SetupPacket->wIndex <= RH_NUMBER_OF_PORTS) {
|
|
|
|
USHORT reg;
|
|
PROOTHUB_PORT hubPort =
|
|
&RootHub->Port[SetupPacket->wIndex-1];
|
|
|
|
reg = UHCD_RootHub_ReadPort (hubPort);
|
|
|
|
// clear the enable bit
|
|
reg &= ~(RH_PORT_ENABLE);
|
|
|
|
LOGENTRY(LOG_MISC, 'rDsP', SetupPacket->wIndex, 0, 0);
|
|
UHCD_RootHub_WritePort(hubPort, reg);
|
|
|
|
rhStatus = RH_SUCCESS;
|
|
}
|
|
break;
|
|
case PORT_POWER:
|
|
// BUGBUG
|
|
// this should turn off power, but for now we silently
|
|
// ignore it.
|
|
//
|
|
rhStatus = RH_SUCCESS;
|
|
break;
|
|
case PORT_CONNECTION:
|
|
case PORT_OVER_CURRENT:
|
|
case PORT_LOW_SPEED:
|
|
case PORT_RESET:
|
|
TRAP();
|
|
break;
|
|
|
|
case C_PORT_CONNECTION:
|
|
case C_PORT_ENABLE:
|
|
|
|
// validate the port number
|
|
if (SetupPacket->wIndex > 0 &&
|
|
SetupPacket->wIndex <= RH_NUMBER_OF_PORTS) {
|
|
|
|
USHORT reg;
|
|
PROOTHUB_PORT hubPort =
|
|
&RootHub->Port[SetupPacket->wIndex-1];
|
|
|
|
reg = UHCD_RootHub_ReadPort (hubPort);
|
|
|
|
// mask off the change bits
|
|
reg &= ~(RH_C_PORT_ENABLE | RH_C_PORT_CONNECT);
|
|
|
|
if (SetupPacket->wValue.W == C_PORT_CONNECTION) {
|
|
#if DBG
|
|
RootHub->DisabledPort[SetupPacket->wIndex - 1]
|
|
&= ~UHCD_FAKE_CONNECT_CHANGE;
|
|
#endif
|
|
reg |= RH_C_PORT_CONNECT;
|
|
LOGENTRY(LOG_MISC, 'rCPC', SetupPacket->wIndex, 0, 0);
|
|
} else {
|
|
// C_PORT_ENABLE
|
|
reg |= RH_C_PORT_ENABLE;
|
|
LOGENTRY(LOG_MISC, 'rCLE', SetupPacket->wIndex, 0, 0);
|
|
}
|
|
UHCD_RootHub_WritePort(hubPort, reg);
|
|
|
|
rhStatus = RH_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case C_PORT_RESET:
|
|
if (SetupPacket->wIndex >0 &&
|
|
SetupPacket->wIndex <= RH_NUMBER_OF_PORTS) {
|
|
|
|
PROOTHUB_PORT hubPort =
|
|
&RootHub->Port[SetupPacket->wIndex-1];
|
|
|
|
LOGENTRY(LOG_MISC, 'rCLP', SetupPacket->wIndex,
|
|
hubPort->ResetChange, 0);
|
|
|
|
hubPort->ResetChange = FALSE;
|
|
|
|
LOGENTRY(LOG_MISC, 'rCLR', SetupPacket->wIndex, 0, 0);
|
|
rhStatus = RH_SUCCESS;
|
|
|
|
}
|
|
break;
|
|
|
|
case PORT_SUSPEND:
|
|
//
|
|
// clearing port suspend triggers a resume
|
|
//
|
|
if (SetupPacket->wIndex > 0 &&
|
|
SetupPacket->wIndex <= RH_NUMBER_OF_PORTS) {
|
|
|
|
USHORT reg;
|
|
PROOTHUB_PORT hubPort =
|
|
&RootHub->Port[SetupPacket->wIndex-1];
|
|
|
|
reg = UHCD_RootHub_ReadPort(hubPort);
|
|
|
|
//
|
|
// signal resume on the port
|
|
//
|
|
if (RootHub->DoSelectiveSuspend) {
|
|
reg |= RH_PORT_RESUME;
|
|
UHCD_RootHub_WritePort(hubPort, reg);
|
|
|
|
UHCD_RootHub_Timer(hubPort->DeviceObject,
|
|
RH_RESET_TIMELENGTH, //bugbug
|
|
&RootHub_SuspendTimerHandler,
|
|
(PVOID)hubPort);
|
|
|
|
rhStatus = RH_SUCCESS;
|
|
LOGENTRY(LOG_MISC, 'rCPS', hubPort, 0, 0);
|
|
} else {
|
|
rhStatus = RH_SUCCESS;
|
|
}
|
|
#ifdef MAX_DEBUG
|
|
TEST_TRAP();
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case C_PORT_SUSPEND:
|
|
if (SetupPacket->wIndex > 0 &&
|
|
SetupPacket->wIndex <= RH_NUMBER_OF_PORTS) {
|
|
|
|
PROOTHUB_PORT hubPort =
|
|
&RootHub->Port[SetupPacket->wIndex-1];
|
|
|
|
hubPort->SuspendChange = FALSE;
|
|
|
|
LOGENTRY(LOG_MISC, 'rCls', SetupPacket->wIndex, 0, 0);
|
|
rhStatus = RH_SUCCESS;
|
|
#ifdef MAX_DEBUG
|
|
TEST_TRAP();
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case C_PORT_OVER_CURRENT:
|
|
TEST_TRAP();
|
|
rhStatus = RH_SUCCESS;
|
|
break;
|
|
default:
|
|
TRAP();
|
|
}
|
|
} else {
|
|
//
|
|
// clear hub feature
|
|
//
|
|
LOGENTRY(LOG_MISC, 'rCHR', SetupPacket->wValue.W, 0, 0);
|
|
switch(SetupPacket->wValue.W) {
|
|
case C_HUB_LOCAL_POWER:
|
|
case C_HUB_OVER_CURRENT:
|
|
default:
|
|
TRAP();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HUB_REQUEST_GET_STATE:
|
|
//
|
|
//
|
|
//
|
|
TRAP();
|
|
break;
|
|
|
|
case HUB_REQUEST_SET_FEATURE:
|
|
//
|
|
// Hub/Port feature request
|
|
//
|
|
if (SetupPacket->bmRequestType.Recipient == RECIPIENT_PORT) {
|
|
//
|
|
// set port feature
|
|
//
|
|
switch(SetupPacket->wValue.W) {
|
|
case PORT_RESET:
|
|
LOGENTRY(LOG_MISC, 'rRES', SetupPacket->wIndex, 0, 0);
|
|
if (SetupPacket->wIndex > 0 &&
|
|
SetupPacket->wIndex <= RH_NUMBER_OF_PORTS) {
|
|
|
|
USHORT reg;
|
|
PROOTHUB_PORT hubPort =
|
|
&RootHub->Port[SetupPacket->wIndex-1];
|
|
|
|
reg = UHCD_RootHub_ReadPort(hubPort);
|
|
|
|
if (reg & RH_PORT_RESET) {
|
|
//
|
|
// stall if the port is already in reset
|
|
//
|
|
rhStatus = RH_STALL;
|
|
TEST_TRAP();
|
|
} else {
|
|
//
|
|
// drive reset on the port
|
|
// for RH_RESET_TIMELENGTH (in ms)
|
|
//
|
|
reg |= RH_PORT_RESET;
|
|
UHCD_RootHub_WritePort(hubPort, reg);
|
|
|
|
UHCD_RootHub_Timer(hubPort->DeviceObject,
|
|
RH_RESET_TIMELENGTH,
|
|
&RootHub_ResetTimerHandler,
|
|
(PVOID)hubPort);
|
|
|
|
rhStatus = RH_SUCCESS;
|
|
LOGENTRY(LOG_MISC, 'rSTM', hubPort, 0, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PORT_SUSPEND:
|
|
if (SetupPacket->wIndex > 0 &&
|
|
SetupPacket->wIndex <= RH_NUMBER_OF_PORTS) {
|
|
|
|
USHORT reg;
|
|
PROOTHUB_PORT hubPort =
|
|
&RootHub->Port[SetupPacket->wIndex-1];
|
|
|
|
reg = UHCD_RootHub_ReadPort(hubPort);
|
|
|
|
if ((reg & (RH_PORT_SUSPEND | RH_PORT_ENABLE)) ==
|
|
(RH_PORT_SUSPEND | RH_PORT_ENABLE)) {
|
|
//
|
|
// stall if the port is already in suspended
|
|
//
|
|
|
|
// 9/7/2000: Just ignore this case and return success
|
|
//
|
|
rhStatus = RH_SUCCESS;
|
|
|
|
} else {
|
|
//
|
|
// write the suspend bit
|
|
//
|
|
#ifdef MAX_DEBUG
|
|
TRAP();
|
|
#endif
|
|
|
|
if (RootHub->DoSelectiveSuspend) {
|
|
reg |= (RH_PORT_SUSPEND | RH_PORT_ENABLE);
|
|
UHCD_RootHub_WritePort(hubPort, reg);
|
|
|
|
rhStatus = RH_SUCCESS;
|
|
} else {
|
|
// just pretend we did it for the piix4
|
|
rhStatus = RH_SUCCESS;
|
|
LOGENTRY(LOG_MISC, 'rSno', hubPort, reg, rhStatus);
|
|
}
|
|
|
|
LOGENTRY(LOG_MISC, 'rSUS', hubPort, reg, rhStatus);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PORT_ENABLE:
|
|
if (SetupPacket->wIndex > 0 &&
|
|
SetupPacket->wIndex <= RH_NUMBER_OF_PORTS) {
|
|
|
|
USHORT reg;
|
|
PROOTHUB_PORT hubPort =
|
|
&RootHub->Port[SetupPacket->wIndex-1];
|
|
|
|
reg = UHCD_RootHub_ReadPort(hubPort);
|
|
#ifdef MAX_DEBUG
|
|
TRAP();
|
|
#endif
|
|
reg |= RH_PORT_ENABLE;
|
|
UHCD_RootHub_WritePort(hubPort, reg);
|
|
|
|
rhStatus = RH_SUCCESS;
|
|
LOGENTRY(LOG_MISC, 'rPEN', hubPort, 0, 0);
|
|
}
|
|
break;
|
|
case PORT_POWER:
|
|
// just return success for a power on request
|
|
rhStatus = RH_SUCCESS;
|
|
break;
|
|
case PORT_CONNECTION:
|
|
case PORT_OVER_CURRENT:
|
|
case PORT_LOW_SPEED:
|
|
|
|
case C_PORT_CONNECTION:
|
|
case C_PORT_ENABLE:
|
|
case C_PORT_SUSPEND:
|
|
case C_PORT_OVER_CURRENT:
|
|
case C_PORT_RESET:
|
|
default:
|
|
TRAP();
|
|
}
|
|
} else {
|
|
//
|
|
// set hub feature
|
|
//
|
|
switch(SetupPacket->wValue.W) {
|
|
case C_HUB_LOCAL_POWER:
|
|
case C_HUB_OVER_CURRENT:
|
|
default:
|
|
TRAP();
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case HUB_REQUEST_GET_DESCRIPTOR:
|
|
//
|
|
// return the hub descriptor
|
|
//
|
|
if (SetupPacket->wValue.W == 0 &&
|
|
// we already know it is a class command
|
|
SetupPacket->bmRequestType.Dir == RH_DEV_TO_HOST) {
|
|
LOGENTRY(LOG_MISC, 'rGHD', SetupPacket, SetupPacket->wLength, 0);
|
|
|
|
RH_CHECK_BUFFER(SetupPacket->wLength,
|
|
*BufferLength,
|
|
sizeof(RH_HubDescriptor));
|
|
|
|
length = MIN(*BufferLength, sizeof(RH_HubDescriptor));
|
|
|
|
RtlCopyMemory(Buffer, RH_HubDescriptor, length);
|
|
*BufferLength = length;
|
|
rhStatus = RH_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case HUB_REQUEST_SET_DESCRIPTOR:
|
|
//
|
|
//
|
|
//
|
|
TRAP();
|
|
break;
|
|
|
|
default:
|
|
// bad command
|
|
TRAP();
|
|
break;
|
|
}
|
|
|
|
return rhStatus;
|
|
}
|
|
|
|
RHSTATUS
|
|
RootHub_Endpoint1(
|
|
IN PROOTHUB RootHub,
|
|
IN PUCHAR Buffer,
|
|
IN OUT PULONG BufferLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
root hub transfer status code
|
|
|
|
--*/
|
|
{
|
|
RHSTATUS rhStatus = RH_NAK;
|
|
PROOTHUB_PORT hubPort;
|
|
USHORT reg;
|
|
PUCHAR bitmap;
|
|
ULONG bit = 2, i;
|
|
|
|
ASSERT_RH(RootHub);
|
|
LOGENTRY(LOG_MISC, 'rPOL', RootHub, Buffer, *BufferLength);
|
|
if (*BufferLength < sizeof(*bitmap)) {
|
|
UHCD_KdTrap(("Bad buffer length passed to root hub\n"));
|
|
return RH_STALL;
|
|
}
|
|
|
|
bitmap = (PUCHAR) Buffer;
|
|
*bitmap = 0;
|
|
// one byte of data returned.
|
|
*BufferLength = 1;
|
|
|
|
UHCD_ASSERT(RootHub->NumberOfPorts < 8);
|
|
for (i=0; i< RootHub->NumberOfPorts; i++) {
|
|
hubPort = &RootHub->Port[i];
|
|
|
|
reg = UHCD_RootHub_ReadPort(hubPort);
|
|
|
|
if (!(reg & RH_PORT_RESET) &&
|
|
((reg & RH_C_PORT_ENABLE) ||
|
|
(reg & RH_C_PORT_CONNECT) ||
|
|
hubPort->ResetChange ||
|
|
hubPort->SuspendChange
|
|
#if DBG
|
|
|| (RootHub->DisabledPort[i] & UHCD_FAKE_CONNECT_CHANGE)
|
|
#endif
|
|
)) {
|
|
|
|
//
|
|
// note we have a change on this port
|
|
//
|
|
*bitmap |= bit;
|
|
rhStatus = RH_SUCCESS;
|
|
LOGENTRY(LOG_MISC, 'rSTA', RootHub, reg, hubPort->ResetChange);
|
|
}
|
|
bit = bit<<1;
|
|
}
|
|
|
|
return rhStatus;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
RootHub_PortsIdle(
|
|
IN PROOTHUB RootHub
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
root hub idle detect
|
|
|
|
--*/
|
|
{
|
|
PROOTHUB_PORT hubPort;
|
|
USHORT reg;
|
|
ULONG i;
|
|
BOOLEAN idle = TRUE;
|
|
|
|
|
|
for (i=0; i< RootHub->NumberOfPorts; i++) {
|
|
hubPort = &RootHub->Port[i];
|
|
|
|
reg = UHCD_RootHub_ReadPort(hubPort);
|
|
|
|
#if DBG
|
|
if(RootHub->DisabledPort[i] & UHCD_FAKE_DISCONNECT) {
|
|
reg &= ~RH_PORT_CONNECT;
|
|
}
|
|
#endif
|
|
|
|
if ((reg & (RH_PORT_CONNECT | RH_PORT_SUSPEND)) == RH_PORT_CONNECT) {
|
|
idle = FALSE;
|
|
}
|
|
|
|
#if DBG
|
|
else {
|
|
LOGENTRY(LOG_MISC, 'rIDL', RootHub, reg, 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return idle;
|
|
}
|