/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    send.c

Abstract:


    NOTE: ZZZ There is a potential priority inversion problem when
    allocating the packet.  For nt it looks like we need to raise
    the irql to dpc when we start the allocation.

Author:

    Thomas J. Dimitri 8-May-1992

Environment:

    Kernel Mode - Or whatever is the equivalent on OS/2 and DOS.

Revision History:

    Ray Patch (raypa)       04/13/94        Modified for new WAN wrapper.

--*/

#include "asyncall.h"
#include "globals.h"

//
//  Forward references.
//

extern
NTSTATUS
AsyncWriteCompletionRoutine(
	IN PDEVICE_OBJECT   DeviceObject,
	IN PIRP             Irp,
	IN PNDIS_WAN_PACKET WanPacket);

extern
VOID
AssembleRASFrame(
	PNDIS_WAN_PACKET Packet);

//=============================================================================
//  Function:
//
//      AsyncSend()
//
//  Description:
//
//      This function is the main entry point for transmitting data to the serial
//      driver. When entered, we get the MAC binding handle (a pointer to our
//      private data structure) and the WAN packet which we are going to send.
//      We don't bother queuing the frame, we simple allocate an IRP and ship
//      it to the serial driver and let him worry about it.
//
//  In Parameters:
//
//      NdisLinkContext - Pointer to the ASYNC_INFO structure.
//
//      Packet - WAN packet containing the data to be framed and shipped.
//
//  Out Parameters:
//
//      None.
//
//  Return status:
//
//      NDIS_STATUS_SUCCESS.
//=============================================================================

NDIS_STATUS
AsyncSend(
	IN NDIS_HANDLE		MacBindingHandle,
    IN NDIS_HANDLE      NdisLinkHandle,
    IN PNDIS_WAN_PACKET Packet)
{

    PASYNC_INFO         AsyncInfo;
    NDIS_STATUS         Status;

    //
    //  Get the open handle for this MAC binding.
    //

    AsyncInfo = (PASYNC_INFO) NdisLinkHandle;

#ifdef	ETHERNET_MAC
	//
	// For ethernet we must complete irps queued up to
	// receive these frames
	//
    if ( AsyncInfo->PortState == PORT_ETHERNET ) {

		if (!TryToCompleteEthernetGetFrame(AsyncInfo, Packet))
		    TryToCompleteEthernetDDCDIrp(AsyncInfo, Packet);

		return(NDIS_STATUS_SUCCESS);
	}
#endif


    //
    //  First make sure this link is still up.
    //

    if ( AsyncInfo->PortState == PORT_FRAMING ) {
        //
        //  Now we can send this frame.
        //

        Status = AsyncSendPacket(
					NdisLinkHandle,
					Packet);

		//
		// If we shipped it, then we already called
		// NdisCompleteSend so don't return SUCCESS
		//

		if (Status == STATUS_SUCCESS) {
			Status=STATUS_PENDING;
		}

    } else {

        DbgTracef(-2,("AsyncSend: Link not found, dropping packet!\n"));

        Status = NDIS_STATUS_SUCCESS;
    }

    return Status;
}

//=============================================================================
//  Function:
//
//      AsyncSendPacket()
//
//  Description:
//      This function is called from AsyncSend() to send an IRP to the serial
//      driver. If this IRP pends, the the I/O complete routine will be called
//      later to complete the request.
//
//  In Parameters:
//
//      Packet - WAN packet containing the data to be framed and shipped.
//
//  Out Parameters:
//
//      None.
//
//  Return status:
//
//      NDIS_STATUS_SUCCESS.
//=============================================================================

NTSTATUS
AsyncSendPacket(
	IN PASYNC_INFO	    AsyncInfo,
	IN PNDIS_WAN_PACKET WanPacket)

{
    NTSTATUS			Status;
    PIRP				irp;
    PIO_STACK_LOCATION	irpSp;
    PFILE_OBJECT		FileObject;
    PDEVICE_OBJECT		DeviceObject;
    PASYNC_ADAPTER		Adapter;
    UCHAR 				irpStackSize;

    //
    //  Initialize locals.
    //

    FileObject   = AsyncInfo->FileObject;

    DeviceObject = AsyncInfo->DeviceObject;

    Adapter = AsyncInfo->Adapter;

    irpStackSize = (UCHAR) Adapter->IrpStackSize;

    //
    //  Get irp from irp pool.
    //

    irp = IoAllocateIrp((UCHAR)irpStackSize, (BOOLEAN)FALSE);

	//
	// The IO subsystem may be out of irps (we are screwed).
	//

	if (irp == NULL) {
		return(NDIS_STATUS_RESOURCES);
	}

    //
    // Tuck pointer to AsyncInfo for completion use
    //

    WanPacket->MacReserved1 = AsyncInfo;

    //
    //  We need to do the following below: We MUST zero out the Irp!!!
    //  Set the initial fields in the irp here (only do it once)
    //

//    IoInitializeIrp(
//	    irp,
//	    IoSizeOfIrp(irpStackSize),
//	    irpStackSize);


    //
    //  Setup this irp with defaults.
    //

	//
    // Set the file object to the Not-Signaled state.
    //  I DON'T THINK THIS NEEDS TO BE DONE.  WHO'S WAITING ON
	//  ON THIS FileObject??
    (VOID) KeResetEvent(&FileObject->Event);

	irp->Tail.Overlay.OriginalFileObject = FileObject;
    irp->RequestorMode = KernelMode;
	irp->PendingReturned = FALSE;

    //
    // Fill in the service independent parameters in the IRP.
    //

    irp->UserEvent = NULL;

	//
	// 8 byte align (also use end of packet for IOSB).
	//

    irp->UserIosb = (PVOID) (((ULONG) (
					(&WanPacket->EndBuffer - sizeof(IO_STATUS_BLOCK)) - 7)) &
					(~7L));

    irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
    irp->Overlay.AsynchronousParameters.UserApcContext = NULL;


    //
    //  Get a pointer to the stack location for the first driver.  This will be
    //  used to pass the original function codes and parameters.
    //

    irpSp = IoGetNextIrpStackLocation(irp);

    irpSp->MajorFunction = IRP_MJ_WRITE;

    irpSp->FileObject = FileObject;

    if (FileObject->Flags & FO_WRITE_THROUGH) {

        irpSp->Flags = SL_WRITE_THROUGH;
    }

    //
    //  If this write operation is to be performed without any caching, set the
    //  appropriate flag in the IRP so no caching is performed.
    //

    if (FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) {

        irp->Flags |= (IRP_NOCACHE | IRP_WRITE_OPERATION);

    } else {

        irp->Flags |= IRP_WRITE_OPERATION;
    }

    //
    //  Assemble a RAS, PPP, or SLIP frame type.
    //

    if (AsyncInfo->GetLinkInfo.SendFramingBits & PPP_FRAMING) {

		AssemblePPPFrame(WanPacket);

    } else

	if (AsyncInfo->GetLinkInfo.SendFramingBits & SLIP_FRAMING) {

		AssembleSLIPFrame(WanPacket);

    } else {

		AssembleRASFrame(WanPacket);
    }

    irp->AssociatedIrp.SystemBuffer =
    irp->UserBuffer = WanPacket->CurrentBuffer;

	DbgTracef(0, ("Writing out %.2x %.2x %.2x %.2x %.2x\n",
		WanPacket->CurrentBuffer[0],
		WanPacket->CurrentBuffer[1],
		WanPacket->CurrentBuffer[2],
		WanPacket->CurrentBuffer[3],
		WanPacket->CurrentBuffer[4]));

    //
    //  Copy the caller's parameters to the service-specific portion of the IRP.
    //

    irpSp->Parameters.Write.Length = WanPacket->CurrentLength;

    irpSp->Parameters.Write.Key =  0;

    irpSp->Parameters.Write.ByteOffset = FileObject->CurrentByteOffset;

    //
    //  Setup IRP for callback.
    //

    IoSetCompletionRoutine(
	    irp,							//  irp to use
	    AsyncWriteCompletionRoutine,	//  routine to call when irp is done
	    WanPacket,						//  context to pass routine
	    TRUE,							//  call on success
	    TRUE,							//  call on error
	    TRUE);							//  call on cancel


    //
    //  We DO NOT insert the packet at the head of the IRP list for the thread.
    //  because we do NOT really have an IoCompletionRoutine that does
    //  anything with the thread or needs to be in that thread's context.
    //

    GlobalXmitWentOut++;

    AsyncInfo->In++;

    //
    //  Now simply invoke the driver at its dispatch entry with the IRP.
    //

    Status = IoCallDriver(DeviceObject, irp);

    //  According to TonyE, the status for the serial driver should
    //  always be STATUS_PENDING.  DigiBoard usually STATUS_SUCCESS.

    return Status;
}
