1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- hub.c
- Abstract:
- This module implements support for interacting with standard USB Hubs.
- Author:
- Evan Green 19-Jan-2013
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/driver.h>
- #include "usbcore.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- /*++
- Structure Description:
- This structure stores internal USB hub context.
- Members:
- DeviceHandle - Stores a pointer to the USB device handle.
- IoBuffer - Stores a pointer to the I/O buffer used to back the hub data
- transfers.
- ControlTransfer - Stores a pointer to the control transfer used to
- communicate with the hub.
- ControlTransferLock - Stores a pointer to the lock that synchronizes
- access to the control transfer.
- InterruptTransfer - Stores a pointer to the interrupt transfer used for
- hub status notifications.
- PortCount - Stores the number of downstream ports in the hub.
- PowerDelayIn2ms - Stores the time, in 2ms intervals, from the time the
- power-on sequence begins on a port until the power is good on that
- port. Software uses this value to determine how long to wait before
- accessing a powered-on port.
- HasIndicators - Stores a boolean indicating whether or not the hub has
- port indicator LEDs.
- HubStatus - Stores the status of each of the hub's ports.
- Interface - Stores a pointer to the hub interface description.
- InterruptWorkItem - Stores a pointer to the work item queued when the
- interrupt transfer completes.
- ChangedPorts - Stores the result of the interrupt transfer, the bitfield
- of changed ports.
- --*/
- struct _USB_HUB {
- HANDLE DeviceHandle;
- PIO_BUFFER IoBuffer;
- PUSB_TRANSFER ControlTransfer;
- PQUEUED_LOCK ControlTransferLock;
- PUSB_TRANSFER InterruptTransfer;
- UCHAR PortCount;
- UCHAR PowerUpDelayIn2ms;
- BOOL HasIndicators;
- USB_HUB_STATUS HubStatus;
- PUSB_INTERFACE_DESCRIPTION Interface;
- PWORK_ITEM InterruptWorkItem;
- USHORT ChangedPorts;
- };
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- KSTATUS
- UsbpGetHubStatus (
- PUSB_HUB Hub,
- BOOL ForceRefresh
- );
- KSTATUS
- UsbpGetRootHubStatus (
- PUSB_HUB RootHub
- );
- KSTATUS
- UsbpSetHubStatus (
- PUSB_HUB Hub
- );
- VOID
- UsbpHubUpdatePortStatus (
- PUSB_HUB Hub,
- ULONG PortIndex,
- ULONG HardwareStatus
- );
- KSTATUS
- UsbpResetHub (
- PUSB_HUB Hub
- );
- KSTATUS
- UsbpReadHubDescriptor (
- PUSB_HUB Hub
- );
- KSTATUS
- UsbpHubSendControlTransfer (
- PUSB_HUB Hub,
- PULONG LengthTransferred
- );
- KSTATUS
- UsbpHubGetHubStatus (
- PUSB_HUB Hub,
- PULONG HubStatus
- );
- KSTATUS
- UsbpHubGetPortStatus (
- PUSB_HUB Hub,
- ULONG PortNumber,
- PULONG PortStatus
- );
- KSTATUS
- UsbpHubSetOrClearFeature (
- PUSB_HUB Hub,
- BOOL SetFeature,
- USHORT Feature,
- USHORT Port
- );
- VOID
- UsbpHubInterruptTransferCompletion (
- PUSB_TRANSFER Transfer
- );
- VOID
- UsbpHubInterruptTransferCompletionWorker (
- PVOID Parameter
- );
- KSTATUS
- UsbpHubClearPortChangeBits (
- PUSB_HUB Hub,
- ULONG PortNumber,
- ULONG PortStatus
- );
- VOID
- UsbpHubAddDevice (
- PUSB_HUB Hub,
- ULONG PortIndex
- );
- KSTATUS
- UsbpHubEnablePortPower (
- PUSB_HUB Hub,
- ULONG PortIndex
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ------------------------------------------------------------------ Functions
- //
- USB_API
- KSTATUS
- UsbCreateHub (
- HANDLE DeviceHandle,
- PUSB_HUB *Hub
- )
- /*++
- Routine Description:
- This routine creates a new USB hub device. This routine must be called at
- low level.
- Arguments:
- DeviceHandle - Supplies the open device handle to the hub.
- Hub - Supplies a pointer where a pointer to the hub context will be
- returned.
- Return Value:
- Status code.
- --*/
- {
- ULONG AllocationSize;
- UINTN BufferAlignment;
- UINTN BufferSize;
- PUSB_DEVICE Device;
- PVOID HubStatus;
- ULONG IoBufferFlags;
- ULONG MaxControlTransferSize;
- ULONG MaxInterruptSize;
- PUSB_HUB NewHub;
- KSTATUS Status;
- ASSERT(Hub != NULL);
- ASSERT(KeGetRunLevel() == RunLevelLow);
- HubStatus = NULL;
- NewHub = MmAllocatePagedPool(sizeof(USB_HUB), USB_CORE_ALLOCATION_TAG);
- if (NewHub == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateHubEnd;
- }
- RtlZeroMemory(NewHub, sizeof(USB_HUB));
- NewHub->DeviceHandle = DeviceHandle;
- //
- // Create an I/O buffer for both control and interrupt transfers. Since the
- // I/O buffer allocation rounds up to a page anyway, this allocation
- // accounts for the maximum possible number of ports on a hub: 127.
- //
- BufferAlignment = MmGetIoBufferAlignment();
- MaxControlTransferSize = ALIGN_RANGE_UP(USB_HUB_MAX_CONTROL_TRANSFER_SIZE,
- BufferAlignment);
- MaxInterruptSize = ALIGN_RANGE_UP(USB_HUB_MAX_INTERRUPT_SIZE,
- BufferAlignment);
- BufferSize = MaxControlTransferSize + MaxInterruptSize;
- IoBufferFlags = IO_BUFFER_FLAG_PHYSICALLY_CONTIGUOUS;
- NewHub->IoBuffer = MmAllocateNonPagedIoBuffer(0,
- MAX_ULONG,
- BufferAlignment,
- BufferSize,
- IoBufferFlags);
- if (NewHub->IoBuffer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateHubEnd;
- }
- ASSERT(NewHub->IoBuffer->FragmentCount == 1);
- //
- // Create a control transfer.
- //
- NewHub->ControlTransfer = UsbAllocateTransfer(
- NewHub->DeviceHandle,
- 0,
- USB_HUB_MAX_CONTROL_TRANSFER_SIZE,
- 0);
- if (NewHub->ControlTransfer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateHubEnd;
- }
- NewHub->ControlTransfer->Buffer =
- NewHub->IoBuffer->Fragment[0].VirtualAddress;
- NewHub->ControlTransfer->BufferPhysicalAddress =
- NewHub->IoBuffer->Fragment[0].PhysicalAddress;
- NewHub->ControlTransfer->BufferActualLength = MaxControlTransferSize;
- NewHub->ControlTransferLock = KeCreateQueuedLock();
- if (NewHub->ControlTransferLock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateHubEnd;
- }
- //
- // Create the interrupt work item.
- //
- NewHub->InterruptWorkItem = KeCreateWorkItem(
- UsbCoreWorkQueue,
- WorkPriorityNormal,
- UsbpHubInterruptTransferCompletionWorker,
- NewHub,
- USB_CORE_ALLOCATION_TAG);
- if (NewHub->InterruptWorkItem == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateHubEnd;
- }
- //
- // Get the number of ports for this hub and finish creating the hub's port
- // count dependent structures.
- //
- Device = (PUSB_DEVICE)DeviceHandle;
- if (Device->Type == UsbDeviceTypeRootHub) {
- NewHub->PortCount = Device->Controller->Device.RootHubPortCount;
- } else {
- Status = UsbpReadHubDescriptor(NewHub);
- if (!KSUCCESS(Status)) {
- goto CreateHubEnd;
- }
- }
- //
- // Allocate space for the hub status arrays.
- //
- AllocationSize = (sizeof(USB_PORT_STATUS) * NewHub->PortCount) +
- (sizeof(USB_DEVICE_SPEED) * NewHub->PortCount);
- HubStatus = MmAllocatePagedPool(AllocationSize, USB_CORE_ALLOCATION_TAG);
- if (HubStatus == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateHubEnd;
- }
- RtlZeroMemory(HubStatus, AllocationSize);
- NewHub->HubStatus.PortStatus = HubStatus;
- HubStatus += (sizeof(USB_PORT_STATUS) * NewHub->PortCount);
- NewHub->HubStatus.PortDeviceSpeed = HubStatus;
- //
- // If this is the root hub, link it up with the host controller.
- //
- if (Device->Type == UsbDeviceTypeRootHub) {
- Device->Controller->RootHub = NewHub;
- }
- Status = STATUS_SUCCESS;
- CreateHubEnd:
- if (!KSUCCESS(Status)) {
- if (NewHub != NULL) {
- if (NewHub->InterruptWorkItem != NULL) {
- KeDestroyWorkItem(NewHub->InterruptWorkItem);
- }
- if (NewHub->ControlTransferLock != NULL) {
- KeDestroyQueuedLock(NewHub->ControlTransferLock);
- }
- if (NewHub->ControlTransfer != NULL) {
- UsbDestroyTransfer(NewHub->ControlTransfer);
- }
- if (NewHub->IoBuffer != NULL) {
- MmFreeIoBuffer(NewHub->IoBuffer);
- }
- if (HubStatus != NULL) {
- MmFreePagedPool(HubStatus);
- }
- MmFreePagedPool(NewHub);
- NewHub = NULL;
- }
- }
- *Hub = NewHub;
- return Status;
- }
- USB_API
- VOID
- UsbDestroyHub (
- PUSB_HUB Hub
- )
- /*++
- Routine Description:
- This routine destroys a USB hub context. This should only be called once
- all of the hub's transfers have completed.
- Arguments:
- Hub - Supplies a pointer to the hub to tear down.
- Return Value:
- None.
- --*/
- {
- PUSB_DEVICE Child;
- PUSB_DEVICE HubDevice;
- ASSERT(Hub != NULL);
- ASSERT(KeGetRunLevel() == RunLevelLow);
- //
- // Get the hub's USB device. It should be disconnected.
- //
- HubDevice = (PUSB_DEVICE)Hub->DeviceHandle;
- ASSERT(HubDevice->Connected == FALSE);
- //
- // Clean up the hub's children, who should all be disconnected. The list
- // will be empty after this.
- //
- KeAcquireQueuedLock(HubDevice->ChildLock);
- while (LIST_EMPTY(&(HubDevice->ChildList)) == FALSE) {
- Child = (PUSB_DEVICE)LIST_VALUE(HubDevice->ChildList.Next,
- USB_DEVICE,
- ListEntry);
- //
- // Assert that the child is disconnected or that there is only one
- // reference on the child. It is not enough to assert that it is
- // disconnected, because devices whose functional driver never came
- // online will not get the remove IRP to disconnect themselves.
- // They may still be "connected". Devices that were connected and are
- // now disconnected may have more than 1 reference if something in the
- // system still holds a handle to the device.
- //
- ASSERT((Child->Connected == FALSE) || (Child->ReferenceCount == 1));
- UsbpRemoveDevice(Child);
- }
- KeReleaseQueuedLock(HubDevice->ChildLock);
- //
- // The hub's interrupt transfer callback queues a work item, which then
- // attempts to re-submit the transfer. Re-submission will fail at this
- // point, so flush the work item, destroy it, and then the transfer can
- // be safely destroyed. The work item is guaranteed to have been queued
- // because the transfer is currently in the inactive state, not in the
- // callback state.
- //
- // The destroy routine attempts to cancel the work item and then flush if
- // the cancel was too late.
- //
- KeDestroyWorkItem(Hub->InterruptWorkItem);
- //
- // There is no guarantee the interrupt transfer was allocated in cases
- // where the hub never got the start IRP. Only release what is necessary.
- //
- if (Hub->InterruptTransfer != NULL) {
- UsbDestroyTransfer(Hub->InterruptTransfer);
- }
- //
- // Release the interface used for the transfer, if necessary.
- //
- if (Hub->Interface != NULL) {
- UsbReleaseInterface(Hub->DeviceHandle,
- Hub->Interface->Descriptor.InterfaceNumber);
- }
- //
- // The control transfer is only used during start, query children, and
- // interrupt callback operations. Given that the interrupt transfer has
- // been destroyed, it is safe to destroy the control transfer and lock.
- //
- UsbDestroyTransfer(Hub->ControlTransfer);
- KeDestroyQueuedLock(Hub->ControlTransferLock);
- //
- // Destroy remaining data and the hub itself.
- //
- MmFreeIoBuffer(Hub->IoBuffer);
- MmFreePagedPool(Hub->HubStatus.PortStatus);
- MmFreePagedPool(Hub);
- return;
- }
- USB_API
- KSTATUS
- UsbStartHub (
- PUSB_HUB Hub
- )
- /*++
- Routine Description:
- This routine starts a USB hub.
- Arguments:
- Hub - Supplies a pointer to the hub to start.
- Return Value:
- None.
- --*/
- {
- PUSB_DEVICE Device;
- BOOL LockHeld;
- KSTATUS Status;
- LockHeld = FALSE;
- Device = (PUSB_DEVICE)(Hub->DeviceHandle);
- //
- // If this is not the root hub, reset the hub. This consists of turning the
- // power on for each port, collecting the hub status, and starting the
- // change notification interrupts.
- //
- if (Device->Type != UsbDeviceTypeRootHub) {
- ASSERT(Device->Type == UsbDeviceTypeHub);
- Status = UsbpResetHub(Hub);
- if (!KSUCCESS(Status)) {
- goto StartHubEnd;
- }
- //
- // Otherwise, just read the port status information out of the hub.
- // Synchronize this with port status change notifications that may also
- // modify the hub's software status.
- //
- } else {
- KeAcquireQueuedLock(Device->ChildLock);
- LockHeld = TRUE;
- Status = UsbpGetHubStatus(Hub, TRUE);
- if (!KSUCCESS(Status)) {
- goto StartHubEnd;
- }
- }
- StartHubEnd:
- if (LockHeld != FALSE) {
- KeReleaseQueuedLock(Device->ChildLock);
- }
- return Status;
- }
- USB_API
- KSTATUS
- UsbHubQueryChildren (
- PIRP Irp,
- PUSB_HUB Hub
- )
- /*++
- Routine Description:
- This routine responds to the Query Children IRP for a USB Hub. This routine
- must be called at low level.
- Arguments:
- Irp - Supplies a pointer to the Query Children IRP.
- Hub - Supplies a pointer to the hub to query.
- Return Value:
- Status code.
- --*/
- {
- ULONG AllocationSize;
- PUSB_DEVICE Child;
- ULONG ChildCount;
- ULONG ChildIndex;
- PDEVICE *Children;
- PLIST_ENTRY CurrentEntry;
- PUSB_DEVICE Device;
- BOOL DeviceLockHeld;
- UCHAR PortIndex;
- PUSB_PORT_STATUS PortStatus;
- KSTATUS Status;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- Children = NULL;
- Device = (PUSB_DEVICE)(Hub->DeviceHandle);
- DeviceLockHeld = FALSE;
- //
- // Loop over all possible ports in the hub.
- //
- KeAcquireQueuedLock(Device->ChildLock);
- DeviceLockHeld = TRUE;
- ASSERT(Hub->PortCount != 0);
- ASSERT(Hub->HubStatus.PortStatus != NULL);
- for (PortIndex = 0; PortIndex < Hub->PortCount; PortIndex += 1) {
- //
- // Loop over all children of this device to find one corresponding to
- // this port.
- //
- CurrentEntry = Device->ChildList.Next;
- while (CurrentEntry != &(Device->ChildList)) {
- Child = LIST_VALUE(CurrentEntry, USB_DEVICE, ListEntry);
- if (Child->PortNumber == PortIndex + 1) {
- break;
- }
- CurrentEntry = CurrentEntry->Next;
- }
- if (CurrentEntry == &(Device->ChildList)) {
- Child = NULL;
- }
- //
- // Handle cases where the port status changed.
- //
- PortStatus = &(Hub->HubStatus.PortStatus[PortIndex]);
- if ((PortStatus->Change & USB_PORT_STATUS_CHANGE_CONNECTED) != 0) {
- //
- // If there had previously been a child at the current port, then
- // remove it from the list. The port is either empty or the child
- // was replaced. It will be reported as missing when this call
- // completes, triggering the removal process.
- //
- if (Child != NULL) {
- UsbpRemoveDevice(Child);
- }
- //
- // If there is a device present, then it's new. Create the new
- // device. Ignore failures here to allow other devices to be
- // enumerated.
- //
- if ((PortStatus->Status & USB_PORT_STATUS_CONNECTED) != 0) {
- UsbpHubAddDevice(Hub, PortIndex);
- }
- //
- // Clear the changed status in the port.
- //
- PortStatus->Change &= ~USB_PORT_STATUS_CHANGE_CONNECTED;
- }
- }
- //
- // Loop once to determine how many children there are. A child should only
- // not have an OS device if it is the debug device.
- //
- ChildCount = 0;
- CurrentEntry = Device->ChildList.Next;
- while (CurrentEntry != &(Device->ChildList)) {
- Child = LIST_VALUE(CurrentEntry, USB_DEVICE, ListEntry);
- ASSERT((Child->Device != NULL) || (Child->DebugDevice != FALSE));
- if (Child->Device != NULL) {
- ChildCount += 1;
- }
- CurrentEntry = CurrentEntry->Next;
- }
- if (ChildCount == 0) {
- Status = STATUS_SUCCESS;
- goto HubEnumerateChildrenEnd;
- }
- //
- // Create the array of OS device objects to report the children.
- //
- AllocationSize = sizeof(PDEVICE) * ChildCount;
- Children = MmAllocatePagedPool(AllocationSize, USB_CORE_ALLOCATION_TAG);
- if (Children == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto HubEnumerateChildrenEnd;
- }
- RtlZeroMemory(Children, AllocationSize);
- ChildIndex = 0;
- CurrentEntry = Device->ChildList.Next;
- while (CurrentEntry != &(Device->ChildList)) {
- Child = LIST_VALUE(CurrentEntry, USB_DEVICE, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- if (Child->Device != NULL) {
- Children[ChildIndex] = Child->Device;
- ChildIndex += 1;
- }
- }
- ASSERT(ChildIndex == ChildCount);
- //
- // Merge this child array with the children already in the IRP. This
- // routine allocates a new array, so release the array allocated here
- // upon the completion of query children.
- //
- Status = IoMergeChildArrays(Irp,
- Children,
- ChildCount,
- USB_CORE_ALLOCATION_TAG);
- if (!KSUCCESS(Status)) {
- goto HubEnumerateChildrenEnd;
- }
- Status = STATUS_SUCCESS;
- HubEnumerateChildrenEnd:
- if (Children != NULL) {
- MmFreePagedPool(Children);
- }
- if (DeviceLockHeld != FALSE) {
- KeReleaseQueuedLock(Device->ChildLock);
- }
- return Status;
- }
- VOID
- UsbpNotifyRootHubStatusChange (
- PUSB_HUB RootHub
- )
- /*++
- Routine Description:
- This routine handles notifications from the host controller indicating that
- a port on the root hub has changed. It queries the port status for the hub
- and notifies the system if something is different.
- Arguments:
- RootHub - Supplies a pointer to the USB root hub.
- Return Value:
- None.
- --*/
- {
- BOOL LockHeld;
- BOOL PortChanged;
- ULONG PortCount;
- ULONG PortIndex;
- PUSB_PORT_STATUS PortStatus;
- PUSB_DEVICE RootDevice;
- KSTATUS Status;
- ASSERT(RootHub != NULL);
- ASSERT(RootHub->HubStatus.PortStatus != NULL);
- //
- // Acquire the device's child lock to synchronize with other accesses to
- // the ports.
- //
- RootDevice = (PUSB_DEVICE)RootHub->DeviceHandle;
- KeAcquireQueuedLock(RootDevice->ChildLock);
- LockHeld = TRUE;
- //
- // Get the status for the root hub.
- //
- Status = UsbpGetRootHubStatus(RootHub);
- if (!KSUCCESS(Status)) {
- goto NotifyRootHubStatusChange;
- }
- //
- // Search through the ports for change notifications.
- //
- PortChanged = FALSE;
- PortCount = RootHub->PortCount;
- for (PortIndex = 0; PortIndex < PortCount; PortIndex += 1) {
- PortStatus = &(RootHub->HubStatus.PortStatus[PortIndex]);
- //
- // Run through the over-current reset sequence as defined in section
- // 11.12.5 of the USB 2.0 Specification.
- //
- if ((PortStatus->Change & USB_PORT_STATUS_CHANGE_OVER_CURRENT) != 0) {
- //
- // Wait until the over current bit is clear.
- //
- while ((PortStatus->Status & USB_PORT_STATUS_OVER_CURRENT) != 0) {
- Status = UsbpGetRootHubStatus(RootHub);
- if (!KSUCCESS(Status)) {
- goto NotifyRootHubStatusChange;
- }
- }
- //
- // Now wipe the port status and reset the port. There is no
- // mechanism to power on a root port, so settle for a reset. The
- // USB specification is not clear on what to do for the root hub's
- // ports.
- //
- RtlZeroMemory(PortStatus, sizeof(USB_PORT_STATUS));
- RootHub->HubStatus.PortDeviceSpeed[PortIndex] =
- UsbDeviceSpeedInvalid;
- Status = UsbpResetHubPort(RootHub, PortIndex);
- if (!KSUCCESS(Status)) {
- continue;
- }
- //
- // Collect the status one more time after the power on. If there is
- // something behind the port then the connection changed bit should
- // get set.
- //
- Status = UsbpGetRootHubStatus(RootHub);
- if (!KSUCCESS(Status)) {
- goto NotifyRootHubStatusChange;
- }
- }
- if ((PortStatus->Change & USB_PORT_STATUS_CHANGE_CONNECTED) != 0) {
- PortChanged = TRUE;
- break;
- }
- }
- KeReleaseQueuedLock(RootDevice->ChildLock);
- LockHeld = FALSE;
- //
- // A change was found. Notify the system.
- //
- if (PortChanged != FALSE) {
- IoNotifyDeviceTopologyChange(RootDevice->Device);
- }
- NotifyRootHubStatusChange:
- if (LockHeld != FALSE) {
- KeReleaseQueuedLock(RootDevice->ChildLock);
- }
- return;
- }
- KSTATUS
- UsbpResetHubPort (
- PUSB_HUB Hub,
- UCHAR PortIndex
- )
- /*++
- Routine Description:
- This routine resets the device behind the given port. The controller lock
- must be held.
- Arguments:
- Hub - Supplies a pointer to the context of the hub whose port is to be
- reset.
- PortIndex - Supplies the zero-based port index on the hub to reset.
- Return Value:
- Status code.
- --*/
- {
- PUSB_DEVICE HubDevice;
- PUSB_PORT_STATUS PortStatus;
- KSTATUS Status;
- ASSERT(Hub->HubStatus.PortStatus != NULL);
- HubDevice = (PUSB_DEVICE)Hub->DeviceHandle;
- ASSERT(KeIsQueuedLockHeld(HubDevice->ChildLock) != FALSE);
- //
- // Reset the port in question.
- //
- PortStatus = &(Hub->HubStatus.PortStatus[PortIndex]);
- PortStatus->Status |= USB_PORT_STATUS_RESET;
- PortStatus->Status &= ~USB_PORT_STATUS_ENABLED;
- PortStatus->Change |= (USB_PORT_STATUS_CHANGE_RESET |
- USB_PORT_STATUS_CHANGE_ENABLED);
- Status = UsbpSetHubStatus(Hub);
- if (!KSUCCESS(Status)) {
- goto ResetHubPortEnd;
- }
- //
- // Stall for 10ms per section 7.1.7.5 of the USB specification (TDRST).
- // This is reduced because around 10ms devices start to suspend themselves
- // and stop responding to requests.
- //
- HlBusySpin(5 * MICROSECONDS_PER_MILLISECOND);
- //
- // Now enable the port.
- //
- PortStatus->Status &= ~USB_PORT_STATUS_RESET;
- PortStatus->Status |= USB_PORT_STATUS_ENABLED;
- PortStatus->Change |= (USB_PORT_STATUS_CHANGE_RESET |
- USB_PORT_STATUS_CHANGE_ENABLED);
- Status = UsbpSetHubStatus(Hub);
- if (!KSUCCESS(Status)) {
- goto ResetHubPortEnd;
- }
- //
- // Stall for 10ms per section 7.1.7.5 of the USB specification (TRSTRCY).
- //
- HlBusySpin(25 * MICROSECONDS_PER_MILLISECOND);
- //
- // Get the status of the port now (actively request it, don't rely on the
- // interrupt transfer, as it's blocked waiting to hold the hub lock.
- //
- Status = UsbpGetHubStatus(Hub, TRUE);
- if (!KSUCCESS(Status)) {
- goto ResetHubPortEnd;
- }
- //
- // If the reset did not enable the port, then clear the changed bit.
- //
- if ((PortStatus->Change & USB_PORT_STATUS_CHANGE_ENABLED) != 0) {
- ASSERT((PortStatus->Status & USB_PORT_STATUS_ENABLED) == 0);
- PortStatus->Change &= ~USB_PORT_STATUS_CHANGE_ENABLED;
- }
- //
- // If the device is not present, then exit claiming success. It may have
- // been removed during the reset.
- //
- if ((PortStatus->Status & USB_PORT_STATUS_CONNECTED) == 0) {
- Status = STATUS_SUCCESS;
- goto ResetHubPortEnd;
- }
- //
- // If the port got disabled, fail the reset. Note that a device might still
- // be in the connected state even though it is in the disabled state, so
- // this must fail. See Section 11.24.2.7.1 PORT_CONNECTION of the USB 2.0
- // Specification.
- //
- if ((PortStatus->Status & USB_PORT_STATUS_ENABLED) == 0) {
- Status = STATUS_NOT_READY;
- goto ResetHubPortEnd;
- }
- ASSERT(Hub->HubStatus.PortDeviceSpeed[PortIndex] != UsbDeviceSpeedInvalid);
- //
- // Stall again to allow the device time to initialize.
- //
- HlBusySpin(20 * MICROSECONDS_PER_MILLISECOND);
- Status = STATUS_SUCCESS;
- ResetHubPortEnd:
- if (((UsbDebugFlags & (USB_DEBUG_HUB | USB_DEBUG_ENUMERATION)) != 0) ||
- ((!KSUCCESS(Status)) && ((UsbDebugFlags & USB_DEBUG_ERRORS) != 0))) {
- RtlDebugPrint("USB: Hub %x reset port %d, status %x.\n",
- Hub,
- PortIndex,
- Status);
- }
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- KSTATUS
- UsbpGetHubStatus (
- PUSB_HUB Hub,
- BOOL ForceRefresh
- )
- /*++
- Routine Description:
- This routine gets the current hub and port status out of a USB hub.
- Arguments:
- Hub - Supplies the pointer to the hub context for this device.
- ForceRefresh - Supplies a boolean indicating if all ports should be
- re-queried.
- Return Value:
- Status code.
- --*/
- {
- PUSB_DEVICE Device;
- ULONG PortIndex;
- ULONG PortStatus;
- KSTATUS Status;
- Device = (PUSB_DEVICE)(Hub->DeviceHandle);
- ASSERT(KeIsQueuedLockHeld(Device->ChildLock) != FALSE);
- //
- // For root hubs, just farm off the question to the host controller.
- //
- if (Device->Type == UsbDeviceTypeRootHub) {
- Status = UsbpGetRootHubStatus(Hub);
- goto GetHubStatusEnd;
- }
- ASSERT(Device->Type == UsbDeviceTypeHub);
- //
- // If no refresh is required, just return what's already found. An interrupt
- // transfer will automatically update these values when they change.
- //
- if (ForceRefresh == FALSE) {
- Status = STATUS_SUCCESS;
- goto GetHubStatusEnd;
- }
- //
- // If a refresh is requested, get each port's status.
- //
- for (PortIndex = 0; PortIndex < Hub->PortCount; PortIndex += 1) {
- Status = UsbpHubGetPortStatus(Hub, PortIndex + 1, &PortStatus);
- if (!KSUCCESS(Status)) {
- goto GetHubStatusEnd;
- }
- //
- // Set the software bits based on the hardware bits.
- //
- UsbpHubUpdatePortStatus(Hub, PortIndex, PortStatus);
- //
- // Clear out any change bits.
- //
- Status = UsbpHubClearPortChangeBits(Hub, PortIndex + 1, PortStatus);
- if (!KSUCCESS(Status)) {
- goto GetHubStatusEnd;
- }
- }
- Status = STATUS_SUCCESS;
- GetHubStatusEnd:
- return Status;
- }
- KSTATUS
- UsbpGetRootHubStatus (
- PUSB_HUB RootHub
- )
- /*++
- Routine Description:
- This routine gets the root hub's port status out of the USB host controller.
- Arguments:
- RootHub - Supplies a pointer to hub context for the root USB hub.
- Return Value:
- Status code.
- --*/
- {
- PUSB_DEVICE Device;
- PUSB_HOST_GET_ROOT_HUB_STATUS GetRootHubStatus;
- PVOID HostControllerContext;
- ULONG PortIndex;
- PUSB_PORT_STATUS PortStatus;
- KSTATUS Status;
- Device = (PUSB_DEVICE)RootHub->DeviceHandle;
- ASSERT(Device->Type == UsbDeviceTypeRootHub);
- ASSERT(RootHub->HubStatus.PortStatus != NULL);
- //
- // Farm the question off to the host controller.
- //
- HostControllerContext = Device->Controller->Device.HostControllerContext;
- GetRootHubStatus = Device->Controller->Device.GetRootHubStatus;
- Status = GetRootHubStatus(HostControllerContext, &(RootHub->HubStatus));
- if (((UsbDebugFlags & USB_DEBUG_HUB) != 0) ||
- ((!KSUCCESS(Status)) && ((UsbDebugFlags & USB_DEBUG_ERRORS) != 0))) {
- for (PortIndex = 0; PortIndex < RootHub->PortCount; PortIndex += 1) {
- PortStatus = &(RootHub->HubStatus.PortStatus[PortIndex]);
- RtlDebugPrint(
- "USB: Root Hub %x Port %d SoftwareStatus %x, SoftwareChange "
- "%x Status %x.\n"
- "USB: Speed %d Enabled %d Suspended %d OverCurrent %d "
- "Present %d\n",
- RootHub,
- PortIndex,
- PortStatus->Status,
- PortStatus->Change,
- Status,
- RootHub->HubStatus.PortDeviceSpeed[PortIndex],
- (PortStatus->Status & USB_PORT_STATUS_ENABLED) != 0,
- (PortStatus->Status & USB_PORT_STATUS_SUSPENDED) != 0,
- (PortStatus->Status & USB_PORT_STATUS_OVER_CURRENT) != 0,
- (PortStatus->Status & USB_PORT_STATUS_CONNECTED) != 0);
- }
- }
- return Status;
- }
- KSTATUS
- UsbpSetHubStatus (
- PUSB_HUB Hub
- )
- /*++
- Routine Description:
- This routine sets the current hub and port status out of a USB hub.
- Arguments:
- Hub - Supplies a pointer to the hub context.
- Return Value:
- Status code.
- --*/
- {
- PUSB_HOST_CONTROLLER Controller;
- PUSB_DEVICE Device;
- PVOID HostControllerContext;
- ULONG PortIndex;
- PUSB_PORT_STATUS PortStatus;
- BOOL SetFeature;
- PUSB_HOST_SET_ROOT_HUB_STATUS SetRootHubStatus;
- KSTATUS Status;
- ASSERT(Hub->HubStatus.PortStatus != NULL);
- Device = (PUSB_DEVICE)(Hub->DeviceHandle);
- //
- // For root hubs, just farm off the work to the host controller.
- //
- if (Device->Type == UsbDeviceTypeRootHub) {
- Controller = Device->Controller;
- HostControllerContext = Controller->Device.HostControllerContext;
- SetRootHubStatus = Controller->Device.SetRootHubStatus;
- Status = SetRootHubStatus(HostControllerContext, &(Hub->HubStatus));
- goto SetHubStatusEnd;
- }
- ASSERT(Device->Type == UsbDeviceTypeHub);
- //
- // Loop through each port looking for a change.
- //
- for (PortIndex = 0; PortIndex < Hub->PortCount; PortIndex += 1) {
- //
- // Determine what changed between the previous status and the current
- // status, and act on those bits.
- //
- PortStatus = &(Hub->HubStatus.PortStatus[PortIndex]);
- //
- // If no bits changed, then there is nothing to do really.
- //
- if (PortStatus->Change == 0) {
- continue;
- }
- //
- // Handle port enabled change events.
- //
- if ((PortStatus->Change & USB_PORT_STATUS_CHANGE_ENABLED) != 0) {
- //
- // Disable the port if it changed and is no longer enabled.
- // Enabling a port directly is not allowed. This must be done
- // through a reset.
- //
- if ((PortStatus->Status & USB_PORT_STATUS_ENABLED) == 0) {
- Status = UsbpHubSetOrClearFeature(Hub,
- FALSE,
- USB_HUB_FEATURE_PORT_ENABLE,
- PortIndex + 1);
- if (!KSUCCESS(Status)) {
- goto SetHubStatusEnd;
- }
- }
- //
- // Clear the change bit now that it has been handled.
- //
- PortStatus->Change &= ~USB_PORT_STATUS_CHANGE_ENABLED;
- }
- //
- // Handle port reset changes.
- //
- if ((PortStatus->Change & USB_PORT_STATUS_CHANGE_RESET) != 0) {
- //
- // If the port is to be reset, then issue a reset. Note that a port
- // cannot be "un-reset", the hardware handles this.
- //
- if ((PortStatus->Status & USB_PORT_STATUS_RESET) != 0) {
- Status = UsbpHubSetOrClearFeature(Hub,
- TRUE,
- USB_HUB_FEATURE_PORT_RESET,
- PortIndex + 1);
- if (!KSUCCESS(Status)) {
- goto SetHubStatusEnd;
- }
- }
- //
- // Clear the change bit now that it has been handled.
- //
- PortStatus->Change &= ~USB_PORT_STATUS_CHANGE_RESET;
- }
- //
- // Handle port suspend changes.
- //
- if ((PortStatus->Change & USB_PORT_STATUS_CHANGE_SUSPENDED) != 0) {
- if ((PortStatus->Status & USB_PORT_STATUS_SUSPENDED) != 0) {
- SetFeature = TRUE;
- } else {
- SetFeature = FALSE;
- }
- Status = UsbpHubSetOrClearFeature(Hub,
- SetFeature,
- USB_HUB_FEATURE_PORT_SUSPEND,
- PortIndex + 1);
- if (!KSUCCESS(Status)) {
- goto SetHubStatusEnd;
- }
- PortStatus->Change &= ~USB_PORT_STATUS_CHANGE_SUSPENDED;
- }
- }
- Status = STATUS_SUCCESS;
- SetHubStatusEnd:
- return Status;
- }
- VOID
- UsbpHubUpdatePortStatus (
- PUSB_HUB Hub,
- ULONG PortIndex,
- ULONG HardwareStatus
- )
- /*++
- Routine Description:
- This routine converts the given hardware port status to software status and
- updates the port status of the given hub at the given index. It saves the
- old port status in the process.
- Arguments:
- Hub - Supplies a pointer to the USB hub device whose port status is to be
- updated.
- PortIndex - Supplies the index of the port whose status is to be updated.
- This is zero-indexed.
- HardwareStatus - Supplies the port's hardware status that needs to be
- converted to software status.
- Return Value:
- None.
- --*/
- {
- USHORT ChangeBits;
- PUSB_PORT_STATUS PortStatus;
- USHORT SoftwareStatus;
- USB_DEVICE_SPEED Speed;
- ASSERT(Hub->HubStatus.PortStatus != NULL);
- Speed = UsbDeviceSpeedInvalid;
- SoftwareStatus = 0;
- if ((HardwareStatus & USB_HUB_PORT_STATUS_DEVICE_CONNECTED) != 0) {
- SoftwareStatus |= USB_PORT_STATUS_CONNECTED;
- if ((HardwareStatus & USB_HUB_PORT_STATUS_HIGH_SPEED) != 0) {
- Speed = UsbDeviceSpeedHigh;
- } else if ((HardwareStatus & USB_HUB_PORT_STATUS_LOW_SPEED) != 0) {
- Speed = UsbDeviceSpeedLow;
- } else {
- Speed = UsbDeviceSpeedFull;
- }
- }
- if ((HardwareStatus & USB_HUB_PORT_STATUS_ENABLED) != 0) {
- SoftwareStatus |= USB_PORT_STATUS_ENABLED;
- }
- if ((HardwareStatus & USB_HUB_PORT_STATUS_SUSPENDED) != 0) {
- SoftwareStatus |= USB_PORT_STATUS_SUSPENDED;
- }
- if ((HardwareStatus & USB_HUB_PORT_STATUS_OVER_CURRENT) != 0) {
- SoftwareStatus |= USB_PORT_STATUS_OVER_CURRENT;
- }
- //
- // If the new status does not match the current status, then mark the
- // appropriate fields as changed and set the new status.
- //
- PortStatus = &(Hub->HubStatus.PortStatus[PortIndex]);
- if (SoftwareStatus != PortStatus->Status) {
- ChangeBits = SoftwareStatus ^ PortStatus->Status;
- //
- // Because the port status bits and changed bits match 1-to-1, just OR
- // in the changed bits.
- //
- PortStatus->Change |= ChangeBits;
- PortStatus->Status = SoftwareStatus;
- }
- if ((UsbDebugFlags & USB_DEBUG_HUB) != 0) {
- RtlDebugPrint(
- "USB: Hub %x Port %d HardwareStatus 0x%x, SoftwareStatus "
- "0x%x, SoftwareChange 0x%x\n"
- "USB: Speed %d Enabled %d Suspended %d OverCurrent %d "
- "Present %d\n",
- Hub,
- PortIndex,
- HardwareStatus,
- PortStatus->Status,
- PortStatus->Change,
- Speed,
- (HardwareStatus & USB_HUB_PORT_STATUS_ENABLED) != 0,
- (HardwareStatus & USB_HUB_PORT_STATUS_SUSPENDED) != 0,
- (HardwareStatus & USB_HUB_PORT_STATUS_OVER_CURRENT) != 0,
- (HardwareStatus & USB_HUB_PORT_STATUS_DEVICE_CONNECTED) != 0);
- }
- //
- // Save the new speed.
- //
- Hub->HubStatus.PortDeviceSpeed[PortIndex] = Speed;
- return;
- }
- KSTATUS
- UsbpResetHub (
- PUSB_HUB Hub
- )
- /*++
- Routine Description:
- This routine resets a USB hub.
- Arguments:
- Hub - Supplies a pointer to the hub to reset.
- Return Value:
- Status code.
- --*/
- {
- ULONG BufferAlignment;
- PUSB_CONFIGURATION_DESCRIPTION Configuration;
- PUSB_DEVICE Device;
- PUSB_ENDPOINT_DESCRIPTION Endpoint;
- UCHAR EndpointNumber;
- PUSB_INTERFACE_DESCRIPTION Interface;
- BOOL LockHeld;
- ULONG MaxControlSize;
- ULONG MaxInterruptSize;
- ULONG PortIndex;
- KSTATUS Status;
- ULONG TransferLength;
- ASSERT(Hub->PortCount != 0);
- Device = (PUSB_DEVICE)Hub->DeviceHandle;
- LockHeld = FALSE;
- //
- // Send the SET_CONFIGURATION request to the port.
- //
- Status = UsbSetConfiguration(Hub->DeviceHandle, 0, TRUE);
- if (!KSUCCESS(Status)) {
- goto ResetHubEnd;
- }
- if (Hub->Interface == NULL) {
- //
- // Get the only configuration.
- //
- Status = UsbGetConfiguration(Hub->DeviceHandle,
- 0,
- TRUE,
- &Configuration);
- if (!KSUCCESS(Status)) {
- goto ResetHubEnd;
- }
- //
- // Find and claim the only interface.
- //
- if (LIST_EMPTY(&(Configuration->InterfaceListHead)) != FALSE) {
- Status = STATUS_INVALID_CONFIGURATION;
- goto ResetHubEnd;
- }
- Interface = LIST_VALUE(Configuration->InterfaceListHead.Next,
- USB_INTERFACE_DESCRIPTION,
- ListEntry);
- Status = UsbClaimInterface(Hub->DeviceHandle,
- Interface->Descriptor.InterfaceNumber);
- if (!KSUCCESS(Status)) {
- goto ResetHubEnd;
- }
- //
- // Get the interrupt endpoint.
- //
- if (LIST_EMPTY(&(Interface->EndpointListHead)) != FALSE) {
- Status = STATUS_INVALID_CONFIGURATION;
- goto ResetHubEnd;
- }
- Endpoint = LIST_VALUE(Interface->EndpointListHead.Next,
- USB_ENDPOINT_DESCRIPTION,
- ListEntry);
- if ((Endpoint->Descriptor.Attributes &
- USB_ENDPOINT_ATTRIBUTES_TYPE_MASK) !=
- USB_ENDPOINT_ATTRIBUTES_TYPE_INTERRUPT) {
- Status = STATUS_INVALID_CONFIGURATION;
- goto ResetHubEnd;
- }
- //
- // Create the interrupt transfer that goes on the status change
- // endpoint.
- //
- BufferAlignment = MmGetIoBufferAlignment();
- ASSERT(POWER_OF_2(BufferAlignment) != FALSE);
- ASSERT(Hub->InterruptTransfer == NULL);
- EndpointNumber = Endpoint->Descriptor.EndpointAddress;
- TransferLength = ALIGN_RANGE_UP(Hub->PortCount + 1, BITS_PER_BYTE) /
- BITS_PER_BYTE;
- Hub->InterruptTransfer = UsbAllocateTransfer(Hub->DeviceHandle,
- EndpointNumber,
- TransferLength,
- 0);
- if (Hub->InterruptTransfer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto ResetHubEnd;
- }
- MaxControlSize = ALIGN_RANGE_UP(USB_HUB_MAX_CONTROL_TRANSFER_SIZE,
- BufferAlignment);
- MaxInterruptSize = ALIGN_RANGE_UP(USB_HUB_MAX_INTERRUPT_SIZE,
- BufferAlignment);
- ASSERT(Hub->IoBuffer->Fragment[0].Size >=
- (MaxControlSize + MaxInterruptSize));
- Hub->InterruptTransfer->Buffer =
- Hub->IoBuffer->Fragment[0].VirtualAddress +
- MaxControlSize;
- Hub->InterruptTransfer->BufferPhysicalAddress =
- Hub->IoBuffer->Fragment[0].PhysicalAddress +
- MaxControlSize;
- Hub->InterruptTransfer->BufferActualLength = MaxInterruptSize;
- Hub->InterruptTransfer->Direction = UsbTransferDirectionIn;
- Hub->InterruptTransfer->Length = TransferLength;
- Hub->InterruptTransfer->CallbackRoutine =
- UsbpHubInterruptTransferCompletion;
- Hub->InterruptTransfer->UserData = Hub;
- Hub->Interface = Interface;
- //
- // This is not the first time the hub has been reset.
- //
- } else {
- //
- // Attempt to cancel the interrupt transfer. If the transfer is on the
- // hardware queue, then the cancel will succeed. Otherwise, it is too
- // late to cancel it. Since the interrupt transfer's callback resubmits
- // the transfer, it should get cancelled if this keeps trying.
- //
- while (TRUE) {
- //
- // Cancel the transfer, which tries to cancel and just waits until
- // the transfer is in the inactive state. It returns successfully
- // only if the transfer was actually pulled off the hardware queue.
- // If this fails with status too early, then the transfer is not in
- // the hardware queue and not in the callback. This means that the
- // hub status change worker is queued or running. It is likely the
- // one requesting a reset. Let it go through.
- //
- Status = UsbCancelTransfer(Hub->InterruptTransfer, TRUE);
- if (KSUCCESS(Status) || (Status == STATUS_TOO_EARLY)) {
- break;
- }
- //
- // If the device has been disconnected, the transfer might not go
- // around again and might have missed the cancel. Just exit.
- //
- // N.B. This case is currently not possible since hub reset is only
- // called during the hub start IRP. This needs to be here,
- // however, if the system tried to reset the hub in parallel
- // with a removal IRP.
- //
- if (Device->Connected == FALSE) {
- Status = STATUS_SUCCESS;
- goto ResetHubEnd;
- }
- //
- // Rest a bit to let stuff progress. This may not be fruitful or
- // necessary since the cancel will do some yielding.
- //
- KeYield();
- }
- }
- //
- // Acquire the hub's child lock so no state changes during the reset.
- //
- KeAcquireQueuedLock(Device->ChildLock);
- LockHeld = TRUE;
- //
- // Reset the state for every port. That is, zero out the state ignoring any
- // change bits.
- //
- RtlZeroMemory(Hub->HubStatus.PortStatus,
- (sizeof(USB_PORT_STATUS) * Hub->PortCount));
- RtlZeroMemory(Hub->HubStatus.PortDeviceSpeed,
- (sizeof(USB_DEVICE_SPEED) * Hub->PortCount));
- //
- // Loop through and power on each port.
- //
- for (PortIndex = 0; PortIndex < Hub->PortCount; PortIndex += 1) {
- Status = UsbpHubSetOrClearFeature(Hub,
- TRUE,
- USB_HUB_FEATURE_PORT_POWER,
- PortIndex + 1);
- if (!KSUCCESS(Status)) {
- goto ResetHubEnd;
- }
- }
- //
- // Set the port indicators to auto. The set power feature set them to the
- // off state.
- //
- if (Hub->HasIndicators != FALSE) {
- for (PortIndex = 0; PortIndex < Hub->PortCount; PortIndex += 1) {
- Status = UsbpHubSetOrClearFeature(
- Hub,
- TRUE,
- USB_HUB_FEATURE_PORT_INDICATOR,
- (PortIndex + 1) | USB_HUB_INDICATOR_AUTOMATIC);
- if (!KSUCCESS(Status)) {
- goto ResetHubEnd;
- }
- }
- }
- //
- // Now that the ports have been powered up, delay for the appropriate
- // amount of time before accessing them again.
- //
- KeDelayExecution(FALSE,
- FALSE,
- Hub->PowerUpDelayIn2ms * 2 * MICROSECONDS_PER_MILLISECOND);
- //
- // After waiting for the ports to power up, get the current status.
- //
- Status = UsbpGetHubStatus(Hub, TRUE);
- if (!KSUCCESS(Status)) {
- goto ResetHubEnd;
- }
- KeReleaseQueuedLock(Device->ChildLock);
- LockHeld = FALSE;
- //
- // Submit the interrupt transfer.
- //
- Status = UsbSubmitTransfer(Hub->InterruptTransfer);
- if (!KSUCCESS(Status)) {
- goto ResetHubEnd;
- }
- ResetHubEnd:
- if (LockHeld != FALSE) {
- KeReleaseQueuedLock(Device->ChildLock);
- }
- return Status;
- }
- KSTATUS
- UsbpReadHubDescriptor (
- PUSB_HUB Hub
- )
- /*++
- Routine Description:
- This routine sends a request to read in the hub descriptor, and sets the
- various fields of the hub structure according to the result.
- Arguments:
- Hub - Supplies a pointer to the hub.
- Return Value:
- Status code.
- --*/
- {
- PUSB_HUB_DESCRIPTOR HubDescriptor;
- ULONG LengthTransferred;
- PUSB_SETUP_PACKET Setup;
- KSTATUS Status;
- Setup = (PUSB_SETUP_PACKET)Hub->ControlTransfer->Buffer;
- KeAcquireQueuedLock(Hub->ControlTransferLock);
- Hub->ControlTransfer->Direction = UsbTransferDirectionIn;
- //
- // Send the GET_DESCRIPTOR request.
- //
- Setup->RequestType = USB_SETUP_REQUEST_TO_HOST |
- USB_SETUP_REQUEST_CLASS |
- USB_SETUP_REQUEST_DEVICE_RECIPIENT;
- Setup->Request = USB_DEVICE_REQUEST_GET_DESCRIPTOR;
- Setup->Value = USB_HUB_DESCRIPTOR_TYPE << 8;
- Setup->Index = 0;
- Setup->Length = USB_HUB_DESCRIPTOR_MAX_SIZE;
- Hub->ControlTransfer->Length = sizeof(USB_SETUP_PACKET) +
- USB_HUB_DESCRIPTOR_MAX_SIZE;
- Status = UsbpHubSendControlTransfer(Hub, &LengthTransferred);
- if (!KSUCCESS(Status)) {
- goto ReadHubDescriptorEnd;
- }
- if (LengthTransferred < sizeof(USB_HUB_DESCRIPTOR)) {
- Status = STATUS_NOT_SUPPORTED;
- goto ReadHubDescriptorEnd;
- }
- HubDescriptor = (PUSB_HUB_DESCRIPTOR)(Setup + 1);
- if ((HubDescriptor->DescriptorType != USB_HUB_DESCRIPTOR_TYPE) ||
- (HubDescriptor->Length < sizeof(USB_HUB_DESCRIPTOR))) {
- Status = STATUS_NOT_SUPPORTED;
- goto ReadHubDescriptorEnd;
- }
- Hub->PortCount = HubDescriptor->PortCount;
- Hub->PowerUpDelayIn2ms = HubDescriptor->PowerUpDelayIn2ms;
- if ((HubDescriptor->HubCharacteristics &
- USB_HUB_CHARACTERISTIC_INDICATORS_SUPPORTED) != 0) {
- Hub->HasIndicators = TRUE;
- }
- Status = STATUS_SUCCESS;
- ReadHubDescriptorEnd:
- KeReleaseQueuedLock(Hub->ControlTransferLock);
- return Status;
- }
- KSTATUS
- UsbpHubSendControlTransfer (
- PUSB_HUB Hub,
- PULONG LengthTransferred
- )
- /*++
- Routine Description:
- This routine sends a synchronous control transfer. It assumes that the hub's
- buffer is already all set up and ready to go.
- Arguments:
- Hub - Supplies a pointer to the hub.
- LengthTransferred - Supplies a pointer to the length to transfer.
- Return Value:
- Status code.
- --*/
- {
- KSTATUS Status;
- ULONG TransferCount;
- ASSERT(Hub->ControlTransfer->Direction != UsbTransferDirectionInvalid);
- TransferCount = 0;
- Status = UsbSubmitSynchronousTransfer(Hub->ControlTransfer);
- if (!KSUCCESS(Status)) {
- goto HubSendControlTransferEnd;
- }
- ASSERT(KSUCCESS(Hub->ControlTransfer->Status));
- TransferCount = Hub->ControlTransfer->LengthTransferred -
- sizeof(USB_SETUP_PACKET);
- Hub->ControlTransfer->Direction = UsbTransferDirectionInvalid;
- Status = STATUS_SUCCESS;
- HubSendControlTransferEnd:
- if (LengthTransferred != NULL) {
- *LengthTransferred = TransferCount;
- }
- return Status;
- }
- KSTATUS
- UsbpHubGetHubStatus (
- PUSB_HUB Hub,
- PULONG HubStatus
- )
- /*++
- Routine Description:
- This routine performs a control transfer to get the current status of the
- given USB hub.
- Arguments:
- Hub - Supplies a pointer to the hub to query.
- HubStatus - Supplies a pointer where the hub status will be returned upon
- success.
- Return Value:
- Status code.
- --*/
- {
- ULONG LengthTransferred;
- PUSB_SETUP_PACKET Setup;
- KSTATUS Status;
- Setup = (PUSB_SETUP_PACKET)Hub->ControlTransfer->Buffer;
- KeAcquireQueuedLock(Hub->ControlTransferLock);
- Setup->RequestType = USB_SETUP_REQUEST_TO_HOST |
- USB_SETUP_REQUEST_CLASS |
- USB_SETUP_REQUEST_DEVICE_RECIPIENT;
- Setup->Request = USB_DEVICE_REQUEST_GET_STATUS;
- Setup->Value = 0;
- Setup->Index = 0;
- Setup->Length = sizeof(ULONG);
- Hub->ControlTransfer->Direction = UsbTransferDirectionIn;
- Hub->ControlTransfer->Length = sizeof(USB_SETUP_PACKET) + sizeof(ULONG);
- Status = UsbpHubSendControlTransfer(Hub, &LengthTransferred);
- if (!KSUCCESS(Status)) {
- goto HubGetHubStatusEnd;
- }
- if (LengthTransferred != sizeof(ULONG)) {
- Status = STATUS_DATA_LENGTH_MISMATCH;
- goto HubGetHubStatusEnd;
- }
- HubGetHubStatusEnd:
- KeReleaseQueuedLock(Hub->ControlTransferLock);
- if (!KSUCCESS(Status)) {
- *HubStatus = 0;
- } else {
- *HubStatus = *((PULONG)(Setup + 1));
- }
- return Status;
- }
- KSTATUS
- UsbpHubGetPortStatus (
- PUSB_HUB Hub,
- ULONG PortNumber,
- PULONG PortStatus
- )
- /*++
- Routine Description:
- This routine performs a control transfer to get the current status of the
- given USB hub port.
- Arguments:
- Hub - Supplies a pointer to the hub to query.
- PortNumber - Supplies the one-indexed port number to query.
- PortStatus - Supplies a pointer where the port status will be returned upon
- success.
- Return Value:
- Status code.
- --*/
- {
- ULONG LengthTransferred;
- PUSB_SETUP_PACKET Setup;
- KSTATUS Status;
- ASSERT(PortNumber != 0);
- ASSERT(PortNumber <= Hub->PortCount);
- Setup = (PUSB_SETUP_PACKET)Hub->ControlTransfer->Buffer;
- KeAcquireQueuedLock(Hub->ControlTransferLock);
- Setup->RequestType = USB_SETUP_REQUEST_TO_HOST |
- USB_SETUP_REQUEST_CLASS |
- USB_SETUP_REQUEST_OTHER_RECIPIENT;
- Setup->Request = USB_DEVICE_REQUEST_GET_STATUS;
- Setup->Value = 0;
- Setup->Index = PortNumber;
- Setup->Length = sizeof(ULONG);
- Hub->ControlTransfer->Direction = UsbTransferDirectionIn;
- Hub->ControlTransfer->Length = sizeof(USB_SETUP_PACKET) + sizeof(ULONG);
- Status = UsbpHubSendControlTransfer(Hub, &LengthTransferred);
- if (!KSUCCESS(Status)) {
- goto HubGetPortStatusEnd;
- }
- if (LengthTransferred != sizeof(ULONG)) {
- Status = STATUS_DATA_LENGTH_MISMATCH;
- goto HubGetPortStatusEnd;
- }
- HubGetPortStatusEnd:
- KeReleaseQueuedLock(Hub->ControlTransferLock);
- if (!KSUCCESS(Status)) {
- *PortStatus = 0;
- } else {
- *PortStatus = *((PULONG)(Setup + 1));
- }
- return Status;
- }
- KSTATUS
- UsbpHubSetOrClearFeature (
- PUSB_HUB Hub,
- BOOL SetFeature,
- USHORT Feature,
- USHORT Port
- )
- /*++
- Routine Description:
- This routine sends a set feature or clear feature request to the hub.
- Arguments:
- Hub - Supplies a pointer to the hub to send the transfer to.
- SetFeature - Supplies a boolean indicating whether this is a SET_FEATURE
- request (TRUE) or a CLEAR_FEATURE request (FALSE).
- Feature - Supplies the feature selector to set or clear. This is the value
- that goes in the Value field of the setup packet.
- Port - Supplies the port number to set or clear. This is the value that
- goes in the Index field of the setup packet. The first port is port 1.
- Supply 0 to set or clear a hub feature.
- Return Value:
- Status code.
- --*/
- {
- ULONG LengthTransferred;
- PUSB_SETUP_PACKET Setup;
- KSTATUS Status;
- Setup = (PUSB_SETUP_PACKET)Hub->ControlTransfer->Buffer;
- KeAcquireQueuedLock(Hub->ControlTransferLock);
- Setup->RequestType = USB_SETUP_REQUEST_TO_DEVICE |
- USB_SETUP_REQUEST_CLASS;
- //
- // Treat port 0 as the hub itself.
- //
- if (Port == 0) {
- Setup->RequestType |= USB_SETUP_REQUEST_DEVICE_RECIPIENT;
- } else {
- ASSERT(Port <= Hub->PortCount);
- Setup->RequestType |= USB_SETUP_REQUEST_OTHER_RECIPIENT;
- }
- if (SetFeature != FALSE) {
- Setup->Request = USB_DEVICE_REQUEST_SET_FEATURE;
- } else {
- Setup->Request = USB_DEVICE_REQUEST_CLEAR_FEATURE;
- }
- Setup->Value = Feature;
- Setup->Index = Port;
- Setup->Length = 0;
- Hub->ControlTransfer->Direction = UsbTransferDirectionOut;
- Hub->ControlTransfer->Length = sizeof(USB_SETUP_PACKET);
- Status = UsbpHubSendControlTransfer(Hub, &LengthTransferred);
- KeReleaseQueuedLock(Hub->ControlTransferLock);
- return Status;
- }
- VOID
- UsbpHubInterruptTransferCompletion (
- PUSB_TRANSFER Transfer
- )
- /*++
- Routine Description:
- This routine is called when the interrupt transfer on the hub's status
- change endpoint completes.
- Arguments:
- Transfer - Supplies a pointer to the transfer that completed.
- Return Value:
- None.
- --*/
- {
- USHORT ChangedPorts;
- PVOID DeviceToken;
- PUSB_HUB Hub;
- PUSB_TRANSFER_INTERNAL InternalTransfer;
- KSTATUS Status;
- BOOL SubmitTransfer;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- Hub = (PUSB_HUB)Transfer->UserData;
- ASSERT(Transfer == Hub->InterruptTransfer);
- //
- // Handle errors.
- //
- SubmitTransfer = FALSE;
- if (!KSUCCESS(Transfer->Status)) {
- //
- // Exit on cancelled transfers. Something else will restart the
- // transfer if necessary.
- //
- if (Transfer->Status == STATUS_OPERATION_CANCELLED) {
- ASSERT(Transfer->Error == UsbErrorTransferCancelled);
- //
- // On IO errors, do not queue the work item, just re-submit.
- //
- } else if (Transfer->Status == STATUS_DEVICE_IO_ERROR) {
- //
- // If the endpoint halted, try to clear the halted feature bit.
- //
- if (Transfer->Error == UsbErrorTransferStalled) {
- InternalTransfer = (PUSB_TRANSFER_INTERNAL)Transfer;
- Status = UsbClearFeature(Hub->DeviceHandle,
- USB_SETUP_REQUEST_ENDPOINT_RECIPIENT,
- USB_FEATURE_ENDPOINT_HALT,
- InternalTransfer->EndpointNumber);
- if (!KSUCCESS(Status)) {
- if ((UsbDebugFlags &
- (USB_DEBUG_HUB | USB_DEBUG_ERRORS)) != 0) {
- RtlDebugPrint("USB HUB: status change transfer "
- "(0x%08x) on hub 0x%08x stalled. Failed "
- "to clear HALT feature on endpoint with "
- "status 0x%08x.\n",
- Transfer,
- Hub,
- Status);
- }
- DeviceToken = UsbGetDeviceToken(Hub->DeviceHandle);
- IoSetDeviceDriverError(DeviceToken,
- UsbCoreDriver,
- Status,
- USB_CORE_ERROR_ENDPOINT_HALTED);
- goto HubInterruptCompletionEnd;
- }
- }
- SubmitTransfer = TRUE;
- //
- // On all other errors, notify the debugger and try again.
- //
- } else {
- RtlDebugPrint("USB HUB: Unexpected error for hub (0x%08x) status "
- "change transfer (0x%08x): status 0x%08x, error "
- "%d.\n",
- Hub,
- Transfer,
- Transfer->Status,
- Transfer->Error);
- SubmitTransfer = TRUE;
- }
- goto HubInterruptCompletionEnd;
- }
- //
- // If the length transferred is correct, read in the changed port data.
- //
- ChangedPorts = 0;
- if (Transfer->LengthTransferred == Transfer->Length) {
- ChangedPorts = *((PUSHORT)Transfer->Buffer);
- }
- Hub->ChangedPorts = ChangedPorts;
- //
- // If something changed, queue the interrupt work item to get off of the
- // callback routine. While running in the callback, the control transfers
- // kicked off here won't complete.
- //
- if (ChangedPorts != 0) {
- Status = KeQueueWorkItem(Hub->InterruptWorkItem);
- ASSERT(KSUCCESS(Status));
- } else {
- SubmitTransfer = TRUE;
- }
- HubInterruptCompletionEnd:
- if (SubmitTransfer != FALSE) {
- UsbSubmitTransfer(Hub->InterruptTransfer);
- }
- return;
- }
- VOID
- UsbpHubInterruptTransferCompletionWorker (
- PVOID Parameter
- )
- /*++
- Routine Description:
- This routine is a work item routine called when the interrupt transfer on
- the hub's status change endpoint completes.
- Arguments:
- Parameter - Supplies a pointer to the work item parameter, which in this
- case is a pointer to the USB hub.
- Return Value:
- None.
- --*/
- {
- USHORT ChangedPorts;
- BOOL ChildLockHeld;
- PUSB_DEVICE Device;
- ULONG HardwareStatus;
- PUSB_HUB Hub;
- USHORT HubChange;
- ULONG HubStatus;
- BOOL PortChanged;
- ULONG PortIndex;
- PUSB_PORT_STATUS PortStatus;
- KSTATUS Status;
- BOOL SubmitTransfer;
- BOOL TopologyChanged;
- PUSB_DEVICE UsbDevice;
- Hub = (PUSB_HUB)Parameter;
- Device = (PUSB_DEVICE)Hub->DeviceHandle;
- ChangedPorts = Hub->ChangedPorts;
- ChildLockHeld = FALSE;
- SubmitTransfer = TRUE;
- TopologyChanged = FALSE;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- ASSERT(Hub->HubStatus.PortStatus != NULL);
- //
- // Bit zero is the hub's index.
- //
- if ((ChangedPorts & 0x0001) != 0) {
- Status = UsbpHubGetHubStatus(Hub, &HubStatus);
- if (KSUCCESS(Status)) {
- HubChange = HubStatus >> USB_HUB_HUB_STATUS_CHANGE_SHIFT;
- //
- // Just clear the local power status.
- //
- if ((HubChange & USB_HUB_HUB_STATUS_LOCAL_POWER) != 0) {
- UsbpHubSetOrClearFeature(Hub,
- FALSE,
- USB_HUB_FEATURE_C_HUB_LOCAL_POWER,
- 0);
- }
- //
- // Handle over current changes according to section 11.12.5 of the
- // USB 2.0 Specification.
- //
- if ((HubChange & USB_HUB_HUB_STATUS_OVER_CURRENT) != 0) {
- //
- // Wait for the hub's over current status bit to go to zero.
- // Assumably, this is to wait for the hub to power off.
- //
- while ((HubStatus & USB_HUB_HUB_STATUS_OVER_CURRENT) != 0) {
- Status = UsbpHubGetHubStatus(Hub, &HubStatus);
- if (!KSUCCESS(Status)) {
- goto HubInterruptTransferCompletionWorkerEnd;
- }
- }
- //
- // Clear the over current change bit.
- //
- Status = UsbpHubSetOrClearFeature(
- Hub,
- FALSE,
- USB_HUB_FEATURE_C_HUB_OVER_CURRENT,
- 0);
- if (!KSUCCESS(Status)) {
- goto HubInterruptTransferCompletionWorkerEnd;
- }
- //
- // Reset the hub. If this succeeds, then it will have
- // re-submitted the interrupt transfer.
- //
- Status = UsbpResetHub(Hub);
- if (!KSUCCESS(Status)) {
- goto HubInterruptTransferCompletionWorkerEnd;
- }
- SubmitTransfer = FALSE;
- //
- // Mark that the topology changed so that the system
- // re-enumerates all the ports on this hub.
- //
- TopologyChanged = TRUE;
- //
- // Exit without checking the individual port status. The whole
- // hub just got reset.
- //
- goto HubInterruptTransferCompletionWorkerEnd;
- }
- }
- }
- ChangedPorts = ChangedPorts >> 1;
- ASSERT(Hub->PortCount != 0);
- for (PortIndex = 0; PortIndex < Hub->PortCount; PortIndex += 1) {
- //
- // Determine if the port changed. If it didn't, move on.
- //
- PortChanged = FALSE;
- if ((ChangedPorts & 0x1) != 0) {
- PortChanged = TRUE;
- }
- ChangedPorts = ChangedPorts >> 1;
- if (PortChanged == FALSE) {
- continue;
- }
- //
- // If the port changed, read its status. Synchronize this with any
- // other port status changes.
- //
- KeAcquireQueuedLock(Device->ChildLock);
- ChildLockHeld = TRUE;
- Status = UsbpHubGetPortStatus(Hub, PortIndex + 1, &HardwareStatus);
- if (!KSUCCESS(Status)) {
- goto HubInterruptTransferCompletionWorkerEnd;
- }
- //
- // Update the software status stored in the hub.
- //
- UsbpHubUpdatePortStatus(Hub, PortIndex, HardwareStatus);
- if ((UsbDebugFlags & USB_DEBUG_HUB) != 0) {
- RtlDebugPrint("USB: Hub %x Port %d Hardware Status %x.\n",
- Hub,
- PortIndex,
- HardwareStatus);
- }
- //
- // Handle over current change notifications.
- //
- PortStatus = &(Hub->HubStatus.PortStatus[PortIndex]);
- if ((PortStatus->Change & USB_PORT_STATUS_CHANGE_OVER_CURRENT) != 0) {
- //
- // Wait until the over current bit is clear.
- //
- while ((PortStatus->Status & USB_PORT_STATUS_OVER_CURRENT) != 0) {
- Status = UsbpHubGetPortStatus(Hub,
- PortIndex + 1,
- &HardwareStatus);
- if (!KSUCCESS(Status)) {
- goto HubInterruptTransferCompletionWorkerEnd;
- }
- UsbpHubUpdatePortStatus(Hub, PortIndex, HardwareStatus);
- }
- //
- // Now wipe the port status and enable the power on the port.
- //
- RtlZeroMemory(PortStatus, sizeof(USB_PORT_STATUS));
- Hub->HubStatus.PortDeviceSpeed[PortIndex] = UsbDeviceSpeedInvalid;
- Status = UsbpHubEnablePortPower(Hub, PortIndex);
- if (!KSUCCESS(Status)) {
- goto HubInterruptTransferCompletionWorkerEnd;
- }
- //
- // Collect the status one more time after the power on. If there is
- // something behind the port then the connection changed bit should
- // get set.
- //
- Status = UsbpHubGetPortStatus(Hub, PortIndex + 1, &HardwareStatus);
- if (!KSUCCESS(Status)) {
- goto HubInterruptTransferCompletionWorkerEnd;
- }
- UsbpHubUpdatePortStatus(Hub, PortIndex, HardwareStatus);
- }
- //
- // Attempt to clear out any change bits.
- //
- Status = UsbpHubClearPortChangeBits(Hub,
- PortIndex + 1,
- HardwareStatus);
- if (!KSUCCESS(Status)) {
- goto HubInterruptTransferCompletionWorkerEnd;
- }
- //
- // If the connection status has changed, then notify the system of a
- // topology change.
- //
- if ((PortStatus->Change & USB_PORT_STATUS_CHANGE_CONNECTED) != 0) {
- TopologyChanged = TRUE;
- }
- KeReleaseQueuedLock(Device->ChildLock);
- ChildLockHeld = FALSE;
- }
- HubInterruptTransferCompletionWorkerEnd:
- if (ChildLockHeld != FALSE) {
- KeReleaseQueuedLock(Device->ChildLock);
- }
- //
- // If there was a topology change on the hub, notify the system.
- //
- if (TopologyChanged != FALSE) {
- UsbDevice = (PUSB_DEVICE)Hub->DeviceHandle;
- IoNotifyDeviceTopologyChange(UsbDevice->Device);
- }
- //
- // Resubmit the transfer even if this routine failed.
- //
- if (SubmitTransfer != FALSE) {
- Status = UsbSubmitTransfer(Hub->InterruptTransfer);
- ASSERT(KSUCCESS(Status));
- }
- return;
- }
- KSTATUS
- UsbpHubClearPortChangeBits (
- PUSB_HUB Hub,
- ULONG PortNumber,
- ULONG PortStatus
- )
- /*++
- Routine Description:
- This routine communicates with the given hub to clear any change status
- bits set in the port status.
- Arguments:
- Hub - Supplies a pointer to the hub that owns the port.
- PortNumber - Supplies the one-indexed port number (zero is not a valid
- value here).
- PortStatus - Supplies the port status bits. Any change bits set here will
- be cleared.
- Return Value:
- Status code.
- --*/
- {
- KSTATUS Status;
- ASSERT(PortNumber != 0);
- //
- // Clear out any change bits.
- //
- PortStatus = PortStatus >> USB_HUB_PORT_STATUS_CHANGE_SHIFT;
- if ((PortStatus & USB_HUB_PORT_STATUS_DEVICE_CONNECTED) != 0) {
- Status = UsbpHubSetOrClearFeature(Hub,
- FALSE,
- USB_HUB_FEATURE_C_PORT_CONNECTION,
- PortNumber);
- if (!KSUCCESS(Status)) {
- goto HubClearPortChangeBits;
- }
- }
- if ((PortStatus & USB_HUB_PORT_STATUS_ENABLED) != 0) {
- Status = UsbpHubSetOrClearFeature(Hub,
- FALSE,
- USB_HUB_FEATURE_C_PORT_ENABLE,
- PortNumber);
- if (!KSUCCESS(Status)) {
- goto HubClearPortChangeBits;
- }
- }
- if ((PortStatus & USB_HUB_PORT_STATUS_SUSPENDED) != 0) {
- Status = UsbpHubSetOrClearFeature(Hub,
- FALSE,
- USB_HUB_FEATURE_C_PORT_SUSPEND,
- PortNumber);
- if (!KSUCCESS(Status)) {
- goto HubClearPortChangeBits;
- }
- }
- if ((PortStatus & USB_HUB_PORT_STATUS_OVER_CURRENT) != 0) {
- Status = UsbpHubSetOrClearFeature(
- Hub,
- FALSE,
- USB_HUB_FEATURE_C_PORT_OVER_CURRENT,
- PortNumber);
- if (!KSUCCESS(Status)) {
- goto HubClearPortChangeBits;
- }
- }
- if ((PortStatus & USB_HUB_PORT_STATUS_RESET) != 0) {
- Status = UsbpHubSetOrClearFeature(Hub,
- FALSE,
- USB_HUB_FEATURE_C_PORT_RESET,
- PortNumber);
- if (!KSUCCESS(Status)) {
- goto HubClearPortChangeBits;
- }
- }
- Status = STATUS_SUCCESS;
- HubClearPortChangeBits:
- return Status;
- }
- VOID
- UsbpHubAddDevice (
- PUSB_HUB Hub,
- ULONG PortIndex
- )
- /*++
- Routine Description:
- This routine attempts to add a device to the given hub at the given port
- index. It resets the port and then tries to enumerate a device.
- Arguments:
- Hub - Supplies a pointer to the USB hub that is adding the new device.
- PortIndex - Supplies the index of the hub port at which the new device is
- to be added.
- Return Value:
- None.
- --*/
- {
- PUSB_DEVICE Child;
- PUSB_DEVICE Device;
- PUSB_PORT_STATUS PortStatus;
- KSTATUS Status;
- Child = NULL;
- Device = (PUSB_DEVICE)Hub->DeviceHandle;
- ASSERT(KeIsQueuedLockHeld(Device->ChildLock) != FALSE);
- ASSERT(Hub->HubStatus.PortStatus != NULL);
- //
- // When the system last checked, there was a device present on this port.
- // Wait the minimum debounce interval according to section 7.1.7.3 of the
- // USB specification, and then recheck the state and proceed only if the
- // device is still present.
- //
- KeDelayExecution(FALSE, FALSE, 100 * MICROSECONDS_PER_MILLISECOND);
- //
- // Get the current hub status.
- //
- Status = UsbpGetHubStatus(Hub, TRUE);
- if (!KSUCCESS(Status)) {
- goto HubAddDevice;
- }
- //
- // If the device is not present, exit.
- //
- PortStatus = &(Hub->HubStatus.PortStatus[PortIndex]);
- ASSERT((PortStatus->Change & USB_PORT_STATUS_CHANGE_CONNECTED) != 0);
- if ((PortStatus->Status & USB_PORT_STATUS_CONNECTED) == 0) {
- Status = STATUS_SUCCESS;
- goto HubAddDevice;
- }
- //
- // Reset the port. If the device is still there after the reset, then
- // create a device.
- //
- Status = UsbpResetHubPort(Hub, PortIndex);
- if (!KSUCCESS(Status)) {
- goto HubAddDevice;
- }
- if ((PortStatus->Status & USB_PORT_STATUS_CONNECTED) != 0) {
- Status = UsbpEnumerateDevice(Hub,
- Device,
- PortIndex + 1,
- Hub->HubStatus.PortDeviceSpeed[PortIndex],
- (PVOID)&Child);
- if (!KSUCCESS(Status)) {
- goto HubAddDevice;
- }
- ASSERT(Child != NULL);
- }
- HubAddDevice:
- return;
- }
- KSTATUS
- UsbpHubEnablePortPower (
- PUSB_HUB Hub,
- ULONG PortIndex
- )
- /*++
- Routine Description:
- This routine enables power on a hub port.
- Arguments:
- Hub - Supplies a pointer to a hub.
- PortIndex - Supplies the zero-based index of the port to be powered on.
- Return Value:
- Status code.
- --*/
- {
- KSTATUS Status;
- Status = UsbpHubSetOrClearFeature(Hub,
- TRUE,
- USB_HUB_FEATURE_PORT_POWER,
- PortIndex + 1);
- if (!KSUCCESS(Status)) {
- goto HubEnablePortPowerEnd;
- }
- if (Hub->HasIndicators != FALSE) {
- Status = UsbpHubSetOrClearFeature(
- Hub,
- TRUE,
- USB_HUB_FEATURE_PORT_INDICATOR,
- (PortIndex + 1) | USB_HUB_INDICATOR_AUTOMATIC);
- if (!KSUCCESS(Status)) {
- goto HubEnablePortPowerEnd;
- }
- }
- //
- // Now that the port has been powered up, delay for the appropriate
- // amount of time before accessing it again.
- //
- KeDelayExecution(FALSE,
- FALSE,
- Hub->PowerUpDelayIn2ms * 2 * MICROSECONDS_PER_MILLISECOND);
- HubEnablePortPowerEnd:
- return Status;
- }
|