/************************************************************************/
/*                                                                      */
/*    *****                       *****                                 */
/*      *****                   *****                                   */
/*        *****               *****                                     */
/*          *****           *****                                       */
/*  ***************       ***************                               */
/*  *****************   *****************                               */
/*  ***************       ***************                               */
/*          *****           *****           TheNetNode                  */
/*        *****               *****         Portable                    */
/*      *****                   *****       Network                     */
/*    *****                       *****     Software                    */
/*                                                                      */
/* File os/go32/scc.c (maintained by: ???)                              */
/*                                                                      */
/* This file is part of "TheNetNode" - Software Package                 */
/*                                                                      */
/* Copyright (C) 1998 - 2008 NORD><LINK e.V. Braunschweig               */
/*                                                                      */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the NORD><LINK ALAS (Allgemeine Lizenz fuer    */
/* Amateurfunk Software) as published by Hans Georg Giese (DF2AU)       */
/* on 13/Oct/1992; either version 1, or (at your option) any later      */
/* version.                                                             */
/*                                                                      */
/* This program is distributed WITHOUT ANY WARRANTY only for further    */
/* development and learning purposes. See the ALAS (Allgemeine Lizenz   */
/* fuer Amateurfunk Software).                                          */
/*                                                                      */
/* You should have received a copy of the NORD><LINK ALAS (Allgemeine   */
/* Lizenz fuer Amateurfunk Software) along with this program; if not,   */
/* write to NORD><LINK e.V., Hinter dem Berge 5, D-38108 Braunschweig   */
/*                                                                      */
/* Dieses Programm ist PUBLIC DOMAIN, mit den Einschraenkungen durch    */
/* die ALAS (Allgemeine Lizenz fuer Amateurfunk Software), entweder     */
/* Version 1, veroeffentlicht von Hans Georg Giese (DF2AU),             */
/* am 13.Oct.1992, oder (wenn gewuenscht) jede spaetere Version.        */
/*                                                                      */
/* Dieses Programm wird unter Haftungsausschluss vertrieben, aus-       */
/* schliesslich fuer Weiterentwicklungs- und Lehrzwecke. Naeheres       */
/* koennen Sie der ALAS (Allgemeine Lizenz fuer Amateurfunk Software)   */
/* entnehmen.                                                           */
/*                                                                      */
/* Sollte dieser Software keine ALAS (Allgemeine Lizenz fuer Amateur-   */
/* funk Software) beigelegen haben, wenden Sie sich bitte an            */
/* NORD><LINK e.V., Hinter dem Berge 5, D-38108 Braunschweig            */
/*                                                                      */
/************************************************************************/

#if defined(__GO32__)
#define SCC_RXSIZE      8192            /* bei DPMI kann es mehr sein   */
#else
#define SCC_RXSIZE      1024            /* Groesse eines Buffers        */
#endif

#define SCC_TXSIZE      512

#define SCCCHANS    8

#define PCLK        4915200L

/*** Z8530 ******************************************************************/

/* Write Registers */

#define RES_EX_INT  0x10        /* WR0 */
#define RES_TX_IP   0x28
#define RES_ERR     0x30
#define RES_TX_CRC  0x80
#define RES_EOM     0xC0

#define EX_IE       0x01        /* WR1 */
#define TX_IE       0x02
#define RX_IE       0x10

#define RX_DIS      0xC8        /* WR3 */
#define RX_EN       0xC9

#define SDLC        0x20        /* WR4 */

#define TX_DIS      0x61        /* WR5 */
#define TX_EN       0x69
#define RTS         0x02

#define SDLC_FLAG   0x7E        /* WR7 */

#define MINT_EN     0x0A        /* WR9 */
#define HW_RES      0xC0

#define NRZ         0x80        /* WR10 */
#define NRZI        0xA0
#define ABUNDER     0x04

#define TRxC_RTxC   0x20        /* WR11 */
#define DPLL_RTxC   0x64
#define DPLL_BRG    0x74
#define TRxOUT_BRG  0x02
#define TRxOUT_DPLL 0x03

#define DPLL_EN     0x23        /* WR14 */
#define DPLL_DIS    0x60
#define DPLL_SRCBRG 0x80
#define DPLL_NRZI   0xE0
#define BRG_EN      0x63

#define ZC_IE       0x02        /* WR15 */
#define DCD_IE      0x08
#define SYNC_IE     0x10
#define CTS_IE      0x20

/* Read Registers */

#define DCD         0x08        /* RR0 */
#define HUNT        0x10
#define CTS         0x20
#define TX_EOM      0x40
#define ABORT       0x80

#define RESIDUE     0x0E        /* RR1 */
#define RES8        0x06
#define RX_OVR      0x20
#define CRC_ERR     0x40
#define SDLC_EOF    0x80

#define MIN_LEN       15

/*** Macros *****************************************************************/

#define LOBYTE(val)         ((val) & 0xFF)
#define HIBYTE(val)         ((val) >> 8)

#define set(var, bit)       var |=  (1 << bit)
#define res(var, bit)       var &= ~(1 << bit)

#define SCCDELAY            inp (0xE4)      /* non-existing port (2us) */

#ifndef outp
#define outp(p,b) outportb(p,b)
#endif
#ifndef inp
#define inp(p) inportb(p)
#endif

#define WR0(val)            outp ((SCCDELAY, scc->ctrl), val)
#define WR8(val)            WR (8, val)
#define WR(reg, val)        outp ((SCCDELAY, outp (scc->ctrl, reg), \
                                   SCCDELAY, scc->ctrl), val)

#define RR0                 inp ((SCCDELAY, scc->ctrl))
#define RR8                 inp ((SCCDELAY, scc->data))
#define RR(reg)             inp ((SCCDELAY, outp (scc->ctrl, reg), \
                                  SCCDELAY, scc->ctrl))

#define not0(par)           (par ? par : 1)

enum {TX_IDLE, TX_DWAIT, TX_DELAY, TX_ACTIVE, TX_FLUSH, TX_TAIL};

typedef struct __scc_struct
{
    int      l2port;

    unsigned chan;
    unsigned data, ctrl;

    unsigned baud;

    int      flags;

    int      dcd;
    int      rx_err;
    unsigned rx_len;

    int      tx_state;
    unsigned tx_timer;
    char    *tx_buf;
    char    *tx_ptr;
    int      tx_len;

    MBHEAD  *rxfhd;

    UBYTE    rr0;

    unsigned errors;

    unsigned tailtime;
    unsigned slottime;
    unsigned persistance;
    unsigned txdelay;

    unsigned overruns;
    unsigned underruns;
    unsigned crcerrors;
    unsigned lenerrors;
    unsigned buferrors;
} __scc_struct;

typedef struct __scc_board {
  char     *name;
  int       chans;
  unsigned  base;
  unsigned  irq;
  int       data[SCCCHANS];
  int       ctrl;
} __scc_board;

static __scc_struct sccchans[SCCCHANS];
static int scc_major = 0;

static unsigned sccbase;
static unsigned sccirq;
static unsigned sccboard;
static int      sccoldmask;
static unsigned sccminors;

static UWORD   *sccbuf, *sccin, *sccout;

static __scc_board sccboardtab[] =
{
  {"DSCC", 8, 0x300, 7, {1, 0, 3, 2, 5, 4, 7, 6}, 16},  /* DSCC */
  {"OSCC", 4, 0x150, 3, {3, 1, 7, 5, 0, 0, 0, 0}, -1},  /* OSCC */
  {"USCC", 4, 0x300, 7, {0, 1, 2, 3, 0, 0, 0, 0},  4},  /* USCC */
  {NULL  , 0,     0, 0, {0, 0, 0, 0, 0, 0, 0, 0},  0}
};

static void scc_int(int);
static void int_tx(__scc_struct *);
static void int_rx(__scc_struct *);
static void int_ex(__scc_struct *);
static void int_sp(__scc_struct *);
static void scc_clk (__scc_struct *, int);
static int  scc_init(void);
static void scc_exit(void);
static void scc_timer(UWORD);

/* Wenn der Sender keine Daten mehr ausstehen hat, wird ein weiteres
 * Frame in den Sendebuffer kopiert. */
static void scc_put_frame(__scc_struct *scc)
{
  MBHEAD         *fbp;
  int             port;
  LHEAD          *l2flp;

  if (scc->tx_len == 0) { /* erst senden wenn alles raus ist */
    if ((port = scc->l2port) == -1) return;
    if (kick[port]) {
      /* ein weiteres Frame aus der Sendeliste holen */
      l2flp = &txl2fl[port];
      ulink((LEHEAD *)(fbp = (MBHEAD *) l2flp->head));
      kick[port] = ((LHEAD *)l2flp->head != l2flp);

      /* Frame aus dem Buffer in den SCC-Buffer kopieren. Hier wird
         der Interrupt nicht gesperrt, da tx_len zuletzt und atomic
         gesetzt wird. */
      scc->tx_len = cpymbflat(scc->tx_ptr = scc->tx_buf, fbp);

      relink((LEHEAD *)fbp,            /* als gesendet betrachten und in */
            (LEHEAD *)stfl.tail);      /* die gesendet Liste umhaengen   */

      /* Wenn der sender noch nicht an ist, starten wir die Kanal-
         Arbitrierung, ansonsten stoppen wir ein eventuelles TXTAIL
         und gehen wieder zum senden ueber. */

      disable();
      switch (scc->tx_state) {
        case TX_IDLE:
          scc->tx_timer = scc->flags & MODE_d ? 1 : not0 (scc->slottime);
          scc->tx_state = TX_DWAIT;
          break;

        case TX_TAIL:
          scc->tx_timer = 1;
          scc->tx_state = TX_DELAY;
      }
      enable();
    }
  }
}

/* Der SCC-Empfangs-Ringbuffer wird geleert. Dort werden von allen Kanaelen
 * Empfangsdaten gesammelt. Das Format entspricht dem alten l1put().
 */
static void scc_get_frame(void)
{
  int       port, len;
  MBHEAD  **rxfbpp;
  unsigned  action;
  __scc_struct *scc;

  LOOP {
    disable();
    if (sccout == sccin) {
      enable();
      break;
    }
    action = *sccout++;            /* Zeichen aus dem Ringbuffer lesen */
    if (sccout >= sccbuf+SCC_RXSIZE)
      sccout = sccbuf;
    enable();

    scc = sccchans + ((action>>8) & 0x7F);

    if ((port = scc->l2port) == -1)
      continue;

    rxfbpp = &scc->rxfhd;                    /* Adresse RX-Framezeiger */
    if (!(action & 0x8000))                  /* Zeichen oder Befehl ?  */
      {                                      /* Zeichen :              */
        if (!*rxfbpp)                        /* RX-Frame aktiv ?       */
          {
            *rxfbpp = (MBHEAD *)allocb(ALLOC_MBHEAD); /* nein - neues anlegen   */
            (*rxfbpp)->l2port = port;        /*        fuer port       */
          }
        putchr(action & 0xFF,*rxfbpp);       /* Zeichen in Frame       */
      }
    else                                     /* Befehl :               */
      if (*rxfbpp)                           /* nur wenn Frame aktiv   */
        {                                    /* Befehl ausfuehren      */
          if (action & 0x0001)               /* Frame in Muelleimer    */
              relink((LEHEAD *)*rxfbpp, (LEHEAD *)trfl.tail);
          else {                             /* Frame in RX-Liste      */
            len = (action>>1)&3;
            if ((*rxfbpp)->mbpc > len)       /* FCS eleminieren        */
              (*rxfbpp)->mbpc -= len;        /* das erste Byte kommt   */
                                             /* noch vor HUNT          */
            relink((LEHEAD *)*rxfbpp, (LEHEAD *)rxfl.tail);
          }
          *rxfbpp = NULL;                    /* kein RX-Frame aktiv    */
        }
  }
}

static void scc(void)
{
  __scc_struct *scc;

  if (scc_major == 0)
    return;

  scc_get_frame();
  for (scc = sccchans; scc < sccchans+sccminors; scc++) {
    scc_put_frame(scc);
  }
}

static WORD scc_dcd(PORTINFO *port)
{
  __scc_struct *scc;
  int           state = 0;

  scc = &sccchans[port->minor];

  if (scc->tx_state)
    state |= PTTFLAG;
  if (scc->dcd)
    state |= DCDFLAG;
  return(state);
}

static void scc_ctl(int req, int port)
{
  __scc_struct *scc;
  int           minor;
  PORTINFO     *p;

  p = portpar+port;
  minor = p->minor;
  scc = &sccchans[minor];

  switch (req) {
    case L1CCMD :
    case L1CRES :
      if (p->speed > 384)
        p->speed = 384;
      if (p->speed < 3)
        p->speed = 3;
      scc->baud = (unsigned) (p->speed * 100);
      scc->slottime = p->slottime;
      scc->persistance = p->persistance;
      scc->txdelay = p->txdelay;
      scc->tailtime = p->speed == 3 ? 4 : TAILTIME;
      scc->flags = p->l1mode;

      WR (4, SDLC);
      WR (10, scc->flags & MODE_z ? NRZ | ABUNDER : NRZI | ABUNDER);
      WR (7, SDLC_FLAG);
      WR (3, RX_DIS);
      WR (5, TX_DIS);

      scc_clk (scc, scc->tx_state != TX_IDLE);

      WR (3, RX_EN);
      WR (5, scc->flags & MODE_t ? TX_EN : TX_DIS);

      WR (15, SYNC_IE | DCD_IE);
      WR0 (RES_EX_INT);
      WR0 (RES_EX_INT);
      WR (1, EX_IE | TX_IE | RX_IE);

      scc->rr0 = RR0;

      /* nochmal zur Sicherheit den CHIP-interrupt-master-enable an */
      WR (2, (scc->chan>>1) << 4);
      WR (9, MINT_EN);
      break;
  }
  default_l1ctl(req, port); /* Flags loeschen */
};

static int scc_istome(int major, char *devname)
{
  char   name[21], *cp;
  int    minor = 0;

  strncpy(name, devname, 20); /* Minor bestimmen und abschneiden */
  name[20] = 0;
  for (cp = name; isalpha(*cp); cp++); /* Zahl suchen */
  if (isdigit(*cp)) minor = atoi(cp);
  *cp = 0; /* kann nicht schaden */

  if (minor < 0 || minor >= sccminors)
    return(NO_MINOR); /* falscher Geraetekanal (minor) */

  return(minor);
}

static int scc_attach(int port, int minor, BOOLEAN check_only)
{
  __scc_struct *scc;

  scc = &sccchans[minor];

  if (scc_major) {
    if (scc->l2port == -1) {
      if (!check_only) {
        scc->l2port = port;
        portpar[port].minor = minor;
        scc_ctl(L1CRES, port);
      }
      return(1);
    }
    if (scc->l2port == port)
      return(1);
  }
  return(0); /* versuchte Doppeleintragung */
}

static int scc_detach(int port)
{
  __scc_struct *scc = &sccchans[portpar[port].minor];

  WR (1, 0);

  if (scc->rxfhd) {
    dealmb(scc->rxfhd);
    scc->rxfhd = NULL;
  }
  scc->l2port = -1;
  return(1);
}

static void scc_info(int what, int port, MBHEAD *mbp)
{
  int           minor, cnt;
  __scc_struct *scc;

  minor = portpar[port].minor;

  switch (what) {
    case HW_INF_IDENT :
    case HW_INF_INFO :
      putprintf(mbp, "%s%u", sccboardtab[sccboard].name, minor);
      break;
    case HW_INF_STAT :
      for (minor = cnt = 0, scc = sccchans; minor < sccminors; minor++, scc++)
        if (scc->l2port != -1) {
          if (cnt++ == 0)
            putstr("\rSCC-Statistics:\r\r", mbp);
          putprintf(mbp, "  SCC%u       RxOvr: %5u  TxUnd: %5u  RxCRC: %5u  RxBuf: %5u\r",
                         minor, scc->overruns, scc->underruns, scc->crcerrors, scc->buferrors);
        }
      break;
    case HW_INF_CLEAR :
      scc = &sccchans[minor];
      scc->overruns =
      scc->underruns =
      scc->crcerrors =
      scc->lenerrors =
      scc->buferrors = 0;
      /* durchfallen */
    default :
      default_l1info(what, port, mbp);
  }
}

/* SCC-Modes:
   t            -> Sendetakt extern (DF9IC)
   e            -> Sendetakt BRG (32-fach)
                -> Sendetakt DPLL (32-fach)
   r            -> Empfangstakt extern (DF9IC)
                -> Empfangstakt DPLL
*/

static void sccput(UWORD action)
{
  *sccin++ = action;
  if (sccin >= sccbuf+SCC_RXSIZE)
    sccin = sccbuf;
}

#define rxput(port, val)    sccput (((port) << 8) | (val))
#define rxeof(port, fcs)    sccput (((port) << 8) | 0x8000 | (fcs<<1))
#define rxdiscard(port)     sccput (((port) << 8) | 0x8001)

/*** SCC Clock **************************************************************/

static void scc_clk (__scc_struct *scc, int tx_on)
{
    UWORD tconst;

    if (scc->flags & MODE_t) /* externer Takt */
    {
        WR (11, TRxC_RTxC);
        WR (14, DPLL_DIS);
    }
    else
    {
        tconst = PCLK / 64L / scc->baud;
        if (tx_on)
            tconst *= 32;
        tconst -= 2;

        WR (12, LOBYTE (tconst));
        WR (13, HIBYTE (tconst));

        WR (11, tx_on ? DPLL_BRG | TRxOUT_BRG
                      : (scc->flags & MODE_e ? DPLL_RTxC | TRxOUT_BRG
                                             : DPLL_RTxC | TRxOUT_DPLL));

        WR (14, DPLL_SRCBRG);
        if ((scc->flags & MODE_z) == 0) WR (14, DPLL_NRZI);
        WR (14, DPLL_EN);
    }
}

/*** SCC TX *****************************************************************/

static void scc_tx (__scc_struct *scc, int tx_on)
{
    /* wenn MODE_t nicht gesetzt ist, muessen wir bei TX umschalten */
    if ((scc->flags & MODE_t) == 0)
    {
        WR (3, RX_DIS);
        WR (5, TX_DIS);

        scc_clk (scc, tx_on);

        if (tx_on)
            WR (5, TX_EN | RTS);
        else
            WR (3, RX_EN);
    }
    else
        WR (5, tx_on ? TX_EN | RTS : TX_EN);
}

/*** Timer ******************************************************************/

static void scc_timer (UWORD ticks)
{
    __scc_struct *scc;

    if (scc_major == 0)
      return;

    for (scc = sccchans; scc < sccchans+sccminors; scc++)
      if (scc->l2port != -1)
      {
        disable ();

        if (scc->tx_timer)
        {
          if (scc->tx_timer <= ticks)
          {
            scc->tx_timer = 0;

            switch (scc->tx_state)
            {
              case TX_DWAIT:
                if (   (scc->flags & MODE_d)
                    || (   (!scc->dcd)
                        && ((rand()&0xff) <= scc->persistance)
                       )
                   )
                {
                  scc_tx(scc, TRUE);
                  scc->tx_timer = not0(scc->txdelay);
                  scc->tx_state = TX_DELAY;
                }
                else
                  scc->tx_timer = not0(scc->slottime);
                break;

              case TX_DELAY:
                WR0 (RES_TX_CRC);
                WR8 (*scc->tx_ptr++);
                WR0 (RES_EOM);
                scc->tx_state = TX_ACTIVE;
                break;

              case TX_TAIL:
                scc_tx(scc, FALSE);
                scc->tx_state = TX_IDLE;
            }
          }
          else
            scc->tx_timer -= ticks;
        }
        enable ();
      }
}

/*** SCC Interrupt **********************************************************/

static void (*handler[]) (__scc_struct *) = {int_tx, int_ex, int_rx, int_sp};

//#define DEBUG(x) _farpokeb(_dos_ds,0xB8000, x);

static void scc_int(int foo)
{
    __scc_struct *scc;
    UBYTE         rr2;

    //DEBUG(ch++);

    for (scc = sccchans; scc < sccchans+sccminors; )
        if (RR (3))
        {
            scc++;
            rr2 = RR (2);
            handler[(rr2 >> 1) & 3] (&sccchans[(rr2 >> 3) ^ 1]);
            scc = sccchans;
        }
        else
            scc += 2;
}

/*** TX Handler *************************************************************/

static void int_tx(__scc_struct *scc)
{
    switch (scc->tx_state)
    {
      case TX_ACTIVE:
        if (RR0 & TX_EOM)
        {
          scc->underruns++;
          scc->errors++;
          scc->tx_ptr = scc->tx_buf; /* txrewind */
          scc->tx_state = TX_FLUSH;
        }
        else
          if (scc->tx_ptr >= scc->tx_buf + scc->tx_len) /* Frameende? */
          {
            WR (10, scc->flags & MODE_z ? NRZ : NRZI);
            WR0 (RES_TX_IP);
            scc->tx_state = TX_FLUSH;
            scc->tx_len = 0; /* Frame gesendet */
          } else
            WR8 (*scc->tx_ptr++);
        break;

    case TX_FLUSH:
        WR (10, scc->flags & MODE_z ? NRZ | ABUNDER : NRZI | ABUNDER);

        if (scc->tx_ptr < scc->tx_buf + scc->tx_len) /* noch Daten? */
        {
            WR0 (RES_TX_CRC);
            WR8 (*scc->tx_ptr++);
            WR0 (RES_EOM);
            scc->tx_state = TX_ACTIVE;
        }
        else
        {
            WR0 (RES_TX_IP);
            scc->tx_timer = scc->tailtime; /* + clkstep */
            scc->tx_state = TX_TAIL;
        }
    }
}

/*** Extern Status Change ***************************************************/

static void int_ex (__scc_struct *scc)
{
    static UBYTE rr0, delta;

    delta = (rr0 = RR0) ^ scc->rr0;

    if (scc->l2port != -1)
    {
        if (scc->rx_len && rr0 & (ABORT | HUNT))
        {
            rxdiscard (scc->chan);
            scc->rx_len = 0;
            scc->rx_err = FALSE;
        }

        if (delta & (scc->flags & MODE_c ? HUNT : DCD))
        {
            scc->dcd =   (scc->flags & MODE_c)
                       ? (!(rr0 & HUNT))
                       : (rr0 & DCD);
        }
    }

    scc->rr0 = rr0;
    WR0 (RES_EX_INT);
}

/*** RX Handler *************************************************************/

static void int_rx (__scc_struct *scc)
{
    UBYTE rr8;

    rr8 = RR8;

    if (!scc->rx_err)
    {
        /* scc->rx_err = */ rxput (scc->chan, rr8);
        scc->rx_len++;
    }
}

/*** Special RX Condition ***************************************************/

static void int_sp (__scc_struct *scc)
{
    UBYTE rr1;

    rr1 = RR (1);
    RR8;

    if (rr1 & RX_OVR)
    {
        scc->overruns++;
        scc->errors++;
        scc->rx_err = TRUE;
    }

    if (rr1 & SDLC_EOF)
    {
        if (   !scc->rx_err
            && scc->rx_len >= MIN_LEN+1
            && (rr1 & (CRC_ERR | RESIDUE)) == RES8)
        {
            rxeof (scc->chan, 1);
        }
        else
            if (scc->rx_len) {
                rxdiscard (scc->chan);
                /*if (scc->rx_len > 2) {
                  if (scc->rx_len < MIN_LEN+1) scc->lenerrors++;
                  if ((rr1 & (CRC_ERR | RESIDUE)) != RES8) scc->crcerrors++;
                }*/
            }
        scc->rx_len = 0;
        scc->rx_err = FALSE;
    }

    WR0 (RES_ERR);
}

/*** SCC Hardware Reset *****************************************************/

static void scc_hw_reset (void)
{
    __scc_struct *scc;
    UWORD         delay;

    for (scc = sccchans; scc < &sccchans[sccminors]; scc += 2)
    {
        RR0;
        WR (9, HW_RES);
        for (delay = 100; delay--;) SCCDELAY;
    }
}

/*** SCC Detect *************************************************************/
static int scc_detect(int max)
{
    __scc_struct *scc;

    for (scc = sccchans; scc < sccchans+max; scc++)
    {
        RR0;
        WR (13, 0x55); if (RR (13) != 0x55) break;
        WR (13, 0xAA); if (RR (13) != 0xAA) break;
    }
    return((int) (scc - sccchans));
}

/* Boardtyp auswaehlen, Defaultparameter setzen */
static int scc_init(void)
{
  char         *str;
  __scc_board  *board;
  __scc_struct *scc;
  int           chan;

  for (sccboard = 0, board = sccboardtab; board->name; board++, sccboard++) {
    if ((str = getenv(board->name)) != NULL) {
      sccbase = board->base;
      sccirq  = board->irq;
      sscanf(str, "%x,%u", &sccbase, &sccirq);
      if (sccbase && sccirq) {

        if ((sccbuf = malloc(SCC_RXSIZE*sizeof(UWORD))) == NULL)
          return(0);

        sccin = sccout = sccbuf;

        for (chan = 0, scc = sccchans; chan < SCCCHANS; chan++, scc++) {
          scc->l2port = -1;
          scc->rxfhd  = NULL;
          scc->chan   = chan;
          scc->data   = sccbase + board->data[chan];
          scc->ctrl   = scc->data + board->ctrl;
          if ((scc->tx_buf = malloc(SCC_TXSIZE)) == NULL) {
            while (--scc >= sccchans)
              free(scc->tx_buf);
            free(sccbuf);
            return(0);
          }
          scc->tx_len = 0;
        }

        scc_hw_reset();

        disable(); /* Interrupt aus */
        allocirq(sccirq, 0, scc_int, 0); /* neuen Vektor setzen */
        sccoldmask = getmask(sccirq);
        maskon(sccirq);
        enable();

        xprintf("--- /dev/SCC (0x%03X,IRQ%u), ", sccbase, sccirq);
        sccminors = scc_detect(board->chans);
        if ((sccminors & 1) == 0) {
          if (sccminors) {
            printf("card is %s, %u channels\n", board->name, sccminors);
            return(1);
          } else xprintf("%s not detected\n", board->name);
        } else xprintf("channel number is odd\n");
      }
    }
  }
  return(0); /* keine Karte gefunden/konfiguriert */
}

static void scc_exit(void)
{
  if (scc_major) {
    if (sccoldmask) maskoff(sccirq);
    freeirq(sccirq);
    scc_hw_reset();
  }
}

static int register_scc(void)
{
  MAJOR *m;

  if (scc_init()) {
    m = register_major();
    m->name    = "USCC/DSCC/OSCC";
    m->istome  = scc_istome;
    m->exit    = scc_exit;
    m->handle  = scc;
    m->ctl     = scc_ctl;
    m->dcd     = scc_dcd;
    m->attach  = scc_attach;
    m->detach  = scc_detach;
    m->info    = scc_info;
    m->timer   = scc_timer;
    return(scc_major = num_major);
  }
  return(0);
}

/* End of os/go32/scc.c */
