Logo Search packages:      
Sourcecode: baycomepp version File versions

drvresident.c

/*****************************************************************************/

/*
 *      drvresident.c  -- HDLC packet radio modem for EPP/ECP using FPGA usermode driver.
 *
 *      Copyright (C) 1998-2000, 2002  Thomas Sailer (sailer@ife.ee.ethz.ch)
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *
 *      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 the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Please note that the GPL allows you to use the driver, NOT the radio.
 *  In order to use the radio, you need a license from the communications
 *  authority of your country.
 *
 */

/*****************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <sys/types.h>
#include <sys/uio.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <dlfcn.h>

#include "drv.h"
#include "parport.h"

/* ---------------------------------------------------------------------- */

int logging = 0;
int logpackets = 0;
struct baycomepp_state state;
const char *progname = "baycomepp";

/* --------------------------------------------------------------------- */
/*
 * Logging functions
 */

void errprintf(int severity, const char *fmt, ...)
{
      va_list args;

      va_start(args, fmt);
      if (logging) {
            char tmp[512];
            vsnprintf(tmp, sizeof(tmp), fmt, args);
            syslog(severity, tmp);
      } else {
            fprintf(stderr, "baycomepp[%u]: ", getpid());
            vfprintf(stderr, fmt, args);
      }
      va_end(args);
      if (severity <= SEV_FATAL)
            exit(1);
}

/* --------------------------------------------------------------------- */

void errstr(int severity, const char *st)
{
      errprintf(severity, "%s: %s\n", st, strerror(errno));
}

/* --------------------------------------------------------------------- */

void epp_error(const char *msg)
{
      errprintf(SEV_FATAL, msg);
}

/* --------------------------------------------------------------------- */
/*
 * the CRC routines are stolen from WAMPES
 * by Dieter Deyke
 */

const u_int16_t crc_ccitt_table[0x100] = {
        0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
        0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
        0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
        0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
        0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
        0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
        0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
        0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
        0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
        0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
        0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
        0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
        0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
        0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
        0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
        0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
        0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
        0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
        0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
        0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
        0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
        0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
        0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
        0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
        0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
        0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
        0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
        0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
        0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
        0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
        0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
        0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};

/* --------------------------------------------------------------------- */

extern inline u_int16_t calc_crc_ccitt(const u_int8_t *buffer, int len)
{
        u_int16_t crc = 0xffff;

        for (;len>0;len--)
                crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
        crc ^= 0xffff;
      return crc;
}

extern inline void append_crc_ccitt(u_int8_t *buffer, int len)
{
        u_int16_t crc = calc_crc_ccitt(buffer, len);
        *buffer++ = crc;
        *buffer++ = crc >> 8;
}

extern inline int check_crc_ccitt(const u_int8_t *buffer, int len)
{
        u_int16_t crc = calc_crc_ccitt(buffer, len);
        return (crc & 0xffff) == 0x0f47;
}

/* ---------------------------------------------------------------------- */

#define ADDRXCH(x)                        \
do {                                      \
      state.kissout.b[wr] = (x);           \
      wr = (wr + 1) % KISSOUTBUF_SIZE;  \
      if (wr == rd)                     \
            goto errout;              \
        total++;                          \
} while (0);

static size_t rxpacket(const struct iovec *vector, size_t count)
{
      u_int8_t *bp;
      size_t len;
      size_t total = 0;
      unsigned wr = state.kissout.wr;
      unsigned rd = state.kissout.rd;

      ADDRXCH(KISS_FEND);
      for (; count > 0; count--, vector++) {
            for (bp = vector->iov_base, len = vector->iov_len; len > 0; len--, bp++) {
                  if (*bp == KISS_FEND) {
                        ADDRXCH(KISS_FESC);
                        ADDRXCH(KISS_TFEND);
                  } else if (*bp == KISS_FESC) {
                        ADDRXCH(KISS_FESC);
                        ADDRXCH(KISS_TFESC);
                  } else {
                        ADDRXCH(*bp);
                  }
            }
      }
      if (total <= 1)
            return 0;
      ADDRXCH(KISS_FEND);
      state.kissout.wr = wr;
      state.stat.kiss_out++;
      return total;

  errout:
      state.stat.kiss_outerr++;
      return 0;
}

#undef ADDRXCH

/* --------------------------------------------------------------------- */
/*
 * high performance HDLC encoder
 * yes, it's ugly, but generates pretty good code
 */

#define ENCODEITERA(j)                         \
{                                              \
        if (!(notbitstream & (0x1f0 << j)))    \
                goto stuff##j;                 \
}                                              \
  encodeend##j:

#define ENCODEITERB(j)                                          \
({                                                              \
  stuff##j:                                                     \
        bitstream &= ~(0x100 << j);                             \
        bitbuf = (bitbuf & (((2 << j) << numbit) - 1)) |        \
                ((bitbuf & ~(((2 << j) << numbit) - 1)) << 1);  \
        numbit++;                                               \
        notbitstream = ~bitstream;                              \
        goto encodeend##j;                                      \
})

static int hdlc_enqueue(u_int8_t *pkt, int len)
{
        unsigned bitstream, notbitstream, bitbuf, numbit, crc;
      u_int8_t crcarr[2];
      unsigned wr = state.htx.wr;

      state.htx.buf[wr] = 0x7e;
      wr = (wr + 1) % TXBUFFER_SIZE;
      if (wr == state.htx.rd)
            return -1;
      crc = calc_crc_ccitt(pkt, len);
      crcarr[0] = crc;
      crcarr[1] = crc >> 8;
      bitstream = bitbuf = numbit = 0;
      while (len > -2) {
            bitstream >>= 8;
            bitstream |= ((unsigned int)*pkt) << 8;
            bitbuf |= ((unsigned int)*pkt) << numbit;
            notbitstream = ~bitstream;
            pkt++;
            len--;
            if (!len)
                  pkt = crcarr;
            ENCODEITERA(0);
            ENCODEITERA(1);
            ENCODEITERA(2);
            ENCODEITERA(3);
            ENCODEITERA(4);
            ENCODEITERA(5);
            ENCODEITERA(6);
            ENCODEITERA(7);
            goto enditer;
            ENCODEITERB(0);
            ENCODEITERB(1);
            ENCODEITERB(2);
            ENCODEITERB(3);
            ENCODEITERB(4);
            ENCODEITERB(5);
            ENCODEITERB(6);
            ENCODEITERB(7);
      enditer:
            numbit += 8;
            while (numbit >= 8) {
                  state.htx.buf[wr] = bitbuf;
                  wr = (wr + 1) % TXBUFFER_SIZE;
                  if (wr == state.htx.rd)
                        return -1;
                  bitbuf >>= 8;
                  numbit -= 8;
            }
      }
      bitbuf |= 0x7e7e << numbit;
      numbit += 16;
      while (numbit >= 8) {
            state.htx.buf[wr] = bitbuf;
            wr = (wr + 1) % TXBUFFER_SIZE;
            if (wr == state.htx.rd)
                  return -1;
            bitbuf >>= 8;
            numbit -= 8;
      }
      state.htx.wr = wr;
      return 0;
}

/* ---------------------------------------------------------------------- */

static void do_rxpacket(void)
{
      static const u_int8_t kissdata = KISS_CMD_DATA;
      struct iovec iov[2];
      char buf[512];

      if (state.hrx.bufcnt < 4) 
            return;
      if (!check_crc_ccitt(state.hrx.buf, state.hrx.bufcnt)) 
            return;
      iov[0].iov_len = 1;
      iov[0].iov_base = &kissdata;
      iov[1].iov_len = state.hrx.bufcnt-2;
      iov[1].iov_base = state.hrx.buf;
      rxpacket(iov, 2);
      state.stat.pkt_in++;
      if (!logpackets)
            return;
      if (snprintpkt(buf, sizeof(buf), state.hrx.buf, state.hrx.bufcnt-2) != -1)
            printf("rx: %s", buf);
}

#define DECODEITERA(j)                                                        \
{                                                                             \
        if (!(notbitstream & (0x0fc << j)))              /* flag or abort */  \
                goto flgabrt##j;                                              \
        if ((bitstream & (0x1f8 << j)) == (0xf8 << j))   /* stuffed bit */    \
                goto stuff##j;                                                \
}                                                                             \
  enditer##j:

#define DECODEITERB(j)                                                                 \
({                                                                                     \
  flgabrt##j:                                                                          \
        if (!(notbitstream & (0x1fc << j))) {              /* abort received */        \
                stat = 0;                                                              \
                goto enditer##j;                                                       \
        }                                                                              \
        if ((bitstream & (0x1fe << j)) != (0x0fc << j))   /* flag received */          \
                goto enditer##j;                                                       \
        if (stat)                                                                      \
                do_rxpacket();                                                         \
        state.hrx.bufcnt = 0;                                                          \
        state.hrx.bufptr = state.hrx.buf;                                              \
        stat = 1;                                                                      \
        numbits = 7-j;                                                                 \
        goto enditer##j;                                                               \
  stuff##j:                                                                            \
        numbits--;                                                                     \
        bitbuf = (bitbuf & ((~0xff) << j)) | ((bitbuf & ~((~0xff) << j)) << 1);        \
        goto enditer##j;                                                               \
})
        
/* --------------------------------------------------------------------- */

static void hdlc_receive(u_int8_t *pkt, int len)
{
        unsigned int bitbuf, notbitstream, bitstream, numbits, stat;

      state.stat.bytes_in += len;
      numbits = state.hrx.numbits;
      stat = state.hrx.state;
      bitstream = state.hrx.bitstream;
      bitbuf = state.hrx.bitbuf;
      for (; len > 0; len--, pkt++) {
            bitstream >>= 8;
            bitstream |= ((unsigned int)*pkt) << 8;
            bitbuf >>= 8;
            bitbuf |= ((unsigned int)*pkt) << 8;
            numbits += 8;
            notbitstream = ~bitstream;
            DECODEITERA(0);
            DECODEITERA(1);
            DECODEITERA(2);
            DECODEITERA(3);
            DECODEITERA(4);
            DECODEITERA(5);
            DECODEITERA(6);
            DECODEITERA(7);
            goto enddec;
            DECODEITERB(0);
            DECODEITERB(1);
            DECODEITERB(2);
            DECODEITERB(3);
            DECODEITERB(4);
            DECODEITERB(5);
            DECODEITERB(6);
            DECODEITERB(7);
      enddec:
            while (stat && numbits >= 8) {
                  if (state.hrx.bufcnt >= RXBUFFER_SIZE) {
                        stat = 0;
                  } else {
                        *(state.hrx.bufptr)++ = bitbuf >> (16-numbits);
                        state.hrx.bufcnt++;
                        numbits -= 8;
                  }
            }
      }
        state.hrx.numbits = numbits;
      state.hrx.state = stat;
      state.hrx.bitstream = bitstream;
      state.hrx.bitbuf = bitbuf;
}

/* ---------------------------------------------------------------------- */

static u_int16_t random_seed;

extern inline u_int16_t random_num(void)
{
      random_seed = 28629 * random_seed + 157;
      return random_seed;
}

/* --------------------------------------------------------------------- */

/*
 * here we do the channel access algorithm, and process calibration bytes
 */

extern inline void arbitrate(void)
{
      if (state.htx.rd == state.htx.wr)
            return;
      if (!state.chaccesspar.fulldup) {
            if (state.access.slotcnt > 0)
                  return;
            state.access.slotcnt = state.chaccesspar.slottime;
            if (state.access.dcd)
                  return;
            if ((random_num() % 256) > state.chaccesspar.ppersist)
                  return;
      }
      state.access.state = tx_keyup;
      state.access.flagcnt = (state.access.bitrateby25 * state.chaccesspar.txdelay) >> 5;
      state.stat.ptt_keyed++;
}

static int hdlc_transmit(u_int8_t *pkt, int len)
{
      int ret = 0, i;

      while (len > 0) {
            switch (state.access.state) {
            case tx_keyup:
                  i = state.access.flagcnt;
                  if (i <= 0) {
                        state.access.state = tx_data;
                        break;
                  }
                  if (i > len)
                        i = len;
                  state.access.flagcnt -= i;
                  memset(pkt, 0x7e, i);
                  ret += i;
                  pkt += i;
                  len -= i;
                  break;
                  
            case tx_data:
                  if (state.htx.rd == state.htx.wr) {
                        state.access.state = tx_tail;
                        state.access.flagcnt = (state.access.bitrateby25 * state.chaccesspar.txtail) >> 5;
                        break;
                  }
                  i = ((state.htx.rd > state.htx.wr) ? TXBUFFER_SIZE : state.htx.wr) - state.htx.rd;
                  if (i > len)
                        i = len;
                  memcpy(pkt, state.htx.buf + state.htx.rd, i);
                  state.htx.rd = (state.htx.rd + i) % TXBUFFER_SIZE;
                  ret += i;
                  pkt += i;
                  len -= i;
                  break;                  

            case tx_tail:
                  i = state.access.flagcnt;
                  if (i <= 0) {
                        if (state.access.ptt)
                              return ret;
                        state.access.state = tx_idle;
                        state.access.slotcnt = state.chaccesspar.slottime;
                        break;
                  }
                  if (i > len)
                        i = len;
                  state.access.flagcnt -= i;
                  memset(pkt, 0x7e, i);
                  ret += i;
                  pkt += i;
                  len -= i;
                  break;

            default:
                  arbitrate();
                  if (state.access.state == tx_keyup)
                        break;
                  i = state.access.calib;
                  if (i <= 0)
                        return ret;
                  if (i > len)
                        i = len;
                  state.access.calib -= i;
                  memset(pkt, 0, i);
                  ret += i;
                  pkt += i;
                  len -= i;
                  break;
            }
      }
      return ret;
}

/* --------------------------------------------------------------------- */

void sighandler(int sig)
{
      errprintf(LOG_INFO, "terminating on receiving signal %i\n", sig);
      reset_modem();
      exit(0);
}

/* --------------------------------------------------------------------- */

void sig_usr2(int sig)
{
      if (state.access.calib > 0) {
            errprintf(LOG_DEBUG, "terminating calibration\n");
            state.access.calib = 0;
      } else {
            errprintf(LOG_DEBUG, "calibrating for 1min\n");
            state.access.calib = state.access.bitrate * (60/8);
      }
      if (signal(SIGUSR2, sig_usr2) == SIG_ERR)
            errstr(SEV_ERROR, "signal");
}

/* --------------------------------------------------------------------- */

void sig_usr1(int sig)
{
      printf("Statistics:\n  KISS out: %ld errors: %ld  in: %ld errors: %ld\n"
             "  Packets out: %ld  in: %ld\n"
             "  Bytes out: %ld  in: %ld\n"
             "  PTT:%c  DCD:%c  PTT keyed: %ld\n"
             "  ch par: txdel %u slot %u ppersist %u txtail %u fulldup %u\n"
             "  status 0x%02x  icnt %d %d %d  ocnt %d %d %d  IntRate: %d\n",
             state.stat.kiss_out, state.stat.kiss_outerr,
             state.stat.kiss_in, state.stat.kiss_inerr,
             state.stat.pkt_out, state.stat.pkt_in, 
             state.stat.bytes_out, state.stat.bytes_in,
             state.access.ptt ? 'P' : '-', state.access.dcd ? 'D' : '-', state.stat.ptt_keyed,
             state.chaccesspar.txdelay, state.chaccesspar.slottime, state.chaccesspar.ppersist,
             state.chaccesspar.txtail, state.chaccesspar.fulldup,
             state.stat.status, state.stat.icnt1, state.stat.icnt2, state.stat.icnt3,
             state.stat.ocnt1, state.stat.ocnt2, state.stat.ocnt3, state.stat.intcnt);

      printf("TX: rd %d wr %d  st: %d  slotcnt: %d\n",
             state.htx.rd, state.htx.wr, state.access.state, state.access.slotcnt);

      if (signal(SIGUSR1, sig_usr1) == SIG_ERR)
            errstr(SEV_ERROR, "signal");
}

/* ---------------------------------------------------------------------- */

/*
 * Process KISS io
 */

extern inline void process_pkt(u_int8_t *pkt, unsigned int len)
{
      char buf[512];

      if (len < 2)
            return;
      switch (pkt[0]) {
      case KISS_CMD_DATA:
            if (hdlc_enqueue(pkt+1, len-1))
                  state.stat.pkt_outerr++;
            else
                  state.stat.pkt_out++;
            if (!logpackets)
                  return;
            if (snprintpkt(buf, sizeof(buf), pkt+1, len-1) != -1)
                  printf("tx: %s", buf);
            return;

      case KISS_CMD_TXDELAY:
            state.chaccesspar.txdelay = pkt[1];
            errprintf(SEV_INFO, "kiss: txdelay = %ums\n", state.chaccesspar.txdelay*10);
            return;
                  
      case KISS_CMD_SLOTTIME:
            state.chaccesspar.slottime = pkt[1];
            errprintf(SEV_INFO, "kiss: slottime = %ums\n", state.chaccesspar.slottime*10);
            return;

      case KISS_CMD_PPERSIST:
            state.chaccesspar.ppersist = pkt[1];
            errprintf(SEV_INFO, "kiss: ppersist = %u/256\n", state.chaccesspar.ppersist);
            return;

      case KISS_CMD_TXTAIL:
            state.chaccesspar.txtail = pkt[1];
            errprintf(SEV_INFO, "kiss: txtail = %ums\n", state.chaccesspar.txtail*10);
            return;

      case KISS_CMD_FULLDUP:
            state.chaccesspar.fulldup = !!pkt[1];
            errprintf(SEV_INFO, "kiss: %sduplex\n", state.chaccesspar.fulldup ? "full" : "half");
            return;

      default:
            errprintf(SEV_WARNING, "unknown kiss packet: 0x%02x 0x%02x\n", pkt[0], pkt[1]);
            return;
      }
}

/* --------------------------------------------------------------------- */

/* we decode on the fly in place */

extern inline void kiss_decode(u_int8_t *b, int len)
{
      int nlen = 0;
      u_int8_t *p1 = b, *p2 = b;

            while (len > 0) {
            if (*p1 != KISS_FESC) {
                  *p2++ = *p1++;
                  nlen++;
                  len--;
                  continue;
            }
            if (len < 2)
                  goto err; /* invalid escape */
            if (p1[1] == KISS_TFEND)
                  *p2++ = KISS_FEND;
            else if (p1[1] == KISS_TFESC)
                  *p2++ = KISS_FESC;
            else
                  goto err; /* invalid escape */
            nlen++;
            p1 += 2;
            len -= 2;
      }
      process_pkt(b, nlen);
      state.stat.kiss_in++;
      return;
 err:
      state.stat.kiss_inerr++;
}

/* --------------------------------------------------------------------- */

extern inline void kiss_read(void)
{
      char *cp1, *cp2, *endp;
      int i;

      if (state.kissin.ptr >= KISSINBUF_SIZE)  /* discard oversized packets */
            state.kissin.ptr = 0;
      i = read(state.kissfd, state.kissin.b + state.kissin.ptr, KISSINBUF_SIZE - state.kissin.ptr);
      if (i < 0) {
            if (errno == EAGAIN)
                  return;
            errstr(SEV_FATAL, "read");
      }
      if (i == 0)
            return;
      state.kissin.ptr += i;
      endp = state.kissin.b + state.kissin.ptr;
      cp1 = memchr(state.kissin.b, KISS_FEND, state.kissin.ptr);
      while (cp1) {
            cp2 = memchr(cp1+1, KISS_FEND, endp - cp1 - 1);
            if (!cp2) {
                  state.kissin.ptr = endp-cp1;
                  memmove(state.kissin.b, cp1, state.kissin.ptr);
                  return;
            }
            kiss_decode(cp1+1, cp2-cp1-1);
            cp1 = cp2;
      }
}

/* ---------------------------------------------------------------------- */

extern inline void kiss_write(void)
{
      struct iovec iov[2];
      int i;

      if (state.kissout.rd == state.kissout.wr)
            return;
      if (state.kissout.rd > state.kissout.wr) {
            iov[0].iov_len = KISSOUTBUF_SIZE - state.kissout.rd;
            iov[0].iov_base = state.kissout.b + state.kissout.rd;
            iov[1].iov_len = state.kissout.wr;
            iov[1].iov_base = state.kissout.b;
            i = writev(state.kissfd, iov, 2);
      } else
            i = write(state.kissfd, state.kissout.b + state.kissout.rd, 
                    state.kissout.wr - state.kissout.rd);
      if (i < 0) {
            if (errno == EAGAIN)
                  return;
            errstr(SEV_FATAL, "read");
      }
      state.kissout.rd = (state.kissout.rd + i) % KISSOUTBUF_SIZE;
}

/* ---------------------------------------------------------------------- */

static void handle_kiss(void)
{
      kiss_read();
      kiss_write();
      /* reschedule */
      usleep(8000); /* must be more than 2ms, Linux busy-waits up to 2ms! */
      state.stat.intcnt++;
      state.access.slotcnt--;
}

/* ---------------------------------------------------------------------- */

/*
 * EPP Mode
 */

/* _MUST_ not return! */

static void epp_modem(void)
{
      unsigned char eio[1024];
      u_int8_t stat;
      int cnt, icnt, ocnt, icntx, ocntx;
      
      /* reset modem */
      eio[0] = 0;
      eio[1] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE;
      if (parport_epp_write_addr(eio, 2) != 2)
            goto errret;
      for (;;) {
            if (parport_epp_read_addr(&stat, 1) != 1)
                  goto errret;
            /* determine TX fifo size from status bits */
            switch (stat & (EPP_NTAEF|EPP_NTHF)) {
            case EPP_NTHF:
                  ocntx = 2048 - 256;
                  break;

            case EPP_NTAEF:
                  ocntx = 2048 - 1793;
                  break;

            case 0:
                  ocntx = 0;
                  break;

            default:
                  ocntx = 2048 - 1025;
                  break;
            }
            /* determine RX fifo size from status bits */
            switch (stat & (EPP_NRAEF|EPP_NRHF)) {
            case EPP_NRHF:
                  icntx = 0;
                  break;
                
            case EPP_NRAEF:
                  icntx = 1025;
                  break;
                
            case 0:
                  icntx = 1793;
                  break;
                
            default:
                  icntx = 256;
                  break;
            }
            /* try to determine the FIFO count if in extended mode */
            if (state.config.extstat) {
                  eio[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|1;
                  if (parport_epp_write_addr(eio, 1) != 1)
                        goto errret;
                  if (parport_epp_read_addr(eio, 2) != 2)
                        goto errret;
                  icnt = eio[0] | (((unsigned int)eio[1]) << 8);
                  eio[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|2;
                  if (parport_epp_write_addr(eio, 1) != 1)
                        goto errret;
                  if (parport_epp_read_addr(eio, 2) != 2)
                        goto errret;
                  ocnt = eio[0] | (((unsigned int)eio[1]) << 8);
                  eio[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE;
                  if (parport_epp_write_addr(eio, 1) != 1)
                        goto errret;
                  ocnt = 16384 - (ocnt & 0x7fff);
                  icnt &= 0x7fff;
            } else {
                  ocnt = ocntx;
                  icnt = icntx;
            }
            /* update status */
            state.stat.icnt3 = state.stat.icnt1;
            state.stat.icnt1 = icnt;
            state.stat.icnt2 = icntx;
            state.stat.ocnt3 = state.stat.ocnt1;
            state.stat.ocnt1 = ocnt;
            state.stat.ocnt2 = ocntx;
            state.stat.status = stat;
            state.access.dcd = !(stat & EPP_DCDBIT);
            state.access.ptt = !!(stat & EPP_PTTBIT);
            /* rx */
            while (icnt > 0) {
                  cnt = icnt;
                  if (cnt > sizeof(eio))
                        cnt = sizeof(eio);
                  icnt -= cnt;
                  if (parport_epp_read_data(eio, cnt) != cnt)
                        goto errret;
                  hdlc_receive(eio, cnt);
            }
            /* tx */
            if (ocnt > 0) {
                  if (ocnt > sizeof(eio))
                        ocnt = sizeof(eio);
                  cnt = hdlc_transmit(eio, ocnt);
                  if (parport_epp_write_data(eio, cnt) != cnt)
                        goto errret;
            }
            /* KISS and reschedule */
            handle_kiss();
      }
errret:
      errprintf(SEV_FATAL, "Modem timeout\n");  
}

/* ---------------------------------------------------------------------- */

/*
 * ECP Mode
 */

/* _MUST_ not return! */

static void ecp_modem(void)
{
      u_int8_t data[1024];
      unsigned char stat[6];
        int cnt, icnt, ocnt;
      
      data[0] = 0xaf;  /* reset register: all reset */
      data[1] = 0xa0;  /* reset register: terminate reset */
      data[2] = 0xb0;
      if (parport_ecp_write_addr(data, 3) != 3)
            goto errret;
      for (;;) {
            data[0] = 0x81;
            if (parport_ecp_write_addr(data, 1) != 1)
                  goto errret;
            if (parport_ecp_read_data(stat, 6) != 6)
                  goto errret;
            data[0] = 0x80;
            if (parport_ecp_write_addr(data, 1) != 1)
                  goto errret;
            icnt = ((unsigned)stat[2] << 8) | stat[1];
            ocnt = ((unsigned)stat[4] << 8) | stat[3];
            /* update status */
            state.stat.icnt3 = state.stat.icnt2;
            state.stat.icnt2 = state.stat.icnt1;
            state.stat.icnt1 = icnt;
            state.stat.ocnt3 = state.stat.ocnt2;
            state.stat.ocnt2 = state.stat.ocnt1;
            state.stat.ocnt1 = ocnt;
            state.stat.status = stat[0];
            state.access.dcd = !(stat[0] & 0x80);
            state.access.ptt = !(stat[0] & 0x40);
            /* read */
            while (icnt > 0) {
                  cnt = icnt;
                  if (cnt > sizeof(data))
                        cnt = sizeof(data);
                  icnt -= cnt;
                  data[0] = 0xc0 | (cnt & 0xf);
                  data[1] = 0xd0 | ((cnt >> 4) & 0xf);
                  data[2] = 0xe0 | ((cnt >> 8) & 0xf);
                  if (parport_ecp_write_addr(data, 3) != 3)
                        goto errret;
                  if (parport_ecp_read_data(data, cnt) != cnt)
                        goto errret;
                  hdlc_receive(data, cnt);
            }
            /* write */
            if (ocnt < 8192) { /* fixme */
                  cnt = 8192 - ocnt;
                  if (cnt > sizeof(data))
                        cnt = sizeof(data);
                  cnt = hdlc_transmit(data, cnt);
                  if (parport_ecp_write_data(data, cnt) != cnt)
                        goto errret;
            }
            /* KISS and reschedule */
            handle_kiss();
      }
errret:
      errprintf(SEV_FATAL, "Modem timeout\n");  
}

/* ---------------------------------------------------------------------- */

/*
 * EPP AFSK Mode
 */

/* _MUST_ not return! */

static void eppafsk_modem(void)
{
      unsigned char eio[32];
      u_int8_t stat;
        int cnt, icnt, ocnt;

      /* reset modem */
      eio[0] = 0x00;
      eio[1] = 0x18;
      if (parport_epp_write_addr(eio, 2) != 2)
            goto errret;
      for (;;) {
            if (parport_epp_read_addr(&stat, 1) != 1)
                  goto errret;
            /* determine the FIFO count */
            eio[0] = 0x19;
            if (parport_epp_write_addr(eio, 1) != 1)
                  goto errret;
            if (parport_epp_read_addr(eio, 1) != 1)
                  goto errret;
            ocnt = 0x10 - (eio[0] & 0x1f);
            eio[0] = 0x1a;
            if (parport_epp_write_addr(eio, 1) != 1)
                  goto errret;
            if (parport_epp_read_addr(eio, 1) != 1)
                  goto errret;
            icnt = eio[0] & 0x1f;
            eio[0] = 0x18;
            if (parport_epp_write_addr(eio, 1) != 1)
                  goto errret;
            /* update status */
            state.stat.icnt3 = state.stat.icnt2;
            state.stat.icnt2 = state.stat.icnt1;
            state.stat.icnt1 = icnt;
            state.stat.ocnt3 = state.stat.ocnt2;
            state.stat.ocnt2 = state.stat.ocnt1;
            state.stat.ocnt1 = ocnt;
            state.stat.status = stat;
            state.access.dcd = !(stat & 0x80);
            state.access.ptt = !(stat & 0x40);
            /* rx */
            if (icnt > 0) {
                  if (parport_epp_read_data(eio, icnt) != icnt)
                        goto errret;
                  hdlc_receive(eio, icnt);
            }
            /* tx */
            if (ocnt > 0) {
                  cnt = hdlc_transmit(eio, ocnt);
                  if (parport_epp_write_data(eio, cnt) != cnt)
                        goto errret;
            }
            /* KISS and reschedule */
            handle_kiss();
      }
errret:
      errprintf(SEV_FATAL, "Modem timeout\n");  
}

/* ---------------------------------------------------------------------- */

/*
 * EPP conventional Mode
 */

/* _MUST_ not return! */

static void eppconv_modem(void)
{
      unsigned char eio[1024];
        u_int8_t stat;
        int cnt, i, icnt, ocnt;
        
        /* reset modem */
      eio[0] = 0;
      eio[1] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE;
      if (parport_epp_write_addr(eio, 2) != 2)
            goto errret;
        for (;;) {
            if (parport_epp_read_addr(&stat, 1) != 1)
                  goto errret;
                /* determine TX fifo size from status bits */
                switch (stat & (EPP_NTAEF|EPP_NTHF)) {
                case EPP_NTHF:
                        ocnt = 2048 - 256;
                        break;

                case EPP_NTAEF:
                        ocnt = 2048 - 1793;
                        break;

                case 0:
                        ocnt = 0;
                        break;

                default:
                        ocnt = 2048 - 1025;
                        break;
                }
                /* determine RX fifo size from status bits */
                switch (stat & (EPP_NRAEF|EPP_NRHF)) {
                case EPP_NRHF:
                        icnt = 0;
                        break;
                
                case EPP_NRAEF:
                        icnt = 1025;
                        break;
                
                case 0:
                        icnt = 1793;
                        break;
                
                default:
                        icnt = 256;
                        break;
                }
                /* update status */
                state.stat.icnt3 = state.stat.icnt2;
                state.stat.icnt2 = state.stat.icnt1;
                state.stat.icnt1 = icnt;
                state.stat.ocnt3 = state.stat.ocnt2;
                state.stat.ocnt2 = state.stat.ocnt1;
                state.stat.ocnt1 = ocnt;
                state.stat.status = stat;
                state.access.dcd = !(stat & EPP_DCDBIT);
                state.access.ptt = !!(stat & EPP_PTTBIT);
                /* rx */
                if (icnt == 0 && state.access.bitrate < 100000) {
                        cnt = 0;
                        i = (state.access.bitrate < 50000) ? 256 : 128;
                        while (i > 0 && cnt < sizeof(eio) && stat & EPP_NREF) {
                        if (parport_epp_read_data(eio+cnt, 1) != 1)
                              goto errret;
                        cnt++;
                                i--;
                        if (parport_epp_read_addr(&stat, 1) != 1)
                              goto errret;
                        }
                        hdlc_receive(eio, cnt);
                }
                while (icnt > 0) {
                        cnt = icnt;
                        if (cnt > sizeof(eio))
                                cnt = sizeof(eio);
                        icnt -= cnt;
                  if (parport_epp_read_data(eio, cnt) != cnt)
                        goto errret;
                        hdlc_receive(eio, cnt);
                }
                /* tx */
                if (ocnt > 0) {
                        if (ocnt > sizeof(eio))
                                ocnt = sizeof(eio);
                        cnt = hdlc_transmit(eio, ocnt);
                  if (parport_epp_write_data(eio, cnt) != cnt)
                        goto errret;
                }
                /* KISS and reschedule */
                handle_kiss();
        }
errret:
      errprintf(SEV_FATAL, "Modem timeout\n");  
}

/* ---------------------------------------------------------------------- */

int main(int argc, char *argv[])
{
      void *so;
      int (*drvinit)(int argc, char *argv[]);
      int mode;

      if (!(so = dlopen("baycomfpga_init.so", RTLD_NOW)) || !(drvinit = dlsym(so, "driver_init"))) {
            fprintf(stderr, "baycomepp: cannot load initialization code from baycomfpga_init.so\n");
            exit(1);
      }
      mode = drvinit(argc, argv);
      dlclose(so);
      switch(mode) {
      case MODE_EPP:
            epp_modem();
      case MODE_ECP:
            ecp_modem();
      case MODE_EPPAFSK:
            eppafsk_modem();
      case MODE_EPPCONV:
            eppconv_modem();
      }
      exit(1);
}

/* ---------------------------------------------------------------------- */

Generated by  Doxygen 1.6.0   Back to index