Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

arch_ser.c

/* $Id: arch_ser.c,v 1.35 2009-01-28 12:59:16 potyra Exp $ 
 *
 * Copyright (C) 2006-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#define DEBUG_CONTROL_FLOW    0

#ifdef STATE

struct {
      unsigned long long tsc;

      struct NAME_(ringbuf) {
            int head;
            int tail;
            int count;
#define UMSER_BUFSIZE   4096
            unsigned char data[UMSER_BUFSIZE];
      } inbuf, outbuf;

      union {
            struct {
                  unsigned rda:1;   /*receive data available interrupt */
                  unsigned thre:1;/*transmitter holding register empty interrupt */
                  unsigned rls:1;   /*receiver line status interrupt */
                  unsigned ms:1;    /*modem status interrupt */
                  unsigned rzvd:4;/*reserved */
            } ier_bits;       /*interrupt enable reg bits */
            unsigned char ier_byte; /*write a whole byte at once */
      } ier; 

      union {
            struct {
                  unsigned fe:1;    /* FIFO enable */
                  unsigned rr:1;    /* receiver FIFO clear */
                  unsigned tr:1;    /* transmitter FIFO clear */
                  unsigned rzvd:3;/* reserved */
                  unsigned tl:1;    /* trigger level */
            } fcr_bits;
            unsigned char fcr_byte; /*write a whole byte at once */
      } fcr;

      union {
            struct {
                  unsigned ip:1;    /* interrupt pending */
                  unsigned b12:2;   /* 00 -> ms interrupt */
                              /* 01 -> thre interrupt */
                              /* 10 -> rda interrupt */
                              /* 11 -> rls interrupt */
                  unsigned rzvd:2;/* reserved */
                  unsigned b5:1;    /* 64 Byte Fifo Enabled, must be set to 0 */
                  unsigned b67:2;   /* FIFO Status, always 00 */
            } _iir_bits;            /*interrupt identification reg bits */
            unsigned char iir_byte;/*write a whole byte at once */
      } iir;

      union {
            struct {
                  unsigned wl:2;    /*word length*/
                              /* 00 -> 5 bits*/
                              /* 01 -> 6 bits*/
                              /* 10 -> 7 bits*/
                              /* 11 -> 8 bits*/
                  unsigned st:1;    /*stopbit length:*/
                              /* 0 -> 1 stopbit */
                              /* 1 -> 2 stop bits */
                              /*   for words of length 6,7,8*/
                              /*   or 1.5 stop bits for wordlen of 5 bits */
                  unsigned ps:3;    /*parity select: */
                              /* xx0 -> no parity */
                              /* 000 -> odd */
                              /* 011 -> even */
                              /* 101 -> high */
                              /* 111 -> low */
                  unsigned bk:1;    /*1->enable break, 0->disable break */
                  unsigned dlab:1;/*Divisor Latch Access Bit */
            } lcr_bits;       /*line control reg bits */
            unsigned char lcr_byte; /*write a whole byte at once */
      } lcr;

      union {
            struct {
                  unsigned dr:1;    /*data ready */
                  unsigned oe:1;    /*overrun error */
                  unsigned pe:1;    /*parity error */
                  unsigned fe:1;    /*framing error */
                  unsigned bi:1;    /*break interrupt */
                  unsigned ethr:1;/*empty transmitter Holding register */
                  unsigned edhr:1;/*empty data holding registers */
                  unsigned fie:1;   /*fifo error */
            } lsr_bits;       /*line status reg bits */
            unsigned char lsr_byte;/*write a whole byte at once */
      } lsr;

      union {
            struct {
                  unsigned dtr:1;   /*force Data Terminal Ready */
                  unsigned rts:1;   /*force Request to send */
                  unsigned aux1:1;/*aux output 1 */
                  unsigned aux2:1;/*aux output 2 */
                  unsigned loop:1;/*loopback mode */
                  unsigned rzvd:3;/*Divisor Latch Access Bit */
            } mcr_bits;       /*modem control reg bits */
            unsigned char mcr_byte; /*write a whole byte at once */
      } mcr;

      union {
            struct {
                  unsigned dcts:1;/*delta clear to send */
                  unsigned ddsr:1;/*delta data set ready */
                  unsigned teri:1;/*trailing edge ring indicator*/
                  unsigned ddcd:1;/*delta data carrier detect */
                  unsigned cts:1;   /*clear to send */
                  unsigned dsr:1;   /*data set ready */
                  unsigned ri:1;    /*ring indicator */
                  unsigned dcd:1;   /*carrier detect */
            } msr_bits;       /*modem status reg bits */
            unsigned char msr_byte; /*write a whole byte at once */
      } msr;

      unsigned char dll;            /*devisor latch low byte */
      unsigned char dlh;            /*devisor latch high byte */
      unsigned char scratch;        /*scratch register */

      int cts;
      int dsr;
      int ri;
      int dcd;

      int thre_int;
} NAME;

#endif /* STATE */
#ifdef BEHAVIOR

static void 
NAME_(get)(struct NAME_(ringbuf) *rb, unsigned char *byte)
{
      assert(0 < rb->count);

      *byte = rb->data[rb->head];
      rb->head = (rb->head + 1) % UMSER_BUFSIZE;
      rb->count--;
}

static void
NAME_(put)(struct NAME_(ringbuf) *rb, const unsigned char *byte)
{
      assert(rb->count < UMSER_BUFSIZE);

      rb->data[rb->tail] = *byte;
      rb->tail = (rb->tail + 1) % UMSER_BUFSIZE;
      rb->count++;
}

static void 
NAME_(check_irq)(struct cpssp *cpssp)
{
      int irq;

      /* Interrupt identification register. */
      if (cpssp->NAME.ier.ier_bits.rls
       && (cpssp->NAME.lsr.lsr_bits.oe
        || cpssp->NAME.lsr.lsr_bits.pe
        || cpssp->NAME.lsr.lsr_bits.fe
        || cpssp->NAME.lsr.lsr_bits.bi)) {
            /* Line status interrupt pending. */
            irq = 1;

      } else if (cpssp->NAME.ier.ier_bits.rda
            && 0 < cpssp->NAME.inbuf.count) {
            /* Receiver interrupt pending. */
            irq = 1;

      } else if (cpssp->NAME.ier.ier_bits.thre
            && cpssp->NAME.thre_int) {
            /* Transmitter interrupt pending. */
            irq = 1;

      } else if (cpssp->NAME.ier.ier_bits.ms
            && (cpssp->NAME.msr.msr_bits.dcts
             || cpssp->NAME.msr.msr_bits.ddsr
             || cpssp->NAME.msr.msr_bits.teri
             || cpssp->NAME.msr.msr_bits.ddcd)) {
            /* Modem status interrupt pending. */
            irq = 1;

      } else {
            irq = 0;
      }

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s irq=%d\n", __FUNCTION__, irq);
      }
      
      NAME_(irq_set)(cpssp, irq);
}

static void 
NAME_(check_modem)(struct cpssp *cpssp)
{
      int cts;
      int dsr;
      int ri;
      int dcd;

      if (cpssp->NAME.mcr.mcr_bits.loop) {
            /* Loop back mode. */
            cts = cpssp->NAME.mcr.mcr_bits.rts;
            dsr = cpssp->NAME.mcr.mcr_bits.dtr;
            ri = cpssp->NAME.mcr.mcr_bits.aux1;
            dcd = cpssp->NAME.mcr.mcr_bits.aux2;
      } else {
            /* Non loop back mode. */
            cts = cpssp->NAME.cts;
            dsr = cpssp->NAME.dsr;
            ri = cpssp->NAME.ri;
            dcd = cpssp->NAME.dcd;
      }

      if (cpssp->NAME.msr.msr_bits.cts != cts) {
            cpssp->NAME.msr.msr_bits.dcts = 1;
            cpssp->NAME.msr.msr_bits.cts = cts;
      }
      if (cpssp->NAME.msr.msr_bits.dsr != dsr) {
            cpssp->NAME.msr.msr_bits.ddsr = 1;
            cpssp->NAME.msr.msr_bits.dsr = dsr;
      }
      if (cpssp->NAME.msr.msr_bits.ri != ri && ri == 1) {
            cpssp->NAME.msr.msr_bits.teri = 1;
            cpssp->NAME.msr.msr_bits.ri = ri;
      }
      if (cpssp->NAME.msr.msr_bits.dcd != dcd) {
            cpssp->NAME.msr.msr_bits.ddcd = 1;
            cpssp->NAME.msr.msr_bits.dcd = dcd;
      }
}

static void 
NAME_(start_break)(struct cpssp *cpssp)
{
#if 0
    struct umser_header header;
    unsigned char buffer[sizeof(header)];
    int ret;

    if (CONFIG->serial[nr].protocol == PROTOCOL_TELNET_TCP)
      return;

    /*flush the out buffer for port nr */
    umser_send(nr);

    /*construct & send the break signal */
    header.command = 0x1;     /*break signal */
    header.parity = cpssp->NAME.lcr.lcr_bits.ps;
    header.wordlen = cpssp->NAME.lcr.lcr_bits.wl;
    header.stoplen = cpssp->NAME.lcr.lcr_bits.st;
    header.dll = cpssp->NAME.dll;
    header.dlh = cpssp->NAME.dlh;
    header.datalen = 0;

    memcpy(buffer, &header, sizeof(header));

    ret = xwrite(cpssp->NAME.fd, buffer, sizeof(header));

    if (ret < 0
      && errno != EAGAIN && errno != ECONNREFUSED && errno != EIO) {
      fprintf(stderr,
            "report to siivdedi@stud.uni-erlangen.de"
            "send of packet failed: ret = %d; errno = %d\n",
            ret, errno);
    }

#endif

}

static void
NAME_(stop_break)(struct cpssp *cpssp)
{
      /* FIXME VOSSI */
}

static void
NAME_(outb)(struct cpssp *cpssp, unsigned char value, unsigned short port)
{
      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s port=0x%04x, val=0x%02x\n", __FUNCTION__,
                        port, value);
      }
      
      switch (port) {
      case 0:
            if (cpssp->NAME.lcr.lcr_bits.dlab == 0) {
                  /* tx register */
                  NAME_(put)(&cpssp->NAME.outbuf, &value);

                  /* Re-arm transmit interrupts. */
                  cpssp->NAME.thre_int = 0;

                  NAME_(check_irq)(cpssp);

            } else {
                  /* devisor latch low byte */
                  cpssp->NAME.dll = value;
            }
            break;

      case 1:
            if (cpssp->NAME.lcr.lcr_bits.dlab == 0) {
                  /* interrupt enable register */
                  value &= 0x0f; /* Only 4 bits are stored. */
                  cpssp->NAME.ier.ier_byte = value;

                  NAME_(check_irq)(cpssp);

            } else {
                  /* devisor latch high byte */
                  cpssp->NAME.dlh = value;
            }
            break;

      case 2:
            /* FIFO control register */
            if ((value & 1) != cpssp->NAME.fcr.fcr_bits.fe) {
                  /* Reset both FIFOs. */
                  cpssp->NAME.inbuf.head = 0;
                  cpssp->NAME.inbuf.tail = 0;
                  cpssp->NAME.inbuf.count = 0;

                  cpssp->NAME.outbuf.head = 0;
                  cpssp->NAME.outbuf.tail = 0;
                  cpssp->NAME.outbuf.count = 0;

                  cpssp->NAME.thre_int = 1;
            }
            if ((value >> 1) & 1) {
                  /* Reset receiver FIFO. */
                  cpssp->NAME.inbuf.head = 0;
                  cpssp->NAME.inbuf.tail = 0;
                  cpssp->NAME.inbuf.count = 0;

                  value &= ~(1 << 1); /* Bit is self-clearing. */
            }
            if ((value >> 2) & 1) {
                  /* Reset transmitter FIFO. */
                  cpssp->NAME.outbuf.head = 0;
                  cpssp->NAME.outbuf.tail = 0;
                  cpssp->NAME.outbuf.count = 0;

                  value &= ~(1 << 2); /* Bit is self-clearing. */
            }

            /* Bit 3 is ignored on IT8661F. */

            value &= ~(1 << 4); /* Reserved */
            value &= ~(1 << 5); /* Reserved */

            /* Bits 6,7 define receiver FIFO trigger level. */

            cpssp->NAME.fcr.fcr_byte = value;

            NAME_(check_irq)(cpssp);
            break;

      case 3:
            /* line control register */
            if (cpssp->NAME.lcr.lcr_bits.bk == 0 && (value & (0x1 << 6)) != 0) {
                  NAME_(start_break)(cpssp);

            } else if (cpssp->NAME.lcr.lcr_bits.bk == 1
                  && (value & (0x1 << 6)) != 1) {
                  NAME_(stop_break)(cpssp);
            }

            cpssp->NAME.lcr.lcr_byte = value;
            break;

      case 4:
            /* modem control register */
            cpssp->NAME.mcr.mcr_byte = value;

            NAME_(check_modem)(cpssp);
            NAME_(check_irq)(cpssp);
            break;

      case 5:
            /* line status register */
            /* read only reg */
            break;

      case 6:
            /* modem status register */
            /* read only register */
            break;

      case 7:
            /* scratch register */
            cpssp->NAME.scratch = value;
            break;

      default:
            assert(0);
      }

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s return\n", __FUNCTION__);
      }
}

static unsigned char 
NAME_(inb)(struct cpssp *cpssp, unsigned short port)
{
      unsigned char val;

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s port=0x%04x\n", __FUNCTION__, port);
      }

      switch (port) {
      case 0:
            if (cpssp->NAME.lcr.lcr_bits.dlab == 0) {
                  /* dr register */
                  if (0 < cpssp->NAME.inbuf.count) {
                        NAME_(get)(&cpssp->NAME.inbuf, &val);
                  } else {
                        val = 0xfa; /* arbitrary content! */
                  }
                  NAME_(check_irq)(cpssp);

            } else {
                  /* devisor latch low byte */
                  val = cpssp->NAME.dll;
            }
            break;

      case 1:
            if (cpssp->NAME.lcr.lcr_bits.dlab == 0) {
                  /* interrupt enable register */
                  val = cpssp->NAME.ier.ier_byte;

            } else {
                  /* devisor latch high byte */
                  val = cpssp->NAME.dlh;
            }
            break;

      case 2:
            val = 0;

            /* interrupt identification register */
            if (cpssp->NAME.ier.ier_bits.rls
             && (cpssp->NAME.lsr.lsr_bits.oe
              || cpssp->NAME.lsr.lsr_bits.pe
              || cpssp->NAME.lsr.lsr_bits.fe
              || cpssp->NAME.lsr.lsr_bits.bi)) {
                  /* line status interrupt pending */
                  val &= ~1;
                  val |= 3 << 1;

            } else if (cpssp->NAME.ier.ier_bits.rda
                  && 0 < cpssp->NAME.inbuf.count) {
                  /* receiver interrupt pending */
                  val &= ~1;
                  val |= 2 << 1;

            } else if (cpssp->NAME.ier.ier_bits.thre
                  && cpssp->NAME.thre_int) {
                  /* transmitter holding register empty interrupt */
                  val &= ~1;
                  val |= 1 << 1;
                  cpssp->NAME.thre_int = 0;

            } else if (cpssp->NAME.ier.ier_bits.ms
                  && (cpssp->NAME.msr.msr_bits.dcts
                   || cpssp->NAME.msr.msr_bits.ddsr
                   || cpssp->NAME.msr.msr_bits.teri
                   || cpssp->NAME.msr.msr_bits.ddcd)) {
                  /* modem status interrupt pending */
                  val &= ~1;
                  val |= 0 << 1;

            } else {
                  /* no interrupt pending */
                  val |= 1;
                  val &= ~(3 << 1);
            }

            val |= cpssp->NAME.fcr.fcr_bits.fe << 6;
            val |= cpssp->NAME.fcr.fcr_bits.fe << 7;

            NAME_(check_irq)(cpssp);
            break;

      case 3:
            /* line control register */
            val = cpssp->NAME.lcr.lcr_byte;
            break;

      case 4:
            /* modem control register */
            /* no modems for now! - FIXME VOSSI */
            val = cpssp->NAME.mcr.mcr_byte;
            break;

      case 5:
            /* line status register */
            val = 0;

            val |= (0 < cpssp->NAME.inbuf.count) << 0;
            val |= cpssp->NAME.lsr.lsr_bits.oe << 1;
            val |= cpssp->NAME.lsr.lsr_bits.pe << 2;
            val |= cpssp->NAME.lsr.lsr_bits.fe << 3;
            val |= cpssp->NAME.lsr.lsr_bits.bi << 4;
            val |= (cpssp->NAME.outbuf.count < UMSER_BUFSIZE) << 5;
            val |= (cpssp->NAME.outbuf.count < UMSER_BUFSIZE) << 6;
            val |= cpssp->NAME.lsr.lsr_bits.fie << 7;

            cpssp->NAME.lsr.lsr_bits.oe = 0;
            cpssp->NAME.lsr.lsr_bits.pe = 0;
            cpssp->NAME.lsr.lsr_bits.fe = 0;
            cpssp->NAME.lsr.lsr_bits.bi = 0;

            NAME_(check_irq)(cpssp);
            break;

      case 6:
            /* modem status register */
            val = 0;

            val |= cpssp->NAME.msr.msr_bits.dcts << 0;
            val |= cpssp->NAME.msr.msr_bits.ddsr << 1;
            val |= cpssp->NAME.msr.msr_bits.teri << 2;
            val |= cpssp->NAME.msr.msr_bits.ddcd << 3;
            val |= cpssp->NAME.msr.msr_bits.cts << 4;
            val |= cpssp->NAME.msr.msr_bits.dsr << 5;
            val |= cpssp->NAME.msr.msr_bits.ri << 6;
            val |= cpssp->NAME.msr.msr_bits.dcd << 7;

            cpssp->NAME.msr.msr_bits.dcts = 0;
            cpssp->NAME.msr.msr_bits.ddsr = 0;
            cpssp->NAME.msr.msr_bits.teri = 0;
            cpssp->NAME.msr.msr_bits.ddcd = 0;

            NAME_(check_irq)(cpssp);
            break;

      case 7:
            /* scratch register */
            val = cpssp->NAME.scratch;
            break;

      default:
            assert(0);
      }

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s port=0x%04x val=0x%02x\n", __FUNCTION__,
                        port, val);
      }

      return val;
}

static void
NAME_(recv)(struct cpssp *cpssp, uint8_t c)
{
      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s c=0x%02x\n", __FUNCTION__, c);
      }

      if (cpssp->NAME.inbuf.count < UMSER_BUFSIZE) {
            NAME_(put)(&cpssp->NAME.inbuf, &c);
      } else {
            /* Overrun Error */
            cpssp->NAME.lsr.lsr_bits.oe = 1;
      }

      NAME_(check_irq)(cpssp);
}

static void 
NAME_(timer)(void *_css)
{
      struct cpssp *cpssp = (struct cpssp *) _css;

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s\n", __FUNCTION__);
      }

      if (! cpssp->state_power) {
            /*
             * Just drop bytes from ringbuffer.
             */
            cpssp->NAME.outbuf.count = 0;
            cpssp->NAME.outbuf.head = 0;
            cpssp->NAME.outbuf.tail = 0;

      } else {
            /*
             * Send bytes from ringbuffer.
             */
            unsigned int bytes;
            unsigned int i;

            bytes = cpssp->NAME.outbuf.count;

            /* Limit bytes according to baud-rate! FIXME VOSSI */

            /* Send bytes. */
            for (i = 0; i < bytes; i++) {
                  uint8_t c;

                  NAME_(get)(&cpssp->NAME.outbuf, &c);
                  if (cpssp->NAME.mcr.mcr_bits.loop) {
                        /* Loop back to serial input. */
                        NAME_(recv)(cpssp, c);
                  } else {
                        /* Send via serial line. */
                        NAME_(send)(cpssp, c);
                  }
                  if (cpssp->NAME.outbuf.count == 0) {
                        cpssp->NAME.thre_int = 1;
                  }
                  if (DEBUG_CONTROL_FLOW) {
                        fprintf(stderr, "%s sent 0x%02x\n",
                                    __FUNCTION__, c);
                  }
            }

            NAME_(check_irq)(cpssp);
      }

      cpssp->NAME.tsc += TIME_HZ / 100;
      time_call_at(cpssp->NAME.tsc, NAME_(timer), cpssp);
}

static void
NAME_(reset)(struct cpssp *cpssp)
{
      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s\n", __FUNCTION__);
      }

      cpssp->NAME.inbuf.head = 0;
      cpssp->NAME.inbuf.tail = 0;
      cpssp->NAME.inbuf.count = 0;
      cpssp->NAME.outbuf.head = 0;
      cpssp->NAME.outbuf.tail = 0;
      cpssp->NAME.outbuf.count = 0;

      cpssp->NAME.ier.ier_byte = 0x00;
      cpssp->NAME.fcr.fcr_byte = 0x00;
      cpssp->NAME.iir.iir_byte = 0x00;
      cpssp->NAME.lcr.lcr_byte = 0x00;
      cpssp->NAME.mcr.mcr_byte = 0x00;
      cpssp->NAME.lsr.lsr_byte = 0x00;

      /* tell data set ready and clear to send */
      cpssp->NAME.msr.msr_byte = 0x30;

      cpssp->NAME.dll = 0x0;
      cpssp->NAME.dlh = 0x0;
      cpssp->NAME.scratch = 0x0;

      cpssp->NAME.cts = 1;
      cpssp->NAME.dsr = 1;
      cpssp->NAME.ri  = 0;
      cpssp->NAME.dcd = 0;

      cpssp->NAME.thre_int = 1;
}

static void 
NAME_(init)(struct cpssp *cpssp)
{
      cpssp->NAME.tsc = TIME_HZ / 100;
      time_call_at(cpssp->NAME.tsc, NAME_(timer), cpssp);
}

#endif /* BEHAVIOR */

#undef DEBUG_CONTROL_FLOW

Generated by  Doxygen 1.6.0   Back to index