/*++
*******************************************************************************
* Copyright (c) 1999 Digi International Inc., All Rights Reserved				   *
* 																										*
* This software contains proprietary and confidential information of Digi		*
* International Inc.  By accepting transfer of this copy, Recipient agrees		*
* to retain this software in confidence, to prevent disclosure to others,		*
* and to make no use of this software other than that for which it was			*
* delivered.  This is an unpublished copyrighted work of Digi International	*
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly	*
* prohibited.																						*
* 																										*
* Restricted Rights Legend																		*
*																										*
* Use, duplication, or disclosure by the Government is subject to					*
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in			*
* Technical Data and Computer Software clause at DFARS 252.227-7031 or			*
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -				*
* Restricted Rights at 48 CFR 52.227-19, as applicable.								*
*																										*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343				*
*																										*
*******************************************************************************

Module Name:

   dxbdma.c

Abstract:

   DMA buffer allocation/initialization/cleanup routines.

Author:

   Michael Zarns

--*/
#include "digidxb.h"
#pragma hdrstop


STATIC PADAPTER_OBJECT
DxbGetAdapter(
	PDEVICE_OBJECT pPdo,
	ULONG BusNumber,
	ULONG cbMaxDma )
/*++

Description:

	Wrapper for HalGetAdapter.  The returned adapter object is used for allocating 
	busmaster buffers.

Parameters:

	BusNumber
		Bus number on which this adapter lives. 

	cbMaxDma
		Maximum number of bytes the device allows per dma operation.  Typically, use the
		buffer size.

Return Value:

	Pointer to adapter object if successful, otherwise NULL.

--*/
{
	ULONG nMapRegisters;
	DEVICE_DESCRIPTION Device = {0};
	Device.Version = DEVICE_DESCRIPTION_VERSION;
	Device.Master = TRUE;
	Device.ScatterGather = FALSE;
	Device.DemandMode = FALSE;
	Device.AutoInitialize = FALSE;
	Device.Dma32BitAddresses = TRUE;
	Device.IgnoreCount = FALSE;
	Device.BusNumber = BusNumber;
	Device.DmaWidth = Width32Bits;
	Device.DmaSpeed = Compatible;
	Device.MaximumLength = cbMaxDma;
#if NT5_BUILD
	return IoGetDmaAdapter( pPdo, &Device, &nMapRegisters );
#else
	return HalGetAdapter( &Device, &nMapRegisters );
#endif
}


STATIC NTSTATUS
DxbAllocQueueBuffers(
	IN PADAPTER_OBJECT        pDmaAdapter, 
	IN OUT PDXB_MESSAGE_QUEUE pDesc, 
	IN ULONG                  nBuffers, 
	IN ULONG                  cbBuffer )
/*++

Description:

	Alloc dma busmaster buffers for a single dxb queue (command or event queue, 
	otherwise called a2h and h2a queues).  Fill in descriptor defining the alloc'd 
	buffers.

Parameters:

	pDmaAdapter
		Adapter object from previous call to DxbGetAdapter.

	pDesc
		Queue descriptor structure.  On successful return, this struct completely 
		describes the 

	nBuffers
		Count of buffers to alloc.  Typically, this is either 1 or n, where n is the max
		supported by the card.  In busmaster_flat mode, there is 1 buffer per queue.  In
		busmaster_paged mode, there are n equally sized buffers (4096 bytes) per queue.

	cbBuffer
		Size of each buffer.  Typically, either 4096 bytes (paged mode) or 
		max_buffer_size (flat mode).

Return Value:

	STATUS_SUCCESS, or error.

--*/
{
	NTSTATUS Status;
	ULONG i;
	DigiDump( DIGIINIT,  ( "   cbPage 0x%08x, nPages 0x%x\n", cbBuffer, nBuffers ) );

	pDesc->cbPage = cbBuffer;
	pDesc->cbTotal = nBuffers * cbBuffer;
	pDesc->Mode.Master.nPages = nBuffers;

	for ( i = 0; i < nBuffers; i++ )
	{
		PULONG pDword;
		pDesc->Mode.Master.VirtualPages[i] = 
#if NT5_BUILD
			pDmaAdapter->DmaOperations->AllocateCommonBuffer( pDmaAdapter, pDesc->cbPage, &pDesc->Mode.Master.PhysicalPages[i], FALSE );
#else
			HalAllocateCommonBuffer( pDmaAdapter, pDesc->cbPage, &pDesc->Mode.Master.PhysicalPages[i], FALSE );
#endif
			if ( pDesc->Mode.Master.VirtualPages[i] == NULL || pDesc->Mode.Master.PhysicalPages[i].QuadPart == 0 )
		{
			Status = STATUS_INSUFFICIENT_RESOURCES;
			goto Exit;
		}
		
		pDword = pDesc->Mode.Master.VirtualPages[i];
		pDword[0] = MAGIC_HOSTBUF;
		pDword[1] = i;


#if DBG
		{
			PCHAR const pChar = (PVOID)pDword;
			DigiDump( DIGIINIT,  ( "      Page %d, Virtual 0x%08x, Physical 0x%08x\n", i, 
	                pDesc->Mode.Master.VirtualPages[i], pDesc->Mode.Master.PhysicalPages[i] ) );
		}
#endif

	}

	Status = STATUS_SUCCESS;

Exit:
	return Status;
}


VOID
DxbCleanupAdapterQueues(
	IN PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Releases resources acquired by InitAdapterQueues

Parameters:

	pExt
		Device extension defining the adapter 

--*/
{
	if ( pExt->pDmaAdapter != NULL )
	{
		ULONG i;
		for ( i = 0; i < MAX_PAGES; i++ )
		{
			if ( pExt->DxbCmdQ.Mode.Master.VirtualPages[i] != NULL )
			{
#if NT5_BUILD
				pExt->pDmaAdapter->DmaOperations->FreeCommonBuffer( pExt->pDmaAdapter, pExt->DxbCmdQ.cbPage, 
				                     pExt->DxbCmdQ.Mode.Master.PhysicalPages[i], 
				                     pExt->DxbCmdQ.Mode.Master.VirtualPages[i], FALSE );
#else
				HalFreeCommonBuffer( pExt->pDmaAdapter, pExt->DxbCmdQ.cbPage, 
				                     pExt->DxbCmdQ.Mode.Master.PhysicalPages[i], 
				                     pExt->DxbCmdQ.Mode.Master.VirtualPages[i], FALSE );
#endif

				pExt->DxbCmdQ.Mode.Master.VirtualPages[i] = NULL;
			}
			if ( pExt->DxbEventQ.Mode.Master.VirtualPages[i] != NULL )
			{
#if NT5_BUILD
				pExt->pDmaAdapter->DmaOperations->FreeCommonBuffer( pExt->pDmaAdapter, pExt->DxbEventQ.cbPage, 
				                     pExt->DxbEventQ.Mode.Master.PhysicalPages[i], 
				                     pExt->DxbEventQ.Mode.Master.VirtualPages[i], FALSE );
#else
				HalFreeCommonBuffer( pExt->pDmaAdapter, pExt->DxbEventQ.cbPage, 
				                     pExt->DxbEventQ.Mode.Master.PhysicalPages[i], 
				                     pExt->DxbEventQ.Mode.Master.VirtualPages[i], FALSE );
#endif				

				pExt->DxbEventQ.Mode.Master.VirtualPages[i] = NULL;
			}
		}
		pExt->pDmaAdapter = NULL;
	}
}


NTSTATUS
DxbInitAdapterQueues(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN DXB_BUS_MODE          BusMode,
	IN ULONG                 cbPage,
	IN ULONG                 nPages )
/*++

Description:

	Sets up dxb command and event queues for communicating with the adapter.
	Initializes the device extension for the specified BusMode.  Alloc's
	busmaster buffers if in busmaster mode.

Parameters:

	pExt
		Device extension defining the adapter 

	BusMode
		Defines the mode of operation, MASTER_FLAT, SLAVE, etc.

	cbPage
		The size of a single buffer within the dxb queue.  The dxb queues will be split 
		up into multiple buffers when in BUSMODE_MASTER_WINDOWED mode.

	nPages
		Number of buffers that make up the queue.

Return Value:

	STATUS_SUCCESS or error.

--*/
{
	NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;

	pExt->DxbCmdQ.cbTotal = pExt->DxbEventQ.cbTotal = cbPage * nPages;
	pExt->DxbCmdQ.cbPage  = pExt->DxbEventQ.cbPage  = cbPage;
	pExt->DxbCmdQ.Mode.Master.nPages = pExt->DxbEventQ.Mode.Master.nPages = nPages;
	pExt->DxbBusMode = BusMode;
   pExt->DxbEventQ.QOffset = 0;
   pExt->DxbCmdQ.QOffset = 0;

	dxbQueueInitAccessMethods( pExt, BusMode );
	DigiInitializeSpinLock( &pExt->DxbCmdQ.Lock );
	DigiInitializeSpinLock( &pExt->DxbEventQ.Lock );

	switch ( BusMode )
	{
		case DXB_BUSMODE_MASTER_WINDOWED:
		case DXB_BUSMODE_MASTER_FLAT:
#if NT5_BUILD
			pExt->pDmaAdapter = DxbGetAdapter( pExt->pPdo, pExt->Hardware.BusNumber, cbPage );
#else
			pExt->pDmaAdapter = DxbGetAdapter( NULL, pExt->Hardware.BusNumber, cbPage );
#endif
			if ( pExt->pDmaAdapter == NULL )
				goto Exit;

			DigiDump( DIGIINIT, ( "DXB: Busmaster mode setup:\n"
			                      "   Command Queue\n" ) );

			Status = DxbAllocQueueBuffers( pExt->pDmaAdapter, &pExt->DxbCmdQ, nPages, cbPage );
			if ( NT_ERROR(Status) )
				goto Exit;

			DigiDump( DIGIINIT, ( "   Event Queue\n" ) );
			Status = DxbAllocQueueBuffers( pExt->pDmaAdapter, &pExt->DxbEventQ, nPages, cbPage );
			break;

		case DXB_BUSMODE_SLAVE:
			Status = STATUS_SUCCESS;
			break;

		default:
			DigiDump( DIGIERROR, ( "Dxb!InitAdapterQueues: Unsupported BusMode 0x%x\n", BusMode ) );
			break;
	}

Exit:
	if ( NT_ERROR(Status) )
		DxbCleanupAdapterQueues( pExt );

	return Status;
}


VOID
DxbCleanupGlobalHostData( 
	IN PDXB_DEVICE_EXTENSION pExt )
{
	if ( pExt->HlcGlobalHost.Virtual )
	{
#if NT5_BUILD
		pExt->pDmaAdapter->DmaOperations->FreeCommonBuffer( pExt->pDmaAdapter, pExt->HlcGlobalHost.Length, pExt->HlcGlobalHost.Physical, pExt->HlcGlobalHost.Virtual, FALSE );
#else
		HalFreeCommonBuffer( pExt->pDmaAdapter, pExt->HlcGlobalHost.Length, pExt->HlcGlobalHost.Physical, pExt->HlcGlobalHost.Virtual, FALSE );
#endif
		pExt->HlcGlobalHost.Virtual = NULL;
	}
	if ( pExt->HlcQueue.Virtual )
	{
#if NT5_BUILD
		pExt->pDmaAdapter->DmaOperations->FreeCommonBuffer( pExt->pDmaAdapter, pExt->HlcQueue.Length, pExt->HlcQueue.Physical, pExt->HlcQueue.Virtual, FALSE );
#else
		HalFreeCommonBuffer( pExt->pDmaAdapter, pExt->HlcQueue.Length, pExt->HlcQueue.Physical, pExt->HlcQueue.Virtual, FALSE );
#endif
		pExt->HlcQueue.Virtual = NULL;
	}
}


NTSTATUS
DxbAllocGlobalHostData(
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 AdapterOffset )
/*++

Description:

	Alloc and init the adapter Global Host Data structure which tells the card where 
	busmaster buffers reside.

Parameters:

	pExt
		Device extension defines the adapter

	AdapterOffset
		Location in card memory at which to place a pointer to the global host data.

Return Value:

	STATUS_SUCCESS or error.

--*/
{
	GLOBAL_HOST_DATA *pGlobal = NULL;
	PDXB_HLC_QUEUE_POINTERS pQueue = NULL;
	NTSTATUS Status;
	ULONG i;

	// Alloc buffer for global host data.  
	pGlobal = pExt->HlcGlobalHost.Virtual = 
#if NT5_BUILD
		pExt->pDmaAdapter->DmaOperations->AllocateCommonBuffer( pExt->pDmaAdapter, sizeof *pGlobal, &pExt->HlcGlobalHost.Physical, FALSE );
#else
		HalAllocateCommonBuffer( pExt->pDmaAdapter, sizeof *pGlobal, &pExt->HlcGlobalHost.Physical, FALSE );
#endif
	if ( pGlobal == NULL )
	{
		Status = STATUS_INSUFFICIENT_RESOURCES;
		goto Exit;
	}

	RtlZeroMemory( pGlobal, sizeof *pGlobal );

	// Header
	pGlobal->magic = MAGIC_HOST;
	pGlobal->intr_period = pExt->Hardware.Irq.Period;

	// Describe the Command Queue
	pGlobal->dxbphys.host2adapter_q.npages = pExt->DxbCmdQ.Mode.Master.nPages;
	pGlobal->dxbphys.host2adapter_q.pagesize = pExt->DxbCmdQ.cbPage;
	pGlobal->dxbphys.host2adapter_q.pageshift = pGlobal->dxbphys.host2adapter_q.pagesize - 1;
	pGlobal->dxbphys.host2adapter_q.size = pExt->DxbCmdQ.cbTotal;
	pGlobal->dxbphys.host2adapter_q.mask = pGlobal->dxbphys.host2adapter_q.size - 1;
	pGlobal->dxbphys.host2adapter_q.mask4 = pGlobal->dxbphys.host2adapter_q.mask & ~3;
	for ( i = 0; i < pExt->DxbCmdQ.Mode.Master.nPages; i++ )
		pGlobal->dxbphys.host2adapter_q.page[i].bus = pExt->DxbCmdQ.Mode.Master.PhysicalPages[i].LowPart;

	// Describe the Event Queue
	pGlobal->dxbphys.adapter2host_q.npages = pExt->DxbEventQ.Mode.Master.nPages;
	pGlobal->dxbphys.adapter2host_q.pagesize = pExt->DxbEventQ.cbPage;
	pGlobal->dxbphys.adapter2host_q.pageshift = pGlobal->dxbphys.adapter2host_q.pagesize - 1;
	pGlobal->dxbphys.adapter2host_q.size = pExt->DxbEventQ.cbTotal;
	pGlobal->dxbphys.adapter2host_q.mask = pGlobal->dxbphys.adapter2host_q.size - 1;
	pGlobal->dxbphys.adapter2host_q.mask4 = pGlobal->dxbphys.adapter2host_q.mask & ~3;
	for ( i = 0; i < pExt->DxbEventQ.Mode.Master.nPages; i++ )
		pGlobal->dxbphys.adapter2host_q.page[i].bus = pExt->DxbEventQ.Mode.Master.PhysicalPages[i].LowPart;

	// Global host data contains pointers to Queue in/out values.  These must be 
	// cache-line aligned (e.g. 32 byte aligned).  Alloc space for them.
	pQueue = pExt->HlcQueue.Virtual = 
#if NT5_BUILD
		pExt->pDmaAdapter->DmaOperations->AllocateCommonBuffer( pExt->pDmaAdapter, sizeof *pQueue, &pExt->HlcQueue.Physical, FALSE );
#else
		HalAllocateCommonBuffer( pExt->pDmaAdapter, sizeof *pQueue, &pExt->HlcQueue.Physical, FALSE );
#endif
	if ( pQueue == NULL )
	{
		Status = STATUS_INSUFFICIENT_RESOURCES;
		goto Exit;
	}

	RtlZeroMemory( pQueue, sizeof *pQueue );

	// Point to Queue pointers
	{
		ULONG LogicalBase = pExt->HlcQueue.Physical.LowPart;
		pGlobal->dxbphys.qphys.host2adapter_in  = LogicalBase + FIELD_OFFSET( DXB_HLC_QUEUE_POINTERS, CmdQIn );
		pExt->DxbCmdQ.Descriptor.pQIn = &pQueue->CmdQIn;

		pGlobal->dxbphys.qphys.host2adapter_out = LogicalBase + FIELD_OFFSET( DXB_HLC_QUEUE_POINTERS, CmdQOut );
		pExt->DxbCmdQ.Descriptor.pQOut = &pQueue->CmdQOut;

		pGlobal->dxbphys.qphys.adapter2host_in  = LogicalBase + FIELD_OFFSET( DXB_HLC_QUEUE_POINTERS, EventQIn );
		pExt->DxbEventQ.Descriptor.pQIn = &pQueue->EventQIn;

		pGlobal->dxbphys.qphys.adapter2host_out = LogicalBase + FIELD_OFFSET( DXB_HLC_QUEUE_POINTERS, EventQOut );
		pExt->DxbEventQ.Descriptor.pQOut = &pQueue->EventQOut;
		
		DigiDump( DIGIINIT, ( "   Queue Pointers (Virtual Addrs):\n"
		                      "      Command Queue: QIn 0x%08x, QOut 0x%08x\n"
		                      "      Event Queue:   QIn 0x%08x, QOut 0x%08x\n",
		                      pExt->DxbCmdQ.Descriptor.pQIn, pExt->DxbCmdQ.Descriptor.pQOut, 
		                      pExt->DxbEventQ.Descriptor.pQIn, pExt->DxbEventQ.Descriptor.pQOut ) );
		DigiDump( DIGIINIT, ( "   Queue Pointers (Physical Addrs):\n"
		                      "      Command Queue: QIn 0x%08x, QOut 0x%08x\n"
		                      "      Event Queue:   QIn 0x%08x, QOut 0x%08x\n",
		                      pGlobal->dxbphys.qphys.host2adapter_in, pGlobal->dxbphys.qphys.host2adapter_out, 
		                      pGlobal->dxbphys.qphys.adapter2host_in, pGlobal->dxbphys.qphys.adapter2host_out ) );
	}

	DigiDump( DIGIINIT, ( "   Queue Descriptor (GlobalHostOffset):\n"
	                      "      Virtual Address: 0x%08x\n"
	                      "      Phys Address:    0x%08x\n"
	                      "      Magic:           '%c%c%c%c'\n",
	                      pExt->HlcGlobalHost.Virtual, pExt->HlcGlobalHost.Physical.LowPart,
	                      ((PCHAR)pGlobal)[0], ((PCHAR)pGlobal)[1],((PCHAR)pGlobal)[2],((PCHAR)pGlobal)[3]) );

	// Tell the card where global host data is located
	DxbAdapterWriteDword( pExt, AdapterOffset, pExt->HlcGlobalHost.Physical.LowPart );
	Status = STATUS_SUCCESS;

Exit:

	if ( NT_ERROR( Status ) )
		DxbCleanupGlobalHostData( pExt );

	return Status;
}