123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200 |
- /*++
- Copyright (c) 2015 Minoca Corp. All Rights Reserved
- Module Name:
- spb.c
- Abstract:
- This module implements support for the Simple Peripheral Bus support
- library driver.
- Author:
- Evan Green 14-Aug-2015
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/driver.h>
- #include "spbp.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define SPB_CONTROLLER_INFORMATION_MAX_VERSION 0x1000
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- VOID
- SpbDriverUnload (
- PVOID Driver
- );
- KSTATUS
- SpbOpen (
- PSPB_INTERFACE Interface,
- PRESOURCE_SPB_DATA Configuration,
- PSPB_HANDLE Handle
- );
- VOID
- SpbClose (
- PSPB_INTERFACE Interface,
- SPB_HANDLE Handle
- );
- KSTATUS
- SpbSetConfiguration (
- SPB_HANDLE Handle,
- PRESOURCE_SPB_DATA Configuration
- );
- VOID
- SpbLockBus (
- SPB_HANDLE Handle
- );
- VOID
- SpbUnlockBus (
- SPB_HANDLE Handle
- );
- KSTATUS
- SpbSubmitTransferSet (
- SPB_HANDLE Handle,
- PSPB_TRANSFER_SET TransferSet
- );
- KSTATUS
- SpbExecuteTransferSet (
- SPB_HANDLE Handle,
- PSPB_TRANSFER_SET TransferSet
- );
- KSTATUS
- SpbpExecuteTransferSet (
- PSPB_CONTROLLER Controller,
- PSPB_TRANSFER_SET TransferSet
- );
- VOID
- SpbpCompleteTransferSet (
- PSPB_CONTROLLER Controller,
- PSPB_TRANSFER_SET TransferSet,
- KSTATUS Status
- );
- VOID
- SpbpSynchronousTransferCompletionCallback (
- PSPB_TRANSFER_SET TransferSet
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- UUID SpbInterfaceUuid = UUID_SPB_INTERFACE;
- SPB_INTERFACE SpbInterfaceTemplate = {
- NULL,
- SpbOpen,
- SpbClose,
- SpbSetConfiguration,
- SpbLockBus,
- SpbUnlockBus,
- SpbSubmitTransferSet,
- SpbExecuteTransferSet
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- KSTATUS
- DriverEntry (
- PDRIVER Driver
- )
- /*++
- Routine Description:
- This routine implements the initial entry point of the SPB core
- library, called when the library is first loaded.
- Arguments:
- Driver - Supplies a pointer to the driver object.
- Return Value:
- Status code.
- --*/
- {
- DRIVER_FUNCTION_TABLE FunctionTable;
- KSTATUS Status;
- RtlZeroMemory(&FunctionTable, sizeof(DRIVER_FUNCTION_TABLE));
- FunctionTable.Version = DRIVER_FUNCTION_TABLE_VERSION;
- FunctionTable.Unload = SpbDriverUnload;
- Status = IoRegisterDriverFunctions(Driver, &FunctionTable);
- return Status;
- }
- SPB_API
- KSTATUS
- SpbCreateController (
- PSPB_CONTROLLER_INFORMATION Registration,
- PSPB_CONTROLLER *Controller
- )
- /*++
- Routine Description:
- This routine creates a new Simple Peripheral Bus controller.
- Arguments:
- Registration - Supplies a pointer to the host registration information.
- Controller - Supplies a pointer where a pointer to the new controller will
- be returned on success.
- Return Value:
- Status code.
- --*/
- {
- UINTN AllocationSize;
- PSPB_CONTROLLER NewController;
- KSTATUS Status;
- if ((Registration->Version < SPB_CONTROLLER_INFORMATION_VERSION) ||
- (Registration->Version > SPB_CONTROLLER_INFORMATION_MAX_VERSION) ||
- (Registration->BusType <= ResourceSpbBusInvalid) ||
- (Registration->BusType >= ResourceSpbBusTypeCount) ||
- (Registration->Device == NULL)) {
- return STATUS_INVALID_PARAMETER;
- }
- AllocationSize = sizeof(SPB_CONTROLLER);
- NewController = MmAllocatePagedPool(AllocationSize, SPB_ALLOCATION_TAG);
- if (NewController == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateControllerEnd;
- }
- RtlZeroMemory(NewController, AllocationSize);
- RtlCopyMemory(&(NewController->Host),
- Registration,
- sizeof(SPB_CONTROLLER_INFORMATION));
- RtlCopyMemory(&(NewController->Interface),
- &SpbInterfaceTemplate,
- sizeof(SPB_INTERFACE));
- NewController->Magic = SPB_CONTROLLER_MAGIC;
- INITIALIZE_LIST_HEAD(&(NewController->HandleList));
- INITIALIZE_LIST_HEAD(&(NewController->TransferQueue));
- NewController->Lock = KeCreateQueuedLock();
- if (NewController->Lock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateControllerEnd;
- }
- NewController->BusLock = KeCreateQueuedLock();
- if (NewController->BusLock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateControllerEnd;
- }
- Status = STATUS_SUCCESS;
- CreateControllerEnd:
- if (!KSUCCESS(Status)) {
- if (NewController != NULL) {
- if (NewController->Lock != NULL) {
- KeDestroyQueuedLock(NewController->Lock);
- }
- if (NewController->BusLock != NULL) {
- KeDestroyQueuedLock(NewController->BusLock);
- }
- MmFreePagedPool(NewController);
- NewController = NULL;
- }
- }
- *Controller = NewController;
- return Status;
- }
- SPB_API
- VOID
- SpbDestroyController (
- PSPB_CONTROLLER Controller
- )
- /*++
- Routine Description:
- This routine destroys a Simple Peripheral Bus controller.
- Arguments:
- Controller - Supplies a pointer to the controller to tear down.
- Return Value:
- None.
- --*/
- {
- ASSERT(LIST_EMPTY(&(Controller->HandleList)));
- if (Controller->Lock != NULL) {
- KeDestroyQueuedLock(Controller->Lock);
- }
- if (Controller->BusLock != NULL) {
- KeDestroyQueuedLock(Controller->BusLock);
- }
- //
- // Ruin the magic (but in a way that's still identifiable to a human).
- //
- Controller->Magic += 1;
- MmFreePagedPool(Controller);
- return;
- }
- SPB_API
- KSTATUS
- SpbStartController (
- PSPB_CONTROLLER Controller
- )
- /*++
- Routine Description:
- This routine starts a Simple Peripheral Bus controller.
- Arguments:
- Controller - Supplies a pointer to the controller.
- Return Value:
- Status code.
- --*/
- {
- PSPB_CONTROLLER_INFORMATION Host;
- KSTATUS Status;
- ASSERT(Controller->Interface.Context == NULL);
- ASSERT(KeGetRunLevel() == RunLevelLow);
- KeAcquireQueuedLock(Controller->Lock);
- Host = &(Controller->Host);
- Controller->Interface.Context = &(Controller->Interface);
- Status = IoCreateInterface(&SpbInterfaceUuid,
- Host->Device,
- &(Controller->Interface),
- sizeof(SPB_INTERFACE));
- if (!KSUCCESS(Status)) {
- Controller->Interface.Context = NULL;
- goto StartControllerEnd;
- }
- //
- // Create a resource arbiter for these pins so that other devices can
- // allocate them as part of their official resource requirements.
- //
- if (Controller->ArbiterCreated == FALSE) {
- Status = IoCreateResourceArbiter(Host->Device, ResourceTypeSimpleBus);
- if ((!KSUCCESS(Status)) && (Status != STATUS_ALREADY_INITIALIZED)) {
- goto StartControllerEnd;
- }
- Status = IoAddFreeSpaceToArbiter(Host->Device,
- ResourceTypeSimpleBus,
- 0,
- -1ULL,
- 0,
- NULL,
- 0);
- if (!KSUCCESS(Status)) {
- goto StartControllerEnd;
- }
- Controller->ArbiterCreated = TRUE;
- }
- StartControllerEnd:
- KeReleaseQueuedLock(Controller->Lock);
- return Status;
- }
- SPB_API
- VOID
- SpbStopController (
- PSPB_CONTROLLER Controller
- )
- /*++
- Routine Description:
- This routine stops a Simple Peripheral Bus controller.
- Arguments:
- Controller - Supplies a pointer to the controller.
- Return Value:
- None.
- --*/
- {
- KSTATUS Status;
- ASSERT(Controller->Interface.Context == &(Controller->Interface));
- ASSERT(KeGetRunLevel() == RunLevelLow);
- KeAcquireQueuedLock(Controller->Lock);
- Status = IoDestroyInterface(&SpbInterfaceUuid,
- Controller->Host.Device,
- &(Controller->Interface));
- ASSERT(KSUCCESS(Status));
- Controller->Interface.Context = NULL;
- ASSERT(LIST_EMPTY(&(Controller->HandleList)));
- KeReleaseQueuedLock(Controller->Lock);
- return;
- }
- SPB_API
- PSPB_TRANSFER
- SpbTransferCompletion (
- PSPB_CONTROLLER Controller,
- PSPB_TRANSFER Transfer,
- KSTATUS Status
- )
- /*++
- Routine Description:
- This routine is called by an SPB host controller when a transfer has
- completed.
- Arguments:
- Controller - Supplies a pointer to the controller.
- Transfer - Supplies a pointer to the transfer that completed.
- Status - Supplies the status code the transfer completed with.
- Return Value:
- Returns a new transfer to begin executing if there are additional transfers
- in this set and the previous transfer completed successfully.
- NULL if no new transfers should be started at this time.
- --*/
- {
- PSPB_TRANSFER NextTransfer;
- ASSERT(Controller->CurrentSet != NULL);
- NextTransfer = NULL;
- Controller->CurrentSet->EntriesProcessed += 1;
- //
- // On failure or if this is the last transfer, complete the whole set.
- //
- if ((!KSUCCESS(Status)) ||
- (Transfer->ListEntry.Next == &(Controller->CurrentSet->TransferList))) {
- SpbpCompleteTransferSet(Controller, Controller->CurrentSet, Status);
- } else {
- NextTransfer = LIST_VALUE(Transfer->ListEntry.Next,
- SPB_TRANSFER,
- ListEntry);
- NextTransfer->Flags &= ~SPB_TRANSFER_FLAG_AUTO_MASK;
- if (NextTransfer->ListEntry.Next ==
- &(Controller->CurrentSet->TransferList)) {
- NextTransfer->Flags |= SPB_TRANSFER_FLAG_LAST;
- }
- }
- return NextTransfer;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- VOID
- SpbDriverUnload (
- PVOID Driver
- )
- /*++
- Routine Description:
- This routine is called before a driver is about to be unloaded from memory.
- The driver should take this opportunity to free any resources it may have
- set up in the driver entry routine.
- Arguments:
- Driver - Supplies a pointer to the driver being torn down.
- Return Value:
- None.
- --*/
- {
- return;
- }
- KSTATUS
- SpbOpen (
- PSPB_INTERFACE Interface,
- PRESOURCE_SPB_DATA Configuration,
- PSPB_HANDLE Handle
- )
- /*++
- Routine Description:
- This routine opens a new connection to a Simple Peripheral Bus.
- Arguments:
- Interface - Supplies a pointer to the interface instance, used to identify
- which specific bus is being opened.
- Configuration - Supplies a pointer to the configuration data that specifies
- bus specific configuration parameters.
- Handle - Supplies a pointer where a handle will be returned on success
- representing the connection to the device.
- Return Value:
- Status code.
- --*/
- {
- PSPB_CONTROLLER Controller;
- PSPB_HANDLE_DATA HandleData;
- KSTATUS Status;
- Controller = PARENT_STRUCTURE(Interface->Context,
- SPB_CONTROLLER,
- Interface);
- ASSERT(Controller->Magic == SPB_CONTROLLER_MAGIC);
- HandleData = MmAllocatePagedPool(sizeof(SPB_HANDLE_DATA),
- SPB_ALLOCATION_TAG);
- if (HandleData == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto OpenEnd;
- }
- RtlZeroMemory(HandleData, sizeof(SPB_HANDLE_DATA));
- HandleData->Magic = SPB_HANDLE_MAGIC;
- HandleData->Controller = Controller;
- Status = SpbSetConfiguration(HandleData, Configuration);
- if (!KSUCCESS(Status)) {
- goto OpenEnd;
- }
- KeAcquireQueuedLock(Controller->Lock);
- INSERT_BEFORE(&(HandleData->ListEntry), &(Controller->HandleList));
- KeReleaseQueuedLock(Controller->Lock);
- OpenEnd:
- if (!KSUCCESS(Status)) {
- if (HandleData != NULL) {
- ASSERT(HandleData->Configuration == NULL);
- MmFreePagedPool(HandleData);
- HandleData = NULL;
- }
- }
- *Handle = HandleData;
- return Status;
- }
- VOID
- SpbClose (
- PSPB_INTERFACE Interface,
- SPB_HANDLE Handle
- )
- /*++
- Routine Description:
- This routine closes a previously opened to a Simple Peripheral Bus.
- Arguments:
- Interface - Supplies a pointer to the interface instance, used to identify
- which specific bus is being operated on.
- Handle - Supplies the open handle to close.
- Return Value:
- None.
- --*/
- {
- PSPB_CONTROLLER Controller;
- PSPB_HANDLE_DATA HandleData;
- HandleData = Handle;
- Controller = HandleData->Controller;
- ASSERT(HandleData->Magic == SPB_HANDLE_MAGIC);
- ASSERT(HandleData->BusReferenceCount == 0);
- ASSERT(Controller ==
- PARENT_STRUCTURE(Interface, SPB_CONTROLLER, Interface));
- if (HandleData->Event != NULL) {
- KeDestroyEvent(HandleData->Event);
- }
- KeAcquireQueuedLock(Controller->Lock);
- LIST_REMOVE(&(HandleData->ListEntry));
- if (Controller->CurrentConfiguration == HandleData->Configuration) {
- Controller->CurrentConfiguration = NULL;
- }
- KeReleaseQueuedLock(Controller->Lock);
- HandleData->ListEntry.Next = NULL;
- if (HandleData->Configuration != NULL) {
- MmFreePagedPool(HandleData->Configuration);
- }
- HandleData->Magic += 1;
- MmFreePagedPool(HandleData);
- return;
- }
- KSTATUS
- SpbSetConfiguration (
- SPB_HANDLE Handle,
- PRESOURCE_SPB_DATA Configuration
- )
- /*++
- Routine Description:
- This routine writes a new set of bus parameters to the bus.
- Arguments:
- Handle - Supplies the open handle to change configuration of.
- Configuration - Supplies the new configuration to set.
- Return Value:
- Status code.
- --*/
- {
- PSPB_CONTROLLER Controller;
- PSPB_HANDLE_DATA HandleData;
- PVOID NewData;
- PVOID OldData;
- HandleData = Handle;
- Controller = HandleData->Controller;
- ASSERT(HandleData->Magic == SPB_HANDLE_MAGIC);
- //
- // Perform some checks against accidental misconfiguration. This isn't
- // nearly a bulletproof set of checks.
- //
- if ((Configuration == NULL) ||
- (Configuration->Size < sizeof(RESOURCE_SPB_DATA)) ||
- (Configuration->VendorDataSize >
- Configuration->Size - sizeof(RESOURCE_SPB_DATA)) ||
- (Configuration->BusType != HandleData->Controller->Host.BusType)) {
- return STATUS_INVALID_PARAMETER;
- }
- NewData = MmAllocatePagedPool(Configuration->Size, SPB_ALLOCATION_TAG);
- if (NewData == NULL) {
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- RtlCopyMemory(NewData, Configuration, Configuration->Size);
- KeAcquireQueuedLock(Controller->Lock);
- OldData = HandleData->Configuration;
- if (OldData != NULL) {
- if (Controller->CurrentConfiguration == OldData) {
- Controller->CurrentConfiguration = NULL;
- }
- }
- HandleData->Configuration = NewData;
- KeReleaseQueuedLock(Controller->Lock);
- if (OldData != NULL) {
- MmFreePagedPool(OldData);
- }
- return STATUS_SUCCESS;
- }
- VOID
- SpbLockBus (
- SPB_HANDLE Handle
- )
- /*++
- Routine Description:
- This routine locks the bus so that this handle may perform a sequence of
- accesses without being interrupted.
- Arguments:
- Handle - Supplies the open handle to the bus to lock.
- Return Value:
- None.
- --*/
- {
- PSPB_CONTROLLER Controller;
- PSPB_HANDLE_DATA HandleData;
- PSPB_CONTROLLER_INFORMATION Host;
- ULONG OldValue;
- HandleData = Handle;
- ASSERT(HandleData->Magic == SPB_HANDLE_MAGIC);
- OldValue = RtlAtomicAdd32(&(HandleData->BusReferenceCount), 1);
- ASSERT(OldValue < 0x1000);
- if (OldValue == 0) {
- KeAcquireQueuedLock(HandleData->Controller->BusLock);
- Controller = HandleData->Controller;
- Host = &(Controller->Host);
- if (Host->FunctionTable.LockBus != NULL) {
- Host->FunctionTable.LockBus(Host->Context,
- HandleData->Configuration);
- }
- }
- return;
- }
- VOID
- SpbUnlockBus (
- SPB_HANDLE Handle
- )
- /*++
- Routine Description:
- This routine unlocks a bus that was previously locked with the lock
- function.
- Arguments:
- Handle - Supplies the open handle to the bus to unlock. The caller must
- have previously locked the bus.
- Return Value:
- None.
- --*/
- {
- PSPB_CONTROLLER Controller;
- PSPB_HANDLE_DATA HandleData;
- PSPB_CONTROLLER_INFORMATION Host;
- PSPB_HANDLE_DATA NextHandleData;
- PSPB_TRANSFER_SET NextSet;
- ULONG OldValue;
- HandleData = Handle;
- Controller = HandleData->Controller;
- ASSERT(HandleData->Magic == SPB_HANDLE_MAGIC);
- OldValue = RtlAtomicAdd32(&(HandleData->BusReferenceCount), -1);
- ASSERT((OldValue != 0) && (OldValue < 0x1000));
- if (OldValue == 1) {
- Host = &(Controller->Host);
- //
- // Let the host know the bus is being unlocked.
- //
- if (Host->FunctionTable.UnlockBus != NULL) {
- Host->FunctionTable.UnlockBus(Host->Context);
- }
- //
- // If there are more items on the transfer queue and someone else
- // hasn't already started executing them, fire it off now. Do an
- // initial unsynchronized check to avoid acquiring the lock if possible.
- //
- NextSet = NULL;
- if ((Controller->CurrentSet == NULL) &&
- (!LIST_EMPTY(&(Controller->TransferQueue)))) {
- KeAcquireQueuedLock(Controller->Lock);
- if ((Controller->CurrentSet == NULL) &&
- (!LIST_EMPTY(&(Controller->TransferQueue)))) {
- NextSet = LIST_VALUE(Controller->TransferQueue.Next,
- SPB_TRANSFER_SET,
- ListEntry);
- Controller->CurrentSet = NextSet;
- //
- // Leave the actual lock acquired the whole time, and just
- // transfer the reference to the next handle.
- //
- HandleData = NextSet->Handle;
- RtlAtomicAdd32(&(HandleData->BusReferenceCount), 1);
- //
- // The host was told the bus was unlocked, so it needs to be
- // told its actually still locked.
- //
- if (Host->FunctionTable.LockBus != NULL) {
- NextHandleData = NextSet->Handle;
- Host->FunctionTable.LockBus(Host->Context,
- NextHandleData->Configuration);
- }
- KeReleaseQueuedLock(Controller->Lock);
- SpbpExecuteTransferSet(Controller, NextSet);
- } else {
- KeReleaseQueuedLock(Controller->Lock);
- }
- }
- //
- // If the bus lock wasn't transferred to another handle, then really
- // release the bus lock.
- //
- if (NextSet == NULL) {
- KeReleaseQueuedLock(Controller->BusLock);
- }
- }
- return;
- }
- KSTATUS
- SpbSubmitTransferSet (
- SPB_HANDLE Handle,
- PSPB_TRANSFER_SET TransferSet
- )
- /*++
- Routine Description:
- This routine submits a set of transfers to the bus for execution. This
- routine will ensure that other devices do not perform transfers while any
- transfer in this set is in progress. The submission is asynchronous, this
- routine will return immediately, and the callback function will be called
- when the transfer is complete.
- Arguments:
- Handle - Supplies the open handle to the bus to unlock.
- TransferSet - Supplies a pointer to the transfer set to execute.
- Return Value:
- Status code. This routine will return immediately, the transfer will not
- have been complete. The caller should utilize the callback function to get
- notified when a transfer has completed.
- --*/
- {
- PSPB_CONTROLLER Controller;
- BOOL ExecuteTransfer;
- PSPB_HANDLE_DATA HandleData;
- KSTATUS Status;
- HandleData = Handle;
- if (HandleData->Configuration == NULL) {
- return STATUS_NOT_CONFIGURED;
- }
- ASSERT(HandleData->Magic == SPB_HANDLE_MAGIC);
- ASSERT(TransferSet->ListEntry.Next == NULL);
- Controller = HandleData->Controller;
- TransferSet->Handle = Handle;
- TransferSet->EntriesProcessed = 0;
- TransferSet->Status = STATUS_NOT_HANDLED;
- ExecuteTransfer = FALSE;
- KeAcquireQueuedLock(Controller->Lock);
- if (Controller->CurrentSet == NULL) {
- ExecuteTransfer = TRUE;
- Controller->CurrentSet = TransferSet;
- }
- INSERT_BEFORE(&(TransferSet->ListEntry), &(Controller->TransferQueue));
- KeReleaseQueuedLock(Controller->Lock);
- //
- // If this was the first item on an empty list, then kick off the party.
- //
- Status = STATUS_SUCCESS;
- if (ExecuteTransfer != FALSE) {
- SpbLockBus(TransferSet->Handle);
- Status = SpbpExecuteTransferSet(Controller, TransferSet);
- }
- return Status;
- }
- KSTATUS
- SpbExecuteTransferSet (
- SPB_HANDLE Handle,
- PSPB_TRANSFER_SET TransferSet
- )
- /*++
- Routine Description:
- This routine submits a set of transfers to the bus for execution. This
- routine will ensure that other devices do not perform transfers while any
- transfer in this set is in progress. This routine is synchronous, it will
- not return until the transfer is complete.
- Arguments:
- Handle - Supplies the open handle to the bus to unlock.
- TransferSet - Supplies a pointer to the transfer set to execute.
- Return Value:
- Status code indicating completion status of the transfer. This routine will
- not return until the transfer is complete (or failed).
- --*/
- {
- PKEVENT Event;
- PSPB_HANDLE_DATA HandleData;
- KSTATUS Status;
- //
- // Create an event for the handle if there isn't one already. This is not
- // thread-safe, it is expected only one synchronous transfer will be
- // submitted at a time.
- //
- HandleData = Handle;
- if (HandleData->Event == NULL) {
- Event = KeCreateEvent(NULL);
- if (Event == NULL) {
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- ASSERT(HandleData->Event == NULL);
- HandleData->Event = Event;
- }
- Event = HandleData->Event;
- ASSERT(Event != NULL);
- ASSERT((TransferSet->CompletionRoutine == NULL) &&
- (TransferSet->Context == NULL));
- KeSignalEvent(Event, SignalOptionUnsignal);
- TransferSet->CompletionRoutine = SpbpSynchronousTransferCompletionCallback;
- TransferSet->Context = Event;
- Status = SpbSubmitTransferSet(Handle, TransferSet);
- if (KSUCCESS(Status)) {
- KeWaitForEvent(Event, FALSE, WAIT_TIME_INDEFINITE);
- TransferSet->CompletionRoutine = NULL;
- TransferSet->Context = NULL;
- Status = TransferSet->Status;
- }
- return Status;
- }
- KSTATUS
- SpbpExecuteTransferSet (
- PSPB_CONTROLLER Controller,
- PSPB_TRANSFER_SET TransferSet
- )
- /*++
- Routine Description:
- This routine begins execution of a new transfer set. It is assumed that the
- bus lock is already held.
- Arguments:
- Controller - Supplies a pointer to the controller to execute on.
- TransferSet - Supplies a pointer to the transfer set to execute.
- Return Value:
- Status code. This routine will return immediately, the transfer will not
- have necessarily been completed. The caller should utilize the callback
- function to get notified when a transfer has completed.
- --*/
- {
- PSPB_HANDLE_DATA HandleData;
- KSTATUS Status;
- PSPB_TRANSFER Transfer;
- HandleData = TransferSet->Handle;
- ASSERT(HandleData->BusReferenceCount != 0);
- ASSERT(HandleData->Configuration != NULL);
- ASSERT(Controller->CurrentSet == TransferSet);
- //
- // Configure the bus if its configuration does not match what the handle
- // needs.
- //
- if (Controller->CurrentConfiguration != HandleData->Configuration) {
- Status = Controller->Host.FunctionTable.Configure(
- Controller->Host.Context,
- HandleData->Configuration);
- if (!KSUCCESS(Status)) {
- goto ExecuteTransferSetEnd;
- }
- Controller->CurrentConfiguration = HandleData->Configuration;
- }
- //
- // Execute the first transfer, or just complete the transfer if there are
- // none (bus configuration only).
- //
- if (LIST_EMPTY(&(TransferSet->TransferList))) {
- Status = STATUS_SUCCESS;
- SpbpCompleteTransferSet(Controller, TransferSet, Status);
- } else {
- Transfer = LIST_VALUE(TransferSet->TransferList.Next,
- SPB_TRANSFER,
- ListEntry);
- Transfer->Flags &= ~SPB_TRANSFER_FLAG_AUTO_MASK;
- Transfer->Flags |= SPB_TRANSFER_FLAG_FIRST;
- Status = Controller->Host.FunctionTable.SubmitTransfer(
- Controller->Host.Context,
- Transfer);
- if (!KSUCCESS(Status)) {
- goto ExecuteTransferSetEnd;
- }
- }
- ExecuteTransferSetEnd:
- if (!KSUCCESS(Status)) {
- SpbpCompleteTransferSet(Controller, TransferSet, Status);
- }
- return Status;
- }
- VOID
- SpbpCompleteTransferSet (
- PSPB_CONTROLLER Controller,
- PSPB_TRANSFER_SET TransferSet,
- KSTATUS Status
- )
- /*++
- Routine Description:
- This routine completes a transfer set. It is called with the bus lock held,
- and may release the bus lock.
- Arguments:
- Controller - Supplies a pointer to the controller that owns the transfer
- set.
- TransferSet - Supplies a pointer to the transfer set to complete.
- Status - Supplies the completion status code.
- Return Value:
- None.
- --*/
- {
- ASSERT(Controller->CurrentSet == TransferSet);
- Controller->CurrentSet = NULL;
- TransferSet->Status = Status;
- KeAcquireQueuedLock(Controller->Lock);
- LIST_REMOVE(&(TransferSet->ListEntry));
- TransferSet->ListEntry.Next = NULL;
- KeReleaseQueuedLock(Controller->Lock);
- //
- // Unlock the bus before calling the completion routine because the
- // transfer set can disappear as soon as the completion routine is called.
- //
- SpbUnlockBus(TransferSet->Handle);
- if (TransferSet->CompletionRoutine != NULL) {
- TransferSet->CompletionRoutine(TransferSet);
- }
- return;
- }
- VOID
- SpbpSynchronousTransferCompletionCallback (
- PSPB_TRANSFER_SET TransferSet
- )
- /*++
- Routine Description:
- This routine is called when a transfer set has completed or errored out.
- Arguments:
- TransferSet - Supplies a pointer to the transfer set that completed.
- Return Value:
- None.
- --*/
- {
- KeSignalEvent(TransferSet->Context, SignalOptionSignalAll);
- return;
- }
|