/*
 * 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
