123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982 |
- /*++
- Copyright (c) 2016 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- dma.c
- Abstract:
- This module implements common infrastructure support for DMA controller
- drivers.
- Author:
- Evan Green 1-Feb-2016
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/driver.h>
- #include "dmap.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- VOID
- DmaDriverUnload (
- PVOID Driver
- );
- KSTATUS
- DmaGetInformation (
- PDMA_INTERFACE Interface,
- PDMA_INFORMATION Information
- );
- KSTATUS
- DmaSubmit (
- PDMA_INTERFACE Interface,
- PDMA_TRANSFER Transfer
- );
- KSTATUS
- DmaCancel (
- PDMA_INTERFACE Interface,
- PDMA_TRANSFER Transfer
- );
- KSTATUS
- DmaControlRequest (
- PDMA_INTERFACE Interface,
- PDMA_TRANSFER Transfer,
- PVOID Request,
- UINTN RequestSize
- );
- KSTATUS
- DmaAllocateTransfer (
- PDMA_INTERFACE Interface,
- PDMA_TRANSFER *Transfer
- );
- VOID
- DmaFreeTransfer (
- PDMA_INTERFACE Interface,
- PDMA_TRANSFER Transfer
- );
- RUNLEVEL
- DmapAcquireChannelLock (
- PDMA_CONTROLLER Controller,
- PDMA_CHANNEL Channel
- );
- VOID
- DmapReleaseChannelLock (
- PDMA_CONTROLLER Controller,
- PDMA_CHANNEL Channel,
- RUNLEVEL OldRunLevel
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- UUID DmaInterfaceUuid = UUID_DMA_INTERFACE;
- DMA_INTERFACE DmaInterfaceTemplate = {
- NULL,
- DmaGetInformation,
- DmaSubmit,
- DmaCancel,
- DmaControlRequest,
- DmaAllocateTransfer,
- DmaFreeTransfer
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- KSTATUS
- DriverEntry (
- PDRIVER Driver
- )
- /*++
- Routine Description:
- This routine implements the initial entry point of the DMA 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 = DmaDriverUnload;
- Status = IoRegisterDriverFunctions(Driver, &FunctionTable);
- return Status;
- }
- DMA_API
- KSTATUS
- DmaCreateController (
- PDMA_CONTROLLER_INFORMATION Registration,
- PDMA_CONTROLLER *Controller
- )
- /*++
- Routine Description:
- This routine creates a new Direct Memory Access 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;
- PDMA_CHANNEL Channel;
- ULONG ChannelCount;
- ULONG ChannelIndex;
- PDMA_CONTROLLER NewController;
- KSTATUS Status;
- if ((Registration->Version < DMA_CONTROLLER_INFORMATION_VERSION) ||
- (Registration->Version > DMA_CONTROLLER_INFORMATION_MAX_VERSION) ||
- (Registration->Device == NULL)) {
- return STATUS_INVALID_PARAMETER;
- }
- ChannelCount = Registration->Information.ChannelCount;
- AllocationSize = sizeof(DMA_CONTROLLER) +
- (ChannelCount * sizeof(DMA_CHANNEL));
- NewController = MmAllocateNonPagedPool(AllocationSize, DMA_ALLOCATION_TAG);
- if (NewController == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateControllerEnd;
- }
- RtlZeroMemory(NewController, AllocationSize);
- RtlCopyMemory(&(NewController->Host),
- Registration,
- sizeof(DMA_CONTROLLER_INFORMATION));
- RtlCopyMemory(&(NewController->Interface),
- &DmaInterfaceTemplate,
- sizeof(DMA_INTERFACE));
- NewController->Magic = DMA_CONTROLLER_MAGIC;
- NewController->ChannelCount = ChannelCount;
- NewController->Channels = (PDMA_CHANNEL)(NewController + 1);
- for (ChannelIndex = 0; ChannelIndex < ChannelCount; ChannelIndex += 1) {
- Channel = &(NewController->Channels[ChannelIndex]);
- INITIALIZE_LIST_HEAD(&(Channel->Queue));
- KeInitializeSpinLock(&(Channel->Lock));
- }
- Status = STATUS_SUCCESS;
- CreateControllerEnd:
- if (!KSUCCESS(Status)) {
- if (NewController != NULL) {
- MmFreeNonPagedPool(NewController);
- NewController = NULL;
- }
- }
- *Controller = NewController;
- return Status;
- }
- DMA_API
- VOID
- DmaDestroyController (
- PDMA_CONTROLLER Controller
- )
- /*++
- Routine Description:
- This routine destroys a Direct Memory Access controller.
- Arguments:
- Controller - Supplies a pointer to the controller to tear down.
- Return Value:
- None.
- --*/
- {
- PDMA_CHANNEL Channel;
- ULONG ChannelIndex;
- for (ChannelIndex = 0;
- ChannelIndex < Controller->ChannelCount;
- ChannelIndex += 1) {
- Channel = &(Controller->Channels[ChannelIndex]);
- ASSERT((Channel->Transfer == NULL) &&
- ((Channel->Queue.Next == NULL) ||
- (LIST_EMPTY(&(Channel->Queue)))));
- }
- //
- // Ruin the magic (but in a way that's still identifiable to a human).
- //
- Controller->Magic += 1;
- MmFreeNonPagedPool(Controller);
- return;
- }
- DMA_API
- KSTATUS
- DmaStartController (
- PDMA_CONTROLLER Controller
- )
- /*++
- Routine Description:
- This routine starts a Direct Memory Access controller. This function is
- not thread safe, as it is meant to be called during the start IRP, which is
- always serialized.
- Arguments:
- Controller - Supplies a pointer to the controller.
- Return Value:
- Status code.
- --*/
- {
- PDMA_CONTROLLER_INFORMATION Host;
- KSTATUS Status;
- ASSERT(Controller->Interface.Context == NULL);
- ASSERT(KeGetRunLevel() == RunLevelLow);
- Host = &(Controller->Host);
- Controller->Interface.Context = Controller;
- Status = IoCreateInterface(&DmaInterfaceUuid,
- Host->Device,
- &(Controller->Interface),
- sizeof(DMA_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, ResourceTypeDmaChannel);
- if ((!KSUCCESS(Status)) && (Status != STATUS_ALREADY_INITIALIZED)) {
- goto StartControllerEnd;
- }
- Status = IoAddFreeSpaceToArbiter(Host->Device,
- ResourceTypeDmaChannel,
- 0,
- Controller->ChannelCount,
- 0,
- NULL,
- 0);
- if (!KSUCCESS(Status)) {
- goto StartControllerEnd;
- }
- Controller->ArbiterCreated = TRUE;
- }
- StartControllerEnd:
- return Status;
- }
- DMA_API
- VOID
- DmaStopController (
- PDMA_CONTROLLER Controller
- )
- /*++
- Routine Description:
- This routine stops a Direct Memory Access controller. This function is not
- thread safe, as it is meant to be called during a state transition IRP,
- which is always serialized.
- Arguments:
- Controller - Supplies a pointer to the controller.
- Return Value:
- None.
- --*/
- {
- KSTATUS Status;
- ASSERT(Controller->Interface.Context == &(Controller->Interface));
- ASSERT(KeGetRunLevel() == RunLevelLow);
- Status = IoDestroyInterface(&DmaInterfaceUuid,
- Controller->Host.Device,
- &(Controller->Interface));
- ASSERT(KSUCCESS(Status));
- Controller->Interface.Context = NULL;
- return;
- }
- DMA_API
- PDMA_TRANSFER
- DmaTransferCompletion (
- PDMA_CONTROLLER Controller,
- PDMA_TRANSFER Transfer
- )
- /*++
- Routine Description:
- This routine is called by a DMA host controller when a transfer has
- completed. This function must be called at or below dispatch level. The
- host should have already filled in the number of bytes completed and the
- status.
- Arguments:
- Controller - Supplies a pointer to the controller.
- Transfer - Supplies a pointer to the transfer that completed.
- Return Value:
- Returns a pointer to the next transfer to start.
- NULL if no more transfers are queued.
- --*/
- {
- PDMA_CHANNEL Channel;
- PDMA_TRANSFER NextTransfer;
- RUNLEVEL OldRunLevel;
- ASSERT(Transfer->Allocation->Allocation < Controller->ChannelCount);
- ASSERT(Transfer->ListEntry.Next == NULL);
- NextTransfer = NULL;
- Channel = &(Controller->Channels[Transfer->Allocation->Allocation]);
- OldRunLevel = DmapAcquireChannelLock(Controller, Channel);
- ASSERT(Channel->Transfer == Transfer);
- Channel->Transfer = NULL;
- if (!LIST_EMPTY(&(Channel->Queue))) {
- NextTransfer = LIST_VALUE(Channel->Queue.Next, DMA_TRANSFER, ListEntry);
- LIST_REMOVE(&(NextTransfer->ListEntry));
- NextTransfer->ListEntry.Next = NULL;
- Channel->Transfer = NextTransfer;
- }
- DmapReleaseChannelLock(Controller, Channel, OldRunLevel);
- Transfer->CompletionCallback(Transfer);
- return NextTransfer;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- VOID
- DmaDriverUnload (
- 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
- DmaGetInformation (
- PDMA_INTERFACE Interface,
- PDMA_INFORMATION Information
- )
- /*++
- Routine Description:
- This routine returns information about a given DMA controller.
- Arguments:
- Interface - Supplies a pointer to the interface instance, used to identify
- which specific controller is being queried.
- Information - Supplies a pointer where the DMA controller information is
- returned on success. The caller should initialize the version number of
- this structure.
- Return Value:
- Status code.
- --*/
- {
- PDMA_CONTROLLER Controller;
- Controller = Interface->Context;
- if (Information == NULL) {
- return STATUS_INVALID_PARAMETER;
- }
- if ((Information->Version == 0) ||
- (Information->Version > DMA_INFORMATION_MAX_VERSION) ||
- (Information->Version < Controller->Host.Information.Version)) {
- Information->Version = Controller->Host.Information.Version;
- return STATUS_VERSION_MISMATCH;
- }
- if (Controller->Host.Information.Version == DMA_INFORMATION_VERSION) {
- RtlCopyMemory(Information,
- &(Controller->Host.Information),
- sizeof(DMA_INFORMATION));
- } else {
- return STATUS_VERSION_MISMATCH;
- }
- return STATUS_SUCCESS;
- }
- KSTATUS
- DmaSubmit (
- PDMA_INTERFACE Interface,
- PDMA_TRANSFER Transfer
- )
- /*++
- Routine Description:
- This routine submits a transfer to the DMA controller for execution. This
- routine will ensure that other devices do not perform transfers on the
- given channel while this transfer 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:
- Interface - Supplies a pointer to the DMA controller interface.
- Transfer - Supplies a pointer to the transfer 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.
- --*/
- {
- PDMA_CHANNEL Channel;
- PDMA_CONTROLLER Controller;
- PRESOURCE_DMA_DATA DmaAllocation;
- ULONG Mask;
- RUNLEVEL OldRunLevel;
- KSTATUS Status;
- ULONG Width;
- Controller = Interface->Context;
- if ((Transfer->Allocation == NULL) ||
- (Transfer->Allocation->Allocation >= Controller->ChannelCount) ||
- (Transfer->Memory == NULL) ||
- (Transfer->CompletionCallback == NULL)) {
- return STATUS_INVALID_PARAMETER;
- }
- Transfer->Status = STATUS_NOT_STARTED;
- //
- // Try to figure out the width based on the resource allocation.
- //
- if (Transfer->Width == 0) {
- //
- // Grab the custom one if there is one.
- //
- if (((Transfer->Allocation->Characteristics &
- DMA_TRANSFER_SIZE_CUSTOM) != 0) &&
- (Transfer->Allocation->Data != NULL) &&
- (Transfer->Allocation->DataSize >= sizeof(RESOURCE_DMA_DATA))) {
- DmaAllocation = Transfer->Allocation->Data;
- Transfer->Width = DmaAllocation->Width;
- //
- // Try to find the width based on one of the characteristics flags.
- //
- } else {
- Mask = DMA_TRANSFER_SIZE_256;
- Width = 256;
- while (Mask >= DMA_TRANSFER_SIZE_8) {
- if ((Transfer->Allocation->Characteristics & Mask) != 0) {
- Transfer->Width = Width;
- break;
- }
- Width >>= 1;
- Mask >>= 1;
- }
- }
- }
- if (Transfer->Width == 0) {
- return STATUS_INVALID_CONFIGURATION;
- }
- ASSERT(Transfer->ListEntry.Next == NULL);
- Channel = &(Controller->Channels[Transfer->Allocation->Allocation]);
- OldRunLevel = DmapAcquireChannelLock(Controller, Channel);
- if (Channel->Transfer == NULL) {
- Channel->Transfer = Transfer;
- Transfer->ListEntry.Next = NULL;
- } else {
- INSERT_BEFORE(&(Transfer->ListEntry), &(Channel->Queue));
- Transfer = NULL;
- }
- DmapReleaseChannelLock(Controller, Channel, OldRunLevel);
- Status = STATUS_SUCCESS;
- //
- // If the transfer wasn't queued, kick it off now.
- //
- if (Transfer != NULL) {
- Status = Controller->Host.FunctionTable.SubmitTransfer(
- Controller->Host.Context,
- Transfer);
- }
- return Status;
- }
- KSTATUS
- DmaCancel (
- PDMA_INTERFACE Interface,
- PDMA_TRANSFER Transfer
- )
- /*++
- Routine Description:
- This routine attempts to cancel a transfer that is currently in flight.
- Arguments:
- Interface - Supplies a pointer to the DMA controller interface.
- Transfer - Supplies a pointer to the transfer to cancel.
- Return Value:
- STATUS_SUCCESS if the transfer was successfully canceled.
- STATUS_TOO_LATE if the transfer is already complete.
- Other status codes on other failures.
- --*/
- {
- PDMA_CHANNEL Channel;
- PDMA_CONTROLLER Controller;
- PDMA_TRANSFER NextTransfer;
- RUNLEVEL OldRunLevel;
- KSTATUS Status;
- KSTATUS SubmitStatus;
- Controller = Interface->Context;
- if ((Transfer->Allocation == NULL) ||
- (Transfer->Allocation->Allocation >= Controller->ChannelCount)) {
- return STATUS_INVALID_PARAMETER;
- }
- Channel = &(Controller->Channels[Transfer->Allocation->Allocation]);
- NextTransfer = NULL;
- OldRunLevel = DmapAcquireChannelLock(Controller, Channel);
- if (Channel->Transfer == Transfer) {
- Status = Controller->Host.FunctionTable.CancelTransfer(
- Controller->Host.Context,
- Transfer);
- if (KSUCCESS(Status)) {
- ASSERT(Channel->Transfer == Transfer);
- Channel->Transfer = NULL;
- //
- // Kick off the next transfer if this one was canceled and there is
- // more left to do.
- //
- if (!LIST_EMPTY(&(Channel->Queue))) {
- NextTransfer = LIST_VALUE(Channel->Queue.Next,
- DMA_TRANSFER,
- ListEntry);
- LIST_REMOVE(&(NextTransfer->ListEntry));
- NextTransfer->ListEntry.Next = NULL;
- Channel->Transfer = NextTransfer;
- }
- }
- } else if (Transfer->ListEntry.Next != NULL) {
- LIST_REMOVE(&(Transfer->ListEntry));
- Transfer->ListEntry.Next = NULL;
- Status = STATUS_SUCCESS;
- } else {
- Status = STATUS_TOO_LATE;
- }
- DmapReleaseChannelLock(Controller, Channel, OldRunLevel);
- //
- // If there's a next transfer, try to submit that. If that one fails,
- // process its completion and potentially submit the next one. Loop until
- // either a transfer is successfully submitted or there is nothing more
- // to do.
- //
- while (NextTransfer != NULL) {
- SubmitStatus = Controller->Host.FunctionTable.SubmitTransfer(
- Controller->Host.Context,
- Transfer);
- if (!KSUCCESS(SubmitStatus)) {
- Transfer->Status = SubmitStatus;
- NextTransfer = DmaTransferCompletion(Controller, NextTransfer);
- } else {
- break;
- }
- }
- return Status;
- }
- KSTATUS
- DmaControlRequest (
- PDMA_INTERFACE Interface,
- PDMA_TRANSFER Transfer,
- PVOID Request,
- UINTN RequestSize
- )
- /*++
- Routine Description:
- This routine is called to perform a DMA controller-specific operation. It
- provides a direct link between DMA controllers and users, for controller-
- specific functionality.
- Arguments:
- Interface - Supplies a pointer to the DMA controller interface.
- Transfer - Supplies an optional pointer to the transfer involved.
- Request - Supplies a pointer to the request/response data.
- RequestSize - Supplies the size of the request in bytes.
- Return Value:
- Status code.
- --*/
- {
- PDMA_CONTROLLER Controller;
- KSTATUS Status;
- Controller = Interface->Context;
- if (Controller->Host.FunctionTable.ControlRequest == NULL) {
- return STATUS_NOT_SUPPORTED;
- }
- //
- // The common DMA library doesn't know much of anything, just pass it on
- // down.
- //
- Status = Controller->Host.FunctionTable.ControlRequest(
- Controller->Host.Context,
- Transfer,
- Request,
- RequestSize);
- return Status;
- }
- KSTATUS
- DmaAllocateTransfer (
- PDMA_INTERFACE Interface,
- PDMA_TRANSFER *Transfer
- )
- /*++
- Routine Description:
- This routine creates a new DMA transfer structure.
- Arguments:
- Interface - Supplies a pointer to the DMA controller interface.
- Transfer - Supplies a pointer where a pointer to the newly allocated
- transfer is returned on success.
- Return Value:
- Status code.
- --*/
- {
- PDMA_TRANSFER DmaTransfer;
- *Transfer = NULL;
- DmaTransfer = MmAllocateNonPagedPool(sizeof(DMA_TRANSFER),
- DMA_ALLOCATION_TAG);
- if (DmaTransfer == NULL) {
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- RtlZeroMemory(DmaTransfer, sizeof(DMA_TRANSFER));
- *Transfer = DmaTransfer;
- return STATUS_SUCCESS;
- }
- VOID
- DmaFreeTransfer (
- PDMA_INTERFACE Interface,
- PDMA_TRANSFER Transfer
- )
- /*++
- Routine Description:
- This routine destroys a previously created DMA transfer. This transfer
- must not be actively submitted to any controller.
- Arguments:
- Interface - Supplies a pointer to the DMA controller interface.
- Transfer - Supplies a pointer to the transfer to destroy.
- Return Value:
- None.
- --*/
- {
- MmFreeNonPagedPool(Transfer);
- return;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- RUNLEVEL
- DmapAcquireChannelLock (
- PDMA_CONTROLLER Controller,
- PDMA_CHANNEL Channel
- )
- /*++
- Routine Description:
- This routine raises to dispatch and acquires the DMA controller's channel
- lock.
- Arguments:
- Controller - Supplies a pointer to the controller that owns the channel.
- Channel - Supplies a pointer to the channel to lock.
- Return Value:
- Returns the previous runlevel, which should be passed into the release
- function.
- --*/
- {
- RUNLEVEL OldRunLevel;
- OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
- KeAcquireSpinLock(&(Channel->Lock));
- return OldRunLevel;
- }
- VOID
- DmapReleaseChannelLock (
- PDMA_CONTROLLER Controller,
- PDMA_CHANNEL Channel,
- RUNLEVEL OldRunLevel
- )
- /*++
- Routine Description:
- This routine releases the DMA channel's lock and lowers to the runlevel
- the system was at before the acquire.
- Arguments:
- Controller - Supplies a pointer to the controller.
- Channel - Supplies a pointer to the channel to unlock.
- OldRunLevel - Supplies the runlevel returned by the acquire function.
- Return Value:
- None.
- --*/
- {
- KeReleaseSpinLock(&(Channel->Lock));
- KeLowerRunLevel(OldRunLevel);
- return;
- }
|