/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

	ltsend.c

Abstract:

	This module contains the send queue processing routines.

Author:

	Stephen Hou			(stephh@microsoft.com)
	Nikhil 	Kamkolkar 	(nikhilk@microsoft.com)

Revision History:
	19 Jun 1992		Initial Version (dch@pacvax.pacersoft.com)

Notes:	Tab stop: 4
--*/

#define		LTSEND_H_LOCALS
#include	"ltmain.h"

//	Define file id for errorlogging
#define		FILENUM		LTSEND


NDIS_STATUS
LtSend(
	IN NDIS_HANDLE 	MacBindingHandle,
	IN PNDIS_PACKET Packet
    )
/*++

Routine Description:

	This routine is called by NDIS to send a packet.

Arguments:

	MacBindingHandle	:	Passed as context to NDIS in OpenAdapter.
	Packet				:	Ndis Packet to send.

Return Value:

	NDIS_STATUS_SUCCESS	:	If successful, else error.

--*/
{
    NDIS_STATUS 	Status;
	UINT 			PacketSize;

	BOOLEAN			DerefAdapter = FALSE;
	PLT_OPEN		Binding = (PLT_OPEN)MacBindingHandle;
    PLT_ADAPTER 	Adapter = Binding->LtAdapter;

	DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_ENTRY,
			("LtSend: Entering...\n"));

    NdisAcquireSpinLock(&Adapter->Lock);

	//	This will go away, when the entry is taken off the adapter transmit queue
	LtReferenceAdapterNonInterlock(Adapter, &Status);
	if (Status == NDIS_STATUS_SUCCESS)
	{
		DerefAdapter = TRUE;
		do
		{
			//	Check to see if there is a reset in progress
			if (Adapter->Flags & ADAPTER_RESET_IN_PROGRESS)
			{
				Status = NDIS_STATUS_RESET_IN_PROGRESS;
				break;
			}
	
			//	If binding is closing down, we get out.
			if (Binding->Flags & BINDING_CLOSING)
			{
				Status = NDIS_STATUS_CLOSING;
				break;
			}

			//	Try to reference the binding. This will go away after
			//	the send completes.
			LtReferenceBindingNonInterlock(Binding, &Status);
	
		} while (FALSE);
	}
	NdisReleaseSpinLock(&Adapter->Lock);

	if (Status != NDIS_STATUS_SUCCESS)
	{
		if (DerefAdapter)
			LtDeReferenceAdapter(Adapter);

		return(Status);
	}


	do
	{
		Status = NDIS_STATUS_PENDING;

		NdisQueryPacket(
			Packet,
			NULL,
			NULL,
			NULL,
			&PacketSize);

		DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO,
				("LtSend: Packet %lx Length %lx\n", Packet, PacketSize));
		
		if ((PacketSize < LT_MIN_PACKET_SIZE ) ||
			(PacketSize >  LT_MAX_PACKET_SIZE))	{
		
			Status = NDIS_STATUS_RESOURCES;

			NdisAcquireSpinLock(&Adapter->Lock);
			++Adapter->GeneralMandatory[GM_TRANSMIT_BAD];
			NdisReleaseSpinLock(&Adapter->Lock);

			break;

		}
		else
		{
			PLT_PACKET_RESERVED Reserved = (PLT_PACKET_RESERVED)Packet->MacReserved;

			//	Initialize the reserved portion
			Reserved->MacBindingHandle = MacBindingHandle;
			InitializeListHead(&Reserved->Linkage);
		
			if (LtUtilsPacketType(Packet) != LT_LOOPBACK)
			{
				// The packet needs to go onto the wire.
				DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO,
						("LtSend: Queuing %lx on transmit q\n", Packet));
			
				NdisAcquireSpinLock(&Adapter->Lock);
				InsertTailList(&Adapter->Transmit, &Reserved->Linkage);
				NdisReleaseSpinLock(&Adapter->Lock);
		
			}
			else
			{
				// Put on the loopback queue
				NdisAcquireSpinLock(&Adapter->Lock);
				InsertTailList(&Adapter->LoopBack, &Reserved->Linkage);
		
				// Since we are doing a Loopback send, lets add up the stats now.
				++Adapter->GeneralMandatory[GM_TRANSMIT_GOOD];
				++Adapter->GeneralOptionalFrameCount[GO_DIRECTED_TRANSMITS];
		
				Adapter ->GeneralOptionalByteCount[GO_DIRECTED_TRANSMITS] =
					LtAddLongToLargeInteger(
						Adapter ->GeneralOptionalByteCount[GO_DIRECTED_TRANSMITS],
						PacketSize);

				NdisReleaseSpinLock(&Adapter->Lock);
			}
		}
		
	} while (FALSE);

	//	Process send queue. We also process the send queue in the timer
	//	in case, some sends have pended and ndis does no further sends.
	//	!!!	Send's get very high priority. In total, the queue is processed
	//	!!!	three times, twice in the timer and once in LtSend
	LtSendProcessQueue(Adapter);

	if (Status != NDIS_STATUS_PENDING)
	{
		//	Send unsuccessful. Remove the binding reference and adapter reference
		LtDeReferenceBinding(Binding);
		LtDeReferenceAdapter(Adapter);
	}

	DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_ENTRY,
			("LtSend: Leaving...\n"));

    return Status;
}



VOID
LtSendProcessQueue(
    IN PLT_ADAPTER	Adapter
    )
/*++

Routine Description:

	SendProcessQueue processes, yeah, you guessed it. The SendQueue.

Arguments:

	Adapter		:	Pointer to the adapter structure.

Return Value:

	None.

--*/
{
    PNDIS_PACKET 		Packet;
    PLT_PACKET_RESERVED Reserved;
    PLT_OPEN 			Binding;
    UINT 				BufferCount, TotalLength, BufLength;
    PNDIS_BUFFER 		Buffer;
    UCHAR 				Data;
    PUCHAR 				Address;
    PLIST_ENTRY 		p;

	//	Use this to avoid multiple threads from calling this routine.
	static	BOOLEAN		QueueActive = FALSE;

	DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_ENTRY,
			("LtSendProcessTransmit: Entering...\n"));

	NdisAcquireSpinLock(&Adapter->Lock);
	if (QueueActive)
	{
		NdisReleaseSpinLock(&Adapter->Lock);
		return;
	}
	QueueActive = TRUE;

    while((!IsListEmpty(&Adapter->Transmit)) &&
          ((Adapter->Flags & ADAPTER_RESET_IN_PROGRESS) == 0))
	{
        // Ok, Can we trasnmit a packet now?
        NdisRawReadPortUchar(SC_PORT, &Data);

        if ((Data & TX_READY) == 0)
		{
			DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_WARN,
					("LtSendProcessTransmit: Media not ready...\n"));

            Adapter->MediaOptional[MO_TRANSMIT_DEFERS]++;
            break;
        }

		p = RemoveHeadList(&Adapter->Transmit);
        NdisReleaseSpinLock(&Adapter->Lock);

        Packet		= CONTAINING_RECORD(
						p,
						NDIS_PACKET,
						MacReserved);

		Reserved 	= (PLT_PACKET_RESERVED)Packet->MacReserved;

		DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO,
				("LtSendProcessTransmit: Dequeued %lx-%lx\n", Packet, Reserved));
	
        NdisQueryPacket(
            Packet,
            NULL,
            &BufferCount,
            &Buffer,
            &TotalLength);

        // Ok, Output the packet length.
        NdisRawWritePortUchar(XFER_PORT, (UCHAR)(TotalLength & 0xFF));

        NdisRawWritePortUchar(XFER_PORT, (UCHAR)((TotalLength >> 8) & 0xFF));

		NdisRawWritePortUchar(XFER_PORT, (UCHAR)LT_CMD_LAP_WRITE);

        while (BufferCount-- > 0)
		{
            NdisQueryBuffer(
				Buffer,
                &Address,
                &BufLength);

			DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO,
					("LtSendProcessTransmit: Buffer #%d\n", BufferCount));

             NdisRawWritePortBufferUchar(XFER_PORT,
										Address,
										BufLength);

			NdisGetNextBuffer(Buffer, &Buffer);
        }

        if (LtUtilsPacketType(Packet) == LT_DIRECTED)
		{
			Binding = (PLT_OPEN)Reserved->MacBindingHandle;

			DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO,
					("LtSendProcessTransmit: Process xmit: Packet %p\n", Packet));

			NdisCompleteSend(
				Binding->NdisBindingContext,
				Packet,
				NDIS_STATUS_SUCCESS);

			NdisAcquireSpinLock(&Adapter->Lock);
			++Adapter->GeneralMandatory[GM_TRANSMIT_GOOD];
			++Adapter->GeneralOptionalFrameCount[GO_DIRECTED_TRANSMITS];
			Adapter ->GeneralOptionalByteCount[GO_DIRECTED_TRANSMITS] =
				LtAddLongToLargeInteger(
					Adapter ->GeneralOptionalByteCount[GO_DIRECTED_TRANSMITS],
					TotalLength);
			NdisReleaseSpinLock(&Adapter->Lock);

			//	Dereference the adapter and the binding for this completed
			//	send.
			LtDeReferenceBinding(Binding);
			LtDeReferenceAdapter(Adapter);

			NdisAcquireSpinLock(&Adapter->Lock);

		}
		else
		{
            // This was a broadcast, loop it back.
			NdisAcquireSpinLock(&Adapter->Lock);
			++Adapter->GeneralOptionalFrameCount[GO_BROADCAST_TRANSMITS];
			++Adapter->GeneralMandatory[GM_TRANSMIT_GOOD];
			Adapter ->GeneralOptionalByteCount[GO_BROADCAST_TRANSMITS] =
			LtAddLongToLargeInteger(
				Adapter ->GeneralOptionalByteCount[GO_BROADCAST_TRANSMITS],
				TotalLength);
			InsertTailList(&Adapter->LoopBack, &Reserved->Linkage);
        }
    }

	QueueActive = FALSE;
	NdisReleaseSpinLock(&Adapter->Lock);

	DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_ENTRY,
			("LtSendProcessTransmit: Leaving...\n"));

	return;
}