/* pkt_test.c - a minimal test program for the pkt driver interafce */

#include <dos.h>
#include <conio.h>
#include <alloc.h>
#include <ctype.h>
#include <errno.h>

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>

#include "..\snmplib\ausnmp.h"
#include "..\wattcp\wattcp.h"
#include "..\types\types.h"
#include "..\wattcp\pktdrv.h"

#define PKT_DEBUG     0
#define TEST_IP       1
#define PKT_TRACE     1
#define TIME_TEST     1

#pragma inline

static void swrite(int c)  /* Write char to screen */
{
   _AH = 14;  _AL = c;
   geninterrupt(0x10);
}

void scpos(int x, int y)  /* Set Cursor Position */
{ }
void w_roll(int tx,int ty, int bx,int by, int n)
{ }

struct pkt {
   unsigned int p_len;
   unsigned int p_lah_len, p_int;
   unsigned char hdr[14];
   };

/* Declared in wattcp\src\pcpkt.c .. */

extern unsigned char eth_addr[6];
extern unsigned long npackets,nbytes;

#define MAXPKTLEN  1526

struct pkt far *pkts;

unsigned short
   maxpkt,  /* Must be a power of 2 */
   pktmask,   /* maxpkt-1 */
   prcv,   /* Index of next pkt to be received (i.e. next empty pkt) */
   pproc;  /* Index of next pkt to be processed (i.e. next full pkt) */

udp_Socket udpsock;
static udp_Socket *s;
int status;  /* For socket */
word _pktdevclass = 1;      /* Ethernet = 1, SLIP = 6 */

/* forward declarations */
void accept_pkt(unsigned int len, unsigned char far *buff, 
   unsigned int i_face, unsigned int lah_len);
int our_packet(unsigned char far *buff);

void receive(void);
void snmp_read(int length);

unsigned long tot;

void accept_pkt(unsigned int len, unsigned char far *buff, 
   unsigned int i_face, unsigned int lah_len)
{
   struct pkt huge *pp;
   int j;
#if PKT_DEBUG
   swrite(':');
#endif
   if (((pproc - prcv) & pktmask) == 1) {  /* No pkts free, ignore the data */
      }
   else {
      pp = &pkts[prcv];
      pp->p_len = len;  pp->p_int = i_face;  pp->p_lah_len = lah_len;
      for (j = 0; j != 14; ++j) pp->hdr[j] = buff[j];
      prcv = (prcv+1) & pktmask;
      }
   }

#define SNAPSIZE  54  /* Matches meter_ux.c */

int our_packet(unsigned char far *buff)
{
   if (buff[0] == 0xFF) {  /* Broadcast */
      if (buff[13] == 0x06 && buff[12] == 0x08)  /* ARP */
	 return 1;
      }
   else if (buff[5] == eth_addr[5] && buff[4] == eth_addr[4] &&
	 buff[3] == eth_addr[3] && buff[2] == eth_addr[2] &&
	 buff[1] == eth_addr[1] && buff[0] == eth_addr[0]) {
      if (buff[12] == 0x08 &&
	    (buff[13] == 0x00 || buff[13] == 0x06 )) {  /* IP or ARP */
	 return 1;
	 }
      }
   return 0;  /* Only want our IP & ARP pkts */
   }

#if PKT_DEBUG
Bit8 *last_buf, *buf;
#endif

Bit8 *pkt_rcv_call1(Bit16 len, Bit16 lah_len, Bit8 *buff, Bit16 if_nbr)
{
   int if_type = en_table[if_nbr].type;
#if PKT_DEBUG
   swrite('|');
#endif
   if (if_type & EN_HP) {
      if (if_type & EN_MT)
	 accept_pkt(len, buff, if_nbr, lah_len);  /* Pass attribute info to meter buffer */
      if (if_type & EN_IP) {
	 if (our_packet(buff)) return get_pktbuf(len);
	 }
      return (unsigned char far *)NULL;  /* Don't bother with call2 */
      }
   else  /* Can't see packet contents until call2 */
#if PKT_DEBUG
      last_buf = get_pktbuf(len);
      if (last_buf == NULL) swrite('@');
      return last_buf;
#else
      return get_pktbuf(len);
#endif
   }

void pkt_rcv_call2(Bit16 len, Bit8 *buff, Bit16 if_nbr)
{
   int if_type = en_table[if_nbr].type;
   struct pbuf *bp;
   int ip_pkt = 0;
#if PKT_DEBUG
   if (buff != last_buf) swrite('X');
   else swrite('=');
#endif
   if (if_type & EN_HP)
      ip_pkt = 1;  /* IP pkt ready for tcp/ip */
   else {
      if (if_type & EN_MT) {
         accept_pkt(len, buff, if_nbr, 0);  /* Pass attribute info to meter buffer */
         if (if_type & EN_IP)
            ip_pkt = our_packet(buff) ? 1 : 0;
         else ip_pkt = 0;
         }
      else if (if_type & EN_IP)
	 ip_pkt = 1;  /* Unicast/broadcast mode */
      else ip_pkt = 0;  /* Not our packet, free the buffer */
      }
   bp = (struct pbuf *)(buff - BUF_HDR_LEN);
#if PKT_DEBUG
   swrite('0' + bp->unused);
   swrite(ip_pkt ? 'I' : '#');
#endif
#if TEST_IP
   if (ip_pkt) {
      bp->state = 2;
      enqueue(&readyq, bp);
      }
   else {
      bp->state = 0;
      enqueue(&freeq, bp);
      }
#else 
   bp->state = 0;
   enqueue(&freeq, bp);
#endif
   return;
   }

int snmp_dump_packet = 0;

int main(int argc, char *argv[])
{
   int arg, a, c;
   char cbuf[20], *ap;
   int n_interfaces, IP_int;

   n_interfaces = 0;  IP_int = -1;
   for (c = 0, arg = 1; arg < argc; ++arg) {
      if (argv[arg][0] == '-') {
         ap = argv[arg]+2;
         switch (a = argv[arg][1]) {
#if TEST_IP
         case 'd':
            snmp_dump_packet++;
            break;
#endif
	 case 'I':  /* IP only - no metering */
	    IP_int = n_interfaces;
	 case 'i':  /* Just plain metering */
	 case 'h':  /* Metering with high-performance driver */
	    if (*ap == NULL) ap = argv[++arg];
            if (arg == argc) c = 0;  /* No more args */
            else c = isdigit(*ap) ? atoi(ap) : 0;  /* -h means -h0 */
	    en_table[n_interfaces].int_nbr = c;
	    if (a != 'I') en_table[n_interfaces].type =
	       a == 'h' ? EN_MT|EN_HP : EN_MT;
	    ++n_interfaces;
	    break;
	 default:
	    printf("Invalid option: -%c\n", argv[arg][1]);
	    exit(0);
	    }
	 }
      }
   if (n_interfaces == 0) {
      en_table[0].type= EN_MT;  n_interfaces =  1;
      }
#if TEST_IP
   if (IP_int == -1) IP_int = 0;
   en_table[IP_int].type |= EN_IP;
#endif

//   maxpkt = 1024;
   maxpkt = 256;
   pktmask = maxpkt-1;
   pkts = (struct pkt far *)farmalloc((maxpkt+1) * sizeof(struct pkt));
   pproc = prcv = 0;

#if TEST_IP
   sock_init();  /* Initialise full TCP/IP stack */
#else
   if (pkt_init())  /* Initialise just the packet driver(s) */
      exit(1);  /* Problems in starting one of the packet driver(s) */
#endif

#if TEST_IP
   s = &udpsock;  /* myport        hisadr hisport */
   if (!udp_open( s, 161,  0, 0,        0 )) {
      puts("udp_open failed!\n");
      exit(1);
      }
#endif

   receive();
   }

void receive()
{
   unsigned short k, n, npkts = 0;
   struct pkt huge *pp;
   unsigned char ch;
   unsigned long save_tot, same_count;

   save_tot = tot = set_timeout(0);
   same_count = 0;
   pproc = prcv;  /* Discard packets collected during initialisation */
   for (;;) {
#if TEST_IP
      sock_tick(s, &status);  /* Keep WATTCP alive */
      if ((n = sock_dataready(s)) != 0) {
         snmp_read(n);
         sock_close(s);
         if (!udp_open( s, 161,  0, 0,   0 )) {
            puts("udp_open failed!\n");
            exit(1);
            }
         }
#endif

      k = (prcv-pproc) & pktmask;
      if (k != 0) {  /* We have received some packets */
         for (; k != 0; --k) {
            pp = &pkts[pproc];
#if PKT_DEBUG
   swrite('.');
#endif
#if PKT_TRACE
            printf("%5u: %5u |%u  #%u  ", ++npkts,
               pp->p_len, pp->p_lah_len, pp->p_int);
	    for (n = 0; n != 6; ++n) printf(" %02X", pp->hdr[n]);
	    printf(" ");
	    for (n = 6; n != 12; ++n) printf(" %02X", pp->hdr[n]);
	    printf("  %02x%02X\n", pp->hdr[12],pp->hdr[13]);
            pproc = (pproc+1) & pktmask;
#endif
            }
	 }

      if (tot == save_tot) ++same_count;
#if TIME_TEST
      else swrite('?');
#endif

      if (chk_timeout(tot)) {
#if TIME_TEST
         printf("count=%lu, tot=%lu, save_tot=%lu",
            same_count, tot,save_tot);
#endif
         save_tot = tot = set_timeout(1);  /* 1 second */
         same_count = 0;
#if TIME_TEST
         printf(":  tot+1s = %lu\n", tot);
#endif
         if (kbhit()) {
            if ((ch = getch()) == 27) {  /* ESC */
               printf("\nShutting down\n");
#if TEST_IP
               // no effort required to stop whole TCP/IP stack
               // because it has an atexit() handler
               //
#else
               // shut down access to packet driver
               //
               pkt_release();
#endif
               exit(0);
               }
            }
         }
      }

#if TEST_IP
sock_err:
   switch (status) {
   case 1 : /* foreign host closed */
      exit(0);
   case -1: /* timeout */
      printf("\nConnection timed out!");
      exit(1);
   default: printf("Aborting");
      exit(1);
      }
#endif
   }

#if TEST_IP
void snmp_read(int length)
{
   int out_length;
   unsigned char packet[1500];
   int count;

   sock_read(s, packet,length);
   if (snmp_dump_packet) {
      printf("received %d bytes:\n", length);
      for (count = 0; count < length; count++) {
         printf("%02X ", packet[count]);
         if ((count % 16) == 15) printf("\n");
         }
      printf("\n\n");
      }
   sock_write(s, packet,length);  /* Just echo the SNMP packet */
   }
#endif

void au_send_mon(int length, unsigned char far *buffer)
{  return;  }  /* Default version, never used */
