#include <sys/types.h>
#include <stdio.h>
#include <go32.h>
#include <dos.h>
#include <dpmi.h>
#include <string.h>
#include <pc.h>

_go32_dpmi_seginfo dosmem;
int rmbuffer_full = 0;

int pd_test(int vector)
{
  char sig[9];
  unsigned short vec[2];
  unsigned long pkt_addr;
  dosmemget(vector*4, 4, vec);
  pkt_addr = vec[1] * 16 + vec[0];
  if (pkt_addr == 0)
    return 0;
  dosmemget(pkt_addr+3, 9, sig);
  if (strcmp(sig, "PKT DRVR"))
    return 0;
  return 1;
}

void pd_get_etheraddr(int v, char *buf, int len)
{
  _go32_dpmi_registers reg;
  memset(&reg, 0, sizeof(reg));
  reg.h.ah = 6;
  reg.x.bx = 0;
  reg.x.di = _go32_info_block.linear_address_of_transfer_buffer & 15;
  reg.x.es = _go32_info_block.linear_address_of_transfer_buffer >> 4;
  reg.x.cx = len;
  _go32_dpmi_simulate_int(v, &reg);
  dosmemget(_go32_info_block.linear_address_of_transfer_buffer, len, buf);
}

void pd_send(int v, void *packet, int length)
{
  _go32_dpmi_registers reg;
  memset(&reg, 0, sizeof(reg));
  reg.h.ah = 4;
  reg.x.si = _go32_info_block.linear_address_of_transfer_buffer & 15;
  reg.x.ds = _go32_info_block.linear_address_of_transfer_buffer >> 4;
  reg.x.cx = length;
  dosmemput(packet, length, _go32_info_block.linear_address_of_transfer_buffer);
  _go32_dpmi_simulate_int(v, &reg);
}

volatile int rmcb_happened = 0;
_go32_dpmi_registers rmcb_registers;

void rmcb(_go32_dpmi_registers *reg)
{
  if (reg->x.ax == 0)
  {
    if (rmcb_happened)
      reg->x.es = 0;
    else
      reg->x.es = dosmem.rm_segment;
    reg->x.di = 0;
  }
  else
  {
    rmcb_happened = reg->x.cx;
  }
}

int pd_read(char *buf)
{
  int len = rmcb_happened;
  while (!rmcb_happened && !kbhit());
  if (kbhit())
  {
    getkey();
    return 0;
  }
  dosmemget(dosmem.rm_segment*16, len, buf);
  rmcb_happened = 0;
  return len;
}

char test_arp[64] = {
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0, 0, 0, 0, 0, 0,
  0x08, 0x06,
  0, 1, 8, 0, 6, 4, 0, 1,
  0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,
};

void pd_do(int v)
{
  unsigned char ether[6];
  int i;
  _go32_dpmi_registers regs;
  _go32_dpmi_seginfo rmcb_seginfo;
  int handle;

  printf("Packet driver detected at vector 0x%02x\n", v);

  pd_get_etheraddr(v, ether, 6);
  printf("This machine's ethernet address is %02x:%02x:%02x:%02x:%02x:%02x\n",
    ether[0], ether[1], ether[2], ether[3], ether[4], ether[5]);

  rmcb_seginfo.pm_offset = (int)rmcb;
  if ((i=_go32_dpmi_allocate_real_mode_callback_retf(&rmcb_seginfo, &rmcb_registers)) != 0)
  {
    printf("Error: cannot allocate real mode callback, error=%04x\n", i);
    return;
  }
  printf("real mode callback is at %04x:%04x\n", rmcb_seginfo.rm_segment, rmcb_seginfo.rm_offset);

  memset(&regs, 0, sizeof(regs));  
  regs.x.es = rmcb_seginfo.rm_segment;
  regs.x.di = rmcb_seginfo.rm_offset;
  regs.h.ah = 2;
  regs.h.al = 1;
  regs.x.bx = 0xffff;
  regs.h.dl = 0;
  regs.x.cx = 0;
  _go32_dpmi_simulate_int(v, &regs);
  if (regs.x.flags & 1)
  {
    printf("Error: packet driver receive accept refused\n");
    return;
  }
  handle = regs.x.ax;
  printf("packet driver acceptor allocated, handle = %d\n", handle);

  memcpy(test_arp+6, ether, 6);
  memcpy(test_arp+22, ether, 6);
  pd_send(v, test_arp, 64);
  printf("ARP packet sent, waiting.  Press a key to abort.\n");
  
  while (!kbhit() && !rmcb_happened);
  if (kbhit())
  {
    printf("Keyboard interrupt\n");
    getkey();
  }
  if (rmcb_happened)
  {
    unsigned char pkt[1600];
    int len;
    printf("Callback happened\n");
    len = pd_read(pkt);
    rmcb_happened = 0;
    printf("Other machine's IP address is %02x:%02x:%02x:%02x:%02x:%02x\n",
      pkt[22], pkt[23], pkt[24], pkt[25], pkt[26], pkt[27]);
  }

  memset(&regs, 0, sizeof(regs));  
  regs.h.ah = 3;
  regs.x.bx = handle;
  _go32_dpmi_simulate_int(v, &regs);
  printf("packet driver acceptor released\n");
  
  _go32_dpmi_free_real_mode_callback(&rmcb_seginfo);
  printf("real mode callback released\n");
}

void get_ip_address(char *msg, unsigned char *buffer)
{
  int i[4], k;
  while (1)
  {
    int invalid = 0;
    i[3] = -1;
    fputs(msg, stdout);
    fflush(stdout);
    scanf("%d.%d.%d.%d", i+0, i+1, i+2, i+3);
    for (k=0; k<4; k++)
    {
      if (i[k] < 0 || i[k] > 255)
        invalid = 1;
      else
        buffer[k] = i[k];
    }
    if (!invalid)
      return;
    printf("Invalid IP address.  Expected something like 128.0.0.7\n");
  }
}

int main()
{
  int v;

  printf("Packet Driver test - arp\n");
  get_ip_address("Enter this machine's IP address  : ", test_arp+28);
  get_ip_address("Enter other machine's IP address : ", test_arp+38);

  printf("go32 segments: cs=%04x ds=%04x ss=%04x\n", _go32_my_cs(), _go32_my_ds(), _go32_my_ss());

  dosmem.size = 100;
  if (_go32_dpmi_allocate_dos_memory(&dosmem))
  {
    printf("Unable to allocate dos memory - max size is %d\n", dosmem.size);
    exit(1);
  }
  printf("dos buffer at 0x%04x:0\n", dosmem.rm_segment);


  for (v=0x60; v<0x80; v++)
  {
    if (pd_test(v))
      pd_do(v);
  }

  _go32_dpmi_free_dos_memory(&dosmem);
  printf("dos buffer released\n");

  return 0;
}
