Goto sanos source index

//
// ndis.c
//
// NDIS network miniport driver interface
//
// Copyright (C) 2002 Michael Ringgaard. 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. Neither the name of the project 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
// 

#include <os/krnl.h>
#include "ndis.h"

#define NDISTRACE(s) kprintf("ndis: %s called\n", s);
#define ndisapi __declspec(dllexport) __stdcall

struct ndis_driver *drivers = NULL;

//
// Configuration functions
//

static struct ndis_config *open_config(struct section *sect) {
  struct ndis_config *cfg;

  cfg = kmalloc(sizeof(struct ndis_config));
  if (!cfg) return NULL;
  cfg->sect = sect;
  cfg->propcache = NULL;

  return cfg;
}

static void free_config(struct ndis_config *cfg) {
  struct ndis_property *prop;
  struct ndis_property *next;

  if (!cfg) return;
  prop = cfg->propcache;
  while (prop) {
    next = prop->next;

    switch (prop->value.parameter_type) {
      case NDIS_PARAMETER_INTEGER:
      case NDIS_PARAMETER_HEXINTEGER:
        break;

      case NDIS_PARAMETER_STRING:
        kfree(prop->value.parameter_data.string_data.buffer);
        break;

      case NDIS_PARAMETER_MULTISTRING:
        panic("unsupported ndis property type");
        break;

      case NDIS_PARAMETER_BINARY:
        kfree(prop->value.parameter_data.binary_data.buffer);
        break;
    }

    kfree(prop);
    prop = next;
  }

  kfree(cfg);
}

static int hexdigit(int digit) {
  if (digit >= '0' && digit <= '9') return digit - '0';
  if (digit >= 'A' && digit <= 'F') return digit - 'A';
  if (digit >= 'a' && digit <= 'f') return digit - 'a';
  return 0;
}

static struct ndis_configuration_parameter *read_config(struct ndis_config *cfg, char *name, enum ndis_parameter_type type) {
  struct ndis_property *np;
  struct property *prop;
  char *value;
  wchar_t *buf;
  unsigned char *data;
  int len;
  int i;

  // Fail if no config or no config section
  if (!cfg) return NULL;
  if (!cfg->sect) return NULL;

  // Try to find property in the property cache
  np = cfg->propcache;
  while (np) {
    if (stricmp(np->name, name) == 0) {
      if (np->value.parameter_type != type) {
        kprintf("ndis: wrong type for parameter %s\n", name);
        return NULL;
      }

      kprintf("ndis: cached parameter %s\n", name);
      return &np->value;
    }
    np = np->next;
  }

  // Try to find the property in the configuration section
  prop = cfg->sect->properties;
  while (prop) {
    if (stricmp(prop->name, name) == 0) break;
    prop = prop->next;
  }

  if (!prop) {
    kprintf("ndis: parameter %s not found\n", name);
    return NULL;
  }

  if (prop->value) {
    value = prop->value;
  } else {
    value = "";
  }

  // Add property to property cache
  np = kmalloc(sizeof(struct ndis_property));
  if (!np) return NULL;
  memset(np, 0, sizeof(struct ndis_property));

  np->name = prop->name;
  np->value.parameter_type = type;

  switch (np->value.parameter_type) {
    case NDIS_PARAMETER_INTEGER:
      np->value.parameter_data.integer_data = strtoul(value, NULL, 0);
      break;

    case NDIS_PARAMETER_HEXINTEGER:
      np->value.parameter_data.integer_data = strtoul(value, NULL, 16);
      break;

    case NDIS_PARAMETER_STRING:
      len = strlen(value);
      buf = kmalloc(sizeof(wchar_t) * (len + 1));
      if (!buf) return NULL;
      for (i = 0; i < len; i++) buf[i] = value[i];
      buf[len] = 0;
      np->value.parameter_data.string_data.buffer = buf;
      np->value.parameter_data.string_data.length = len * sizeof(wchar_t);
      np->value.parameter_data.string_data.maxlength = (len + 1) * sizeof(wchar_t);
      break;

    case NDIS_PARAMETER_MULTISTRING:
      kprintf("ndis: '%s', multisz properties not supported\n", name);
      break;

    case NDIS_PARAMETER_BINARY:
      len = strlen(value) / 2;
      data = kmalloc(len);
      if (!data) return NULL;
      for (i = 0; i < len; i++) data[i] = hexdigit(value[i * 2]) + (hexdigit(value[i * 2 + 1]) << 4);
      np->value.parameter_data.binary_data.buffer = data;
      np->value.parameter_data.binary_data.length = len;
      break;
  }

  np->next = cfg->propcache;
  cfg->propcache = np;

  return &np->value;
}

static struct section *get_config_section(struct ndis_adapter *adapter) {
  char buf[MAXPATH];
  char *cfgname;
  char *p;
  char *lastdot;
  struct section *sect;

  if (getmodpath(adapter->driver->hmod, buf, sizeof buf) < 0) return NULL;
  cfgname = buf;
  lastdot = NULL;
  
  p = buf;
  while (*p) {
    if (*p == PS1 || *p == PS2) cfgname = p + 1;
    if (*p == '.') lastdot = p;
    p++;
  }
  if (lastdot > cfgname) *lastdot = 0;
  
  p = cfgname + strlen(cfgname);
  *p++ = '-';
  *p++ = '0' + adapter->adapterno;
  *p = 0;

  sect = krnlcfg;
  while (sect) {
    if (stricmp(sect->name, cfgname) == 0) return sect;
    sect = sect->next;
  }

  return NULL;
}

//
// NTOSKRNL functions
//

boolean ndisapi RtlEqualUnicodeString(const struct ndis_string *string1, const struct ndis_string *string2, boolean case_insensitive) {
  wchar_t *s1, *s2;
  wchar_t f, l;
  unsigned int n;

  NDISTRACE("RtlEqualUnicodeString");
  
  {
    char buf[256];
    wchar_t *p;
    char *q;

    p = string1->buffer;
    q = buf;
    while (*p) *q++ = (char) *p++;
    *q = 0;

    kprintf("string1='%s'\n", buf);

    p = string2->buffer;
    q = buf;
    while (*p) *q++ = (char) *p++;
    *q = 0;

    kprintf("string2='%s'\n", buf);
  }

  kprintf("len=%d %d\n", string1->length, string2->length);
  if (string1->length != string2->length) return 0;

  s1 = string1->buffer;
  s2 = string2->buffer;
  for (n = 0; n < string1->length / sizeof(wchar_t); n++) {
    if (case_insensitive) {
      f = ((*s1 <= 'Z') && (*s1 >= 'A')) ? *s1 + 'a' - 'A' : *s1;
      l = ((*s2 <= 'Z') && (*s2 >= 'A')) ? *s2 + 'a' - 'A' : *s2;
    } else {
      f = *s1;
      l = *s2;
    }

    if (f != l) return 0;
    s1++;
    s2++;
  }

  kprintf("match\n");
  return 1;
}

unsigned long __cdecl DbgPrint(char *format, ...) {
  va_list args;
  char buffer[1024];
  int len;

  va_start(args, format);
  len = vsprintf(buffer, format, args);
  va_end(args);
    
  kprintf("ndis: %s\n", buffer);
  return len;
}

//
// Port I/O (from HAL.DLL)
//

unsigned char ndisapi READ_PORT_UCHAR(unsigned char *port) {
  //NDISTRACE("READ_PORT_UCHAR");
  kprintf("ndis: inp(0x%x)\n", port);
  return inp((unsigned short) port);
}

void ndisapi WRITE_PORT_USHORT(unsigned short *port, unsigned short value) {
  //NDISTRACE("WRITE_PORT_USHORT");
  kprintf("ndis: outpw(0x%x,0x%x)\n", port, value);
  outpw((unsigned short) port, value);
}

unsigned short ndisapi READ_PORT_USHORT(unsigned short *port) {
  //NDISTRACE("READ_PORT_USHORT");
  kprintf("ndis: inp(0x%x)\n", port);
  return inpw((unsigned short) port);
}

void ndisapi KeStallExecutionProcessor(unsigned long microseconds) {
  NDISTRACE("KeStallExecutionProcessor");
  udelay(microseconds);
}

//
// NDIS Initialization and Registration Functions
//

void ndisapi NdisInitializeWrapper(ndis_handle_t *ndis_wrapper_handle, void *system_specific1, void *system_specific2, void *system_specific3) {
  struct ndis_driver *driver = (struct ndis_driver *) system_specific1;
  NDISTRACE("NdisInitializeWrapper");
  *ndis_wrapper_handle = driver;
}

void ndisapi NdisTerminateWrapper(ndis_handle_t ndis_wrapper_handle, void *system_specific) {
  NDISTRACE("NdisTerminateWrapper");
}

ndis_status ndisapi NdisMRegisterMiniport(ndis_handle_t ndis_wrapper_handle, struct ndis_miniport_characteristics *miniport_characteristics, unsigned int characteristics_length) {
  struct ndis_driver *driver = (struct ndis_driver *) ndis_wrapper_handle;
  int len;

  NDISTRACE("NdisMRegisterMiniport");
  len = characteristics_length;
  if (len > sizeof(struct ndis_miniport_characteristics)) len = sizeof(struct ndis_miniport_characteristics);
  memcpy(&driver->handlers, miniport_characteristics, len);

  return 0;
}

void ndisapi NdisMSetAttributesEx(
    ndis_handle_t miniport_adapter_handle,
    ndis_handle_t miniport_adapter_context,
    unsigned int hang_check_interval,
    unsigned long attribute_flags,
    enum ndis_interface_type adapter_type) {
  NDISTRACE("NdisMSetAttributesEx");
}

void ndisapi NdisOpenConfiguration(ndis_status *status, ndis_handle_t *configuration_handle, ndis_handle_t wrapper_configuration_context) {
  struct ndis_adapter *adapter = (struct ndis_adapter *) wrapper_configuration_context;
  struct section *sect;
  struct ndis_config *cfg;

  NDISTRACE("NdisOpenConfiguration");

  sect = get_config_section(adapter);
  cfg = open_config(sect);
  if (!cfg) {
    *status = NDIS_STATUS_FAILURE;
    return;
  }

  *configuration_handle = cfg;
  *status = 0;
}

void ndisapi NdisCloseConfiguration(ndis_handle_t configuration_handle) {
  struct ndis_config *cfg = (struct ndis_config *) configuration_handle;

  NDISTRACE("NdisCloseConfiguration");
  free_config(cfg);
}

void ndisapi NdisReadConfiguration(
    ndis_status *status,
    struct ndis_configuration_parameter **parameter_value,
    ndis_handle_t configuration_handle,
    struct ndis_string *keyword,
    enum ndis_parameter_type parameter_type) {
  struct ndis_config *cfg = (struct ndis_config *) configuration_handle;
  char buf[256];
  int len;
  int i;

  //NDISTRACE("NdisReadConfiguration");

  // Parameter names are restricted to max 255 chars
  len = keyword->length / sizeof(wchar_t);
  if (len > sizeof(buf) - 1) {
    kprintf("ndis: parameter name too long\n");
    *status = NDIS_STATUS_RESOURCES;
    return;
  }

  // Convert parameters name from unicode to ansi
  for (i = 0; i < len; i++) buf[i] = (char) keyword->buffer[i];
  buf[len] = 0;

  kprintf("ndis: read config %s len %d type %d\n", buf, len, parameter_type);

  // Read property from configuration
  *parameter_value = read_config(cfg, buf, parameter_type);
  if (!*parameter_value) {
    *status = NDIS_STATUS_FAILURE;
    return;
  }

  kprintf("ndis: value: %d\n", (*parameter_value)->parameter_data.integer_data);

  *status = 0;
}

void ndisapi NdisReadNetworkAddress(
    ndis_status *status,
    void **network_address,
    unsigned int *network_address_length,
    ndis_handle_t configuration_handle) {
  struct ndis_config *cfg = (struct ndis_config *) configuration_handle;
  struct ndis_configuration_parameter *param;
  wchar_t *p;
  unsigned char *q;
  int addrlen;

  NDISTRACE("NdisReadNetworkAddress");

  param = read_config(cfg, "NetworkAddress", NDIS_PARAMETER_STRING);
  if (!param) {
    *status = NDIS_STATUS_FAILURE;
    return;
  }

  p = param->parameter_data.string_data.buffer;
  q = cfg->hwaddr.addr;
  addrlen = 0;
  while (*p) {
    if (addrlen > ETHER_ADDR_LEN) break;
    if (*p == '-') *p++;
    if (*p)  {
      *q = hexdigit(*p++);
      if (*p) *q = (*q << 4) | hexdigit(*p++);
      q++;
      addrlen++;
    }
  }

 *network_address = &cfg->hwaddr;
 *network_address_length = addrlen;
 *status = 0;
}

void ndisapi NdisMRegisterAdapterShutdownHandler(
    ndis_handle_t miniport_handle,
    void *shutdown_context,
    adapter_shutdown_handler shutdown_handler) {
  NDISTRACE("NdisMRegisterAdapterShutdownHandler");
}

void ndisapi NdisMDeregisterAdapterShutdownHandler(ndis_handle_t miniport_handle) {
  NDISTRACE("NdisMDeregisterAdapterShutdownHandler");
}

void ndisapi NdisMQueryAdapterResources(
    ndis_status *status,
    ndis_handle_t wrapper_configuration_context,
    struct ndis_resource_list *resource_list,
    unsigned int *buffer_size) {
  struct ndis_adapter *adapter = (struct ndis_adapter *) wrapper_configuration_context;
  struct resource *res;
  struct ndis_resource_descriptor *desc;
  unsigned int buflen;
  int resno;

  NDISTRACE("NdisMQueryAdapterResources");

  buflen = sizeof(struct ndis_resource_list);
  resno = 0;

  if (*buffer_size < buflen) {
    *status = NDIS_STATUS_RESOURCES;
    return;
  }

  resource_list->version = 1;
  resource_list->revision = 1;
  
  res = adapter->unit->resources;
  desc = resource_list->descriptors;
  while (res) {
    buflen += sizeof(struct ndis_resource_descriptor);
    if (*buffer_size < buflen) {
      *status = NDIS_STATUS_RESOURCES;
      return;
    }

    switch (res->type) {
      case RESOURCE_IO:
        desc->type = NDIS_RESOURCE_TYPE_PORT;
        desc->flags = NDIS_RESOURCE_PORT_IO;
        desc->u.port.start = res->start;
        desc->u.port.length = res->len;
        break;

      case RESOURCE_MEM:
        desc->type = NDIS_RESOURCE_TYPE_MEMORY;
        desc->flags = 0;
        desc->u.memory.start = res->start;
        desc->u.memory.length = res->len;
        break;

      case RESOURCE_IRQ:
        desc->type = NDIS_RESOURCE_TYPE_INTERRUPT;
        desc->flags = 0;
        desc->u.interrupt.level = res->start;
        desc->u.interrupt.vector = res->start;
        desc->u.interrupt.affinity = -1;
        break;

      case RESOURCE_DMA:
        desc->type = NDIS_RESOURCE_TYPE_DMA;
        desc->flags = 0;
        desc->u.dma.channel = res->start;
        desc->u.dma.port = 0;
        desc->u.dma.reserved1 = 0;
        break;
    }

    resource_list->descriptors[resno].share_disposition = NDIS_RESOURCE_SHARE_UNDETERMINED;

    resno++;
    desc++;
    res = res->next;
  }
  
  // TEST
  //buflen += sizeof(struct ndis_resource_descriptor);
  //desc->type = RESOURCE_DMA;
  //desc->u.dma.channel = 0x0F;
  //resno++;

  kprintf("ndis: returned %d resource descriptors\n", resno);

  resource_list->count = resno;
  *buffer_size = buflen;
  *status = 0;
}

//
// NDIS Hardware Configuration Functions
//

unsigned long ndisapi NdisReadPciSlotInformation(
    ndis_handle_t ndis_adapter_handle, 
    unsigned long slot_number, 
    unsigned long offset, 
    void *buffer, 
    unsigned long length) {
  struct ndis_adapter *adapter = (struct ndis_adapter *) ndis_adapter_handle;

  NDISTRACE("NdisReadPciSlotInformation");
  kprintf("ndis: read pci slot %d offset %d length %d\n", slot_number, offset, length);

  pci_read_buffer(adapter->unit, offset, buffer, length);

  return 0;
}

unsigned long ndisapi NdisWritePciSlotInformation(
    ndis_handle_t ndis_adapter_handle, 
    unsigned long slot_number, 
    unsigned long offset, 
    void *buffer, 
    unsigned long length) {
  struct ndis_adapter *adapter = (struct ndis_adapter *) ndis_adapter_handle;

  NDISTRACE("NdisWritePciSlotInformation");
  kprintf("ndis: write pci slot %d offset %d length %d\n", slot_number, offset, length);

  pci_write_buffer(adapter->unit, offset, buffer, length);

  return 0;
}

//
// NDIS I/O Port Functions
//

ndis_status ndisapi NdisMRegisterIoPortRange(
    void **port_offset,
    ndis_handle_t miniport_adapter_handle,
    unsigned int initial_port,
    unsigned int number_of_ports) {
  NDISTRACE("NdisMRegisterIoPortRange");
  kprintf("ndis: register io ports start=0x%x len=%d\n", initial_port, number_of_ports);
  *port_offset = (void *) initial_port;
  return 0;
}

void ndisapi NdisMDeregisterIoPortRange(
    ndis_handle_t miniport_adapter_handle,
    unsigned int initial_port,
    unsigned int number_of_ports,
    void *port_offset) {
  NDISTRACE("NdisMDeregisterIoPortRange");
}

//
// NDIS DMA-Related Functions
//

void ndisapi NdisMAllocateSharedMemory(
    ndis_handle_t miniport_adapter_handle,
    unsigned long length,
    boolean cached,
    void **virtual_address,
    ndis_physical_address_t *physical_address) {
  NDISTRACE("NdisMAllocateSharedMemory");
}

void ndisapi NdisMFreeSharedMemory(
    ndis_handle_t miniport_adapter_handle,
    unsigned long length,
    boolean cached,
    void *virtual_address,
    ndis_physical_address_t physical_address) {
  NDISTRACE("NdisMFreeSharedMemory");
}

void ndisapi NdisMAllocateMapRegisters(
    ndis_handle_t miniport_adapter_handle,
    unsigned int dma_channel,
    ndis_dma_size_t dma_size,
    unsigned long base_map_registers_needed,
    unsigned long maximum_physical_mapping) {
  NDISTRACE("NdisMAllocateMapRegisters");
}

void ndisapi NdisMFreeMapRegisters(ndis_handle_t miniport_adapter_handle) {
  NDISTRACE("NdisMFreeMapRegisters");
}

//
// NDIS Interrupt Handling Functions
//

ndis_status ndisapi NdisMRegisterInterrupt(
    struct ndis_miniport_interrupt *interrupt,
    ndis_handle_t miniport_adapter_handle,
    unsigned int interrupt_vector,
    unsigned int interrupt_level,
    boolean request_isr,
    boolean shared_interrupt,
    enum ndis_interrupt_mode interrupt_mode) {
  NDISTRACE("NdisMRegisterInterrupt");
  return 0;
}

void ndisapi NdisMDeregisterInterrupt(struct ndis_miniport_interrupt *interrupt) {
  NDISTRACE("NdisMDeregisterInterrupt");
}

boolean ndisapi NdisMSynchronizeWithInterrupt(
    struct ndis_miniport_interrupt *interrupt,
    void *synchronize_function,
    void *synchronize_context) {
  NDISTRACE("NdisMSynchronizeWithInterrupt");
  return 0;
}

//
// NDIS Synchronization Functions
//

void ndisapi NdisMInitializeTimer(struct ndis_miniport_timer *timer, ndis_handle_t miniport_adapter_handle, ndis_timer_func_t  timer_function, void *function_context) {
  NDISTRACE("NdisMInitializeTimer");
}

void ndisapi NdisMCancelTimer(struct ndis_miniport_timer *timer, boolean *timer_cancelled) {
  NDISTRACE("NdisMCancelTimer");
}

void ndisapi NdisMSetPeriodicTimer(struct ndis_miniport_timer *timer, unsigned int millisecond_period) {
  NDISTRACE("NdisMSetPeriodicTimer");
}

void ndisapi NdisAllocateSpinLock(struct ndis_spin_lock *spin_lock) {
  NDISTRACE("NdisAllocateSpinLock");
}

void ndisapi NdisFreeSpinLock(struct ndis_spin_lock *spin_lock) {
  NDISTRACE("NdisFreeSpinLock");
}

void ndisapi NdisAcquireSpinLock(struct ndis_spin_lock *spin_lock) {
  NDISTRACE("NdisAcquireSpinLock");
}

void ndisapi NdisReleaseSpinLock(struct ndis_spin_lock *spin_lock) {
  NDISTRACE("NdisReleaseSpinLock");
}

//
// NDIS Query and Set Completion Functions
//

void __stdcall NdisMQueryInformationComplete(
    ndis_handle_t miniport_adapter_handle,
    ndis_status status) {
  NDISTRACE("NdisMQueryInformationComplete");
}

void __stdcall NdisMSetInformationComplete(
    ndis_handle_t miniport_adapter_handle,
    ndis_status status) {
  NDISTRACE("NdisMSetInformationComplete");
}

//
// NDIS Status and Reset Indication Functions
//

void __stdcall NdisMIndicateStatus(
    ndis_handle_t miniport_handle,
    ndis_status general_status,
    void *status_buffer,
    unsigned int status_buffer_size) {
  NDISTRACE("NdisMIndicateStatus");
}


void __stdcall NdisMIndicateStatusComplete(ndis_handle_t miniport_adapter_handle) {
  NDISTRACE("NdisMIndicateStatusComplete");
}

void __stdcall NdisMResetComplete(ndis_handle_t miniport_adapter_handle, ndis_status status, boolean addressing_reset) {
  NDISTRACE("NdisMResetComplete");
}

//
// NDIS Send and Receive Functions for Connectionless Miniport Drivers
//

void __stdcall NdisMIndicateReceivePacket(
    ndis_handle_t miniport,
    struct ndis_packet **packet_array,
    unsigned int number_of_packets) {
  NDISTRACE("NdisMIndicateReceivePacket");
}

void __stdcall NdisMEthIndicateReceive(
    void *filter,
    ndis_handle_t mac_receive_context,
    char *address,
    void *header_buffer,
    unsigned int header_buffer_size,
    void *lookahead_buffer,
    unsigned int lookahead_buffer_size,
    unsigned int packet_size) {
  NDISTRACE("NdisMEthIndicateReceive");
}

void __stdcall NdisMTrIndicateReceive(
    void *filter,
    ndis_handle_t mac_receive_context,
    void *header_buffer,
    unsigned int header_buffer_size,
    void *lookahead_buffer,
    unsigned int lookahead_buffer_size,
    unsigned int packet_size) {
  NDISTRACE("NdisMTrIndicateReceive");
}

void __stdcall NdisMFddiIndicateReceive(
    void *filter,
    ndis_handle_t mac_receive_context,
    char *address,
    unsigned int address_length,
    void *header_buffer,
    unsigned int header_buffer_size,
    void *lookahead_buffer,
    unsigned int lookahead_buffer_size,
    unsigned int packet_size) {
  NDISTRACE("NdisMFddiIndicateReceive");
}

void __stdcall NdisMEthIndicateReceiveComplete(void *filter) {
  NDISTRACE("NdisMEthIndicateReceiveComplete");
}

void __stdcall NdisMTrIndicateReceiveComplete(void *filter) {
  NDISTRACE("NdisMTrIndicateReceiveComplete");
}

void __stdcall NdisMFddiIndicateReceiveComplete(void *filter) {
  NDISTRACE("NdisMFddiIndicateReceiveComplete");
}

void __stdcall NdisMSendComplete(ndis_handle_t miniport_adapter_handle, struct ndis_packet *packet, ndis_status status) {
  NDISTRACE("NdisMSendComplete");
}

void __stdcall NdisMSendResourcesAvailable(ndis_handle_t miniport_adapter_handle) {
  NDISTRACE("NdisMSendResourcesAvailable");
}

void __stdcall NdisMTransferDataComplete(
    ndis_handle_t miniport_adapter_handle,
    struct ndis_packet *packet,
    ndis_status status,
    unsigned int bytes_transferred) {
  NDISTRACE("NdisMTransferDataComplete");
}

//
// NDIS Send and Receive Functions for WAN Miniport Drivers
//

void __stdcall NdisMWanSendComplete(
    ndis_handle_t miniport_adapter_handle,
   void *packet,
    ndis_status status) {
  NDISTRACE("NdisMWanSendComplete");
}

void __stdcall NdisMWanIndicateReceive(
    ndis_status *status,
    ndis_handle_t miniport_adapter_handle,
    ndis_handle_t ndis_link_context,
    unsigned char *packet,
    unsigned long packet_size) {
  NDISTRACE("NdisMWanIndicateReceive");
}

void __stdcall NdisMWanIndicateReceiveComplete(
    ndis_handle_t miniport_adapter_handle,
    ndis_handle_t ndis_link_context) {
  NDISTRACE("NdisMWanIndicateReceiveComplete");
}

//
// NDIS Send and Receive Functions for Connection-Oriented Miniport Drivers
//

//
// NDIS Packet and Buffer Handling Functions
//

void ndisapi NdisAllocatePacketPool(
    ndis_status *status, 
    ndis_handle_t *pool_handle, 
    unsigned int number_of_descriptors, 
    unsigned int protocol_reserved_length) {
  NDISTRACE("NdisAllocatePacketPool");
}

void ndisapi NdisFreePacketPool(ndis_handle_t pool_handle) {
  NDISTRACE("NdisFreePacketPool");
}

void ndisapi NdisAllocatePacket(ndis_status *status, struct ndis_packet **packet, ndis_handle_t pool_handle) {
  NDISTRACE("NdisAllocatePacket");
}

void ndisapi NdisFreePacket(struct ndis_packet *packet) {
  NDISTRACE("NdisFreePacket");
}

void ndisapi NdisAllocateBufferPool(
    ndis_status *status, 
    ndis_handle_t *pool_handle, 
    unsigned int number_of_descriptors) {
  NDISTRACE("NdisAllocateBufferPool");
}

void ndisapi NdisFreeBufferPool(ndis_handle_t pool_handle) {
  NDISTRACE("NdisFreeBufferPool");
}

void ndisapi NdisAllocateBuffer(
    ndis_status *status,
    struct ndis_buffer **Buffer,
    ndis_handle_t pool_handle,
    void *virtual_address,
    unsigned int length) {
  NDISTRACE("NdisAllocateBuffer");
}

void ndisapi NdisFreeBuffer(struct ndis_buffer *buffer) {
  NDISTRACE("NdisFreeBuffer");
}

void ndisapi NdisQueryBuffer(struct ndis_buffer *buffer, void **virtual_address,  unsigned int *length) {
  NDISTRACE("NdisQueryBuffer");
}

void ndisapi NdisAdjustBufferLength(struct ndis_buffer *buffer, unsigned int length) {
  NDISTRACE("NdisAdjustBufferLength");
}

void ndisapi NdisQueryBufferOffset(struct ndis_buffer *buffer,  unsigned int *offset, unsigned int *length) {
  NDISTRACE("NdisQueryBufferOffset");
}

void ndisapi NdisUnchainBufferAtFront(struct ndis_packet *packet, struct ndis_buffer *buffer) {
  NDISTRACE("NdisUnchainBufferAtFront");
}

unsigned long ndisapi NDIS_BUFFER_TO_SPAN_PAGES(struct ndis_buffer *buffer) {
  NDISTRACE("NDIS_BUFFER_TO_SPAN_PAGES");
  return 0;
}

//
// NDIS Memory Support Functions
//

ndis_status ndisapi NdisAllocateMemoryWithTag(void **virtual_address, unsigned int length, unsigned long tag) {
  void *addr;

  NDISTRACE("NdisAllocateMemoryWithTag");
  
  addr = kmalloc(length);
  if (!addr) return NDIS_STATUS_FAILURE;

  *virtual_address = addr;  
  return 0;
}

void ndisapi NdisFreeMemory(void *virtual_address, unsigned int length, unsigned int memory_flags) {
  NDISTRACE("NdisFreeMemory");

  kfree(virtual_address);
}

//
// NDIS Logging Support Functions
//

void ndisapi NdisWriteErrorLogEntry(ndis_handle_t ndis_adapter_handle, unsigned long error_code, unsigned long errvals, ...) {
  NDISTRACE("NdisWriteErrorLogEntry");
  kprintf("ndis: error code 0x%x %d values\n", error_code, errvals);
}

//
// Setup callbacks
//

static void ndis_setup_callbacks(struct ndis_miniport_block *nmpb) {
  nmpb->packet_indicate_handler = NdisMIndicateReceivePacket;
  nmpb->send_complete_handler = NdisMSendComplete;
  nmpb->send_resources_handler = NdisMSendResourcesAvailable;
  nmpb->reset_complete_handler = NdisMResetComplete;

  nmpb->eth_rx_indicate_handler = NdisMEthIndicateReceive;
  nmpb->tr_rx_indicate_handler = NdisMTrIndicateReceive;
  nmpb->fddi_rx_indicate_handler = NdisMFddiIndicateReceive;

  nmpb->eth_rx_complete_handler = NdisMEthIndicateReceiveComplete;
  nmpb->tr_rx_complete_handler = NdisMTrIndicateReceiveComplete;
  nmpb->fddi_rx_complete_handler = NdisMFddiIndicateReceiveComplete;

  nmpb->status_handler = NdisMIndicateStatus;
  nmpb->status_complete_handler = NdisMIndicateStatusComplete;
  nmpb->td_complete_handler = NdisMTransferDataComplete;
  nmpb->query_complete_handler = NdisMQueryInformationComplete;
  nmpb->set_complete_handler = NdisMSetInformationComplete;

  nmpb->wan_send_complete_handler = NdisMWanSendComplete;
  nmpb->wan_rcv_handler = NdisMWanIndicateReceive;
  nmpb->wan_rcv_complete_handler = NdisMWanIndicateReceiveComplete;
}

//
// Module initialization
//

int __declspec(dllexport) install(struct unit *unit, char *opts) {
  char *modfn = opts;
  struct ndis_driver *driver;
  struct ndis_adapter *adapter;
  hmodule_t hmod;
  int rc;
  ndis_status open_status;
  ndis_status status;
  enum ndis_medium media;
  unsigned int selected_index;

  kprintf("ndis: loading driver %s for unit %08X\n", opts, unit->unitcode);

  // Try to find existing driver
  driver = NULL;
  hmod = getmodule(modfn);
  if (hmod) {
    driver = drivers;
    while (driver) {
      if (driver->hmod == hmod) break;
      driver = driver->next;
    }
  }

  // If driver not loaded, load it now and initialize
  if (!driver) {
    int (__stdcall *entry)(struct ndis_driver *driver, void *reserved);

    hmod = load(modfn, MODLOAD_NOINIT);
    if (!hmod) return -ENOEXEC;

    entry = getentrypoint(hmod);
    if (!entry) return -ENOEXEC;

    driver = kmalloc(sizeof(struct ndis_driver));
    if (!driver) return -ENOMEM;
    memset(driver, 0, sizeof(struct ndis_driver));

    rc = entry(driver, NULL);
    if (rc < 0) {
      kprintf("ndis: driver initialization failed with error code %08X\n", rc);
      kfree(driver);
      unload(hmod);
      return -ENXIO;
    }

    driver->hmod = hmod;
    driver->next = drivers;
    drivers = driver;
  }

  // Initialize new adapter
  adapter = kmalloc(sizeof(struct ndis_adapter));
  if (!adapter) return -ENOMEM;
  memset(adapter, 0, sizeof(struct ndis_adapter));
  ndis_setup_callbacks(&adapter->callbacks);
  adapter->unit = unit;
  adapter->adapterno = ++driver->numadapters;
  adapter->driver = driver;
  adapter->next = driver->adapters;
  driver->adapters = adapter;
  
  // Initialize adapter
  open_status = 0;
  selected_index = 0;
  media = NDIS_MEDIUM_802_3;
  status = adapter->driver->handlers.initialize_handler(&open_status, &selected_index, &media, 1, adapter, adapter);
  kprintf("ndis: initialize returned %08X\n", status);
  
  return 0;
}

int __stdcall start(hmodule_t hmod, int reason, void *reserved2) {
  kprintf("ndis: loaded\n");
  return 1;
}