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