/**************************************************************************************************************************
 *  OPENCLOS.C SigmaTel STIR4200 init/shutdown module
 **************************************************************************************************************************
 *  (C) Unpublished Copyright of Sigmatel, Inc. All Rights Reserved.
 *
 *
 *		Created: 04/06/2000 
 *			Version 0.9
 *		Edited: 04/24/2000 
 *			Version 0.91
 *		Edited: 04/27/2000 
 *			Version 0.92
 *		Edited: 05/12/2000 
 *			Version 0.94
 *		Edited: 05/19/2000 
 *			Version 0.95
 *	
 *
 **************************************************************************************************************************/

#define DOBREAKS    // enable debug breaks

#include <ndis.h>
#include <ntddndis.h>  // defines OID's

#include <usbdi.h>
#include <usbdlib.h>

#include "debug.h"
#include "ircommon.h"
#include "irndis.h"

/*****************************************************************************
*
*  Function:	InitializeDevice
*
*  Synopsis:	initialize resources for a single IR device object
*
*  Arguments:	pThisDev - IR device object to initialize
*
*  Returns:		NDIS_STATUS_SUCCESS      - if device is successfully opened
*				NDIS_STATUS_RESOURCES    - could not claim sufficient
*                                         resources
*
*
*  Notes:
*              we do a lot of stuff in this open device function
*              - allocate packet pool
*              - allocate buffer pool
*              - allocate packets/buffers/memory and chain together
*                (only one buffer per packet)
*              - initialize send queue
*
*  This function should be called with device lock held.
*
*  We don't initialize the following ir device object entries, since
*  these values will outlast an reset.
*       pUsbDevObj
*       hNdisAdapter
*       dongleCaps
*       fGotFilterIndication
*
*****************************************************************************/
NDIS_STATUS
InitializeDevice(
		IN OUT PIR_DEVICE pThisDev
	)
{
    int				i;
    NDIS_STATUS		status = NDIS_STATUS_SUCCESS;

    DEBUGMSG(DBG_FUNC|DBG_PNP, ("+InitializeDevice\n"));

    IRUSB_ASSERT( pThisDev != NULL );

    //
    // Current speed is the default (9600).
    //
    pThisDev->linkSpeedInfo = &supportedBaudRateTable[BAUDRATE_9600];
    pThisDev->currentSpeed  = DEFAULT_BAUD_RATE;

    //
    // Init statistical info. 
    // We need to do this cause reset won't free and realloc pThisDev!
    //
    pThisDev->packetsReceived         = 0;
    pThisDev->packetsReceivedDropped  = 0;
    pThisDev->packetsReceivedOverflow = 0;
	pThisDev->packetsReceivedChecksum = 0;
	pThisDev->packetsReceivedRunt	  = 0;
	pThisDev->packetsReceivedNoBuffer = 0;
    
	pThisDev->packetsSent             = 0;
    pThisDev->packetsSentDropped      = 0;
	pThisDev->packetsSentRejected	  = 0;
	pThisDev->packetsSentInvalid	  = 0;

	pThisDev->NumDataErrors			  = 0;
	pThisDev->NumReadWriteErrors	  = 0;

	pThisDev->NumReads				  = 0;
	pThisDev->NumWrites				  = 0;
	pThisDev->NumReadWrites			  = 0;

#if DBG
	pThisDev->TotalBytesReceived      = 0;
	pThisDev->TotalBytesSent          = 0;
	pThisDev->NumYesQueryMediaBusyOids		= 0;
	pThisDev->NumNoQueryMediaBusyOids		= 0;
	pThisDev->NumSetMediaBusyOids			= 0;
	pThisDev->NumMediaBusyIndications		= 0;
	pThisDev->packetsHeldByProtocol			= 0;
	pThisDev->MaxPacketsHeldByProtocol		= 0;
	pThisDev->NumPacketsSentRequiringTurnaroundTime		= 0;
	pThisDev->NumPacketsSentNotRequiringTurnaroundTime	= 0;
#endif

    //
	// Variables about the state of the device
	//
	pThisDev->fDeviceStarted          = FALSE;
    pThisDev->fGotFilterIndication    = FALSE;
    pThisDev->fPendingHalt            = FALSE;
    pThisDev->fPendingReadClearStall  = FALSE;
    pThisDev->fPendingWriteClearStall = FALSE;
	pThisDev->fPendingReset			  = FALSE;

	pThisDev->fPendingClearTotalStall = FALSE;

    pThisDev->fKillPollingThread      = FALSE;

    pThisDev->fKillPassiveLevelThread  = FALSE;

    pThisDev->LastQueryTime.QuadPart   = 0;
    pThisDev->LastSetTime.QuadPart     = 0;
	pThisDev->PendingIrpCount          = 0;

	//
	// OID Set/Query pending
	// 
	pThisDev->fQuerypending            = FALSE;
	pThisDev->fSetpending              = FALSE;

	//
	// Diags are off
	//
#if defined(DIAGS)
	pThisDev->DiagsActive			   = FALSE;
	pThisDev->DiagsPendingActivation   = FALSE;
#endif

    //
	// Some more state variables
	// 
	InterlockedExchange( &pThisDev->fMediaBusy, FALSE ); 
    InterlockedExchange( &pThisDev->fIndicatedMediaBusy, FALSE ); 

	pThisDev->pCurrentRecBuf			= NULL;

    pThisDev->fProcessing				= FALSE;
    pThisDev->fCurrentlyReceiving		= FALSE;

    pThisDev->fReadHoldingReg			= FALSE;

	pThisDev->BaudRateMask				= 0xffff;  // as per Class Descriptor; may be reset in registry

	//
	// Initialize the queues.
	//
	if( TRUE != IrUsb_InitSendStructures( pThisDev ) )
	{
		DEBUGMSG(DBG_ERR, (" Failed to init WDM objects\n"));
		goto done;
	}

    //
    // Allocate the NDIS packet and NDIS buffer pools
    // for this device's RECEIVE buffer queue.
    // Our receive packets must only contain one buffer a piece,
    // so #buffers == #packets.
    //
    NdisAllocatePacketPool(
			&status,                    // return status
			&pThisDev->hPacketPool,     // handle to the packet pool
			NUM_RCV_BUFS,               // number of packet descriptors
			16                          // number of bytes reserved for ProtocolReserved field
		);

    if( status != NDIS_STATUS_SUCCESS )
    {
        DEBUGMSG(DBG_ERR, (" NdisAllocatePacketPool failed. Returned 0x%.8x\n", status));
        goto done;
    }

    NdisAllocateBufferPool(
			&status,               // return status
			&pThisDev->hBufferPool,// handle to the buffer pool
			NUM_RCV_BUFS           // number of buffer descriptors
		);

    if( status != NDIS_STATUS_SUCCESS )
    {
        DEBUGMSG(DBG_ERR, (" NdisAllocateBufferPool failed. Returned 0x%.8x\n", status));
        pThisDev->BufferPoolAllocated = FALSE;
		goto done;
    }
        
	pThisDev->BufferPoolAllocated = TRUE;

    //
	// Prepare the work items
	// 
	for( i = 0; i < NUM_WORK_ITEMS; i++ )
    {
		PIR_WORK_ITEM pWorkItem;

		pWorkItem = &(pThisDev->WorkItems[i]);

		pWorkItem->pIrDevice    = pThisDev;
		pWorkItem->pInfoBuf     = NULL;
		pWorkItem->InfoBufLen   = 0;
		pWorkItem->fInUse       = FALSE;
		pWorkItem->Callback     = NULL;
	}

    //
    //  Initialize each of the RECEIVE objects for this device.
    //
    for( i = 0; i < NUM_RCV_BUFS; i++ )
    {
        PNDIS_BUFFER pBuffer = NULL;
        PRCV_BUFFER pReceivBuffer = &pThisDev->rcvBufs[i];

        //
        // Allocate a data buffer
        //
        pReceivBuffer->pDataBuf = MyMemAlloc( MAX_RCV_DATA_SIZE ); 

        if( pReceivBuffer->pDataBuf == NULL )
        {
            status = NDIS_STATUS_RESOURCES;
            goto done;
        }

        NdisZeroMemory( 
				pReceivBuffer->pDataBuf,
				MAX_RCV_DATA_SIZE
			);

		pReceivBuffer->pThisDev = pThisDev;
        pReceivBuffer->DataLen = 0;
        pReceivBuffer->BufferState = RCV_STATE_FREE;

#if defined(WORKAROUND_MISSING_C1)
		pReceivBuffer->MissingC1Detected = FALSE;
#endif

        //
        //  Allocate the NDIS_PACKET.
        //
        NdisAllocatePacket(
				&status,									// return status
				&((PNDIS_PACKET)pReceivBuffer->pPacket),	// return pointer to allocated descriptor
				pThisDev->hPacketPool						// handle to packet pool
			);

        if( status != NDIS_STATUS_SUCCESS )
        {
            DEBUGMSG(DBG_ERR, (" NdisAllocatePacket failed. Returned 0x%.8x\n", status));
            goto done;
        }
    }

	//
	// These are the receive objects for the USB
	//
    pThisDev->PreReadBuffer.pDataBuf = MyMemAlloc( STIR4200_FIFO_SIZE ); 

    if( pThisDev->PreReadBuffer.pDataBuf == NULL )
    {
        status = NDIS_STATUS_RESOURCES;
        goto done;
    }

    NdisZeroMemory( 
			pThisDev->PreReadBuffer.pDataBuf,
			STIR4200_FIFO_SIZE
		);

	pThisDev->PreReadBuffer.pThisDev = pThisDev;
    pThisDev->PreReadBuffer.DataLen = 0;
    pThisDev->PreReadBuffer.BufferState = RCV_STATE_FREE;

	//
	// Synchronization events
	//
	KeInitializeEvent(
        &pThisDev->EventSyncUrb,
        NotificationEvent,    // non-auto-clearing event
        FALSE                 // event initially non-signalled
    );

    KeInitializeEvent(
            &pThisDev->EventAsyncUrb,
            NotificationEvent,    // non-auto-clearing event
            FALSE                 // event initially non-signalled
        );

done:
    //
    // If we didn't complete the init successfully, then we should clean
    // up what we did allocate.
    //
    if( status != NDIS_STATUS_SUCCESS )
    {
        DEBUGMSG(DBG_ERR, (" InitializeDevice() FAILED\n"));
        DeinitializeDevice(pThisDev);
    }
    else
    {
        DEBUGMSG(DBG_OUT, (" InitializeDevice() SUCCEEDED\n"));
    }

    DEBUGMSG(DBG_FUNC|DBG_PNP, ("-InitializeDevice()\n"));
    return status;
}


/*****************************************************************************
*
*  Function:   DeinitializeDevice
*
*  Synopsis:   deallocate the resources of the IR device object
*
*  Arguments:  pThisDev - the IR device object to close
*
*  Returns:    none
*
*
*  Notes:
*
*  Called for shutdown and reset.
*  Don't clear hNdisAdapter, since we might just be resetting.
*  This function should be called with device lock held.
*
*****************************************************************************/
VOID
DeinitializeDevice(
		IN OUT PIR_DEVICE pThisDev
	)
{
    UINT			i;

    DEBUGMSG( DBG_FUNC|DBG_PNP, ("+DeinitializeDevice\n"));

    pThisDev->linkSpeedInfo = NULL;

    //
    // Free all resources for the RECEIVE buffer queue.
    //
    for( i = 0; i < NUM_RCV_BUFS; i++ )
    {
        PNDIS_BUFFER pBuffer = NULL;
        PRCV_BUFFER  pRcvBuf = &pThisDev->rcvBufs[i];

        if( pRcvBuf->pPacket != NULL )
        {
            NdisFreePacket( (PNDIS_PACKET)pRcvBuf->pPacket );
            pRcvBuf->pPacket = NULL;
        }

        if( pRcvBuf->pDataBuf != NULL )
        {
            MyMemFree( pRcvBuf->pDataBuf, MAX_RCV_DATA_SIZE ); 
            pRcvBuf->pDataBuf = NULL;
        }

        pRcvBuf->DataLen = 0;
    }

	//
	// Deallocate the USB receive buffers
	//
    if( pThisDev->PreReadBuffer.pDataBuf != NULL )
        MyMemFree( pThisDev->PreReadBuffer.pDataBuf, STIR4200_FIFO_SIZE ); 

    //
    // Free the packet and buffer pool handles for this device.
    //
    if( pThisDev->hPacketPool )
    {
        NdisFreePacketPool( pThisDev->hPacketPool );
        pThisDev->hPacketPool = NULL;
    }

    if( pThisDev->BufferPoolAllocated )
    {
        NdisFreeBufferPool( pThisDev->hBufferPool );
        pThisDev->BufferPoolAllocated = FALSE;
    }

    if( pThisDev->fDeviceStarted )
    {
		NTSTATUS ntstatus;

		ntstatus = IrUsb_StopDevice( pThisDev ); 
        DEBUGMSG(DBG_FUNC, (" DeinitializeDevice IrUsb_StopDevice() status = 0x%x\n",ntstatus));
    }

    InterlockedExchange( &pThisDev->fMediaBusy, FALSE ); 
    InterlockedExchange( &pThisDev->fIndicatedMediaBusy, FALSE ); 

	IrUsb_FreeSendStructures( pThisDev );

	DEBUGMSG(DBG_FUNC|DBG_PNP, ("-DeinitializeDevice\n"));
}