altos: Add support for TeleBT v3.0
[fw/altos] / src / drivers / 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 #ifdef AO_BTM_INT_PORT
20 #include <ao_exti.h>
21 #endif
22
23 #ifndef ao_serial_btm_getchar
24 #define ao_serial_btm_putchar   ao_serial1_putchar
25 #define _ao_serial_btm_pollchar _ao_serial1_pollchar
26 #define _ao_serial_btm_sleep()  ao_sleep((void *) &ao_serial1_rx_fifo)
27 #define ao_serial_btm_set_speed ao_serial1_set_speed
28 #define ao_serial_btm_drain     ao_serial1_drain
29 #endif
30
31 int8_t                  ao_btm_stdio;
32 __xdata uint8_t         ao_btm_connected;
33
34 #define BT_DEBUG 0
35
36 #if BT_DEBUG
37 __xdata char            ao_btm_buffer[256];
38 int                     ao_btm_ptr;
39 char                    ao_btm_dir;
40
41 static void
42 ao_btm_add_char(char c)
43 {
44         if (ao_btm_ptr < sizeof (ao_btm_buffer))
45                 ao_btm_buffer[ao_btm_ptr++] = c;
46 }
47
48 static void
49 ao_btm_log_char(char c, char dir)
50 {
51         if (dir != ao_btm_dir) {
52                 ao_btm_add_char(dir);
53                 ao_btm_dir = dir;
54         }
55         ao_btm_add_char(c);
56 }
57
58 static void
59 ao_btm_log_out_char(char c)
60 {
61         ao_btm_log_char(c, '>');
62 }
63
64 static void
65 ao_btm_log_in_char(char c)
66 {
67         ao_btm_log_char(c, '<');
68 }
69
70 /*
71  * Dump everything received from the bluetooth device during startup
72  */
73 static void
74 ao_btm_dump(void)
75 {
76         int i;
77         char c;
78
79         for (i = 0; i < ao_btm_ptr; i++) {
80                 c = ao_btm_buffer[i];
81                 if (c < ' ' && c != '\n')
82                         printf("\\%03o", ((int) c) & 0xff);
83                 else
84                         putchar(ao_btm_buffer[i]);
85         }
86         putchar('\n');
87 }
88
89 static void
90 ao_btm_speed(void)
91 {
92         ao_cmd_decimal();
93         if (ao_cmd_lex_u32 == 57600)
94                 ao_serial_btm_set_speed(AO_SERIAL_SPEED_57600);
95         else if (ao_cmd_lex_u32 == 19200)
96                 ao_serial_btm_set_speed(AO_SERIAL_SPEED_19200);
97         else
98                 ao_cmd_status = ao_cmd_syntax_error;
99 }
100
101 __code struct ao_cmds ao_btm_cmds[] = {
102         { ao_btm_dump,          "d\0Dump btm buffer." },
103         { ao_btm_speed,         "s <19200,57600>\0Set btm serial speed." },
104         { 0, NULL },
105 };
106
107 #define ao_btm_log_init()       ao_cmd_register(&ao_btm_cmds[0])
108
109 #else
110 #define ao_btm_log_in_char(c)
111 #define ao_btm_log_out_char(c)
112 #define ao_btm_log_init()
113 #endif
114
115 #define AO_BTM_MAX_REPLY        16
116 __xdata char            ao_btm_reply[AO_BTM_MAX_REPLY];
117
118 /*
119  * Read one bluetooth character.
120  * Returns AO_READ_AGAIN if no character arrives within 10ms
121  */
122
123 static int
124 ao_btm_getchar(void)
125 {
126         int     c;
127
128         ao_arch_block_interrupts();
129         while ((c = _ao_serial_btm_pollchar()) == AO_READ_AGAIN) {
130                 ao_alarm(AO_MS_TO_TICKS(10));
131                 c = _ao_serial_btm_sleep();
132                 ao_clear_alarm();
133                 if (c) {
134                         c = AO_READ_AGAIN;
135                         break;
136                 }
137         }
138         ao_arch_release_interrupts();
139         return c;
140 }
141
142 /*
143  * Read a line of data from the serial port, truncating
144  * it after a few characters.
145  */
146
147 uint8_t
148 ao_btm_get_line(void)
149 {
150         uint8_t ao_btm_reply_len = 0;
151         int c;
152         uint8_t l;
153
154         while ((c = ao_btm_getchar()) != AO_READ_AGAIN) {
155                 ao_btm_log_in_char(c);
156                 if (ao_btm_reply_len < sizeof (ao_btm_reply))
157                         ao_btm_reply[ao_btm_reply_len++] = c;
158                 if (c == '\r' || c == '\n')
159                         break;
160         }
161         for (l = ao_btm_reply_len; l < sizeof (ao_btm_reply);)
162                 ao_btm_reply[l++] = '\0';
163         return ao_btm_reply_len;
164 }
165
166 /*
167  * Drain the serial port completely
168  */
169 void
170 ao_btm_drain()
171 {
172         while (ao_btm_get_line())
173                 ;
174 }
175
176 /*
177  * Set the stdio echo for the bluetooth link
178  */
179 void
180 ao_btm_echo(uint8_t echo)
181 {
182         ao_stdios[ao_btm_stdio].echo = echo;
183 }
184
185 /*
186  * Delay between command charaters; the BT module
187  * can't keep up with 57600 baud
188  */
189
190 void
191 ao_btm_putchar(char c)
192 {
193         ao_btm_log_out_char(c);
194         ao_serial_btm_putchar(c);
195         ao_delay(1);
196 }
197
198 /*
199  * Wait for the bluetooth device to return
200  * status from the previously executed command
201  */
202 uint8_t
203 ao_btm_wait_reply(void)
204 {
205         for (;;) {
206                 ao_btm_get_line();
207                 if (!strncmp(ao_btm_reply, "OK", 2))
208                         return 1;
209                 if (!strncmp(ao_btm_reply, "ERROR", 5))
210                         return -1;
211                 if (ao_btm_reply[0] == '\0')
212                         return 0;
213         }
214 }
215
216 void
217 ao_btm_string(__code char *cmd)
218 {
219         char    c;
220
221         while ((c = *cmd++) != '\0')
222                 ao_btm_putchar(c);
223 }
224
225 uint8_t
226 ao_btm_cmd(__code char *cmd)
227 {
228         ao_btm_drain();
229         ao_btm_string(cmd);
230         return ao_btm_wait_reply();
231 }
232
233 uint8_t
234 ao_btm_set_name(void)
235 {
236         char    sn[8];
237         char    *s = sn + 8;
238         char    c;
239         int     n;
240         ao_btm_string("ATN=TeleBT-");
241         *--s = '\0';
242         *--s = '\r';
243         n = ao_serial_number;
244         do {
245                 *--s = '0' + n % 10;
246         } while (n /= 10);
247         while ((c = *s++))
248                 ao_btm_putchar(c);
249         return ao_btm_wait_reply();
250 }
251
252 uint8_t
253 ao_btm_try_speed(uint8_t speed)
254 {
255         ao_serial_btm_set_speed(speed);
256         ao_serial_btm_drain();
257         (void) ao_btm_cmd("\rATE0\rATQ0\r");
258         if (ao_btm_cmd("AT\r") == 1)
259                 return 1;
260         return 0;
261 }
262
263 /*
264  * A thread to initialize the bluetooth device and
265  * hang around to blink the LED when connected
266  */
267 void
268 ao_btm(void)
269 {
270 #ifdef AO_BTM_RESET_PORT
271         ao_gpio_set(AO_BTM_RESET_PORT, AO_BTM_RESET_PIN, AO_BTM_RESET, 0);
272         ao_delay(AO_MS_TO_TICKS(20));
273         ao_gpio_set(AO_BTM_RESET_PORT, AO_BTM_RESET_PIN, AO_BTM_RESET, 1);
274 #endif
275
276         /*
277          * Wait for the bluetooth device to boot
278          */
279         ao_delay(AO_SEC_TO_TICKS(3));
280
281         /*
282          * The first time we connect, the BTM-180 comes up at 19200 baud.
283          * After that, it will remember and come up at 57600 baud. So, see
284          * if it is already running at 57600 baud, and if that doesn't work
285          * then tell it to switch to 57600 from 19200 baud.
286          */
287         while (!ao_btm_try_speed(AO_SERIAL_SPEED_57600)) {
288                 ao_delay(AO_SEC_TO_TICKS(1));
289                 if (ao_btm_try_speed(AO_SERIAL_SPEED_19200))
290                         ao_btm_cmd("ATL4\r");
291                 ao_delay(AO_SEC_TO_TICKS(1));
292         }
293
294         /* Disable echo */
295         ao_btm_cmd("ATE0\r");
296
297         /* Enable flow control */
298         ao_btm_cmd("ATC1\r");
299
300         /* Set the reported name to something we can find on the host */
301         ao_btm_set_name();
302
303         /* Turn off status reporting */
304         ao_btm_cmd("ATQ1\r");
305
306         ao_btm_stdio = ao_add_stdio(_ao_serial_btm_pollchar,
307                                     ao_serial_btm_putchar,
308                                     NULL);
309         ao_btm_echo(0);
310
311         for (;;) {
312                 while (!ao_btm_connected)
313                         ao_sleep(&ao_btm_connected);
314                 while (ao_btm_connected) {
315                         ao_led_for(AO_BT_LED, AO_MS_TO_TICKS(20));
316                         ao_delay(AO_SEC_TO_TICKS(3));
317                 }
318         }
319 }
320
321 __xdata struct ao_task ao_btm_task;
322
323 #if BT_LINK_ON_P2
324 #define BT_PICTL_ICON   PICTL_P2ICON
325 #define BT_PIFG         P2IFG
326 #define BT_PDIR         P2DIR
327 #define BT_PINP         P2INP
328 #define BT_IEN2_PIE     IEN2_P2IE
329 #define BT_CC1111       1
330 #endif
331 #if BT_LINK_ON_P1
332 #define BT_PICTL_ICON   PICTL_P1ICON
333 #define BT_PIFG         P1IFG
334 #define BT_PDIR         P1DIR
335 #define BT_PINP         P1INP
336 #define BT_IEN2_PIE     IEN2_P1IE
337 #define BT_CC1111       1
338 #endif
339
340 void
341 ao_btm_check_link()
342 {
343 #if BT_CC1111
344         ao_arch_critical(
345                 /* Check the pin and configure the interrupt detector to wait for the
346                  * pin to flip the other way
347                  */
348                 if (BT_LINK_PIN) {
349                         ao_btm_connected = 0;
350                         PICTL |= BT_PICTL_ICON;
351                 } else {
352                         ao_btm_connected = 1;
353                         PICTL &= ~BT_PICTL_ICON;
354                 }
355                 );
356 #endif
357 }
358
359 #if BT_CC1111
360 void
361 ao_btm_isr(void)
362 #if BT_LINK_ON_P1
363         __interrupt 15
364 #endif
365 {
366 #if BT_LINK_ON_P1
367         P1IF = 0;
368 #endif
369         if (BT_PIFG & (1 << BT_LINK_PIN_INDEX)) {
370                 ao_btm_check_link();
371                 ao_wakeup(&ao_btm_connected);
372         }
373         BT_PIFG = 0;
374 }
375 #endif
376
377 #ifdef AO_BTM_INT_PORT
378 void
379 ao_btm_isr(void)
380 {
381         ao_btm_check_link();
382         ao_wakeup(&ao_btm_connected);
383 }
384 #endif
385
386 void
387 ao_btm_init (void)
388 {
389         ao_serial_init();
390
391         ao_serial_btm_set_speed(AO_SERIAL_SPEED_19200);
392
393 #ifdef AO_BTM_RESET_PORT
394         ao_enable_output(AO_BTM_RESET_PORT,AO_BTM_RESET_PIN,AO_BTM_RESET,0);
395 #endif
396
397 #ifdef AO_BTM_INT_PORT
398         ao_enable_port(AO_BTM_INT_PORT);
399         ao_exti_setup(AO_BTM_INT_PORT, AO_BTM_INT_PIN,
400                       AO_EXTI_MODE_FALLING|AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_LOW,
401                       ao_btm_isr);
402 #endif
403
404 #if BT_CC1111
405 #if BT_LINK_ON_P1
406         /*
407          * Configure ser reset line
408          */
409
410         P1_6 = 0;
411         P1DIR |= (1 << 6);
412 #endif
413
414         /*
415          * Configure link status line
416          */
417
418         /* Set pin to input */
419         BT_PDIR &= ~(1 << BT_LINK_PIN_INDEX);
420
421         /* Set pin to tri-state */
422         BT_PINP |= (1 << BT_LINK_PIN_INDEX);
423
424         /* Enable interrupts */
425         IEN2 |= BT_IEN2_PIE;
426 #endif
427
428         /* Check current pin state */
429         ao_btm_check_link();
430
431 #ifdef AO_BTM_INT_PORT
432         ao_exti_enable(AO_BTM_INT_PORT, AO_BTM_INT_PIN);
433 #endif
434
435 #if BT_LINK_ON_P2
436         /* Eable the pin interrupt */
437         PICTL |= PICTL_P2IEN;
438 #endif
439 #if BT_LINK_ON_P1
440         /* Enable pin interrupt */
441         P1IEN |= (1 << BT_LINK_PIN_INDEX);
442 #endif
443
444         ao_add_task(&ao_btm_task, ao_btm, "bt");
445         ao_btm_log_init();
446 }