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