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

        This file contains the CAPI event handler function.


        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 "vars.h"
#include "cstates.inc"

#define CTRLC  3
#define CTRLK  11
#define MAXPEND 8
#define MAXTRANS MAXPEND

#define msgbyte(i)   (msgbuf [i])
#define msgword(i)   (*(word *) (msgbuf+i))
#define msgdword(i)  (*(void _FAR **) (msgbuf+i))

long atol (char *);
int atoi (char *);

byte msgbuf [MSGLEN+1];   /* first message data element at index 8 */
word numofconnections;
int in_event=0, fast_event=0;
long timediff=0;

static int statesinit = 0;

word cstate [MAXPORTS];
int  disco [MAXPORTS];
   /* 0 = no disconnect, 1 = disco D req, 2 = disco B3 req */
word ncci [MAXPORTS];
word plci [MAXPORTS];
int  used [MAXPORTS];   /* 1 = port used, plci valid */
int  isprn [MAXPORTS];
word bytesintrans [MAXPORTS];
byte b3number [MAXPORTS];
word datab3num [MAXPORTS];
byte pendmsg [MAXPEND] [MAXPORTS] [MSGLEN];  /* buffer for pending data_b3 messages */
word pendrem [MAXPORTS];
word pendcnt [MAXPORTS];
word transnum [MAXTRANS] [MAXPORTS];
word transcnt [MAXPORTS];
word transrem [MAXPORTS];
dword rxbytes [MAXPORTS], rxblocks [MAXPORTS];
dword txbytes [MAXPORTS], txblocks [MAXPORTS];
byte sb2prot [MAXPORTS];
byte sservice [MAXPORTS];
byte sservadd [MAXPORTS];


word disconnectinfo [MAXPORTS];    // to be displayed on ati request
word disconnectb3info [MAXPORTS];  //   .
byte eaz [MAXPORTS];               //   .
byte rservice [MAXPORTS];          //   .
byte rserviceadd [MAXPORTS];       //   .
word infocnt [MAXPORTS];
byte infoelement [MAXINFO] [MAXPORTS] [MSGLEN];
byte callerid [MAXPORTS] [MSGLEN+1];
long charge [MAXPORTS];
byte dreason [MAXPORTS];
/* disconnect reason : 0=none, 1=passive, 2=active, 3=dis.b3.timeout
                       4=dis.timout, 5=capi_rereg., 6=con.timout */
byte disp_txcnt [MAXPORTS], disp_rxcnt [MAXPORTS];
char * statstr [MAXPORTS] = {
"cFos>                                                                           ",
#if MAXPORTS > 1
"cFos>                                                                           "
#endif
};
/*
cFos>  C-B3  ACOD  R:0000  T:0000  C:00000  V110 19200  02289111041             "};
cFos>  C-B3  ACOD  0R:0000  0T:0000  C:000  X75 ISO8208  B  02289111041 (new)
*/

int  connecttimer [MAXPORTS];
int  discob3timer [MAXPORTS];
int  discotimer [MAXPORTS];
int  discotries [MAXPORTS];
int  ringtimer [MAXPORTS];
int  disptimer [MAXPORTS];
int  inacttimer [MAXPORTS];
int  txtimer [MAXPORTS];
int  rxtimer [MAXPORTS];
int  debugtimer [MAXPORTS];

void init_cstates (void) {
   int i, j, k;

   numofconnections = 0;
   for (i=0; i < MAXPORTS; i++) {
      disco [i] = 0;     cstate [i] = 0;
      used [i] = 0;      bytesintrans [i] = 0;
      infocnt [i] = 0;   charge [i] = 0;
      datab3num [i] = 0; rserviceadd [i] = 0;
      b3number [i] = 0;  disconnectinfo [i] = 0;
      eaz [i] = 0;       disconnectb3info [i] = 0;
      rservice [i] = 0;  pendrem [i] = 0;
      pendcnt [i] = 0;   transcnt [i] = 0;
      disptimer [i] = 18; transrem [i] = 0;
      disp_txcnt [i] = 99; dreason [i] = 0;
      disp_rxcnt [i] = 99;
      rxbytes [i] = 0L;  rxblocks [i] = 0L;
      txbytes [i] = 0L;  txblocks [i] = 0L;
      rxtimer [i] = 0;    txtimer [i] = 0;

      for (j=0; j < MSGLEN; j++) {
         for (k=0; k < MAXINFO; k++)
            infoelement [k][i][j] = 0;
      }
   }
   statesinit = 1;
}

static void savemsg (word cport) {
   memcpy3 (infoelement [infocnt [cport]][cport], msgbuf, msgword (0));
   infocnt [cport]++;
   if (infocnt [cport] >= MAXINFO) infocnt [cport] = 0;   /* wrap around */
   DUMP ();
}

static dword isdndate (char *buf, word len)
{
   word year, mon, day, hour, min, sec;
   day = (word) atoi (buf);
   mon = (word) atoi (buf+3);
   if ((year = (word) atoi (buf+6)+1900) < 1990)
      year += 100;
   hour = (word) atoi (buf+9);
   min = (word) atoi (buf+12);
   sec = (len > 14)? (word) atoi (buf+15): 0;
#ifndef NDEBUG
   myaprintf ("%i %i %i %i %i %i\n", year, mon, day, hour, min, sec);
#endif
   return unixdate (year, mon, day, hour, min, sec);
}

static void signal_info (word cport, int isinfo) {
   word len;
   dword d_date, i_date;

   if (isinfo) {
      switch (msgbyte (10)) {
         case 7:
            hayes_result (cport, HAYES_RINGING);
            break;
         case 2:
            len = msgbyte (12)-1;
            msgbyte (14+len) = 0;
            charge [cport] = atol ((char *) msgbuf+14);
#ifndef NDEBUG
            myaprintf ("Charge: %ld\n", charge [cport]);
#endif
            mysprintf (statstr [cport]+STAT_CHARGE, "C:%-4li", charge [cport]);
            isprn [cport] = 1;
            break;
         case 3:
            len = msgbyte (12);
            if (len > 13 && *_indos == 0) {    /* ignore if not at least mins stated */
               msgbuf[13+len] = 0;
               d_date = dosdate () + (long) timediff;
               i_date = isdndate ((char*) msgbuf+13, len);
               if (len <= 16) {                  /* no sec in string */
                  if (i_date > d_date)
                     timediff += (long) (i_date-d_date);
                  else if (i_date/60L != d_date/60L)
                     timediff += (long) (i_date+59-d_date);
               } else
                  timediff += (long) (i_date-d_date);
#ifndef NDEBUG
               myaprintf ("timediff: %li, d:%lx, i:%lx\n", timediff, d_date, i_date);
#endif
            }
#ifndef NDEBUG
            msgbyte (13+len) = 0;
            myaprintf ("Date: %s\n", msgbuf+13);
#endif
            break;
         default:
            savemsg (cport);
            break;
      }
   }
   else
      savemsg (cport);
}

#ifndef NDEBUG
void dump (byte *buf, int len) {
   int i;
   for (i=0; i<len; i++) {
      if (i%32 == 0)
         adisp ("  ");
      myaprintf ("%02x", buf[i]);
      if (i%4 == 3)
         _adisp_char (' ');
      if (i%32 == 31)
         _adisp_char ('\n');
   }
   if (i != 0)
      _adisp_char ('\n');
}
#endif

static void reject_call (word cport, word tmpplci, int dealloc) {
   if (dealloc) {
      used [cport] = 0; numofconnections--;
   }
   capi_ans (tmpplci, 0xff);
}

static byte urate (word cport) {
   if ((sservadd [cport] & 0xc0) == 0xc0 ||   /* v110 async */
       (sservadd [cport] & 0xc7) == 0x40 ||   /* v110 38400 async special */
       (sservadd [cport] & 0xf0) == 0xa0 ||   /* v110 sync x30 ecma 102 */
       (sservadd [cport] & 0xf0) == 0xb0) {   /* v120 sync */
      sb2prot [cport] = 8;
      return rserviceadd [cport];
   }
   sb2prot [cport] = sreg[cport][43] >> 8;   /* don't know -> use S43 */
   return sreg[cport][43] & 0xff;
}

static void passive_select (word cport) {
   sservice [cport] = rservice [cport];
   switch (sservice [cport]) {
      case 7: if (rserviceadd [cport] != 0) sservadd [cport] = urate (cport);
              else {
                 if (eaz [cport] >= '0' && eaz [cport] <= '9' &&
                    (sreg [cport][50+eaz [cport]-0x30] & 0xff) != 0) {
                    sservadd [cport] = sreg[cport][50+eaz[cport]-0x30] & 0xff;
                    sb2prot [cport] = sreg[cport][50+eaz[cport]-0x30] >> 8;
                 }
                 else {
                    sservadd [cport] = 0;
                    sb2prot [cport] = 1;
                 }
              }
              break;
      case 1: sb2prot [cport] = 3;
              sservadd [cport] = rserviceadd [cport];
              break;
      default:sb2prot [cport] = 1;
              sservadd [cport] = 0;
              break;
   }
   capi_select_b2_protocol_req (cport, sb2prot [cport], sservadd [cport]);
}

void trig_transmitter (word cport) {
   word di;
   word cpu_flags;
   word transbytes;
   byte _FAR * p;
   word b2len = sreg[cport][28]-sreg[cport][29];

   if (disco [cport] != 0 || !carrier_detect [cport] ||
      !tx_enabled [cport] || txcnt [cport] == 0) return;

   cpu_flags = mycli ();
//   while (transcnt [cport] < min (sreg [cport][26], MAXTRANS) &&
   while ((transcnt [cport] < MAXTRANS) &&
      bytesintrans [cport] < txcnt [cport]) {
      if (transcnt [cport] >= 1 &&
         (txcnt [cport] - bytesintrans [cport]) < b2len) break;

      di = txrem [cport] + bytesintrans [cport];
      if (di >= txbufsize [cport]) di -= txbufsize [cport];
      transbytes = min (txbufsize [cport] - di,
         txcnt [cport] - bytesintrans [cport]);
      transbytes = min (transbytes, b2len);
      p = txbuf [cport] + di;
      txbytes [cport] += transbytes;  txblocks [cport]++;

      debugtimer [cport] = 15*18;
      inacttimer [cport] = sreg[cport][19] * 1092;
      bytesintrans [cport] += transbytes;
      di = transcnt [cport] + transrem [cport];
      if (di >= MAXTRANS) di -= MAXTRANS;
      transnum [di][cport] = transbytes;
      transcnt [cport]++;
      capi_data_b3_req (ncci [cport], transbytes, p, datab3num [cport]++, 0);

      disp_txcnt[cport] = (disp_txcnt[cport]+1) % 4;
      statstr [cport][STAT_TX-3] = '0'+transcnt [cport];
      mysprintf (statstr [cport]+STAT_TX, "%-5u", transbytes);
      disp_stat (cport);
   }
   myseti (cpu_flags);
}

static void datab3conf (word cport) {
/* this function does not support packet reassembly ! */
   word transbytes = transnum [transrem [cport]][cport];

   if (transcnt [cport] > 0) {
      bytesintrans [cport] -= transbytes;
      txcnt [cport] -= transbytes;
      txrem [cport] += transbytes;
      if (txrem [cport] >= txbufsize [cport])
         txrem [cport] -= txbufsize [cport];
      transcnt [cport]--;
      transrem [cport]++;
      if (transrem [cport] >= MAXTRANS) transrem [cport] = 0;
      statstr [cport][STAT_TX-3] = '0'+transcnt [cport];
      isprn [cport] = 1;
   }

   if (txcnt [cport] == 0) txrem [cport] = 0;
   trig_transmitter (cport);
}

static int handle_b3_data (word cport, word cnt, byte b3num, byte _FAR * p, int fast) {
/* the flags in the data_b3_ind message are ignored */
   word i;
   word si, mcnt;
   word ctrlcs;

   if (disco [cport] != 0 || !carrier_detect [cport]) return 1;
   b3number [cport] = b3num;
   if ((rxbufsize [cport] - rxcnt [cport]) >= cnt && !fast) {
      while (cnt > 0) {
         ctrlcs = 0;
         si = rxrem [cport] + rxcnt [cport];
         if (si >= rxbufsize [cport]) si -= rxbufsize [cport];
         mcnt = min (rxbufsize [cport] - si, cnt);
         cnt -= mcnt;

         if (!ctrl_k_check [cport]) {
            word cpu_flags = mysti ();
            rxcnt [cport] += mcnt;
            memcpy3 (rxbuf [cport]+si, p, mcnt);
            p += mcnt;
            myseti (cpu_flags);
         } else {
            for (i=0; i < mcnt; i++) {
               if ((*p != CTRLC) && (*p != CTRLK))
                  rxbuf [cport][si++] = *p;
               else
                  ctrlcs++;
               p++;
            }
            rxcnt [cport] += (mcnt-ctrlcs);
         }
         rxbytes [cport] += mcnt;  rxblocks [cport]++;
         if (ctrlcs > 0) ctrl_k_seen [cport] = 1;
      }
      return 1;   /* incoming data accepted */
   }
   else {
#ifndef NDEBUG
      if (!fast) myaprintf ("%d buffer full\n", cport);
#endif
      if (pendcnt [cport] >= MAXPEND) {
         if (!fast) {
#ifndef NDEBUG
            myaprintf ("%d buffer overflow\n", cport);
#endif
            rx_overflow [cport] = 1;
            return 1;   /* discard incoming data */
         }
         else {
            checkpending (cport, 0);
            if (pendcnt [cport] >= MAXPEND) {
#ifndef NDEBUG
               myaprintf ("%d buffer overflow\n", cport);
#endif
               rx_overflow [cport] = 1;
               return 1;   /* discard incoming data */
            }
         }
      }
      si = pendcnt [cport] + pendrem [cport];
      if (si >= MAXPEND) si -= MAXPEND;
      memcpy3 (pendmsg [si][cport], msgbuf, msgword (0));
      pendcnt [cport]++;
      statstr [cport][STAT_RX-3] = '0'+pendcnt [cport];
      isprn [cport] = 1;
      return 0;
   }
}

void checkpending (word cport, int ispurge) {
   word si, cnt, cpu_flags;

   cpu_flags = mycli ();
   si = pendrem [cport];
   cnt = *(word *) (pendmsg [si][cport]+10);
   if ((rxbufsize [cport] - rxcnt [cport]) >= cnt || ispurge) {
      b3number [cport] = pendmsg [si][cport][16]; /* needed by data b3 resp */
      if (!ispurge)
         handle_b3_data (cport, cnt, b3number [cport],
            *(void _FAR **) (pendmsg [si][cport]+12), 0);
      pendcnt [cport]--;
      pendrem [cport]++;
      if (pendrem [cport] >= MAXPEND) pendrem [cport] = 0;
      statstr [cport][STAT_RX-3] = '0'+pendcnt [cport];
      isprn [cport] = 1;
      capi_data_b3_resp (cport);
   }
   myseti (cpu_flags);
}

int connect (word cport, int spv) {
   int i;
   for (i=0; i<MAXPORTS; i++)
      if (cstate [i] == 10) return 0;
   cstate [cport] = 10;
   sb2prot [cport] = sreg[cport][20];
   sservice [cport] = sreg[cport][16];
   sservadd [cport] = sreg[cport][17];
   capi_connect_req (cport, spv);
   return 1;
}

void disconnect (word cport, byte reason) {
   while (pendcnt [cport] > 0) checkpending (cport, 1);
   if ((cstate [cport] == 0) || (cstate [cport] == 5) || (cstate [cport] == 15)) {
      disco [cport] = 2;
      capi_disconnect_b3_req (cport, ncci [cport]);
      memcpy3 (statstr [cport]+STAT_STATE, "D-B3", 4); isprn [cport] = 1;
   }
   else {
      disco [cport] = 1;
      capi_disconnect_req (plci [cport], reason);
      memcpy3 (statstr [cport]+STAT_STATE, "D-D ", 4); isprn [cport] = 1;
   }
   dreason [cport] = 2;
}

static void reset_carrier (word cport) {
   memcpy3 (statstr [cport]+STAT_STATE, "    ", 4); isprn [cport] = 1;
   statstr[cport][STAT_LED_C] = ' ';
   disp_txcnt [cport] = 99;     /* don't display */
   disp_rxcnt [cport] = 99;     /* don't display */
   hayes_result (cport, HAYES_NO_CARRIER, disconnectinfo [cport]);

   disco [cport] = 0;
   cstate [cport] = 0;
   carrier_detect [cport] = 0;
   command_mode [cport] = 1;
   numofconnections--;
   used [cport] = 0;
   offhook [cport] = 0;
   pendcnt [cport] = 0; pendrem [cport] = 0;
   purge_txbuf (cport);
   transcnt [cport] = 0;
   bytesintrans [cport] = 0;
   hayes [cport].connecting = 0;
   if (numofconnections == 0) api_reregister ();
   if (watchdog [cport]) {    /* watchdog enabled */
      reboot (1);    /* warm boot */
   }
}

static word allocport (word tplci, int isring) {
   word i;
   int allowed;

   numofconnections++;
   for (i=0; i < MAXPORTS; i++) {
      allowed = !used [i] && !offhook [i];
      if (!isring && cstate [i] != 10) allowed = 0;
      if (allowed) {
         plci [i] = tplci;
         used [i] = 1;
         return i;
      }
   }
   return 0;
}

static word multiplex_message (int msgcmd, int subcmd) {
   word offset;
   word givenplci;
   word givenncci;
   word i;

   offset = (msgbits [msgcmd] & 1) != 0 ? 8 : 0;
   if ((offset != 0) || (msgcmd == 0x82)) {
      if (msgcmd == 0x82) {
         offset = (subcmd == 1) ? 8 : 10;
      }
      givenplci = msgword (offset);
      for (i=0; i < MAXPORTS; i++)
         if ((plci [i] == givenplci) && used [i]) return i;
#ifndef NDEBUG
      myaprintf ("mux_mess: unkown plci=%4x\n", givenplci);
#endif
   }
   else {
      offset = (msgbits [msgcmd] & 2) != 0 ? 8 : 0;
      if (offset != 0) {
         givenncci = msgword (offset);
         for (i=0; i < MAXPORTS; i++)
            if ((ncci [i] == givenncci) && used [i]) return i;
#ifndef NDEBUG
      myaprintf ("mux_mess: unknown ncci=%4x\n", givenncci);
#endif
      }
   }
   return 0xffff;
}

static word checkport (word cport) {
   int cmp, i, j;
   word l;

   for (i=0; i < MAXPORTS; i++) {
      if (hayes [i].connecting) {
         cmp = 1;
         l = min (diallen [i], msgbyte (10)-1)-1;
         for (j = 0; j < l; j++)
            if (msgbyte (11+l-j) != dialnumber [i][l-1-j]) {
               cmp = 0;
               break;
            }
         if (cmp) break;
      }
   }
#ifndef NDEBUG
   if (!cmp) {
      myaprintf ("Connecting: COM port confusion\n");
      disconnect (cport, 0xf1);
      return 0;
   }
#endif
   if (cmp) {
      if (i != cport) {   /* we have to swap COM ports */
         used [i] = 1; used [cport] = 0;
         plci [i] = plci [cport];
         cstate [i] = cstate [cport]; cstate [cport] = 0;
         disco [i] = disco [cport]; disco [cport] = 0;
         return i;
      }
      else return cport;
   }
   return 0;
}

void c_states (void) {
   word cpu_flags;
   word message;
   word cport;
   int no_resp;

   if (in_event) return;
   in_event++;
   if (fast_event) cpu_flags = mysti ();

   do {
      if (api_getmsg () != 0) break;

      message = (word) (msgbuf [4]) * 4 + msgbuf [5];

      if (message == LISTEN_CONF) continue;
      cport = (message == CONNECT_CONF || message == CONNECT_IND) ? 0 :
         multiplex_message (msgbuf [4], msgbuf [5]);

#ifndef NDEBUG
      if (cport >= MAXPORTS)
         myaprintf ("unexp cport=%02x, message=%u\n", cport, message);
#endif

      if (cport == 0xffff) continue;
      no_resp = 0;
      isprn [cport] = 0;
      if (disco [cport] != 0) cstate [cport] = 0;
      switch (message) {
         case DISCONNECT_CONF:
         case DISCONNECT_B3_CONF:
         case INFO_CONF:
         case CONNECT_INFO_CONF:
         case DATA_CONF:
         case GET_PARAMS_CONF:
         case GET_B3_PARAMS_CONF:
         case HANDSET_IND:
            break;
         case RESET_B3_CONF:
         case RESET_B3_IND:
         case MANUFACTURER_CONF:
         case MANUFACTURER_IND:
#ifndef NDEBUG
            myaprintf ("unexpected message, ignored\n");
            dump (msgbuf, msgword (0));
#endif
            break;
         case DATA_B3_CONF:
            datab3conf (cport);
            break;
         case INFO_IND:
            signal_info (cport, 1);
            break;
         case DATA_IND:
            signal_info (cport, 0);
            break;
         case DATA_B3_IND:
            inacttimer [cport] = sreg[cport][19] * 1092;
            disp_rxcnt[cport] = (disp_rxcnt[cport]+1) % 4;
            mysprintf (statstr [cport]+STAT_RX, "%-5u", msgword (10));
            isprn [cport] = 1;
            if (!handle_b3_data (cport, msgword (10),
               msgbyte (16), (void _FAR *) msgdword (12),
               fast_event)) no_resp = 1;
            break;
         case DISCONNECT_IND:
            if (dreason [cport] == 0) dreason [cport] = 1;
            disco [cport] = 0;
            disconnectinfo [cport] = msgword (10);
#ifndef NDEBUG
            if (disconnectinfo [cport] != 0) {
               myaprintf ("  cause=%s\n", capi_cause (disconnectinfo [cport]));
            }
#endif
            reset_carrier (cport);
            break;
         case DISCONNECT_B3_IND:
            while (pendcnt [cport] > 0) checkpending (cport, 1);
            if (dreason [cport] == 0) dreason [cport] = 1;
            disconnectb3info [cport] = msgword (10);
#ifndef NDEBUG
            if (disconnectb3info [cport] != 0) {
               myaprintf ("  cause=%s\n", capi_cause (disconnectb3info [cport]));
            }
#endif
            disco [cport] = 1;
            capi_disconnect_req (plci [cport], 0xda);
            memcpy3 (statstr [cport]+STAT_STATE, "D-D ", 4); isprn [cport] = 1;
            break;
         case CONNECT_CONF:

            if (numofconnections < MAXPORTS) {
               cport = allocport (msgword (8), 0);
               dreason [cport] = 0;
               memcpy3 (statstr [cport]+STAT_STATE, "C-D ", 4); isprn [cport] = 1;
               if (cstate [cport] != 10)
                  disconnect (cport, 0xf1);
               else
                  cstate [cport]++;
            }
#ifndef NDEBUG
            else myaprintf ("UNEXP CONNECT_CONF\n");
#endif
            break;
         case CONNECT_IND:
            rservice [cport] = msgbyte (11);
            rserviceadd [cport] = msgbyte (12);
            eaz [cport] = msgbyte (13);
            signal_info (cport, 0);
            if (msgbyte (14) > 0) {
               memcpy3 (callerid [cport], msgbuf+14+2, msgbyte (14)-1);
               callerid [cport][msgbyte (14)-1] = 0;
            } else
               memcpy3 (callerid [cport], "unknown", 8);
#ifndef NDEBUG
            myaprintf ("%s\n", callerid [cport]);
#endif
            if (numofconnections < MAXPORTS) {
               cport = allocport (msgword (8), 1);

               if ((!initialized [cport] && bios_emul == 0) || !dtr_high [cport] ||
                   offhook [cport] || (cstate [cport] != 0)) {
                  reject_call (cport, plci [cport], 1);
                  no_resp = 1;
               }
               else {
                  dreason [cport] = 0;
                  cstate [cport] = 1;
                  passive_select (cport);
                  memcpy3 (statstr [cport]+STAT_STATE, "C-D ", 4);
                  memcpy3 (statstr [cport]+STAT_CHARGE, "Free ", 5);
                  memcpy3 (statstr [cport]+STAT_RX-3, "                       ", 23);
                  isprn [cport] = 1;
                  no_resp = 1;
               }
            }
            else {
#ifndef NDEBUG
               myaprintf ("Ring. Out of Ports\n");
#endif
               reject_call (cport, msgword (8), 0);
               no_resp = 1;
            }
            break;
         case CONNECT_ACTIVE_IND:
            signal_info (cport, 0);
            if (cstate [cport] == 11)
               cport = checkport (cport);
            capi_info_req (plci [cport]);
            memcpy3 (statstr [cport]+7, "C-B3", 4); isprn [cport] = 1;
            if (cstate [cport] == 11)
               capi_select_b2_protocol_req (cport, sreg [cport][20],
                  sreg [cport][27]);
            else if (cstate [cport] != 3) disconnect (cport, 0xf1);
            cstate [cport]++;
            break;
         case SELECT_B2_PROTOCOL_CONF:
            if (msgword (10) != 0) {   /* info field */
               hayes_result (cport, HAYES_ERROR, HAYES_B2_ERR);
               disconnect (cport, 0xf1);
            }
            else if ((cstate [cport] != 12) && (cstate [cport] != 1)) {
               disconnect (cport, 0xf1);
            } else {
               capi_select_b3_protocol_req (cport, plci [cport]);
               cstate [cport]++;
            }
            break;
         case SELECT_B3_PROTOCOL_CONF:
            if (msgword (10) != 0) {   /* info field */
               hayes_result (cport, HAYES_ERROR, HAYES_B3_ERR);
               disconnect (cport, 0xf1);
            }
            else if (cstate [cport] == 13)
               capi_connect_b3_req (cport, plci [cport]);
            else if (cstate [cport] == 2)
               capi_listen_b3_req (plci [cport]);
            else
               disconnect (cport, 0xf1);
            cstate [cport]++;
            break;
         case LISTEN_B3_CONF:
            if (cstate [cport] != 3) disconnect (cport, 0xf1);
            else {
               hayes_result (cport, HAYES_RING);
               if (sreg [cport][0] != 0)
                  capi_ans (plci [cport], 0); /* auto ans */
            }
            break;
         case CONNECT_B3_CONF:
            if (cstate [cport] != 14) disconnect (cport, 0xf1);
            else {
               ncci [cport] = msgword (10);
               cstate [cport]++;
            }
            break;
         case CONNECT_B3_IND:
            if (cstate [cport] != 4) disconnect (cport, 0xf1);
            else {
               ncci [cport] = msgword (8);
               cstate [cport]++;
            }
            break;
         case CONNECT_B3_ACTIVE_IND:
            if ((cstate [cport] == 5) || (cstate [cport] == 15)) {
               cstate [cport] = 0;
               carrier_detect [cport] = 1;
               command_mode [cport] = 0;
               memcpy3 (statstr [cport]+7, "    ", 4);
               statstr[cport][14] = 'C';
               hayes_result (cport, HAYES_CONNECT);
               hayes[cport].connecting = 0;
               rxbytes [cport] = 0L;  rxblocks [cport] = 0L;
               txbytes [cport] = 0L;  txblocks [cport] = 0L;
            }
            else disconnect (cport, 0xf1);
            break;
#ifndef NDEBUG
         default:
            myaprintf ("Unknown Message %d\n", message);
            break;
#endif
      }
      if (!no_resp && ((message & 3) == 2)) capi_resp (cport, msgbuf [4]);
      if (isprn [cport]) disp_stat (cport);
   } while (1);
   if (fast_event) myseti (cpu_flags);
   in_event--;
}

void c_timer (void) {
   int i, j;
#ifndef NDEBUG
   char _FAR *shift = (char _FAR *) 0x00000417;
   static debugxx=0;
#endif

   if (!statesinit) return;
#ifndef NDEBUG
   if (debugxx > 0) debugxx -= min (numticks, debugxx);
   if ((*shift & 0x10) && debugxx == 0) {
      myaprintf ("\xc""tx_enabled: %u\ntxcnt: %u\ntranscnt: %u\n"
                 "sreg[26]: %u\nbytesintrans: %u\ntransrem: %u\n"
                 "transnum[0]: %u\n"
                 "transnum[1]: %u\n"
                 "transnum[2]: %u\n"
                 "transnum[3]: %u\n"
                 "transnum[4]: %u\n"
                 "transnum[5]: %u\n"
                 "transnum[6]: %u\n"
                 "transnum[7]: %u\n",
                 tx_enabled[0], txcnt[0], transcnt[0],
                 sreg[0][26], bytesintrans[0], transrem[0],
                  transnum[0][0],
                  transnum[1][0],
                  transnum[2][0],
                  transnum[3][0],
                  transnum[4][0],
                  transnum[5][0],
                  transnum[6][0],
                  transnum[7][0]);
       debugxx = 18;
   }
#endif
   for (i=0; i < MAXPORTS; i++) {
      if (!carrier_detect [i]) inacttimer [i] = sreg [i][19] * 1092;
      else if (sreg [i][19] != 0) {
         inacttimer [i] -= numticks;
         if (inacttimer [i] <= 0 && !in_event) {
            disconnect (i, 0);
            dreason [i] = 7;
         }
      }
      disptimer[i] -= numticks;
      if (disptimer[i] < 0)
         disp_stat (i);
      if (cstate [i] != 0 && cstate [i] != 3 && disco [i] == 0) {
         if (connecttimer [i] > 0)
            connecttimer [i] -= numticks;
         else if (!in_event) {
#ifndef NDEBUG
            myaprintf ("c_timer: disconnect\n");
#endif
            disconnect (i, 0xba);
            dreason [i] = 6;
         }
      }
      else connecttimer [i] = (sreg[i][7] & 0xff) * 18;

      if (disco [i] == 0) discotries [i] = 4;
      if (disco [i] != 2) discob3timer [i] = 5*18;
      if (disco [i] != 1) discotimer [i] = 5*18;
      if (discob3timer [i] > 0) discob3timer [i] -= numticks;
      else if (!in_event) {
         if (discotries [i] > 2) {
#ifndef NDEBUG
            myaprintf ("b3_timer: disconnect\n");
#endif
            dreason [i] = 3;
            while (pendcnt [i] > 0) checkpending (i, 1);
            capi_disconnect_b3_req (i, ncci [i]);
            discotries [i]--;
            discob3timer [i] = 5*18;
         }
         else disco [i] = 1;
      }
      if (discotimer [i] <= 0) {
         if (discotries [i] > 0 && !in_event) {
#ifndef NDEBUG
            myaprintf ("d_timer: disconnect\n");
#endif
            capi_disconnect_req (plci [i], 0xf1);
            dreason [i] = 4;
            discotries [i]--;
            discotimer [i] = 5*18;
         }
         else {
            for (j=0; j < MAXPORTS; j++)
               if (used [j]) reset_carrier (j);
             dreason [i] = 5;
         }
      }
      else discotimer [i] -= numticks;
      if (cstate [i] != 3) ringtimer [i] = 3*18;
      else {
         if (ringtimer [i] > 0)
            ringtimer [i] -= numticks;
         else if (!in_event) {
            hayes_result (i, HAYES_RING);
            ringtimer [i] = 3*18;
         }
      }
      if (txtimer [i] > 0) txtimer [i] -= numticks;
      if (rxtimer [i] > 0) rxtimer [i] -= numticks;
      if (debugtimer [i] > 0) debugtimer [i] -= numticks;
      else if (!in_event && bytesintrans [i] > 0) datab3conf (i);
   }
}
