Accept both CR and LF to denote newline.
[uccvend-snackrom.git] / ROM2 / comm.c
1 #include "comm.h"
2 #include "vend.h"
3
4 #define DLAB_SET() bset((void*)&_uart_regs[UART_LINE_CTL], LC_DLAB);
5 #define DLAB_CLR() bclr((void*)&_uart_regs[UART_LINE_CTL], LC_DLAB);
6
7 char tx_buffer[TX_BUFFER_LEN+2];
8 volatile char rx_buffer[RX_BUFFER_LEN+1];
9 volatile u8 rx_buf_pos; /* -ve denotes not sending/receiving */
10 volatile char msg_buf[RX_BUFFER_LEN+1]; /* rx_buffer copied here without \n */
11 volatile u8 rx_queue_state, tx_queue_state;
12 volatile bool packet_is_bad;
13
14 /* If we were controlling a modem we'd probably do a lot more such as toggling
15  * the control lines (DTR,RTS etc). But given this is a very stripped down
16  * version talking over merely TX/RX, we don't care about these control lines.
17  *
18  * This code speak 8 data bits, No parity, 1 Stop bit, at the baud rate specified
19  * in comm.h (should be 9600 bps)
20  */
21
22 u8 uart_init() {
23         /*
24          * Because in our daughterboard circuit we are not connecting the reset line
25          * to the CPU's, we can not make any assumptions about the state of the UART.
26          * Hence we initialize all registers and flush the FIFOs
27          */
28
29         u16 divisor;
30         lock(); /* disable interrupts for this */
31         
32         /* set baud rate */
33         divisor = UART_SPEED / BAUD_RATE;
34         DLAB_SET();
35         _uart_regs[UART_DLAB_MSB] = (u8)(divisor >> 8);
36         _uart_regs[UART_DLAB_LSB] = (u8)(divisor & 0xff);
37         DLAB_CLR();
38
39         /* set RX, TX & LS interrupts */
40         _uart_regs[UART_INT_ENABLE] =
41                 IE_RECEIVER_READY | IE_TRANSMITTER_READY | IE_LINE_STATUS_CHANGE;
42
43         /* Enable the FIFO and empty them */
44         _uart_regs[UART_FIFO_CTL] =
45                 FIFO_ENABLE | FIFO_RX_CLEAR | FIFO_TX_CLEAR | FIFO_LVL_1;
46
47         /* 8 data bits, 1 stop bit, no parity. */
48         _uart_regs[UART_LINE_CTL] = LC_8BITS;
49
50         /* modem controls: clear them all */
51         _uart_regs[UART_MODEM_CTL] = 0;
52
53         rx_queue_state = 0;
54         tx_queue_state = 0;
55         rx_buf_pos = 0;
56         
57         unlock();
58         return 1;
59 }
60
61 /*******************************************************************************
62  * Interrupt handler for UART.
63  *
64  * This actually consists of several functions, rolled into one.
65  */
66
67 extern inline void rx_int() {
68         char c = _uart_regs[UART_RX_BUFFER];
69         if (rx_queue_state == 2) return; /* buffers full. too bad */
70
71         if (rx_buf_pos < RX_BUFFER_LEN) {
72                 rx_buffer[rx_buf_pos] = c;
73
74                 if (c == '\n') {
75                         rx_buf_pos = 0;
76                         if (packet_is_bad) {
77                                 packet_is_bad = 0;
78                                 /* just forget this packet ever happened */
79                         } else {
80                                 rx_buffer[rx_buf_pos] = 0;
81                                 switch (rx_queue_state) {
82                                         case 0:
83                                                 my_strncpy((char*)msg_buf, (char*)rx_buffer, RX_BUFFER_LEN);
84                                                 rx_queue_state++;
85                                         case 1:
86                                                 /* we can't copy it in yet. will be done from msg_clr() */
87                                                 rx_queue_state++;
88                                                 break;
89                                         case 2:   /* another weird case? */
90                                         default:
91                                                 break;
92                                 }
93                         }
94                 } else 
95                         rx_buf_pos++;
96         } /* else drop it (buffer full) - probably a corrupt packet */
97 }
98
99 extern inline void tx_int() {
100         /* this interrupt is called with the precondition that the TX fifo of the UART
101          * is completely empty and will hold a complete message (of less than 14 chars)
102          */
103         if (tx_queue_state & 0x01) { /* msg to be sent pending */
104                 /* load it in */
105                 int i;
106                 for (i=0; tx_buffer[i]; i++)
107                         _uart_regs[UART_TX_BUFFER] = tx_buffer[i];
108                 bclr((void*)&tx_queue_state, 0x01);
109         } else
110                 bclr((void*)&tx_queue_state, 0x02); /* mark the FIFO as free */
111 }
112
113 void uart_interrupt() {
114         u8 status;
115         while (1) {
116                 status = _uart_regs[UART_INT_IDENT];
117                 switch (status) {
118                         case IS_FIFO_TIMEOUT:
119                         case IS_RECEIVER_READY:
120                                 rx_int();
121                                 break;
122                         case IS_TRANSMITTER_READY:
123                                 tx_int();
124                                 break;
125                         case IS_MODEM_STATUS_CHANGE:
126                                 /* read the modem status register to clear the interrupt */
127                                 (void)_uart_regs[UART_MODEM_STATUS];
128                                 break;
129                         case IS_LINE_STATUS_CHANGE:
130                                 status = _uart_regs[UART_LINE_STATUS];
131                                 if (status & LS_OVERRUN_ERR) packet_is_bad = 1;
132                                 if (status & LS_PARITY_ERR)  packet_is_bad = 1;
133                                 if (status & LS_FRAMING_ERR) packet_is_bad = 1;
134                                 /* LS_BREAK_INTERRUPT ignored for now. Do we want it? */
135                                 break;
136                         default:
137                                 return;
138                 }
139         }
140 }
141
142 /*******************************************************************************
143  * End of interrupt handler
144  */
145
146 void send_ack() {
147         wait_for_tx_free();
148         tx_buffer[0] = '!';
149         tx_buffer[1] = '\n';
150         tx_buffer[2] = 0;
151         send_packet();
152 }
153
154 void send_nack() {
155         wait_for_tx_free();
156         tx_buffer[0] = '?';
157         tx_buffer[1] = '\n';
158         tx_buffer[2] = 0;
159         send_packet();
160 }
161
162 /* sends the packet in tx_buffer and doesn't return until it's been sent */
163 void send_packet() {
164         bset((void*)&tx_queue_state, 0x01);
165         lock();
166         tx_int();
167         unlock();
168         while (tx_queue_state & 0x01); /* wait for completion */
169 }
170
171 void msg_clr() {
172         /* called when a msg in msg_buf has been read */
173         switch (rx_queue_state) {
174                 case 0: /* this shouldn't happen */
175                         break;
176                 case 1:
177                         lock();
178                         rx_queue_state = 0;
179                         unlock();
180                         break;
181                 case 2:
182                         /* copy the rx_buffer into msg_buf */
183                         my_strncpy((char*)msg_buf, (char*)rx_buffer, RX_BUFFER_LEN);
184                         lock();
185                         rx_queue_state = 1;
186                         unlock();
187                         break;
188                 default:
189                         lock();
190                         rx_queue_state = 0;
191                         unlock();
192         }
193 }
194
195
196 void comm_init() {
197         uart_init();
198         lock();
199         tx_buffer[0] = rx_buffer[0] = msg_buf[0] = 0;
200         packet_is_bad = 0;
201         unlock();
202 }

UCC git Repository :: git.ucc.asn.au