/*
        This file is part of the cFos project,
        Copyright (c) 1993 by Martin Winkler, Christoph Lders

        This file contains the C main() routines and commandline
        evaluation.


        You are allowed to use, copy, distribute, publish, redistribute
        and/or modify this program under the terms of the 'cFos public
        licence and warranty'.

        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 COPYING.CF for the 'cFos public license and warranty'.
*/

#include <string.h>
#include "vars.h"

int manufact=0,
    is_bundle=0,        /* default = off, because not standardized */
    is_v110=1,          /* default = on */
    use_mpx=0,          /* set to 1 if int 2f doesn't point to NULL */
    use_xms=1;

word _res_size;
static char _FAR * _FAR * idt = (char _FAR * _FAR *) 0L;       /* intr table */
void _FAR *api_memptr;
static int error=0;

word segadr[MAXPORTS*2+1+1+1], segadrcnt=2;

/* this are the settings for the api_register function */
word capi_maxmess=20*MAXPORTS;          /* at least 6*MAXPORT msg needed */
word capi_maxb3=MAXPORTS;

word capi_int=0xf1;
word capi_maxwin=1;                     /* max windowsize */
word capi_framelen=2048;                /* receive buffer size + t70nl overhead */

word fos_rxsize=4*1024;
word fos_txsize=4*1024;


static char *msg_tsrnotfound = "** Program not found in memory\n";
static char msg_extra[40];


static void _FAR *tsr_installed (void)
{
   union REGS r;
   dword _FAR *fp;

   r.x.ax = 0x8f00;
   myint86 (0x14, &r, &r);

   fp = MK_FP (r.x.dx, r.x.ax);
   if (*(fp-1) == 0x736f4663L)
      return fp;
   else
      return NULL;
}


extern int init_kerneluse(void);
extern unsigned provider(const char far *);

static int is_teles (void)
{
   if (!use_mpx || init_kerneluse () != 0)
      return -1;

   manufact = MAN_TELES;
   if ((is_v110 = (provider ("V110"))) != 0)
      strcpy (msg_extra, "V110, ");
   if ((is_bundle = (provider ("Bundle"))) != 0)
      strcat (msg_extra, "Bundle, ");

   if (msg_extra[0])
      strcpy (msg_extra+strlen (msg_extra)-2, " detected");

   return 0;
}


static short init_c_interface (void)
{
   int i, j;
   word cause;
   char *sep, _FAR *fp, _FAR * _FAR *ffp;
   union REGS r;
   struct SREGS s;
   long l;

   if (!api_installed ()) {
      printf ("** No CAPI found at INT %#02x\n", capi_int);
      return -1;
   }


   /*
    *  Check if we are already installed
    */

   fp = tsr_installed ();
   if (fp != NULL) {
      printf ("** Program already installed at segment %#04x\n", FP_SEG (fp));
      return -1;
   }


   /*
    *  Init memory and stacks
    */

   _res_size = (((FP_SEG (&resident_end) - _psp) * 16 + FP_OFF (&resident_end) + 15) & 0xfff0) + _stack_num * _stack_size;
   s.es = _psp;
   r.x.bx = _res_size >> 4;
   r.h.ah = 0x4a;
   myintdosx (&r, &r, &s);
   if (r.x.cflag) {
      printf ("** SETBLOCK failed\n");
      return -1;
   }

   _stackbottom = (FP_OFF (&resident_end) + 15) & 0xfff0;
   _next_stack = _stackbottom + _stack_num * _stack_size;

   l = (long) MSGLEN * (long) capi_maxmess + (long) capi_maxb3 *
       (long) capi_maxwin * (long) capi_framelen;
   if (l >= 0x10000L) {
      printf ("** CAPI can only register 64k\n");
      return -1;
   }

   if (fossil_init () != 0 || (api_memptr = dos_malloc ((word) l)) == 0L) {
      printf ("** Out of memory\n");
      return -1;
   }
   segadr[segadrcnt++] = FP_SEG (api_memptr);

   if ((cause = api_register ()) != 0) {
      printf ("** CAPI could not register us; cause=%s\n", capi_cause (cause));
      return -1;
   }
#ifndef NDEBUG
   printf ("   Application-ID: %04x\n", applid);
#endif


   /*
    *  Determine the name of the PROFILE file
    */

   if (_osmajor > 2) {
      for (exename = MK_FP (aenvseg, 0); !(exename[0] == 0 && exename[1] == 0); exename++);
      exename += 4;
      sep = profile;
      for (i=0; exename[i]; i++) {
         profile[i] = exename[i];
         if (profile[i] == '\\' || profile[i] == ':')
            sep = profile+i+1;
      }
   } else {
      strcpy (profile, get_currdir (NULL));
      sep = profile + strlen (profile);
      if (sep[-1] != '\\')
         *sep++ = '\\';
   }
   strcpy (sep, "PROFILE");


   /*
    *  init all engines
    */

   _timerval = *(dword _FAR *) 0x0040006cL;             /* timetick count */

   if (is_teles () == -1) {
       /* other manufacturer */ ;
   }

   init_cstates ();             /* must be done before handler install */
   hayes_init ();
   api_set_signal ((void _FAR *) capi_event_entry);
   capi_listen_req ();
   sreg_bounds[22][1] = capi_framelen;  /* max b2 framelen inc b3 overhead */
   sreg_bounds[26][1] = capi_maxwin;    /* window size */


   /*
    *  make resident
    */

   segadr[0] = segadrcnt-1;     /* count */
   segadr[1] = _psp;            /* program segment */

   mysegread (&s);              /* free environment segment */
   r.h.ah = 0x49;
   s.es = aenvseg;
   myintdosx (&r, &r, &s);

   ffp = (char _FAR * _FAR *) ptr2segs;
   *ffp = (void _FAR *) &segadr;

   mycli ();
   ffp = (char _FAR * _FAR *) old_int14;
   *ffp = idt[0x14];
   idt[0x14] = (void _FAR *) int14_entry;

   ffp = (char _FAR * _FAR *) old_int08;
   *ffp = idt[0x08];
   idt[0x08] = (void _FAR *) int08_entry;
   mysti ();


   /*
    *  ready
    */

   printf ("   RX-bufsize: %5u    CAPI by: %s\n", fos_rxsize, api_getinfos (0));
   printf ("   TX-bufsize: %5u    Version: %s\n", fos_txsize, api_getinfos (1));
   printf ("   B2-framelen:%5u        S/N: %s\n", capi_framelen, api_getinfos (2));
   printf ("   B2-winsize: %5u       %s%s\n\n", capi_maxwin, msg_extra[0]? "Note: ": "", msg_extra[0]? msg_extra: "");

   printf ("   Driver installed for port#: ");
   for (i=0, j=0; i<MAXPORTS; i++)
      if (portnum[i] != 0xff) {
         printf ("%s%i", j==0? "": ", ", portnum[i]);
         j++;
      }
   printf (";  total %i port%s\n", MAXPORTS, MAXPORTS==1? "": "s");

   r.x.ax = 0x3100;
   r.x.dx = _res_size >> 4;
   myintdos (&r, &r);                   /* tsr */

   return 0;
}



static int deinit_c (void)
{
   int i;
   char _FAR *fp;
   word _FAR *wfp;
   word cause;
//   union REGS r;
//   struct SREGS s;

   /*
    *  Check for installation
    */

   fp = (char _FAR *) tsr_installed ();
   if (fp == NULL) {
      printf (msg_tsrnotfound);
      return -1;
   }

   if (FP_SEG (idt[0x08]) != FP_SEG (fp) ||
       FP_SEG (idt[0x14]) != FP_SEG (fp)) {
      printf ("** Hooked vectors; cannot uninstall\n");
      return -1;
   }

   /*
    *  Deinstall
    */

   applid = * (word _FAR *) (fp+4);
#ifndef NDEBUG
   printf ("   Application-ID: %04x\n", applid);
#endif

   if ((cause = api_set_signal (0L)) != 0)
      printf ("** Failed to deinstall event handler; cause=%04x\n", capi_cause (cause));
   if ((cause = api_release ()) != 0) {
      printf ("** Failed to deregister from CAPI; cause=%s\n", capi_cause (cause));
      return -1;
   }

   mycli ();
   idt[0x08] = * (char _FAR * _FAR *) (fp-12);
   idt[0x14] = * (char _FAR * _FAR *) (fp-8);
   mysti ();

   wfp = * (void _FAR * _FAR *) (fp-16);
   for (i=1; i<=wfp[0]; i++) {
      if (wfp[i]) {
         if (dos_free (MK_FP (wfp[i], 0)) == 0)
            printf ("** Could not free segment %#04x\n", wfp[i]);
      }
   }

   printf ("   Program successfully deinstalled\n");

   return 0;
}


static int read_number (char _FAR *cp, word *wp, word low, word hi)
{
   word val=0, radix=10;
   int c, i=1;
   char option=*cp++;

   if (cp[0] == '0' && cp[1] == 'x') {
      radix = 16;
      cp += 2;
      i += 2;
   }

   for (;; cp++, i++) {
      c = toupper (*cp);
      if (c >= '0' && c <= '9')
         val = val*radix + c - '0';
      else if (c >= 'A' && c <= 'F' && radix == 16)
         val = val*radix + c - 'A' + 10;
      else {
         if (c == 'K') {
            val <<= 10;
            i++;
         }
         break;
      }
   }

   if (val < low) {
      printf ("** '%c': Value too low\n", option);
      error = 1;
   } else if (val > hi) {
      printf ("** '%c': Value too high\n", option);
      error = 1;
   } else
      *wp = val;

   return i-1;
}


static void reregister (void)
{
   union REGS r;

   if (!tsr_installed ()) {
      printf (msg_tsrnotfound);
      return;
   }

   r.x.ax = 0x8f01;             /* reregister application */
   myint86 (0x14, &r, &r);

   printf ("   Application reregistered\n");
}


static void tranx (void)
{
   union REGS r;
   long off;

   if (!tsr_installed ()) {
      printf (msg_tsrnotfound);
      return;
   }

   r.x.ax = 0x8f02;               /* tranx request */
   myint86 (0x14, &r, &r);
   off = (long) ((((long) r.x.dx)<<16)+r.x.ax);
   set_dosdate (dosdate ()+off);

   printf ("   TRANX: clock was %li seconds too %s.\n", off>0? off: -off, off>0? "late": "early");
}


word main (char _FAR *arg)
{
   int mode=0, help=0, i;
   word comport;
   int comcnt=0;
   char _FAR *oldarg = arg;

   for (;*arg && !help; arg++) {
      if (*arg == ' ' || *arg == '\t' || *arg == '\r')
         continue;
      if (*arg == '-' || *arg == '/') {
         arg++;
         switch (toupper (*arg)) {
            case 'W':                   /* max winsize */
               arg += read_number (arg, &capi_maxwin, 1, 7);
               break;
            case 'B':                   /* max capi rcv blocksize */
               arg += read_number (arg, &capi_framelen, 1, 16*1024+3);
               break;
            case 'R':                 /* FOSSIL receive buffer size */
               arg += read_number (arg, &fos_rxsize, 1, 0xffff);
               fos_rxsize = (fos_rxsize + 63) & 0xffc0;
               break;
            case 'T':                 /* FOSSIL transmit buffer size */
               arg += read_number (arg, &fos_txsize, 1, 0xffff);
               fos_txsize = (fos_txsize + 63) & 0xffc0;
               break;
            case 'V':                 /* capi interrupt vector */
               arg += read_number (arg, &capi_int, 0, 0xff);
               break;
            case 'E':                 /* bios emulator */
               arg += read_number (arg, (word*) &bios_emul, 0, 2);
               break;
            case 'F':                 /* fast_event enable/disable */
               arg += read_number (arg, (word*) &fast_event, 0, 1);
               break;
            case 'X':                 /* xms usage enable/disable */
               arg += read_number (arg, (word*) &use_xms, 0, 1);
               break;
            case 'C':
               arg += read_number (arg, &comport, 0, 7);
               if (comcnt < MAXPORTS) {
                  for (i=0; i<comcnt; i++)
                     if (portnum [i] == comport)
                        break;
                  if (i == comcnt)
                     portnum [comcnt++] = comport;
                  else
                     printf ("** Port %u already used\n");
               } else
                  printf ("** No more ports; increase MAXPORTS and recompile\n");
               break;
            case '?':
               help = 1;
               break;
            default:
               printf ("** Unknown option '%c'\n", *arg);
error:         printf ("\n   Type `cfos -?' for help\n");
               return 1;
         }

      } else if (mode == 0) {
         if (toupper (*arg) == 'I') {
            mode = 1;
         } else if (toupper (*arg) == 'D') {
            mode = 2;
         } else if (toupper (*arg) == 'T') {
            mode = 3;
         } else if (toupper (*arg) == 'R') {
            mode = 4;
         } else {
            printf ("** Unknown command '%c'\n", *arg);
            goto error;
         }

      } else if (*arg) {
         printf ("** Only one command allowed\n");
         goto error;
      }
   }

   if (capi_framelen > fos_rxsize) {
      printf ("** B2-frames may not be bigger than RX-buffersize\n");
      error = 1;
   }

   if (error) goto error;

   if (help || mode == 0) {
      printf ("   The following commands are valid:\n"
              "      i         install driver\n"
              "      d         remove driver from memory\n"
              "      r         reregister a resident cFos at CAPI (this kills ALL\n"
              "                current connections)\n"
              "      t         tranx: get the actual time/date from resident cFos\n"
            "\n   The following switches alter operation:\n"
              "      -b        CAPI max B2-framelength [%u]\n"
              "      -c        Comport numbers to service, may be more than one [0]\n"
              "      -e        BIOS emulator: 0=off, 1=on, 2=force [%u]\n"
              "      -f        CAPI fast event mode: 0=off, 1=on [%u]\n"
              "      -t        FOSSIL transmit buffer size [%u]\n"
              "      -r        FOSSIL receive buffer size [%u]\n"
              "      -v        CAPI interrupt vector number [%u]\n"
              "      -w        CAPI max windowsize [%u]\n"
              "      -x        allocate memory in UMBs: 0=off, 1=on [%u]\n"
              , capi_framelen, bios_emul, fast_event, fos_txsize, fos_rxsize, capi_int, capi_maxwin, use_xms);
      return 0;
   }

   if (comcnt == 0)
      portnum [comcnt++] = 0;
   for (i=comcnt; i<MAXPORTS; i++)
      portnum [i] = 0xff;

   use_mpx = (idt[0x2f] != 0L);         /* may int 2f be used? */

   switch (mode) {
      case 1:
         init_c_interface ();           /* does only return in case of error */
         break;
      case 2:
         deinit_c ();
         break;
      case 3:
         tranx ();
         break;
      case 4:
         reregister ();
         break;
   }

   return 0;
}


/* --- eof main.c */
