/*
 *  b2ether driver -- derived from DDK packet driver sample
 *
 *  Basilisk II (C) 1997-1999 Christian Bauer
 *
 *  Windows platform specific code copyright (C) Lauri Pesonen
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

//#include "stdarg.h"
#include "ntddk.h"
#include "ntiologc.h"
#include "ndis.h"
#include "b2ether.h"

#undef DBG
#define DBG 0
#include "debug.h"

static UINT            Medium;
static NDIS_MEDIUM     MediumArray=NdisMedium802_3;

NTSTATUS PacketOpen( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
/*
    This is the dispatch routine for create/open and close requests.
    These requests complete successfully.
*/
{
  PDEVICE_EXTENSION DeviceExtension;

  POPEN_INSTANCE    Open;

  PIO_STACK_LOCATION  IrpSp;

  NDIS_STATUS     Status;
  NDIS_STATUS     ErrorStatus;

  UINT            i;

  IF_LOUD(DbgPrint("Packet: OpenAdapter\n");)

  DeviceExtension = DeviceObject->DeviceExtension;

  IrpSp = IoGetCurrentIrpStackLocation(Irp);

  Open = ExAllocatePool(NonPagedPool,sizeof(OPEN_INSTANCE));
  if (Open==NULL) {
      Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
		  Irp->IoStatus.Information = 0;
		  IoCompleteRequest(Irp, IO_NO_INCREMENT);
      return STATUS_INSUFFICIENT_RESOURCES;
  }

  RtlZeroMemory( Open, sizeof(OPEN_INSTANCE) );

  //  Save or open here
  IrpSp->FileObject->FsContext = Open;
  Open->DeviceExtension = DeviceExtension;
  Open->OpenCloseIrp = Irp;

  //  Allocate a packet pool for our xmit and receive packets
  NdisAllocatePacketPool(
      &Status,
      &Open->PacketPool,
      TRANSMIT_PACKETS,
      sizeof(PACKET_RESERVED));

  if (Status != NDIS_STATUS_SUCCESS) {
      IF_LOUD(DbgPrint("Packet: Failed to allocate packet pool\n");)
      ExFreePool(Open);
      Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
		  Irp->IoStatus.Information = 0;
		  IoCompleteRequest(Irp, IO_NO_INCREMENT);
      return STATUS_INSUFFICIENT_RESOURCES;
  }

  //  list to hold irp's want to reset the adapter
  InitializeListHead(&Open->ResetIrpList);

  //  Initialize list for holding pending read requests
  KeInitializeSpinLock(&Open->RcvQSpinLock);
  InitializeListHead(&Open->RcvList);

  //  Initialize the request list
  KeInitializeSpinLock(&Open->RequestSpinLock);
  InitializeListHead(&Open->RequestList);

  //  link up the request stored in our open block
  for ( i=0; i<MAX_REQUESTS; i++ ) {
      ExInterlockedInsertTailList(
          &Open->RequestList,
          &Open->Requests[i].ListElement,
          &Open->RequestSpinLock);

  }

  IoMarkIrpPending(Irp);
  Irp->IoStatus.Status = STATUS_PENDING;

  //  Try to open the MAC
  NdisOpenAdapter(
      &Status,
      &ErrorStatus,
      &Open->AdapterHandle,
      &Medium,
      &MediumArray,
      1,
      DeviceExtension->NdisProtocolHandle,
      Open,
      &DeviceExtension->AdapterName,
      0,
      NULL);

  if (Status != NDIS_STATUS_PENDING) {
    PacketOpenAdapterComplete( Open, Status, NDIS_STATUS_SUCCESS );
  }
  return(STATUS_PENDING);
}


VOID PacketOpenAdapterComplete(
  IN NDIS_HANDLE  ProtocolBindingContext,
  IN NDIS_STATUS  Status,
  IN NDIS_STATUS  OpenErrorStatus
)
{
  PIRP              Irp;
  POPEN_INSTANCE    Open;

  IF_LOUD(DbgPrint("Packet: OpenAdapterComplete\n");)

  Open = (POPEN_INSTANCE)ProtocolBindingContext;

  Irp = Open->OpenCloseIrp;

  if (Status != NDIS_STATUS_SUCCESS) {
      IF_LOUD(DbgPrint("Packet: OpenAdapterComplete-FAILURE\n");)
      NdisFreePacketPool(Open->PacketPool);
      ExFreePool(Open);
  }

  Irp->IoStatus.Status = Status;
  IoCompleteRequest(Irp, IO_NO_INCREMENT);

  return;
}


NTSTATUS PacketClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
{
  POPEN_INSTANCE    Open;
  NDIS_STATUS     Status;
  PIO_STACK_LOCATION  IrpSp;

  IF_LOUD(DbgPrint("Packet: CloseAdapter\n");)

  IrpSp = IoGetCurrentIrpStackLocation(Irp);
  Open = IrpSp->FileObject->FsContext;
  Open->OpenCloseIrp =Irp;

  IoMarkIrpPending(Irp);
  Irp->IoStatus.Status = STATUS_PENDING;

  NdisCloseAdapter( &Status, Open->AdapterHandle );

  if (Status != NDIS_STATUS_PENDING) {
      PacketCloseAdapterComplete( Open, Status );
  }

  return(STATUS_PENDING);
}

VOID PacketCloseAdapterComplete(
  IN NDIS_HANDLE  ProtocolBindingContext,
  IN NDIS_STATUS  Status
)
{
  POPEN_INSTANCE    Open;
  PIRP              Irp;

  IF_LOUD(DbgPrint("Packet: CloseAdapterComplete\n");)

  Open = (POPEN_INSTANCE)ProtocolBindingContext;
  Irp = Open->OpenCloseIrp;

  NdisFreePacketPool(Open->PacketPool);
  ExFreePool(Open);

  Irp->IoStatus.Status = STATUS_SUCCESS;
  IoCompleteRequest(Irp, IO_NO_INCREMENT);
}


NTSTATUS PacketCleanup(
  IN PDEVICE_OBJECT DeviceObject,
  IN PIRP FlushIrp
)
{
  POPEN_INSTANCE      Open;
  PIO_STACK_LOCATION  IrpSp;
  PLIST_ENTRY         PacketListEntry;
  PNDIS_PACKET        pPacket;
  NDIS_STATUS         Status;

  IF_LOUD(DbgPrint("Packet: Cleanup\n");)

  IrpSp = IoGetCurrentIrpStackLocation(FlushIrp);

  Open=IrpSp->FileObject->FsContext;

  IoMarkIrpPending(FlushIrp);
  FlushIrp->IoStatus.Status = STATUS_PENDING;

  //
  //  The open instance of the device is about to close
  //  We need to complete all pending Irp's
  //  First we complete any pending read requests
  //
  while ((PacketListEntry=ExInterlockedRemoveHeadList(
                              &Open->RcvList,
                              &Open->RcvQSpinLock
                              )) != NULL) {

      IF_LOUD(DbgPrint("Packet: CleanUp - Completeing read\n");)

      pPacket=CONTAINING_RECORD(PacketListEntry,NDIS_PACKET,ProtocolReserved);

      //  complete normally
      PacketTransferDataComplete(
          Open,
          pPacket,
          NDIS_STATUS_SUCCESS,
          0
          );
  }

  // IoMarkIrpPending(FlushIrp);
  // FlushIrp->IoStatus.Status = STATUS_PENDING;

  //  We now place the Irp on the Reset list
  ExInterlockedInsertTailList(
          &Open->ResetIrpList,
          &FlushIrp->Tail.Overlay.ListEntry,
          &Open->RequestSpinLock);

  //  Now reset the adapter, the mac driver will complete any
  //  pending requests we have made to it.
  NdisReset( &Status, Open->AdapterHandle );

  if (Status != NDIS_STATUS_PENDING) {
      IF_LOUD(DbgPrint("Packet: Cleanup - ResetComplte being called\n");)
      PacketResetComplete( Open, Status );
  }

  return(STATUS_PENDING);
}


VOID PacketResetComplete(
  IN NDIS_HANDLE  ProtocolBindingContext,
  IN NDIS_STATUS  Status
)
{
  POPEN_INSTANCE      Open;
  PIRP                Irp;
  PLIST_ENTRY         ResetListEntry;

  IF_LOUD(DbgPrint("Packet: PacketResetComplte\n");)

  Open = (POPEN_INSTANCE)ProtocolBindingContext;

  //  remove the reset IRP from the list
  ResetListEntry=ExInterlockedRemoveHeadList(
                     &Open->ResetIrpList,
                     &Open->RequestSpinLock
                     );

#if DBG
  if (ResetListEntry == NULL) {
      DbgBreakPoint();
      return;
  }
#endif

  Irp = CONTAINING_RECORD(ResetListEntry,IRP,Tail.Overlay.ListEntry);
  Irp->IoStatus.Status = STATUS_SUCCESS;
  IoCompleteRequest(Irp, IO_NO_INCREMENT);

  IF_LOUD(DbgPrint("Packet: PacketResetComplte exit\n");)
}
