/*
 * COPYRIGHT:   See COPYING in the top level directory
 * PROJECT:     ReactOS Ancillary Function Driver
 * FILE:        afd/event.c
 * PURPOSE:     TDI event handlers
 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
 * REVISIONS:
 *   CSH 01/09-2000 Created
 */
#include <afd.h>

NTSTATUS AfdEventError(
    IN PVOID TdiEventContext,
    IN NTSTATUS Status)
{
    AFD_DbgPrint(MAX_TRACE, ("Called.\n"));

    return STATUS_SUCCESS;
}


NTSTATUS AfdEventDisconnect(
    IN PVOID TdiEventContext,
    IN CONNECTION_CONTEXT ConnectionContext,
    IN LONG DisconnectDataLength,
    IN PVOID DisconnectData,
    IN LONG DisconnectInformationLength,
    IN PVOID DisconnectInformation,
    IN ULONG DisconnectFlags)
{
    AFD_DbgPrint(MAX_TRACE, ("Called.\n"));

    return STATUS_SUCCESS;
}


NTSTATUS AfdEventReceive(
    IN PVOID TdiEventContext,
    IN CONNECTION_CONTEXT ConnectionContext,
    IN ULONG ReceiveFlags,
    IN ULONG BytesIndicated,
    IN ULONG BytesAvailable,
    OUT ULONG *BytesTaken,
    IN PVOID Tsdu,
    OUT PIRP *IoRequestPacket)
{
  PAFDFCB FCB = (PAFDFCB)TdiEventContext;
  PVOID ReceiveBuffer;
  PAFD_BUFFER Buffer;
  KIRQL OldIrql;

  AFD_DbgPrint(MAX_TRACE, ("Called.\n"));

  AFD_DbgPrint(MID_TRACE, ("Receiving (%d) bytes on socket\n",
			   BytesAvailable));

  ReceiveBuffer = ExAllocatePool(NonPagedPool, BytesAvailable);
  if (!ReceiveBuffer)
    return STATUS_INSUFFICIENT_RESOURCES;

  /*Buffer = (PAFD_BUFFER)ExAllocateFromNPagedLookasideList(
    &BufferLookasideList);*/
  Buffer = (PAFD_BUFFER)ExAllocatePool(NonPagedPool, sizeof(AFD_BUFFER));
  if (!Buffer) {
    ExFreePool(ReceiveBuffer);
    return STATUS_INSUFFICIENT_RESOURCES;
  }

  /* Copy the data to a local buffer */
  RtlCopyMemory(ReceiveBuffer, Tsdu, BytesAvailable);

  Buffer->Buffer.len = BytesAvailable;
  Buffer->Buffer.buf = ReceiveBuffer;
  Buffer->Offset = 0;

  KeAcquireSpinLock(&FCB->ReceiveQueueLock, &OldIrql);

  InsertTailList( &FCB->ReceiveQueue, &Buffer->ListEntry );

  TryToSatisfyRecvRequest( FCB, TRUE );

  KeReleaseSpinLock(&FCB->ReceiveQueueLock, OldIrql);

  *BytesTaken = BytesAvailable;

  AFD_DbgPrint(MAX_TRACE, ("Leaving.\n"));

  return STATUS_SUCCESS;
}


TDI_STATUS ClientEventReceiveExpedited(
    IN PVOID TdiEventContext,
    IN CONNECTION_CONTEXT ConnectionContext,
    IN ULONG ReceiveFlags,
    IN ULONG BytesIndicated,
    IN ULONG BytesAvailable,
    OUT ULONG *BytesTaken,
    IN PVOID Tsdu,
    OUT PIRP *IoRequestPacket)
{
    AFD_DbgPrint(MAX_TRACE, ("Called.\n"));

    return STATUS_SUCCESS;
}


NTSTATUS ClientEventChainedReceive(
    IN PVOID TdiEventContext,
    IN CONNECTION_CONTEXT ConnectionContext,
    IN ULONG ReceiveFlags,
    IN ULONG ReceiveLength,
    IN ULONG StartingOffset,
    IN PMDL Tsdu,
    IN PVOID TsduDescriptor)
{
    AFD_DbgPrint(MAX_TRACE, ("Called.\n"));

    return STATUS_SUCCESS;
}


NTSTATUS AfdEventReceiveDatagramHandler(
  IN PVOID TdiEventContext,
  IN LONG SourceAddressLength,
  IN PVOID SourceAddress,
  IN LONG OptionsLength,
  IN PVOID Options,
  IN ULONG ReceiveDatagramFlags,
  IN ULONG BytesIndicated,
  IN ULONG BytesAvailable,
  OUT ULONG *BytesTaken,
  IN PVOID Tsdu,
  OUT PIRP *IoRequestPacket)
{
  PAFDFCB FCB = (PAFDFCB)TdiEventContext;
  PAFD_READ_REQUEST ReadRequest;
  PVOID ReceiveBuffer;
  PAFD_BUFFER Buffer;
  PLIST_ENTRY Entry;
  NTSTATUS Status;
  KIRQL OldIrql;
  ULONG Count;

  AFD_DbgPrint(MAX_TRACE, ("Called.\n"));

  AFD_DbgPrint(MID_TRACE, ("Receiving (%d) bytes from (0x%X).\n",
    BytesAvailable, *(PULONG)SourceAddress));

  ReceiveBuffer = ExAllocatePool(NonPagedPool, BytesAvailable);
  if (!ReceiveBuffer)
    return STATUS_INSUFFICIENT_RESOURCES;

  /*Buffer = (PAFD_BUFFER)ExAllocateFromNPagedLookasideList(
    &BufferLookasideList);*/
  Buffer = (PAFD_BUFFER)ExAllocatePool(NonPagedPool, sizeof(AFD_BUFFER));
  if (!Buffer) {
    ExFreePool(ReceiveBuffer);
    return STATUS_INSUFFICIENT_RESOURCES;
  }

  /* Copy the data to a local buffer */
  RtlCopyMemory(ReceiveBuffer, Tsdu, BytesAvailable);

  Buffer->Buffer.len = BytesAvailable;
  Buffer->Buffer.buf = ReceiveBuffer;
  Buffer->Offset = 0;

  ExInterlockedInsertTailList(
    &FCB->ReceiveQueue,
    &Buffer->ListEntry,
    &FCB->ReceiveQueueLock);

  KeAcquireSpinLock(&FCB->ReceiveQueueLock, &OldIrql);

  if (!IsListEmpty(&FCB->ReadRequestQueue)) {
    AFD_DbgPrint(MAX_TRACE, ("Satisfying read request.\n"));

    Entry = RemoveHeadList(&FCB->ReceiveQueue);
    ReadRequest = CONTAINING_RECORD(Entry, AFD_READ_REQUEST, ListEntry);

    Status = FillWSABuffers(
      FCB,
      ReadRequest->RecvFromRequest->Buffers,
      ReadRequest->RecvFromRequest->BufferCount,
      &Count,
      FALSE ); /* Packet oriented */
    ReadRequest->RecvFromReply->NumberOfBytesRecvd = Count;
    ReadRequest->RecvFromReply->Status = NO_ERROR;

    ReadRequest->Irp->IoStatus.Information = 0;
    ReadRequest->Irp->IoStatus.Status = Status;

    AFD_DbgPrint(MAX_TRACE, ("Completing IRP at (0x%X).\n", ReadRequest->Irp));

    IoCompleteRequest(ReadRequest->Irp, IO_NETWORK_INCREMENT);
  }

  KeReleaseSpinLock(&FCB->ReceiveQueueLock, OldIrql);

  *BytesTaken = BytesAvailable;

  AFD_DbgPrint(MAX_TRACE, ("Leaving.\n"));

  return STATUS_SUCCESS;
}


NTSTATUS AfdRegisterEventHandlers(
    PAFDFCB FCB)
{
    NTSTATUS Status;

    assert(FCB->TdiAddressObject);

    /* Report errors for all types of sockets */
    Status = TdiSetEventHandler(FCB->TdiAddressObject,
        TDI_EVENT_ERROR,
        (PVOID)AfdEventError,
        (PVOID)FCB);
    if (!NT_SUCCESS(Status)) { return Status; }

    switch (FCB->SocketType) {
    case SOCK_STREAM:
        Status = TdiSetEventHandler(FCB->TdiAddressObject,
            TDI_EVENT_DISCONNECT,
            (PVOID)AfdEventDisconnect,
            (PVOID)FCB);
        if (!NT_SUCCESS(Status)) { return Status; }

        Status = TdiSetEventHandler(FCB->TdiAddressObject,
            TDI_EVENT_RECEIVE,
            (PVOID)AfdEventReceive,
            (PVOID)FCB);
        if (!NT_SUCCESS(Status)) { return Status; }

        Status = TdiSetEventHandler(FCB->TdiAddressObject,
            TDI_EVENT_RECEIVE_EXPEDITED,
            (PVOID)ClientEventReceiveExpedited,
            (PVOID)FCB);
        if (!NT_SUCCESS(Status)) { return Status; }

        Status = TdiSetEventHandler(FCB->TdiAddressObject,
            TDI_EVENT_CHAINED_RECEIVE,
            (PVOID)ClientEventChainedReceive,
            (PVOID)FCB);
        if (!NT_SUCCESS(Status)) { return Status; }
        break;

    case SOCK_DGRAM:
    case SOCK_RAW:
        Status = TdiSetEventHandler(FCB->TdiAddressObject,
            TDI_EVENT_RECEIVE_DATAGRAM,
            (PVOID)AfdEventReceiveDatagramHandler,
            (PVOID)FCB);
        if (!NT_SUCCESS(Status)) { return Status; }
    }
    return STATUS_SUCCESS;
}


NTSTATUS AfdDeregisterEventHandlers(
    PAFDFCB FCB)
{
    NTSTATUS Status;

    Status = TdiSetEventHandler(FCB->TdiAddressObject,
        TDI_EVENT_ERROR,
        NULL,
        NULL);
    if (!NT_SUCCESS(Status)) { return Status; }

    switch (FCB->SocketType) {
    case SOCK_STREAM:
        Status = TdiSetEventHandler(FCB->TdiAddressObject,
            TDI_EVENT_DISCONNECT,
            NULL,
            NULL);
        if (!NT_SUCCESS(Status)) { return Status; }

        Status = TdiSetEventHandler(FCB->TdiAddressObject,
            TDI_EVENT_RECEIVE,
            NULL,
            NULL);
        if (!NT_SUCCESS(Status)) { return Status; }

        Status = TdiSetEventHandler(FCB->TdiAddressObject,
            TDI_EVENT_RECEIVE_EXPEDITED,
            NULL,
            NULL);
        if (!NT_SUCCESS(Status)) { return Status; }

        Status = TdiSetEventHandler(FCB->TdiAddressObject,
            TDI_EVENT_CHAINED_RECEIVE,
            NULL,
            NULL);
        if (!NT_SUCCESS(Status)) { return Status; }
        break;

    case SOCK_DGRAM:
    case SOCK_RAW:
        Status = TdiSetEventHandler(FCB->TdiAddressObject,
            TDI_EVENT_RECEIVE_DATAGRAM,
            NULL,
            NULL);
        if (!NT_SUCCESS(Status)) { return Status; }
    }
    return STATUS_SUCCESS;
}


/* EOF */
