altos: Hook up the P1 ISR for TeleBT v0.1 bt_link line
[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 #if BT_LINK_ON_P2
21 #define BT_PICTL_ICON   PICTL_P2ICON
22 #define BT_PIFG         P2IFG
23 #define BT_PDIR         P2DIR
24 #define BT_PINP         P2INP
25 #define BT_IEN2_PIE     IEN2_P2IE
26 #endif
27 #if BT_LINK_ON_P1
28 #define BT_PICTL_ICON   PICTL_P1ICON
29 #define BT_PIFG         P1IFG
30 #define BT_PDIR         P1DIR
31 #define BT_PINP         P1INP
32 #define BT_IEN2_PIE     IEN2_P1IE
33 #endif
34
35 int8_t                  ao_btm_stdio;
36 __xdata uint8_t         ao_btm_connected;
37
38 #define AO_BTM_MAX_REPLY        16
39 __xdata char            ao_btm_reply[AO_BTM_MAX_REPLY];
40
41 extern volatile __xdata struct ao_fifo  ao_usart1_rx_fifo;
42
43 /*
44  * Read a line of data from the serial port, truncating
45  * it after a few characters.
46  */
47
48 uint8_t
49 ao_btm_get_line(void)
50 {
51         uint8_t ao_btm_reply_len = 0;
52         char c;
53
54         for (;;) {
55
56                 while ((c = ao_serial_pollchar()) != AO_READ_AGAIN) {
57                         if (ao_btm_reply_len < sizeof (ao_btm_reply))
58                                 ao_btm_reply[ao_btm_reply_len++] = c;
59                         if (c == '\r' || c == '\n')
60                                 goto done;
61                 }
62                 for (c = 0; c < 10; c++) {
63                         ao_delay(AO_MS_TO_TICKS(10));
64                         if (!ao_fifo_empty(ao_usart1_rx_fifo))
65                                 break;
66                 }
67                 if (c == 10)
68                         goto done;
69         }
70 done:
71         for (c = ao_btm_reply_len; c < sizeof (ao_btm_reply);)
72                 ao_btm_reply[c++] = '\0';
73         return ao_btm_reply_len;
74 }
75
76 /*
77  * Drain the serial port completely
78  */
79 void
80 ao_btm_drain()
81 {
82         while (ao_btm_get_line())
83                 ;
84 }
85
86 /*
87  * Set the stdio echo for the bluetooth link
88  */
89 void
90 ao_btm_echo(uint8_t echo)
91 {
92         ao_stdios[ao_btm_stdio].echo = echo;
93 }
94
95 /*
96  * Delay between command charaters; the BT module
97  * can't keep up with 57600 baud
98  */
99
100 void
101 ao_btm_putchar(char c)
102 {
103         ao_serial_putchar(c);
104         ao_delay(1);
105 }
106
107 /*
108  * Wait for the bluetooth device to return
109  * status from the previously executed command
110  */
111 uint8_t
112 ao_btm_wait_reply(void)
113 {
114         for (;;) {
115                 ao_btm_get_line();
116                 if (!strncmp(ao_btm_reply, "OK", 2))
117                         return 1;
118                 if (!strncmp(ao_btm_reply, "ERROR", 5))
119                         return -1;
120                 if (ao_btm_reply[0] == '\0')
121                         return 0;
122         }
123 }
124
125 void
126 ao_btm_string(__code char *cmd)
127 {
128         char    c;
129
130         while (c = *cmd++)
131                 ao_btm_putchar(c);
132 }
133
134 uint8_t
135 ao_btm_cmd(__code char *cmd)
136 {
137         ao_btm_drain();
138         ao_btm_string(cmd);
139         return ao_btm_wait_reply();
140 }
141
142 uint8_t
143 ao_btm_set_name(void)
144 {
145         char    sn[8];
146         char    *s = sn + 8;
147         char    c;
148         int     n;
149         ao_btm_string("ATN=TeleBT-");
150         *--s = '\0';
151         *--s = '\r';
152         n = ao_serial_number;
153         do {
154                 *--s = '0' + n % 10;
155         } while (n /= 10);
156         while ((c = *s++))
157                 ao_btm_putchar(c);
158         return ao_btm_wait_reply();
159 }
160
161 uint8_t
162 ao_btm_try_speed(uint8_t speed)
163 {
164         ao_serial_set_speed(speed);
165         ao_btm_drain();
166         (void) ao_btm_cmd("\rATE0\rATQ0\r");
167         if (ao_btm_cmd("AT\r") == 1)
168                 return 1;
169         return 0;
170 }
171
172 /*
173  * A thread to initialize the bluetooth device and
174  * hang around to blink the LED when connected
175  */
176 void
177 ao_btm(void)
178 {
179         /*
180          * Wait for the bluetooth device to boot
181          */
182         ao_delay(AO_SEC_TO_TICKS(3));
183
184 #if BT_LINK_ON_P1
185         if ((P1DIR & (1 << 6)) == 0)
186                 ao_panic(AO_PANIC_BT);
187         if ((P1DIR & (1 << 7)) != 0)
188                 ao_panic(AO_PANIC_BT);
189         if ((P0SEL & ((1 << 5) | (1 << 4) | (1 << 3) | (1 << 2))) !=
190             ((1 << 5) | (1 << 4) | (1 << 3) | (1 << 2)))
191                 ao_panic(AO_PANIC_BT);
192 #endif
193 #if HAS_BEEP
194         ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
195 #endif
196
197         /*
198          * The first time we connect, the BTM-180 comes up at 19200 baud.
199          * After that, it will remember and come up at 57600 baud. So, see
200          * if it is already running at 57600 baud, and if that doesn't work
201          * then tell it to switch to 57600 from 19200 baud.
202          */
203         while (!ao_btm_try_speed(AO_SERIAL_SPEED_57600)) {
204                 ao_delay(AO_SEC_TO_TICKS(1));
205                 if (ao_btm_try_speed(AO_SERIAL_SPEED_19200))
206                         ao_btm_cmd("ATL4\r");
207                 ao_delay(AO_SEC_TO_TICKS(1));
208         }
209
210         /* Disable echo */
211         ao_btm_cmd("ATE0\r");
212
213         /* Enable flow control */
214         ao_btm_cmd("ATC1\r");
215
216         /* Set the reported name to something we can find on the host */
217         ao_btm_set_name();
218
219         /* Turn off status reporting */
220         ao_btm_cmd("ATQ1\r");
221
222         ao_btm_stdio = ao_add_stdio(ao_serial_pollchar,
223                                     ao_serial_putchar,
224                                     NULL);
225         ao_btm_echo(0);
226
227         for (;;) {
228                 while (!ao_btm_connected)
229                         ao_sleep(&ao_btm_connected);
230                 while (ao_btm_connected) {
231                         ao_led_for(AO_LED_GREEN, AO_MS_TO_TICKS(20));
232                         ao_delay(AO_SEC_TO_TICKS(3));
233                 }
234         }
235 }
236
237 __xdata struct ao_task ao_btm_task;
238
239 void
240 ao_btm_check_link() __critical
241 {
242         /* Check the pin and configure the interrupt detector to wait for the
243          * pin to flip the other way
244          */
245         if (BT_LINK_PIN) {
246                 ao_btm_connected = 0;
247                 PICTL |= BT_PICTL_ICON;
248         } else {
249                 ao_btm_connected = 1;
250                 PICTL &= ~BT_PICTL_ICON;
251         }
252 }
253
254 void
255 ao_btm_isr(void)
256 #if BT_LINK_ON_P1
257         __interrupt 15
258 #endif
259 {
260         if (BT_PIFG & (1 << BT_LINK_PIN_INDEX)) {
261                 ao_btm_check_link();
262                 ao_wakeup(&ao_btm_connected);
263         }
264         BT_PIFG = 0;
265 }
266
267 void
268 ao_btm_init (void)
269 {
270         ao_serial_init();
271         ao_serial_set_speed(AO_SERIAL_SPEED_19200);
272
273 #if BT_LINK_ON_P1
274         /*
275          * Configure ser reset line
276          */
277
278         P1_6 = 0;
279         P1DIR |= (1 << 6);
280 #endif
281
282         /*
283          * Configure link status line
284          */
285
286         /* Set pin to input */
287         BT_PDIR &= ~(1 << BT_LINK_PIN_INDEX);
288
289         /* Set pin to tri-state */
290         BT_PINP |= (1 << BT_LINK_PIN_INDEX);
291
292         /* Enable interrupts */
293         IEN2 |= BT_IEN2_PIE;
294
295         /* Check current pin state */
296         ao_btm_check_link();
297
298 #if BT_LINK_ON_P2
299         /* Eable the pin interrupt */
300         PICTL |= PICTL_P2IEN;
301 #endif
302 #if BT_LINK_ON_P1
303         /* Enable pin interrupt */
304         P1IEN |= (1 << BT_LINK_PIN_INDEX);
305 #endif
306
307         ao_add_task(&ao_btm_task, ao_btm, "bt");
308 }