+#include "comm.h"
+#include "vend.h"
+
+#define DLAB_SET() bset((void*)&_uart_regs[UART_LINE_CTL], LC_DLAB);
+#define DLAB_CLR() bclr((void*)&_uart_regs[UART_LINE_CTL], LC_DLAB);
+
+char tx_buffer[TX_BUFFER_LEN+2];
+volatile char rx_buffer[RX_BUFFER_LEN+1];
+volatile u8 rx_buf_pos; /* -ve denotes not sending/receiving */
+volatile char msg_buf[RX_BUFFER_LEN+1]; /* rx_buffer copied here without \n */
+volatile u8 rx_queue_state, tx_queue_state;
+volatile bool packet_is_bad;
+
+/* If we were controlling a modem we'd probably do a lot more such as toggling
+ * the control lines (DTR,RTS etc). But given this is a very stripped down
+ * version talking over merely TX/RX, we don't care about these control lines.
+ *
+ * This code speak 8 data bits, No parity, 1 Stop bit, at the baud rate specified
+ * in comm.h (should be 9600 bps)
+ */
+
+u8 uart_init() {
+ /*
+ * Because in our daughterboard circuit we are not connecting the reset line
+ * to the CPU's, we can not make any assumptions about the state of the UART.
+ * Hence we initialize all registers and flush the FIFOs
+ */
+
+ u16 divisor;
+ lock(); /* disable interrupts for this */
+
+ /* set baud rate */
+ divisor = UART_SPEED / BAUD_RATE;
+ DLAB_SET();
+ _uart_regs[UART_DLAB_MSB] = (u8)(divisor >> 8);
+ _uart_regs[UART_DLAB_LSB] = (u8)(divisor & 0xff);
+ DLAB_CLR();
+
+ /* set RX, TX & LS interrupts */
+ _uart_regs[UART_INT_ENABLE] =
+ IE_RECEIVER_READY | IE_TRANSMITTER_READY | IE_LINE_STATUS_CHANGE;
+
+ /* Use the FIFO and empty them */
+ _uart_regs[UART_FIFO_CTL] =
+ FIFO_ENABLE | FIFO_RX_CLEAR | FIFO_TX_CLEAR | FIFO_LVL_1;
+
+ /* 8 data bits, 1 stop bit, no parity. */
+ _uart_regs[UART_LINE_CTL] = LC_8BITS;
+
+ /* modem controls: clear them all */
+ _uart_regs[UART_MODEM_CTL] = 0;
+
+ rx_queue_state = 0;
+ tx_queue_state = 0;
+ rx_buf_pos = 0;
+
+ unlock();
+ return 1;
+}
+
+/*******************************************************************************
+ * Interrupt handler for UART.
+ *
+ * This actually consists of several functions, rolled into one.
+ */
+
+extern inline void rx_int() {
+ char c = _uart_regs[UART_RX_BUFFER];
+ if (rx_queue_state == 2) return; /* buffers full. too bad */
+
+ if (rx_buf_pos < RX_BUFFER_LEN) {
+ rx_buffer[rx_buf_pos] = c;
+
+ if (c == '\n') {
+ rx_buf_pos = 0;
+ if (packet_is_bad) {
+ packet_is_bad = 0;
+ /* just forget this packet ever happened */
+ } else {
+ rx_buffer[rx_buf_pos] = 0;
+ switch (rx_queue_state) {
+ case 0:
+ my_strncpy((char*)msg_buf, (char*)rx_buffer, RX_BUFFER_LEN);
+ rx_queue_state++;
+ case 1:
+ /* we can't copy it in yet. will be done from msg_clr() */
+ rx_queue_state++;
+ break;
+ case 2: /* another weird case? */
+ default:
+ break;
+ }
+ }
+ } else
+ rx_buf_pos++;
+ } /* else drop it (buffer full) - probably a corrupt packet */
+}
+
+extern inline void tx_int() {
+ /* this interrupt is called with the precondition that the TX fifo of the UART
+ * is completely empty and will hold a complete message (of less than 14 chars)
+ */
+ if (tx_queue_state & 0x01) { /* msg to be sent pending */
+ /* load it in */
+ int i;
+ for (i=0; tx_buffer[i]; i++)
+ _uart_regs[UART_TX_BUFFER] = tx_buffer[i];
+ bclr((void*)&tx_queue_state, 0x01);
+ } else
+ bclr((void*)&tx_queue_state, 0x02); /* mark the FIFO as free */
+}
+
+void uart_interrupt() {
+ u8 status;
+ while (1) {
+ status = _uart_regs[UART_INT_IDENT];
+ switch (status) {
+ case IS_FIFO_TIMEOUT:
+ case IS_RECEIVER_READY:
+ rx_int();
+ break;
+ case IS_TRANSMITTER_READY:
+ tx_int();
+ break;
+ case IS_MODEM_STATUS_CHANGE:
+ /* read the modem status register to clear the interrupt */
+ (void)_uart_regs[UART_MODEM_STATUS];
+ break;
+ case IS_LINE_STATUS_CHANGE:
+ status = _uart_regs[UART_LINE_STATUS];
+ if (status & LS_OVERRUN_ERR) packet_is_bad = 1;
+ if (status & LS_PARITY_ERR) packet_is_bad = 1;
+ if (status & LS_FRAMING_ERR) packet_is_bad = 1;
+ /* LS_BREAK_INTERRUPT ignored for now. Do we want it? */
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+/*******************************************************************************
+ * End of interrupt handler
+ */
+
+/* sends the packet in tx_buffer and doesn't return until it's been sent */
+void send_packet() {
+ bset((void*)&tx_queue_state, 0x01);
+ lock();
+ tx_int();
+ unlock();
+ while (tx_queue_state & 0x01); /* wait for completion */
+}
+
+void msg_clr() {
+ /* called when a msg in msg_buf has been read */
+ switch (rx_queue_state) {
+ case 0: /* this shouldn't happen */
+ break;
+ case 1:
+ lock();
+ rx_queue_state = 0;
+ unlock();
+ break;
+ case 2:
+ /* copy the rx_buffer into msg_buf */
+ my_strncpy((char*)msg_buf, (char*)rx_buffer, RX_BUFFER_LEN);
+ lock();
+ rx_queue_state = 1;
+ unlock();
+ break;
+ default:
+ lock();
+ rx_queue_state = 0;
+ unlock();
+ }
+}
+
+
+void comm_init() {
+ uart_init();
+ lock();
+ tx_buffer[0] = rx_buffer[0] = msg_buf[0] = 0;
+ packet_is_bad = 0;
+ unlock();
+}