altos: add telebt-v0.0 Makefile
[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 void
212 ao_btm_check_link() __critical
213 {
214         if (P2_1) {
215                 ao_btm_connected = 0;
216                 PICTL |= PICTL_P2ICON;
217         } else {
218                 ao_btm_connected = 1;
219                 PICTL &= ~PICTL_P2ICON;
220         }
221 }
222
223 void
224 ao_btm_isr(void)
225 {
226         if (P2IFG & (1 << 1)) {
227                 ao_btm_check_link();
228                 ao_wakeup(&ao_btm_connected);
229         }
230         P2IFG = 0;
231 }
232
233 void
234 ao_btm_init (void)
235 {
236         ao_serial_init();
237         ao_serial_set_speed(AO_SERIAL_SPEED_19200);
238
239         /*
240          * Configure link status line
241          */
242
243         /* Set P2_1 to input, pull-down */
244         P2DIR &= ~(1 << 1);
245         P2INP |= P2INP_MDP2_1_TRISTATE;
246
247         /* Enable P2 interrupts */
248         IEN2 |= IEN2_P2IE;
249         ao_btm_check_link();
250         PICTL |= PICTL_P2IEN;
251
252         ao_add_task(&ao_btm_task, ao_btm, "bt");
253 }