altos: Provide for a pre-filter on commands
[fw/altos] / src / ao_serial.c
1 /*
2  * Copyright © 2009 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include "ao.h"
19
20 volatile __xdata struct ao_fifo ao_usart1_rx_fifo;
21 volatile __xdata struct ao_fifo ao_usart1_tx_fifo;
22
23 #if USE_SERIAL_STDIN
24 __pdata uint8_t ao_serial_stdin;
25 #endif
26
27 void
28 ao_serial_rx1_isr(void) __interrupt 3
29 {
30         if (!ao_fifo_full(ao_usart1_rx_fifo))
31                 ao_fifo_insert(ao_usart1_rx_fifo, U1DBUF);
32         ao_wakeup(&ao_usart1_rx_fifo);
33 #if USE_SERIAL_STDIN
34         if (ao_serial_stdin)
35                 ao_wakeup(&ao_stdin_ready);
36 #endif
37 }
38
39 static __xdata uint8_t ao_serial_tx1_started;
40
41 static void
42 ao_serial_tx1_start(void)
43 {
44         if (!ao_fifo_empty(ao_usart1_tx_fifo) &&
45             !ao_serial_tx1_started)
46         {
47                 ao_serial_tx1_started = 1;
48                 ao_fifo_remove(ao_usart1_tx_fifo, U1DBUF);
49         }
50 }
51
52 void
53 ao_serial_tx1_isr(void) __interrupt 14
54 {
55         UTX1IF = 0;
56         ao_serial_tx1_started = 0;
57         ao_serial_tx1_start();
58         ao_wakeup(&ao_usart1_tx_fifo);
59 }
60
61 static __pdata serial_echo;
62
63 char
64 ao_serial_getchar(void) __critical
65 {
66         char    c;
67         while (ao_fifo_empty(ao_usart1_rx_fifo))
68                 ao_sleep(&ao_usart1_rx_fifo);
69         ao_fifo_remove(ao_usart1_rx_fifo, c);
70         if (serial_echo) {
71                 printf("%02x ", ((int) c) & 0xff);
72                 if (c >= ' ')
73                         putchar(c);
74                 putchar('\n');
75                 flush();
76         }
77         return c;
78 }
79
80 #if USE_SERIAL_STDIN
81 char
82 ao_serial_pollchar(void) __critical
83 {
84         char    c;
85 #if 0
86         if (!ao_serial_stdin)
87                 return AO_READ_AGAIN;
88 #endif
89         if (ao_fifo_empty(ao_usart1_rx_fifo))
90                 return AO_READ_AGAIN;
91         ao_fifo_remove(ao_usart1_rx_fifo,c);
92         return c;
93 }
94
95 void
96 ao_serial_set_stdin(uint8_t stdin)
97 {
98         ao_serial_stdin = stdin;
99 }
100
101 #endif
102
103 void
104 ao_serial_putchar(char c) __critical
105 {
106         while (ao_fifo_full(ao_usart1_tx_fifo))
107                 ao_sleep(&ao_usart1_tx_fifo);
108         ao_fifo_insert(ao_usart1_tx_fifo, c);
109         ao_serial_tx1_start();
110 }
111
112 static void
113 ao_serial_drain(void) __critical
114 {
115         while (!ao_fifo_empty(ao_usart1_tx_fifo))
116                 ao_sleep(&ao_usart1_tx_fifo);
117 }
118
119 static void
120 monitor_serial(void)
121 {
122         ao_cmd_hex();
123         serial_echo = ao_cmd_lex_i != 0;
124 }
125
126 __code struct ao_cmds ao_serial_cmds[] = {
127         { monitor_serial,               "M <enable>\0Monitor serial data" },
128         { 0, NULL },
129 };
130
131 static const struct {
132         uint8_t baud;
133         uint8_t gcr;
134 } ao_serial_speeds[] = {
135         /* [AO_SERIAL_SPEED_4800] = */ {
136                 /* .baud = */ 163,
137                 /* .gcr  = */ (7 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
138         },
139         /* [AO_SERIAL_SPEED_9600] = */ {
140                 /* .baud = */ 163,
141                 /* .gcr  = */ (8 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
142         },
143         /* [AO_SERIAL_SPEED_19200] = */ {
144                 /* .baud = */ 163,
145                 /* .gcr  = */ (9 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
146         },
147         /* [AO_SERIAL_SPEED_57600] = */ {
148                 /* .baud = */ 59,
149                 /* .gcr =  */ (11 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
150         },
151 };
152
153 void
154 ao_serial_set_speed(uint8_t speed)
155 {
156         ao_serial_drain();
157         if (speed > AO_SERIAL_SPEED_57600)
158                 return;
159         U1BAUD = ao_serial_speeds[speed].baud;
160         U1GCR = ao_serial_speeds[speed].gcr;
161 }
162
163 void
164 ao_serial_init(void)
165 {
166         /* Set up the USART pin assignment */
167         PERCFG = (PERCFG & ~PERCFG_U1CFG_ALT_MASK) | PERCFG_U1CFG_ALT_2;
168
169         /* ee has already set the P2SEL bits */
170
171         /* Make the USART pins be controlled by the USART */
172         P1SEL |= (1 << 6) | (1 << 7);
173
174         /* UART mode with receiver enabled */
175         U1CSR = (UxCSR_MODE_UART | UxCSR_RE);
176
177         /* Pick a 4800 baud rate */
178         ao_serial_set_speed(AO_SERIAL_SPEED_4800);
179
180         /* Reasonable serial parameters */
181         U1UCR = (UxUCR_FLUSH |
182                  UxUCR_FLOW_DISABLE |
183                  UxUCR_D9_ODD_PARITY |
184                  UxUCR_BIT9_8_BITS |
185                  UxUCR_PARITY_DISABLE |
186                  UxUCR_SPB_1_STOP_BIT |
187                  UxUCR_STOP_HIGH |
188                  UxUCR_START_LOW);
189
190         IEN0 |= IEN0_URX1IE;
191         IEN2 |= IEN2_UTX1IE;
192
193         ao_cmd_register(&ao_serial_cmds[0]);
194 #if 0
195 #if USE_SERIAL_STDIN
196         ao_add_stdio(ao_serial_pollchar,
197                      ao_serial_putchar,
198                      NULL);
199 #endif
200 #endif
201 }