/////////////////////////////////////////////////////////
//
//    Copyright (c) 2001  Microsoft Corporation
//
//    Module Name:
//       receive
//
//    Abstract:
//       This module contains code which deals with receiving data
//
//////////////////////////////////////////////////////////


#include "sysvars.h"


//////////////////////////////////////////////////////////////
// private constants, types, and prototypes
//////////////////////////////////////////////////////////////

const PCHAR strFunc1  = "TSReceive";
const PCHAR strFunc2  = "TSReceiveHandler";
const PCHAR strFunc3  = "TSRcvExpeditedHandler";
const PCHAR strFunc4  = "TSChainedReceiveHandler";
const PCHAR strFunc5  = "TSChainedRcvExpeditedHandler";
const PCHAR strFuncP1 = "TSReceiveComplete";
const PCHAR strFuncP2 = "TSShowReceiveInfo";
const PCHAR strFuncP3 = "TSGetRestOfData";
const PCHAR strFuncP4 = "TSCommonReceive";
const PCHAR strFuncP5 = "TSCommonChainedReceive";

//
// completion context
//
struct   RECEIVE_CONTEXT
{
   PMDL              pLowerMdl;           // mdl from lower irp
   PRECEIVE_DATA     pReceiveData;        // above structure
   PADDRESS_OBJECT   pAddressObject;
   BOOLEAN           fIsExpedited;
};
typedef  RECEIVE_CONTEXT  *PRECEIVE_CONTEXT;


//
// completion function
//
TDI_STATUS
TSReceiveComplete(
   PDEVICE_OBJECT DeviceObject,
   PIRP           Irp,
   PVOID          Context
   );


PIRP
TSGetRestOfData(
   PADDRESS_OBJECT   pAddressObject,
   PRECEIVE_DATA     pReceiveData,
   BOOLEAN           fIsExpedited
   );

VOID
TSShowReceiveInfo(
   PADDRESS_OBJECT      pAddressObject,
   CONNECTION_CONTEXT   ConnectionContext,
   ULONG                ulReceiveFlags,
   ULONG                ulBytesIndicated,
   ULONG                ulBytesAvailable,
   PVOID                pvTsdu,
   BOOLEAN              fIsChained
   );


TDI_STATUS
TSCommonReceive(
   PADDRESS_OBJECT   pAddressObject,
   ULONG             ulBytesTotal,
   ULONG             ulBytesIndicated,
   ULONG             ulReceiveFlags,
   PVOID             pvTsdu,
   BOOLEAN           fIsExpedited,
   PULONG            pulBytesTaken,
   PIRP              *pIoRequestPacket
   );



TDI_STATUS
TSCommonChainedReceive(
   PADDRESS_OBJECT   pAddressObject,
   ULONG             ulReceiveLength,
   ULONG             ulStartingOffset,
   PMDL              pReceiveMdl,
   BOOLEAN           fIsExpedited,
   PVOID             pvTsduDescriptor
   );


//////////////////////////////////////////////////////////////
// public functions
//////////////////////////////////////////////////////////////


// -----------------------------------------------------------------
//
// Function:   TSReceive
//
// Arguments:  pEndpointObject -- current endpoint
//             pIrp           -- completion information
//
// Returns:    NTSTATUS (normally pending)
//
// Descript:   This function receives data over a connection
//
// ----------------------------------------------------------------------------

NTSTATUS
TSReceive(PENDPOINT_OBJECT pEndpoint,
          PSEND_BUFFER     pSendBuffer,
          PRECEIVE_BUFFER  pReceiveBuffer)
{
   PADDRESS_OBJECT   pAddressObject = pEndpoint->pAddressObject;

   //
   // assume no packet
   //
   pReceiveBuffer->RESULTS.RecvDgramRet.ulBufferLength = 0;

   if (pAddressObject)
   {
      PRECEIVE_DATA  pReceiveData;

      //
      // just get the first packet from the queue
      //  check expedited list First
      //
      TSAcquireSpinLock(&pAddressObject->TdiSpinLock);
      pReceiveData = pAddressObject->pHeadRcvExpData;
      if (pReceiveData)
      {
         //
         // fix up the lists as necessary
         //
         if (pReceiveData->pNextReceiveData)
         {
            pReceiveData->pNextReceiveData->pPrevReceiveData = NULL;
         }
         else
         {
            pAddressObject->pTailRcvExpData = NULL;
         }
         pAddressObject->pHeadRcvExpData = pReceiveData->pNextReceiveData;
      }

      //
      // if no expedited receives, check normal list
      //
      else
      {
         pReceiveData = pAddressObject->pHeadReceiveData;
         if (pReceiveData)
         {
            //
            // fix up the lists as necessary
            //
            if (pReceiveData->pNextReceiveData)
            {
               pReceiveData->pNextReceiveData->pPrevReceiveData = NULL;
            }
            else
            {
               pAddressObject->pTailReceiveData = NULL;
            }
            pAddressObject->pHeadReceiveData = pReceiveData->pNextReceiveData;
         }
      }
      TSReleaseSpinLock(&pAddressObject->TdiSpinLock);
      
      //
      // if we got a packet to return, then lets return it..
      // and release its memory
      //
      if (pReceiveData)
      {
         //
         // show debug, if it is turned on, and only if we actually
         // are returning a packet
         //
         if (ulDebugLevel & ulDebugShowCommand)
         {
            DebugPrint1("\nCommand = ulRECEIVE\n"
                        "FileObject = %p\n",
                         pEndpoint);
         }
   
         if (pReceiveData->ulBufferLength > pSendBuffer->COMMAND_ARGS.SendArgs.ulBufferLength)
         {
            pReceiveData->ulBufferLength = pSendBuffer->COMMAND_ARGS.SendArgs.ulBufferLength;
         }
   
         //
         // attempt to lock down the memory
         //
         PMDL  pMdl = TSMakeMdlForUserBuffer(pSendBuffer->COMMAND_ARGS.SendArgs.pucUserModeBuffer, 
                                             pReceiveData->ulBufferLength,
                                             IoModifyAccess);
         if (pMdl)
         {
            RtlCopyMemory(MmGetSystemAddressForMdl(pMdl),
                          pReceiveData->pucDataBuffer,
                          pReceiveData->ulBufferLength);
            TSFreeUserBuffer(pMdl);
         }
         else
         {
            pReceiveData->ulBufferLength = 0;
         }
   
         pReceiveBuffer->RESULTS.RecvDgramRet.ulBufferLength = pReceiveData->ulBufferLength;

         TSFreeMemory(pReceiveData->pucDataBuffer);
         TSFreeMemory(pReceiveData);
      }
   }

   return STATUS_SUCCESS;
}

// -----------------------------------------------
//
// Function:   TSReceiveHandler
//
// Arguments:  pvTdiEventContext -- really pointer to our AddressObject
//             Connection_Context -- really pointer to our Endpoint
//             ReceiveFlags -- nature of received data
//             ulBytesIndicated --- length of data in buffer
//             ulBytesTotal     -- total length of datagram
//             pulBytesTaken -- stuff with bytes used by this driver
//             pvTsdu        -- data buffer
//             pIoRequestPacket -- pIrp in case not all data received
//
// Returns:    STATUS_DATA_NOT_ACCEPTED (we didn't want data)
//             STATUS_SUCCESS (we used all data & are done with it)
//             STATUS_MORE_PROCESSING_REQUIRED -- we supplied an IRP for rest
//
// Descript:   Event handler for incoming receives on connection
//
// -----------------------------------------------

TDI_STATUS
TSReceiveHandler(PVOID              pvTdiEventContext,
                 CONNECTION_CONTEXT ConnectionContext,
                 ULONG              ulReceiveFlags,
                 ULONG              ulBytesIndicated,
                 ULONG              ulBytesTotal,
                 PULONG             pulBytesTaken,
                 PVOID              pvTsdu,
                 PIRP               *ppIoRequestPacket)

{
   PADDRESS_OBJECT   pAddressObject = (PADDRESS_OBJECT)pvTdiEventContext;

   if (ulDebugLevel & ulDebugShowHandlers)
   {
      DebugPrint1("\n >>>> %s\n", strFunc2);
      TSShowReceiveInfo(pAddressObject,
                        ConnectionContext,
                        ulReceiveFlags,
                        ulBytesIndicated,
                        ulBytesTotal,
                        pvTsdu,
                        FALSE);
   }

   return  TSCommonReceive(pAddressObject,
                           ulBytesTotal,
                           ulBytesIndicated,
                           ulReceiveFlags,
                           pvTsdu,
                           ((ulReceiveFlags & TDI_RECEIVE_EXPEDITED) != 0),
                           pulBytesTaken,
                           ppIoRequestPacket);
}



// -----------------------------------------------
//
// Function:   TSRcvExpeditedHandler
//
// Arguments:  pvTdiEventContext -- really pointer to our AddressObject
//             Connection_Context -- really pointer to our Endpoint
//             ReceiveFlags -- nature of received data
//             ulBytesIndicated --- length of data in buffer
//             ulBytesTotal     -- total length of datagram
//             pulBytesTaken -- stuff with bytes used by this driver
//             pvTsdu        -- data buffer
//             pIoRequestPacket -- pIrp in case not all data received
//
// Returns:    STATUS_DATA_NOT_ACCEPTED (we didn't want data)
//             STATUS_SUCCESS (we used all data & are done with it)
//             STATUS_MORE_PROCESSING_REQUIRED -- we supplied an IRP for rest
//
// Descript:   Event handler for incoming expedited receives on connection
//
// -----------------------------------------------

TDI_STATUS
TSRcvExpeditedHandler(PVOID               pvTdiEventContext,
                      CONNECTION_CONTEXT  ConnectionContext,
                      ULONG               ulReceiveFlags,
                      ULONG               ulBytesIndicated,
                      ULONG               ulBytesTotal,
                      PULONG              pulBytesTaken,
                      PVOID               pvTsdu,
                      PIRP                *ppIoRequestPacket)

{
   PADDRESS_OBJECT   pAddressObject = (PADDRESS_OBJECT)pvTdiEventContext;

   if (ulDebugLevel & ulDebugShowHandlers)
   {
      DebugPrint1("\n >>>> %s\n", strFunc3);
      TSShowReceiveInfo(pAddressObject,
                        ConnectionContext,
                        ulReceiveFlags,
                        ulBytesIndicated,
                        ulBytesTotal,
                        pvTsdu,
                        FALSE);
   }
   
   return TSCommonReceive(pAddressObject,
                          ulBytesTotal,
                          ulBytesIndicated,
                          ulReceiveFlags,
                          pvTsdu,
                          TRUE,
                          pulBytesTaken,
                          ppIoRequestPacket);
}


// -----------------------------------------------
//
// Function:   TSChainedReceiveHandler
//
// Arguments:  pvTdiEventContext -- really pointer to our AddressObject
//             Connection_Context -- really pointer to our Endpoint
//             ReceiveFlags -- nature of received data
//             ulReceiveLength --- length of data in buffer
//             ulStartingOffset     -- total length of datagram
//             pReceiveMdl        -- data buffer
//             pTsduDescriptor -- returns value for TdiReturnChainedReceives
//
// Returns:    STATUS_DATA_NOT_ACCEPTED (we didn't want data)
//             STATUS_SUCCESS (we used all data & are done with it)
//             STATUS_MORE_PROCESSING_REQUIRED -- we supplied an IRP for rest
//
// Descript:   Event handler for incoming receives on connection
//
// -----------------------------------------------

TDI_STATUS
TSChainedReceiveHandler(PVOID                pvTdiEventContext,
                        CONNECTION_CONTEXT   ConnectionContext,
                        ULONG                ulReceiveFlags,
                        ULONG                ulReceiveLength,
                        ULONG                ulStartingOffset,
                        PMDL                 pReceiveMdl,
                        PVOID                pvTsduDescriptor)
{
   PADDRESS_OBJECT   pAddressObject = (PADDRESS_OBJECT)pvTdiEventContext;
   
   if (ulDebugLevel & ulDebugShowHandlers)
   {
      DebugPrint1("\n >>>> %s\n", strFunc4);
      TSShowReceiveInfo(pAddressObject,
                        ConnectionContext,
                        ulReceiveFlags,
                        ulReceiveLength,
                        ulStartingOffset,
                        pReceiveMdl,
                        TRUE);
   }

   return TSCommonChainedReceive(pAddressObject,
                                 ulReceiveLength,
                                 ulStartingOffset,
                                 pReceiveMdl,
                                 ((ulReceiveFlags & TDI_RECEIVE_EXPEDITED) != 0),
                                 pvTsduDescriptor);
}


// -----------------------------------------------
//
// Function:   TSChainedRcvExpeditedHandler
//
// Arguments:  pvTdiEventContext -- really pointer to our AddressObject
//             Connection_Context -- really pointer to our Endpoint
//             ReceiveFlags -- nature of received data
//             ulReceiveLength --- length of data in buffer
//             ulStartingOffset     -- total length of datagram
//             pReceiveMdl        -- data buffer
//             pTsduDescriptor -- returns value for TdiReturnChainedReceives
//
// Returns:    STATUS_DATA_NOT_ACCEPTED (we didn't want data)
//             STATUS_SUCCESS (we used all data & are done with it)
//             STATUS_MORE_PROCESSING_REQUIRED -- we supplied an IRP for rest
//
// Descript:   Event handler for incoming receives on connection
//
// -----------------------------------------------


TDI_STATUS
TSChainedRcvExpeditedHandler(PVOID              pvTdiEventContext,
                             CONNECTION_CONTEXT ConnectionContext,
                             ULONG              ulReceiveFlags,
                             ULONG              ulReceiveLength,
                             ULONG              ulStartingOffset,
                             PMDL               pReceiveMdl,
                             PVOID              pvTsduDescriptor)
{
   PADDRESS_OBJECT   pAddressObject = (PADDRESS_OBJECT)pvTdiEventContext;

   if (ulDebugLevel & ulDebugShowHandlers)
   {
      DebugPrint1("\n >>>> %s\n", strFunc5);
      TSShowReceiveInfo(pAddressObject,
                        ConnectionContext,
                        ulReceiveFlags,
                        ulReceiveLength,
                        ulStartingOffset,
                        pReceiveMdl,
                        TRUE);
   }

   return TSCommonChainedReceive(pAddressObject,
                                 ulReceiveLength,
                                 ulStartingOffset,
                                 pReceiveMdl,
                                 TRUE,
                                 pvTsduDescriptor);
}


/////////////////////////////////////////////////////////////
// private functions
/////////////////////////////////////////////////////////////

// ---------------------------------------------------------
//
// Function:   TSReceiveComplete
//
// Arguments:  pDeviceObject  -- device object that called Receive/Datagram
//             pIrp           -- IRP used in the call
//             pContext       -- context used for the call
//
// Returns:    status of operation (STATUS_MORE_PROCESSING_REQUIRED)
//
// Descript:   Gets the result of the receive, stuffs result into
//             receive buffer, completes the IRP from the dll, and
//             cleans up the Irp and associated data from the receive
//
// ---------------------------------------------------------

#pragma warning(disable: UNREFERENCED_PARAM)


TDI_STATUS
TSReceiveComplete(PDEVICE_OBJECT pDeviceObject,
                  PIRP           pLowerIrp,
                  PVOID          pvContext)

{
   PRECEIVE_CONTEXT  pReceiveContext = (PRECEIVE_CONTEXT)pvContext;
   NTSTATUS          lStatus         = pLowerIrp->IoStatus.Status;
   ULONG             ulBytesCopied   = (ULONG)pLowerIrp->IoStatus.Information;
   PADDRESS_OBJECT   pAddressObject  = pReceiveContext->pAddressObject;
   PRECEIVE_DATA     pReceiveData    = pReceiveContext->pReceiveData;
                  

   if (NT_SUCCESS(lStatus))
   {
      if (ulDebugLevel & ulDebugShowCommand)
      {
         DebugPrint2("%s:  %u BytesCopied\n",
                      strFuncP1,
                      ulBytesCopied);
      }
      pReceiveData->ulBufferUsed += ulBytesCopied;
      if (pReceiveData->ulBufferUsed >= pReceiveData->ulBufferLength)
      {
         TSPacketReceived(pAddressObject,
                          pReceiveData,
                          pReceiveContext->fIsExpedited);
      }
      else
      {
         DebugPrint1("%s:  Data Incomplete\n", strFuncP1);
         TSFreeMemory(pReceiveData->pucDataBuffer);
         TSFreeMemory(pReceiveData);
      }
   }
   else
   {
      DebugPrint2("%s:  Completed with status 0x%08x\n", 
                   strFuncP1,
                   lStatus);
      TSFreeMemory(pReceiveData->pucDataBuffer);
      TSFreeMemory(pReceiveData);
   }

   //
   // now cleanup
   //
   TSFreeIrp(pLowerIrp, pAddressObject->pIrpPool);
   TSFreeBuffer(pReceiveContext->pLowerMdl);

   TSFreeMemory(pReceiveContext);

   return TDI_MORE_PROCESSING;
}

#pragma warning(default: UNREFERENCED_PARAM)


// ---------------------------------
//
// Function:   TSShowReceiveInfo
//
// Arguments:  pAddressObject -- the address object associated with this endpoint
//             ConnectionContext -- endpoint of this connection
//             ulReceiveFlags -- info about the receive
//             ulBytesIndicated -- bytes indicated up
//             ulBytesAvailable -- total bytes (or starting offset)
//             pvTsdu  -- ptr to the data (or to its mdl)
//             fIsChained -- if TRUE, then this is chained receive
//
// Returns:    none
//
// Descript:   shows info passed to receive handler
//
// --------------------------------

VOID
TSShowReceiveInfo(PADDRESS_OBJECT      pAddressObject,
                  CONNECTION_CONTEXT   ConnectionContext,
                  ULONG                ulReceiveFlags,
                  ULONG                ulBytesIndicated,
                  ULONG                ulBytesAvailable,
                  PVOID                pvTsdu,
                  BOOLEAN              fIsChained)
{
   DebugPrint3("pAddressObject = %p\n"
               "pEndpoint      = %p\n"
               "ulReceiveFlags = 0x%08x\n",
                pAddressObject,
                ConnectionContext,
                ulReceiveFlags);

   if (ulReceiveFlags & TDI_RECEIVE_NORMAL)
   {
      DebugPrint0("                  TDI_RECEIVE_NORMAL\n");
   }
   
   if (ulReceiveFlags & TDI_RECEIVE_EXPEDITED)
   {
      DebugPrint0("                  TDI_RECEIVE_EXPEDITED\n");
   }
   
   if (ulReceiveFlags & TDI_RECEIVE_ENTIRE_MESSAGE)
   {
      DebugPrint0("                  TDI_RECEIVE_ENTIRE_MESSAGE\n");
   }
   
   if (ulReceiveFlags & TDI_RECEIVE_AT_DISPATCH_LEVEL)
   {
      DebugPrint0("                  TDI_RECEIVE_AT_DISPATCH_LEVEL\n");
   }
   
   if (fIsChained)
   {
      DebugPrint3("ReceiveLength  = %u\n"
                  "StartingOffset = 0x%08x\n"
                  "pMdl           = %p\n",
                   ulBytesIndicated,
                   ulBytesAvailable,
                   pvTsdu);
   }
   else
   {
      DebugPrint3("BytesIndicated = %u\n"
                  "TotalBytes     = %u\n"
                  "pDataBuffer    = %p\n",
                   ulBytesIndicated,
                   ulBytesAvailable,
                   pvTsdu);
   }
}


// ------------------------------------------------------
//
// Function:   TSGetRestOfData
//
// Arguments:  pAddressObject -- address object we are receiving on
//             pReceiveData   -- what we have received so far..
//
// Returns:    Irp to return to transport, to get rest of data (NULL if error)
//
// Descript:   This function sets up the IRP to get the rest of a datagram
//             that was only partially delivered via the event handler
//
// -------------------------------------------------

PIRP
TSGetRestOfData(PADDRESS_OBJECT  pAddressObject,
                PRECEIVE_DATA    pReceiveData,
                BOOLEAN          fIsExpedited)

{
   PUCHAR            pucDataBuffer   = pReceiveData->pucDataBuffer + pReceiveData->ulBufferUsed;
   ULONG             ulBufferLength  = pReceiveData->ulBufferLength - pReceiveData->ulBufferUsed;
   PRECEIVE_CONTEXT  pReceiveContext = NULL;
   PMDL              pReceiveMdl     = NULL;
   

   //
   // allocate all the necessary structures
   // our context
   //
   if ((TSAllocateMemory((PVOID *)&pReceiveContext,
                          sizeof(RECEIVE_CONTEXT),
                          strFuncP3,
                          "ReceiveContext")) != STATUS_SUCCESS)
   {
      goto cleanup;
   }


   //
   // then the actual mdl
   //
   pReceiveMdl = TSAllocateBuffer(pucDataBuffer, 
                                  ulBufferLength);

   if (pReceiveMdl)
   {
      //
      // set up the completion context
      //
      pReceiveContext->pLowerMdl      = pReceiveMdl;
      pReceiveContext->pReceiveData   = pReceiveData;
      pReceiveContext->pAddressObject = pAddressObject;
      pReceiveContext->fIsExpedited   = fIsExpedited;

      //
      // finally, the irp itself
      //
      PIRP  pLowerIrp = TSAllocateIrp(pAddressObject->pEndpoint->GenHead.pDeviceObject,
                                      pAddressObject->pIrpPool);

      if (pLowerIrp)
      {
         //
         // if made it to here, everything is correctly allocated
         // set up the irp for the call
         //
#pragma  warning(disable: CONSTANT_CONDITIONAL)

         TdiBuildReceive(pLowerIrp,
                         pAddressObject->pEndpoint->GenHead.pDeviceObject,
                         pAddressObject->pEndpoint->GenHead.pFileObject,
                         TSReceiveComplete,
                         pReceiveContext,
                         pReceiveMdl,
                         TDI_RECEIVE_NORMAL,
                         ulBufferLength);

#pragma  warning(default: CONSTANT_CONDITIONAL)

         return pLowerIrp;
      }
   }

//
// get here if there was an allocation failure
// need to clean up everything else...
//
cleanup:
   if (pReceiveContext)
   {
      TSFreeMemory(pReceiveContext);
   }
   if (pReceiveMdl)
   {
      TSFreeBuffer(pReceiveMdl);
   }
   return NULL;
}




// ---------------------------------------------
//
// Function:   TSCommonReceive
//
// Arguments:  see TSReceiveHandler
//
// Returns:    status to return to protocol
//
// Descript:   Common code for TSReceiveHandler and 
//             TSRcvExpeditedHandler.  Pretty much all the work is
//             done here
//
// ---------------------------------------------


TDI_STATUS
TSCommonReceive(PADDRESS_OBJECT  pAddressObject,
                ULONG            ulBytesTotal,
                ULONG            ulBytesIndicated,
                ULONG            ulReceiveFlags,
                PVOID            pvTsdu,
                BOOLEAN          fIsExpedited,
                PULONG           pulBytesTaken,
                PIRP             *pIoRequestPacket)
{
   //
   // check for a bad condition -- more bytes indicated that total
   //
   if (ulBytesIndicated > ulBytesTotal)
   {
      DebugPrint2("%u bytes indicated > %u bytes total\n",
                   ulBytesIndicated,
                   ulBytesTotal);
      return TDI_NOT_ACCEPTED;
   }


   PRECEIVE_DATA  pReceiveData;

   if ( (TSAllocateMemory((PVOID *)&pReceiveData,
                           sizeof(RECEIVE_DATA),
                           strFuncP4,
                           "ReceiveData")) == STATUS_SUCCESS)
   {
      PUCHAR   pucDataBuffer = NULL;
         
      if ((TSAllocateMemory((PVOID *)&pucDataBuffer,
                             ulBytesTotal,
                             strFuncP4,
                             "DataBuffer")) == STATUS_SUCCESS)
      {
         pReceiveData->pucDataBuffer = pucDataBuffer;
         pReceiveData->ulBufferLength = ulBytesTotal;
      }
      else
      {
         TSFreeMemory(pReceiveData);
         return TDI_NOT_ACCEPTED;
      }
   }
   else
   {
      return TDI_NOT_ACCEPTED;
   }

   //
   // copy the data indicated to us into the buffer
   //
   TdiCopyLookaheadData(pReceiveData->pucDataBuffer,
                        pvTsdu,
                        ulBytesIndicated,
                        ulReceiveFlags);
         
   pReceiveData->ulBufferUsed = ulBytesIndicated;

   //
   // first case -- entire packet indicated to us
   //
   if (ulBytesIndicated == ulBytesTotal)
   {
      TSPacketReceived(pAddressObject,
                       pReceiveData,
                       fIsExpedited);

      *pulBytesTaken    = ulBytesTotal;
      *pIoRequestPacket = NULL;
      return TDI_SUCCESS;
   }

   //
   // second case -- only part of data indicated up
   //
   else
   {
      PIRP  pLowerIrp = TSGetRestOfData(pAddressObject,
                                        pReceiveData,
                                        fIsExpedited);

      if (pLowerIrp)
      {
         //
         // need to do this since we are bypassing IoCallDriver
         //
         IoSetNextIrpStackLocation(pLowerIrp);
         
         *pulBytesTaken    = ulBytesIndicated;
         *pIoRequestPacket = pLowerIrp;
         return TDI_MORE_PROCESSING;
      }
      else
      {
         DebugPrint1("%s:  unable to get rest of packet\n", strFuncP4);
         TSFreeMemory(pReceiveData->pucDataBuffer);
         TSFreeMemory(pReceiveData);
         return TDI_NOT_ACCEPTED;
      }
   }
}


// ---------------------------------------------
//
// Function:   TSCommonChainedReceive
//
// Arguments:  see TStChainedRcvhandler
//
// Returns:    status
//
// Descript:   Common code for TSChainedReceiveHandler and 
//             TSChainedRcvExpeditedHandler.  Pretty much all the work is
//             done here
//
// ---------------------------------------------

TDI_STATUS
TSCommonChainedReceive(PADDRESS_OBJECT pAddressObject,
                       ULONG           ulReceiveLength,
                       ULONG           ulStartingOffset,
                       PMDL            pReceiveMdl,
                       BOOLEAN         fIsExpedited,
                       PVOID           pvTsduDescriptor)
{
   PRECEIVE_DATA  pReceiveData;


   if ((TSAllocateMemory((PVOID *)&pReceiveData,
                          sizeof(RECEIVE_DATA),
                          strFuncP5,
                          "ReceiveData")) == STATUS_SUCCESS)
   {
      PUCHAR   pucDataBuffer;
      
      if((TSAllocateMemory((PVOID *)&pucDataBuffer,
                            ulReceiveLength,
                            strFuncP5,
                            "DataBuffer")) == STATUS_SUCCESS)
      {
         ULONG ulBytesCopied;

         TdiCopyMdlToBuffer(pReceiveMdl,
                            ulStartingOffset,
                            pucDataBuffer,
                            0,
                            ulReceiveLength,
                            &ulBytesCopied);

         //
         // if successfully copied all data
         //
         if (ulBytesCopied == ulReceiveLength)
         {
            pReceiveData->pucDataBuffer  = pucDataBuffer;
            pReceiveData->ulBufferLength = ulReceiveLength;
            pReceiveData->ulBufferUsed   = ulReceiveLength;
            TSPacketReceived(pAddressObject,
                             pReceiveData,
                             fIsExpedited);

            return TDI_SUCCESS;
         }

         //
         // error in copying data!
         //
         else
         {
            DebugPrint1("%s: error copying data\n", strFuncP5);
            TSFreeMemory(pucDataBuffer);
            TSFreeMemory(pReceiveData);
         }
      }
      else        // unable to allocate pucDataBuffer
      {
         TSFreeMemory(pReceiveData);
      }
   }
   return TDI_NOT_ACCEPTED;
}


///////////////////////////////////////////////////////////////////////////////
// end of file receive.cpp
///////////////////////////////////////////////////////////////////////////////