/* 
 * Copyright (c) 1993,1994
 *      Texas A&M University.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Texas A&M University
 *      and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * Developers:
 *             David K. Hess, Douglas Lee Schales, David R. Safford
 *
 * Heavily modified for Metaware HighC + GNU C 2.8+
 *             Gisle Vanem 1998
 */

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <io.h>
#include <fcntl.h>
#include <malloc.h>
#include <string.h>

#include "pcap-dos.h"
#include "pcap-int.h"
#include "msdos/ndis2.h"

#if defined(USE_NDIS2)

/*
 *  Packet buffer handling
 */
extern int     FreePktBuf  (PktBuf *buf);
extern int     EnquePktBuf (PktBuf *buf);
extern PktBuf* AllocPktBuf (void);

/*
 *  Various defines
 */
#define MAX_NUM_DEBUG_STRINGS 90
#define DEBUG_STRING_LENGTH   80
#define STACK_POOL_SIZE       6
#define STACK_SIZE            256

#define MEDIA_FDDI            1
#define MEDIA_ETHERNET        2
#define MEDIA_TOKEN           3

static int     startDebug     = 0;
static int     stopDebug      = 0;

static DWORD   droppedPackets = 0L;
static WORD    frameSize      = 0;
static WORD    headerSize     = 0;
static int     mediaType      = 0;
static char   *lastErr        = NULL;

static BYTE    debugStrings [MAX_NUM_DEBUG_STRINGS][DEBUG_STRING_LENGTH];
static BYTE   *freeStacks   [STACK_POOL_SIZE];
static int     freeStackPtr = STACK_POOL_SIZE - 1;

static ProtMan protManEntry = NULL;
static WORD    protManDS    = 0;
static volatile int xmitPending;

static struct _PktBuf        *txBufPending;
static struct _CardHandle    *handle;
static struct _CommonChars    common;
static struct _ProtocolChars  protChars;
static struct _ProtDispatch   lowerTable;

static struct _FailingModules failingModules;
static struct _BindingsList   bindings;

static struct {
         WORD  err_num;
         char *err_text;
       } ndis_errlist[] = {

  { ERR_SUCCESS,
    "The function completed successfully.\n"  },

  { ERR_WAIT_FOR_RELEASE,
    "The ReceiveChain completed successfully but the protocol has\n"
    "retained control of the buffer.\n"  },

  { ERR_REQUEST_QUEUED,
    "The current request has been queued.\n"  },

  { ERR_FRAME_NOT_RECOGNIZED,
    "Frame not recognized.\n"  },

  { ERR_FRAME_REJECTED,
    "Frame was discarded.\n"  },

  { ERR_FORWARD_FRAME,
    "Protocol wishes to forward frame to another protocol.\n"  },

  { ERR_OUT_OF_RESOURCE,
    "Out of resource.\n"  },

  { ERR_INVALID_PARAMETER,
    "Invalid parameter.\n"  },

  { ERR_INVALID_FUNCTION,
    "Invalid function.\n"  },

  { ERR_NOT_SUPPORTED,
    "Not supported.\n"  },

  { ERR_HARDWARE_ERROR,
    "Hardware error.\n"  },

  { ERR_TRANSMIT_ERROR,
    "The packet was not transmitted due to an error.\n"  },

  { ERR_NO_SUCH_DESTINATION,
    "Token ring packet was not recognized when transmitted.\n"  },

  { ERR_BUFFER_TOO_SMALL,
    "Provided buffer was too small.\n"  },

  { ERR_ALREADY_STARTED,
    "Network drivers already started.\n"  },

  { ERR_INCOMPLETE_BINDING,
    "Protocol driver could not complete its bindings.\n"  },

  { ERR_DRIVER_NOT_INITIALIZED,
    "MAC did not initialize properly.\n"  },

  { ERR_HARDWARE_NOT_FOUND,
    "Hardware not found.\n"  },

  { ERR_HARDWARE_FAILURE,
    "Hardware failure.\n"  },

  { ERR_CONFIGURATION_FAILURE,
    "Configuration failure.\n"  },

  { ERR_INTERRUPT_CONFLICT,
    "Interrupt conflict.\n"  },

  { ERR_INCOMPATIBLE_MAC,
    "The MAC is not compatible with the protocol.\n"  },

  { ERR_INITIALIZATION_FAILED,
    "Initialization failed.\n"  },

  { ERR_NO_BINDING,
    "Binding did not occur.\n"  },

  { ERR_NETWORK_MAY_NOT_BE_CONNECTED,
    "The network may not be connected to the adapter.\n"  },

  { ERR_INCOMPATIBLE_OS_VERSION,
    "The version of the operating system is incompatible with the protocol.\n"  },

  { ERR_ALREADY_REGISTERED,
    "The protocol is already registered.\n"  },

  { ERR_PATH_NOT_FOUND,
    "PROTMAN.EXE could not be found.\n"  },

  { ERR_INSUFFICIENT_MEMORY,
    "Insufficient memory.\n"  },

  { ERR_INFO_NOT_FOUND,
    "Protocol Mananger info structure is lost or corrupted.\n"  },

  { ERR_GENERAL_FAILURE,
    "General failure.\n"  }
};

/*
 *  Some handy macros
 */       
#define PERROR(str)    printf("%s (%d): %s\n", __FILE__,__LINE__,str)
#define DEBUG_RING()   (debugStrings[stopDebug+1 == MAX_NUM_DEBUG_STRINGS ? \
                        stopDebug = 0 : ++stopDebug])

/*
 * needs rewrite for DOSX
 */
#define MAC_DISPATCH(hnd)  ((struct _MacUpperDispatch*)(hnd)->common->upperDispatchTable)
#define MAC_STATUS(hnd)    ((struct _MacStatusTable*)  (hnd)->common->serviceStatus)
#define MAC_CHAR(hnd)      ((struct _MacChars*)        (hnd)->common->serviceChars)

#ifdef NDIS_DEBUG
  #define DEBUG0(str)      printf (str)
  #define DEBUG1(fmt,a)    printf (fmt,a)
  #define DEBUG2(fmt,a,b)  printf (fmt,a,b)
  #define TRACE0(str)      sprintf (DEBUG_RING(),str)
  #define TRACE1(fmt,a)    sprintf (DEBUG_RING(),fmt,a)
#else
  #define DEBUG0(str)      ((void)0)
  #define DEBUG1(fmt,a)    ((void)0)
  #define DEBUG2(fmt,a,b)  ((void)0)
  #define TRACE0(str)      ((void)0)
  #define TRACE1(fmt,a)    ((void)0)
#endif

/*
 * This routine is called from both threads
 */
void NdisFreeStack (BYTE *aStack)
{
  GUARD();

  if (freeStackPtr == STACK_POOL_SIZE - 1)
     PERROR ("tried to free too many stacks");

  freeStacks[++freeStackPtr] = aStack;

  if (freeStackPtr == 0)
     TRACE0 ("freeStackPtr went positive\n");

  UNGUARD();
}

/*
 * This routine is called from callbacks to allocate local data
 */
BYTE *NdisAllocStack (void)
{
  BYTE *stack;

  GUARD();

  if (freeStackPtr < 0)
  {
    /* Ran out of stack buffers. Return NULL which will start
     * dropping packets
     */
    TRACE0 ("freeStackPtr went negative\n");
    stack = 0;
  }
  else
    stack = freeStacks[freeStackPtr--];

  UNGUARD();
  return (stack);
}

CALLBACK (NdisSystemRequest (DWORD param1, DWORD param2, WORD param3,
                             WORD opcode, WORD targetDS))
{
  static int            bindEntry = 0;
  struct _CommonChars  *macCommon;
  volatile WORD result;

  switch (opcode)
  {
    case REQ_INITIATE_BIND:
         macCommon = (struct _CommonChars*) param2;
         if (macCommon == NULL)
	 {
           printf ("There is an NDIS misconfiguration.\n");
           result = ERR_GENERAL_FAILURE;
	   break;
	 }
         DEBUG2 ("module name %s\n"
                 "module type %s\n",
                 macCommon->moduleName,
                 ((MacChars*) macCommon->serviceChars)->macName);

         /* Binding to the MAC */
         result = macCommon->systemRequest ((DWORD)&common, (DWORD)&macCommon,
                                            0, REQ_BIND,
                                            macCommon->moduleDS);

         if (!strcmp(bindings.moduleName[bindEntry], handle->moduleName))
              handle->common = macCommon;
         else PERROR ("unknown module");
         ++bindEntry;
	 break;

    case REQ_INITIATE_UNBIND:
         macCommon = (struct _CommonChars*) param2;
         result = macCommon->systemRequest ((DWORD)&common, 0,
                                            0, REQ_UNBIND,
                                            macCommon->moduleDS);
         break;

    default:
         result = ERR_GENERAL_FAILURE;
	 break;
  }
  ARGSUSED (param1);
  ARGSUSED (param3);
  ARGSUSED (targetDS);
  return (result);
}

CALLBACK (NdisRequestConfirm (WORD protId, WORD macId,   WORD reqHandle,
                              WORD status, WORD request, WORD protDS))
{
  ARGSUSED (protId);    ARGSUSED (macId);
  ARGSUSED (reqHandle); ARGSUSED (status);
  ARGSUSED (request);   ARGSUSED (protDS);
  return (ERR_SUCCESS);
}

CALLBACK (NdisTransmitConfirm (WORD protId, WORD macId, WORD reqHandle,
                               WORD status, WORD protDS))
{
  xmitPending--;
  FreePktBuf (txBufPending);  /* Add passed ECB back to the free list */

  ARGSUSED (reqHandle);
  ARGSUSED (status);
  ARGSUSED (protDS);
  return (ERR_SUCCESS);
}


/*
 * The primary function for receiving packets
 */
CALLBACK (NdisReceiveLookahead (WORD  macId,      WORD  frameSize,
                                WORD  bytesAvail, BYTE *buffer,
                                BYTE *indicate,   WORD  protDS))
{
  int     result;
  PktBuf *pktBuf;
  WORD    bytesCopied;
  struct _TDBufDescr tDBufDescr;

#if 0
  TRACE1 ("lookahead length = %d, ", bytesAvail);
  TRACE1 ("ecb = %08lX, ",          *ecb);
  TRACE1 ("count = %08lX\n",         count);
  TRACE1 ("offset = %08lX, ",        offset);
  TRACE1 ("timesAllowed = %d, ",     timesAllowed);
  TRACE1 ("packet size = %d\n",      look->dataLookAheadLen);
#endif

  /* Allocate a buffer for the packet
   */
  if ((pktBuf = AllocPktBuf()) == NULL)
  {
    droppedPackets++;
    return (ERR_FRAME_REJECTED);
  }

  /*
   * Now kludge things. Note we will have to undo this later. This will
   * make the packet contiguous after the MLID has done the requested copy.
   */

  tDBufDescr.tDDataCount = 1;
  tDBufDescr.tDBufDescrRec[0].tDPtrType = NDIS_PTR_PHYSICAL;
  tDBufDescr.tDBufDescrRec[0].tDDataPtr = pktBuf->buffer;
  tDBufDescr.tDBufDescrRec[0].tDDataLen = pktBuf->length;
  tDBufDescr.tDBufDescrRec[0].dummy     = 0;

  result = MAC_DISPATCH(handle)->transferData (&bytesCopied, 0, &tDBufDescr,
                                               handle->common->moduleDS);
  pktBuf->packetLength = bytesCopied;

  if (result == ERR_SUCCESS)
       EnquePktBuf(pktBuf);
  else FreePktBuf (pktBuf);

  ARGSUSED (frameSize);
  ARGSUSED (bytesAvail);
  ARGSUSED (indicate);
  ARGSUSED (protDS);

  return (ERR_SUCCESS);
}

CALLBACK (NdisIndicationComplete (WORD macId, WORD protDS))
{
  ARGSUSED (macId);
  ARGSUSED (protDS);

  /* We don't give a hoot about these. Just return
   */
  return (ERR_SUCCESS);
}

/*
 * This is the OTHER way we may receive packets
 */
CALLBACK (NdisReceiveChain (WORD macId, WORD frameSize, WORD reqHandle,
                            struct _RxBufDescr *rxBufDescr,
                            BYTE *indicate, WORD protDS))
{
  struct _PktBuf *pktBuf;
  int     i;

  /*
   * For now we copy the entire packet over to a PktBuf structure. This may be
   * a performance hit but this routine probably isn't called very much, and
   * it is a lot of work to do it otherwise. Also if it is a filter protocol
   * packet we could end up sucking up MAC buffes.
   */

  if ((pktBuf = AllocPktBuf()) == NULL)
  {
    droppedPackets++;
    return (ERR_FRAME_REJECTED);
  }
  pktBuf->packetLength = 0;

  /* Copy the packet to the buffer
   */
  for (i = 0; i < rxBufDescr->rxDataCount; ++i)
  {
    struct _RxBufDescrRec *rxDescr = &rxBufDescr->rxBufDescrRec[i];

    memcpy (pktBuf->buffer + pktBuf->packetLength,
            rxDescr->rxDataPtr, rxDescr->rxDataLen);
    pktBuf->packetLength += rxDescr->rxDataLen;
  }

  EnquePktBuf (pktBuf);

  ARGSUSED (frameSize);
  ARGSUSED (reqHandle);
  ARGSUSED (indicate);
  ARGSUSED (protDS);   

  /* This frees up the buffer for the MAC to use
   */
  return (ERR_SUCCESS);
}

CALLBACK (NdisStatusProc (WORD macId,  WORD param1, BYTE *indicate,
                          WORD opcode, WORD protDS))
{
  switch (opcode)
  {
    case STATUS_RING_STATUS:
	 break;
    case STATUS_ADAPTER_CHECK:
	 break;
    case STATUS_START_RESET:
	 break;
    case STATUS_INTERRUPT:
	 break;
    case STATUS_END_RESET:
	 break;
    default:
	 break;
  }
  ARGSUSED (macId);
  ARGSUSED (param1);
  ARGSUSED (indicate);
  ARGSUSED (opcode);
  ARGSUSED (protDS);

  /* We don't need to do anything about this stuff yet
   */
  return (ERR_SUCCESS);
}

/*
 * Tell the NDIS driver to start the delivery of the packet
 */
int NdisSendPacket (struct _PktBuf *pktBuf, int macId)
{ 
  struct _TxBufDescr txBufDescr;
  int     result;

  xmitPending++;
  txBufPending = pktBuf;    /* we only have 1 pending Tx at a time */

  txBufDescr.txImmedLen  = 0;
  txBufDescr.txImmedPtr  = NULL;
  txBufDescr.txDataCount = 1;
  txBufDescr.txBufDescrRec[0].txPtrType = NDIS_PTR_PHYSICAL;
  txBufDescr.txBufDescrRec[0].dummy     = 0;
  txBufDescr.txBufDescrRec[0].txDataLen = pktBuf->packetLength;
  txBufDescr.txBufDescrRec[0].txDataPtr = pktBuf->buffer;

  result = MAC_DISPATCH(handle)->transmitChain (common.moduleId,
                                                pktBuf->handle,
                                                &txBufDescr,
                                                handle->common->moduleDS);
  switch (result)
  {
    case ERR_OUT_OF_RESOURCE:
         /* Note that this should not happen but if it does there is not
          * much we can do about it
          */
         printf ("ERROR: transmit queue overflowed\n");
         return (0);

    case ERR_SUCCESS:
         /* Everything was hunky dory and synchronous. Free up the 
          * packet buffer
          */
         xmitPending--;
         FreePktBuf (pktBuf);
         return (1);

    case ERR_REQUEST_QUEUED:
         /* Everything was hunky dory and asynchronous. Do nothing
          */
         return (1);

    default:
         printf ("Tx fail, code = %04X\n", result);
         return (0);
  }
}



static int ndis_nerr = sizeof(ndis_errlist) / sizeof(ndis_errlist[0]);

static char *Ndis_strerror (WORD errorCode)
{
  static char buf[30];
  int    i;

  for (i = 0; i < ndis_nerr; i++)
      if (errorCode == ndis_errlist[i].err_num)
         return (ndis_errlist[i].err_text);

  sprintf (buf,"unknown error %d",errorCode);
  return (buf);
}


char *NdisLastError (void)
{
  char *errStr = lastErr;
  lastErr = NULL;
  return (errStr);
}

int NdisOpen (void)
{
  struct _ReqBlock reqBlock;
  int     result;
  int     ndisFd = open (NDIS_PATH, O_RDONLY);

  if (ndisFd < 0)
  {
    printf ("Could not open NDIS Protocol Manager device.\n");
    return (0);
  }

  memset (&reqBlock, 0, sizeof(ReqBlock));

  reqBlock.opcode = PM_GET_PROTOCOL_MANAGER_LINKAGE;

  result = NdisGetLinkage (ndisFd, (char*)&reqBlock, sizeof(ReqBlock));
  if (result != 0)
  {
    printf ("Could not get Protocol Manager linkage.\n");
    close (ndisFd);
    return (0);
  }

  close (ndisFd);
  protManEntry = (ProtMan) reqBlock.pointer1;
  protManDS    = reqBlock.word1;

  DEBUG2 ("Entry Point = %04X:%04X\n", FP_SEG(protManEntry),FP_OFF(protManEntry));
  DEBUG1 ("ProtMan DS  = %04X\n", protManDS);
  return (1);
}


int NdisRegisterAndBind (int promis)
{
  struct _ReqBlock reqBlock;
  WORD    result;

  memset (&common,0,sizeof(common));

  common.tableSize = sizeof (common);

  common.majorNdisVersion   = 2;
  common.minorNdisVersion   = 0;
  common.majorModuleVersion = 2;
  common.minorModuleVersion = 0;

  /* Indicates binding from below and dynamically loaded
   */
  common.moduleFlags = 0x00000006L;

  strcpy (common.moduleName, "PCAP");

  common.protocolLevelUpper = 0xFF;
  common.protocolLevelLower = 1;
  common.interfaceLower     = 1;
#ifdef __DJGPP__
  common.moduleDS           = _dos_ds; /* the callback data segment */
#else
  common.moduleDS           = _DS;
#endif

  common.systemRequest      = (SystemRequest) systemRequestGlue;
  common.serviceChars       = (BYTE*) &protChars;
  common.serviceStatus      = NULL;
  common.upperDispatchTable = NULL;
  common.lowerDispatchTable = (BYTE*) &lowerTable;

  protChars.length  = sizeof (protChars);
  protChars.name[0] = 0;
  protChars.type    = 0;

  lowerTable.backPointer        = &common;
  lowerTable.requestConfirm     = requestConfirmGlue;
  lowerTable.transmitConfirm    = transmitConfirmGlue;
  lowerTable.receiveLookahead   = receiveLookaheadGlue;
  lowerTable.indicationComplete = indicationCompleteGlue;
  lowerTable.receiveChain       = receiveChainGlue;
  lowerTable.status             = statusGlue;
  lowerTable.flags              = 3;
  if (promis)
     lowerTable.flags |= 4;   /* promiscous mode (receive everything) */

  bindings.numBindings = 1;
  strcpy (bindings.moduleName[0], handle->moduleName);

  /* Register ourselves with NDIS
   */
  reqBlock.opcode   = PM_REGISTER_MODULE;
  reqBlock.pointer1 = (BYTE FAR*) &common;
  reqBlock.pointer2 = (BYTE FAR*) &bindings;

  result = (*protManEntry) (&reqBlock, protManDS);
  if (result)
  {
    printf ("Protman registering failed: %s\n", Ndis_strerror(result));
    return (0);
  }

  /* Start the binding process
   */
  reqBlock.opcode   = PM_BIND_AND_START;
  reqBlock.pointer1 = (BYTE FAR*) &failingModules;

  result = (*protManEntry) (&reqBlock, protManDS);
  if (result)
  {
    printf ("Start binding failed: %s\n", Ndis_strerror(result));
    return (0);
  }
  return (1);
}

static int CheckMacFeatures (CardHandle *card)
{
  DWORD      serviceFlags;
  BYTE _far *mediaString;
  BYTE _far *mac_addr;

  DEBUG2 ("checking card features\n"
          "common table address = %08lX, macId = %d\n",
          card->common, card->common->moduleId);

  serviceFlags = MAC_CHAR (handle)->serviceFlags;

  if ((serviceFlags & SF_PROMISCUOUS) == 0)
  {
    printf ("The MAC %s does not support promiscuous mode.\n",
            card->moduleName);
    return (0);
  }

  mediaString = MAC_CHAR (handle)->macName;

  DEBUG1 ("media type = %s\n",mediaString);

  /* Get the media type. And set the header size
   */
  if (!strncmp(mediaString,"802.3",5) ||
      !strncmp(mediaString,"DIX",3)   ||
      !strncmp(mediaString,"DIX+802.3",9))
       headerSize = sizeof (EthernetIIHeader);

  else if (!strncmp(mediaString,"FDDI",4))
       headerSize = sizeof (FddiHeader) +
                    sizeof (Ieee802Dot2SnapHeader);
  else
  {
    printf ("Unsupported MAC type: `%s'\n", mediaString);
    return (0);
  }

  frameSize = MAC_CHAR (handle)->maxFrameSize;
  mac_addr  = MAC_CHAR (handle)->currentAddress;

  printf ("Hardware address: %02X:%02X:%02X:%02X:%02X:%02X\n",
          mac_addr[0], mac_addr[1], mac_addr[2],
          mac_addr[3], mac_addr[4], mac_addr[5]);
  return (1);
}

static int NdisStartMac (CardHandle *card)
{
  WORD result;

  /* Set the lookahead length
   */
  result = MAC_DISPATCH(handle)->request (common.moduleId, 0,
                                          headerSize, 0,
                                          REQ_SET_LOOKAHEAD,
                                          card->common->moduleDS);

  /* We assume that if we got INVALID PARAMETER then either this
   * is not supported or will work anyway. NE2000 does this.
   */
  if (result != ERR_SUCCESS && result != ERR_INVALID_PARAMETER)
  {
    DEBUG1 ("Set lookahead failed: %s\n", Ndis_strerror(result));
    return (0);
  }

  /* Set the packet filter. Note that for some medias and drivers we
   * must specify all three flags or the card(s) will not operate correctly.
   */
  result = MAC_DISPATCH(handle)->request (common.moduleId, 0,
                      /* all packets */   FILTER_PROMISCUOUS |
                      /* packets to us */ FILTER_DIRECTED    |
                      /* broadcasts */    FILTER_BROADCAST,
                                          0, REQ_SET_PACKET_FILTER,
                                          card->common->moduleDS);
  if (result != ERR_SUCCESS)
  {
    DEBUG1 ("Set packet filter failed: %s\n", Ndis_strerror(result));
    return (0);
  }

  /* If OPEN/CLOSE supported then open the adapter
   */
  if (MAC_CHAR(handle)->serviceFlags & SF_OPEN_CLOSE)
  {
    result = MAC_DISPATCH(handle)->request (common.moduleId, 0, 0, NULL,
                                            REQ_OPEN_ADAPTER,
                                            card->common->moduleDS);
    if (result != ERR_SUCCESS)
    {
      DEBUG1 ("Opening the MAC failed: %s\n", Ndis_strerror(result));
      return (0);
    }
  }
  return (1);
}

void NdisShutdown (void)
{
  struct _ReqBlock reqBlock;
  int     result, i;

  if (!handle)
     return;

  /* If the adapters support open and are open then close them
   */
  if ((MAC_CHAR(handle)->serviceFlags & SF_OPEN_CLOSE) &&
      (MAC_STATUS(handle)->macStatus & MAC_OPEN))
  {
    result = MAC_DISPATCH(handle)->request (common.moduleId, 0, 0, 0,
                                            REQ_CLOSE_ADAPTER,
                                            handle->common->moduleDS);
    if (result != ERR_SUCCESS)
    {
      printf ("Closing the MAC failed: %s\n", Ndis_strerror(result));
      return;
    }
  }

  /* Tell the Protocol Manager to unbind and stop
   */
  reqBlock.opcode   = PM_UNBIND_AND_STOP;
  reqBlock.pointer1 = (BYTE FAR*) &failingModules;
  reqBlock.pointer2 = NULL;

  result = (*protManEntry) (&reqBlock, protManDS);
  if (result)
     printf ("Unbind failed: %s\n",  Ndis_strerror(result));

  for (i = 0; i < STACK_POOL_SIZE; ++i)
     free (freeStacks[i] - STACK_SIZE);

  handle = NULL;
}

int NdisInit (int promis)
{
  int i, result;

  /* Allocate the real mode stacks used for NDIS callbacks
   */
  for (i = 0; i < STACK_POOL_SIZE; ++i)
  {
    freeStacks[i] = malloc (STACK_SIZE);
    if (!freeStacks[i])
       return (0);
    freeStacks[i] += STACK_SIZE;
  }

  if (!NdisOpen())
     return (0);

  if (!NdisRegisterAndBind(promis))
     return (0);

  DEBUG1 ("My module id: %d\n", common.moduleId);
  DEBUG1 ("Handle id;    %d\n", handle->common->moduleId);
  DEBUG1 ("MAC card:     %-16s - ", handle->moduleName);

  atexit (NdisShutdown);

  if (!CheckMacFeatures(&handle))
     return (0);

  switch (mediaType)
  {
    case MEDIA_FDDI:
         DEBUG0 ("Media type: FDDI");
	 break;
    case MEDIA_ETHERNET:
         DEBUG0 ("Media type: ETHERNET");
	 break;
    default:
         DEBUG0 ("Unsupported media.\n");
         return (0);
  }

  DEBUG1 (" - Frame size: %d\n", frameSize);

  if (!NdisStartMac(&handle))
     return (0);
  return (1);
}
#endif  /* USE_NDIS2 */

