altos: Clean up BT serial communcations
[fw/altos] / src / ao_btm.c
1 /*
2  * Copyright © 2011 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 uint8_t                 ao_btm_running;
21 uint8_t                 ao_btm_stdio;
22 __xdata uint8_t         ao_btm_connected;
23 uint8_t                 ao_btm_chat;
24
25 __xdata char            ao_btm_buffer[1024];
26 int                     ao_btm_ptr;
27
28 #define AO_BTM_MAX_REPLY        16
29 __xdata char            ao_btm_reply[AO_BTM_MAX_REPLY];
30
31 extern volatile __xdata struct ao_fifo  ao_usart1_rx_fifo;
32
33 /*
34  * Read a line of data from the serial port, truncating
35  * it after a few characters.
36  */
37
38 uint8_t
39 ao_btm_get_line(void)
40 {
41         uint8_t ao_btm_reply_len = 0;
42         char c;
43
44         for (;;) {
45
46                 while ((c = ao_serial_pollchar()) != AO_READ_AGAIN) {
47                         if (ao_btm_reply_len < sizeof (ao_btm_reply))
48                                 ao_btm_reply[ao_btm_reply_len++] = c;
49                         if (ao_btm_ptr < sizeof (ao_btm_buffer))
50                                 ao_btm_buffer[ao_btm_ptr++] = c;
51                         if (c == '\r' || c == '\n')
52                                 goto done;
53                 }
54                 for (c = 0; c < 10; c++) {
55                         ao_delay(AO_MS_TO_TICKS(10));
56                         if (!ao_fifo_empty(ao_usart1_rx_fifo))
57                                 break;
58                 }
59                 if (c == 10)
60                         goto done;
61         }
62 done:
63         for (c = ao_btm_reply_len; c < sizeof (ao_btm_reply);)
64                 ao_btm_reply[c++] = '\0';
65         return ao_btm_reply_len;
66 }
67
68 /*
69  * Drain the serial port completely
70  */
71 void
72 ao_btm_drain()
73 {
74         while (ao_btm_get_line())
75                 ;
76 }
77
78 void
79 ao_btm_echo(uint8_t echo)
80 {
81         ao_stdios[ao_btm_stdio].echo = echo;
82 }
83
84 /*
85  * A command line pre-processor to detect connect/disconnect messages
86  * and update the internal state
87  */
88
89 uint8_t
90 ao_cmd_filter(void)
91 {
92         ao_cmd_lex();
93         while (ao_cmd_lex_c != '\n') {
94                 if (ao_match_word("CONNECT")) {
95                         ao_btm_connected = 1;
96                         ao_btm_echo(1);
97                         ao_wakeup(&ao_btm_connected);
98                         return 1;
99                 }
100                 if (ao_match_word("DISCONNECT")) {
101                         ao_btm_connected = 0;
102                         ao_btm_echo(0);
103                         ao_wakeup(&ao_btm_connected);
104                         return 1;
105                 }
106                 if (ao_match_word("ERROR"))
107                         return 1;
108                 if (ao_match_word("OK"))
109                         return 1;
110                 ao_cmd_lex();
111         }
112         ao_cmd_status = 0;
113         return !ao_btm_connected;
114 }
115
116 /*
117  * A wrapper for ao_serial_pollchar that
118  * doesn't return any characters while we're
119  * initializing the bluetooth device
120  */
121 char
122 ao_btm_pollchar(void)
123 {
124         char    c;
125         if (!ao_btm_running)
126                 return AO_READ_AGAIN;
127         c = ao_serial_pollchar();
128         if (c != AO_READ_AGAIN)
129                 if (ao_btm_ptr < sizeof (ao_btm_buffer))
130                         ao_btm_buffer[ao_btm_ptr++] = c;
131         return c;
132 }
133
134 /*
135  * Wait for the bluetooth device to return
136  * status from the previously executed command
137  */
138 uint8_t
139 ao_btm_wait_reply(void)
140 {
141         for (;;) {
142                 ao_btm_get_line();
143                 if (!strcmp(ao_btm_reply, "OK"))
144                         return 1;
145                 if (!strcmp(ao_btm_reply, "ERROR"))
146                         return -1;
147                 if (ao_btm_reply[0] == '\0')
148                         return 0;
149         }
150 }
151
152 void
153 ao_btm_cmd(__code char *cmd)
154 {
155         ao_cur_stdio = ao_btm_stdio;
156         printf(cmd);
157         ao_btm_wait_reply();
158 }
159
160 /*
161  * A thread to initialize the bluetooth device and
162  * hang around to blink the LED when connected
163  */
164 void
165 ao_btm(void)
166 {
167         ao_serial_set_speed(AO_SERIAL_SPEED_19200);
168         ao_add_stdio(ao_btm_pollchar,
169                      ao_serial_putchar,
170                      NULL);
171         ao_btm_stdio = ao_num_stdios - 1;
172         ao_cur_stdio = ao_btm_stdio;
173         ao_btm_echo(0);
174         ao_btm_drain();
175         ao_delay(AO_SEC_TO_TICKS(1));
176         printf("+++");
177         ao_btm_drain();
178         ao_delay(AO_SEC_TO_TICKS(1));
179         printf("\r");
180         ao_btm_drain();
181         ao_btm_cmd("ATQ0\r");
182         ao_btm_cmd("ATE0\r");
183         ao_btm_cmd("ATH\r");
184         ao_delay(AO_SEC_TO_TICKS(1));
185         ao_btm_cmd("ATC0\r");
186         ao_btm_cmd("ATL4\r");
187         ao_serial_set_speed(AO_SERIAL_SPEED_57600);
188         ao_btm_drain();
189         printf("ATN=TeleBT-%d\r", ao_serial_number);
190         ao_btm_wait_reply();
191         ao_btm_running = 1;
192         for (;;) {
193                 while (!ao_btm_connected && !ao_btm_chat)
194                         ao_sleep(&ao_btm_connected);
195                 if (ao_btm_chat) {
196                         ao_btm_running = 0;
197                         while (ao_btm_chat) {
198                                 char    c;
199                                 c = ao_serial_pollchar();
200                                 if (c != AO_READ_AGAIN)
201                                         ao_usb_putchar(c);
202                                 else {
203                                         ao_usb_flush();
204                                         ao_sleep(&ao_usart1_rx_fifo);
205                                 }
206                         }
207                         ao_btm_running = 1;
208                 }
209                 while (ao_btm_connected) {
210                         ao_led_for(AO_LED_GREEN, AO_MS_TO_TICKS(20));
211                         ao_delay(AO_SEC_TO_TICKS(3));
212                 }
213         }
214 }
215
216 __xdata struct ao_task ao_btm_task;
217
218 /*
219  * Connect directly to the bluetooth device, mostly
220  * useful for testing
221  */
222 static void
223 ao_btm_forward(void)
224 {
225         char c;
226
227         ao_btm_chat = 1;
228         ao_wakeup(&ao_btm_connected);
229         ao_usb_flush();
230         while ((c = ao_usb_getchar()) != '~') {
231                 if (c == '\n') c = '\r';
232                 ao_serial_putchar(c);
233         }
234         ao_btm_chat = 0;
235         while (!ao_btm_running) {
236                 ao_wakeup(&ao_usart1_rx_fifo);
237                 ao_delay(AO_MS_TO_TICKS(10));
238         }
239 }
240
241 /*
242  * Dump everything received from the bluetooth device during startup
243  */
244 static void
245 ao_btm_dump(void)
246 {
247         int i;
248
249         for (i = 0; i < ao_btm_ptr; i++)
250                 putchar(ao_btm_buffer[i]);
251         putchar('\n');
252 }
253
254 __code struct ao_cmds ao_btm_cmds[] = {
255         { ao_btm_forward,       "B\0BTM serial link." },
256         { ao_btm_dump,          "d\0Dump btm buffer." },
257         { 0, NULL },
258 };
259
260 void
261 ao_btm_init (void)
262 {
263         ao_serial_init();
264         ao_serial_set_speed(AO_SERIAL_SPEED_19200);
265         ao_add_task(&ao_btm_task, ao_btm, "bt");
266         ao_cmd_register(&ao_btm_cmds[0]);
267 }