altos: Add RN4678 Bluetooth module driver
[fw/altos] / src / drivers / ao_rn4678.c
1 /*
2  * Copyright © 2017 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
15 #include <ao.h>
16 #include <ao_rn4678.h>
17 #include <ao_exti.h>
18 #include <stdarg.h>
19
20 static uint8_t  ao_rn_connected;
21
22 #define AO_RN_DEBUG 0
23
24 #if AO_RN_DEBUG
25 static void ao_rn_dbg(char *format, ...) {
26         va_list a;
27         uint32_t        irq = ao_arch_irqsave();
28         ao_arch_release_interrupts();
29         va_start(a, format);
30         vprintf(format, a);
31         va_end(a);
32         flush();
33         ao_arch_irqrestore(irq);
34 }
35
36 static char     ao_rn_dir;
37
38 static void
39 ao_rn_log_char(char c, char dir)
40 {
41         if (dir != ao_rn_dir) {
42                 putchar(dir);
43                 ao_rn_dir = dir;
44         }
45         switch (c) {
46         case '\r':
47                 putchar('\\'); putchar('r');
48                 break;
49         case '\n':
50                 putchar('\\'); putchar('n');
51                 break;
52         default:
53                 putchar(c);
54         }
55         flush();
56 }
57
58 static void
59 ao_rn_log_out_char(char c)
60 {
61         ao_rn_log_char(c, '}');
62 }
63
64 static void
65 ao_rn_log_in_char(char c)
66 {
67         ao_rn_log_char(c, '{');
68 }
69
70 static inline void
71 ao_rn_putchar(char c)
72 {
73         ao_rn_log_out_char(c);
74         ao_serial_rn_putchar(c);
75 }
76
77 static inline int
78 _ao_rn_pollchar(void)
79 {
80         int     c = _ao_serial_rn_pollchar();
81
82         if (c != AO_READ_AGAIN) {
83                 ao_arch_release_interrupts();
84                 ao_rn_log_in_char((char) c);
85                 ao_arch_block_interrupts();
86         }
87         return c;
88 }
89 #else
90 #define ao_rn_dbg(fmt, ...)
91 #define ao_rn_putchar(c)        ao_serial_rn_putchar(c)
92 #define _ao_rn_pollchar()       _ao_serial_rn_pollchar()
93 #endif
94
95 /* For stdio, this skips all status messages *sigh* */
96
97 #define STATUS_CHAR     '%'
98
99 static const char *status_strings[] = {
100         "RFCOMM_CLOSE",
101         "RFCOMM_OPEN",
102         "CONNECT",
103         "DISCONN",
104         "BONDED",
105 };
106
107 #define NUM_STATUS_STRING       (sizeof status_strings/sizeof status_strings[0])
108
109 int
110 _ao_wrap_rn_pollchar(void)
111 {
112         static char     buffer[64];
113         static int      buf_cnt, buf_ptr;
114         static int      draining;
115         int             c = AO_READ_AGAIN;
116         unsigned        i;
117         int             done = 0;
118
119         while (!done && !draining) {
120                 c = _ao_serial_rn_pollchar();
121
122                 if (c == AO_READ_AGAIN)
123                         return AO_READ_AGAIN;
124
125                 if (buf_cnt) {
126                         /* buffering chars */
127
128                         if (c == STATUS_CHAR) {
129                                 /* End of status string, drop it and carry on */
130                                 buffer[buf_cnt] = '\0';
131                                 ao_rn_dbg("discard %s\n", buffer);
132                                 buf_cnt = 0;
133                         } else if (buf_cnt == sizeof(buffer)) {
134                                 /* If we filled the buffer, just give up */
135                                 draining = 1;
136                         } else {
137                                 buffer[buf_cnt++] = c;
138                                 for (i = 0; i < NUM_STATUS_STRING; i++) {
139                                         int cmp = strlen(status_strings[i]);
140                                         if (cmp >= buf_cnt)
141                                                 cmp = buf_cnt-1;
142                                         if (memcmp(buffer+1, status_strings[i], cmp) == 0)
143                                                 break;
144                                 }
145                                 if (i == NUM_STATUS_STRING)
146                                         draining = 1;
147                         }
148                 } else if (c == STATUS_CHAR) {
149                         buffer[0] = c;
150                         buf_cnt = 1;
151                         buf_ptr = 0;
152                 } else
153                         done = 1;
154         }
155         if (draining) {
156                 c = buffer[buf_ptr++] & 0xff;
157                 if (buf_ptr == buf_cnt) {
158                         buf_ptr = buf_cnt = 0;
159                         draining = 0;
160                 }
161         }
162 #if AO_RN_DEBUG
163         ao_arch_release_interrupts();
164         ao_usb_putchar(c); ao_usb_flush();
165         ao_arch_block_interrupts();
166 #endif
167         return c;
168 }
169
170 #if AO_RN_DEBUG
171 static void
172 ao_wrap_rn_putchar(char c)
173 {
174         ao_usb_putchar(c); ao_usb_flush();
175         ao_serial_rn_putchar(c);
176 }
177 #else
178 #define ao_wrap_rn_putchar ao_serial_rn_putchar
179 #endif
180
181 static void
182 ao_rn_puts(char *s)
183 {
184         char c;
185
186         while ((c = *s++))
187                 ao_rn_putchar(c);
188 }
189
190 static void
191 ao_rn_drain(void)
192 {
193         int     timeout = 0;
194
195         ao_rn_dbg("drain...\n");
196         ao_serial_rn_drain();
197         while (!timeout) {
198                 ao_arch_block_interrupts();
199                 while (_ao_rn_pollchar() == AO_READ_AGAIN) {
200                         if (_ao_serial_rn_sleep_for(AO_MS_TO_TICKS(10))) {
201                                 timeout = 1;
202                                 break;
203                         }
204                 }
205                 ao_arch_release_interrupts();
206         }
207         ao_rn_dbg("drain done\n");
208 }
209
210 static void
211 ao_rn_send_cmd(char *cmd, char *param)
212 {
213         ao_rn_dbg("send_cmd %s%s\n", cmd, param ? param : "");
214         ao_rn_drain();
215         ao_rn_puts(cmd);
216         if (param)
217                 ao_rn_puts(param);
218         ao_rn_putchar('\r');
219 }
220
221 static int
222 ao_rn_wait_char(AO_TICK_TYPE giveup_time)
223 {
224         int c;
225
226         ao_arch_block_interrupts();
227         while ((c = _ao_rn_pollchar()) == AO_READ_AGAIN) {
228                 AO_TICK_SIGNED  delay = (AO_TICK_SIGNED) (giveup_time - ao_time());
229                 if (delay < 0) {
230                         ao_arch_release_interrupts();
231                         return AO_READ_AGAIN;
232                 }
233                 _ao_serial_rn_sleep_for(delay);
234         }
235         ao_arch_release_interrupts();
236         return c;
237 }
238
239 static int
240 ao_rn_wait_for(int timeout, char *match)
241 {
242         char            reply[AO_RN_MAX_REPLY_LEN + 1];
243         int             match_len = strlen(match);
244         AO_TICK_TYPE    giveup_time = ao_time() + timeout;
245         int             c;
246
247         ao_rn_dbg("wait for %d, \"%s\"\n", timeout, match);
248         memset(reply, ' ', sizeof(reply));
249         while (memcmp(reply, match, match_len) != 0) {
250                 c = ao_rn_wait_char(giveup_time);
251                 if (c == AO_READ_AGAIN) {
252                         ao_rn_dbg("\twait for timeout\n");
253                         return AO_RN_TIMEOUT;
254                 }
255                 reply[match_len] = (char) c;
256                 memmove(reply, reply+1, match_len);
257                 reply[match_len] = '\0';
258                 ao_rn_dbg("\tmatch now \"%s\"\n", reply);
259         }
260         ao_rn_dbg("\twait for ok\n");
261         return AO_RN_OK;
262 }
263
264 static int
265 ao_rn_wait_line(AO_TICK_TYPE giveup_time, char *line, int len)
266 {
267         char *l = line;
268
269         ao_rn_dbg("wait line\n");
270         for (;;) {
271                 int c = ao_rn_wait_char(giveup_time);
272
273                 /* timeout */
274                 if (c == AO_READ_AGAIN) {
275                         ao_rn_dbg("\twait line timeout\n");
276                         return AO_RN_TIMEOUT;
277                 }
278
279                 /* done */
280                 if (c == '\r') {
281                         *l = '\0';
282                         ao_rn_dbg("\twait line \"%s\"\n", line);
283                         return AO_RN_OK;
284                 }
285
286                 if (c == '\n')
287                         continue;
288
289                 /* buffer overrun */
290                 if (len <= 1)
291                         return AO_RN_ERROR;
292
293                 *l++ = (char) c;
294                 len--;
295         }
296 }
297
298 static int
299 ao_rn_wait_status(void)
300 {
301         char            message[AO_RN_MAX_REPLY_LEN];
302         AO_TICK_TYPE    giveup_time = ao_time() + AO_RN_CMD_TIMEOUT;
303         int             status;
304
305         ao_rn_dbg("wait status\n");
306         status = ao_rn_wait_line(giveup_time, message, sizeof (message));
307         if (status == AO_RN_OK)
308                 if (strncmp(message, "AOK", 3) != 0)
309                         status = AO_RN_ERROR;
310         return status;
311 }
312
313 static int
314 ao_rn_set_name(void)
315 {
316         char    sn[8];
317         char    *s = sn + 8;
318         char    c;
319         int     n;
320
321         ao_rn_dbg("set name...\n");
322         ao_rn_send_cmd(AO_RN_SET_NAME_CMD, "TeleBT-");
323         *--s = '\0';
324         *--s = '\r';
325         n = ao_serial_number;
326         do {
327                 *--s = '0' + n % 10;
328         } while (n /= 10);
329         while ((c = *s++))
330                 ao_rn_putchar(c);
331         return ao_rn_wait_status();
332 }
333
334 static int
335 ao_rn_get_name(char *name, int len)
336 {
337         ao_rn_dbg("get name...\n");
338         ao_rn_send_cmd(AO_RN_GET_NAME_CMD, NULL);
339         return ao_rn_wait_line(ao_time() + AO_RN_CMD_TIMEOUT, name, len);
340 }
341
342 static void
343 ao_rn_check_link(void)
344 {
345         ao_rn_connected = 1 - ao_gpio_get(AO_RN_CONNECTED_PORT, AO_RN_CONNECTED_PIN, foo);
346 }
347
348 static void
349 ao_rn_isr(void)
350 {
351         ao_rn_check_link();
352         ao_wakeup(&ao_rn_connected);
353 }
354
355 static void
356 ao_bt_panic(int where)
357 {
358         int i;
359         for (;;) {
360                 for (i = 0; i < 50; i++) {
361                         ao_led_toggle(AO_BT_LED);
362                         ao_delay(AO_MS_TO_TICKS(10));
363                 }
364                 ao_led_off(AO_BT_LED);
365                 ao_delay(AO_MS_TO_TICKS(500));
366                 for (i = 0; i < where; i++) {
367                         ao_led_for(AO_BT_LED, AO_MS_TO_TICKS(200));
368                         ao_delay(AO_MS_TO_TICKS(200));
369                 }
370         }
371 }
372
373 static uint8_t  ao_rn_stdio;
374
375 /*
376  * Set the stdio echo for the bluetooth link
377  */
378 void
379 ao_rn_echo(uint8_t echo)
380 {
381         ao_stdios[ao_rn_stdio].echo = echo;
382 }
383
384 static void
385 ao_rn(void)
386 {
387         int     status = AO_RN_ERROR;
388         char    name[17];
389         int     i;
390
391         ao_rn_dbg("ao_rn top\n");
392
393         /* Select CMD mode after the device gets out of reset */
394         ao_gpio_set(AO_RN_CMD_PORT, AO_RN_CMD_PIN, foo, AO_RN_CMD_CMD);
395
396         for (i = 0; i < 3; i++) {
397                 ao_rn_dbg("reset device\n");
398
399                 ao_gpio_set(AO_RN_RST_N_PORT, AO_RN_RST_N_PIN, foo, 0);
400                 ao_delay(AO_MS_TO_TICKS(100));
401
402                 /* Reboot the RN4678 and wait for it to start talking */
403                 ao_rn_drain();
404                 ao_gpio_set(AO_RN_RST_N_PORT, AO_RN_RST_N_PIN, foo, 1);
405                 status = ao_rn_wait_for(AO_RN_REBOOT_TIMEOUT, AO_RN_REBOOT_MSG);
406                 if (status != AO_RN_OK) {
407                         ao_rn_dbg("reboot failed\n");
408                         continue;
409                 }
410
411                 /* After it reboots, it can take a moment before it responds
412                  * to commands
413                  */
414                 (void) ao_rn_wait_for(AO_RN_REBOOT_TIMEOUT, "CMD> ");
415
416                 /* Check to see if the name is already set and assume
417                  * that the device is ready to go
418                  */
419                 status = ao_rn_get_name(name, sizeof (name));
420                 if (status != AO_RN_OK) {
421                         ao_rn_dbg("get name failed\n");
422                         continue;
423                 }
424
425                 if (strncmp(name, "TeleBT", 6) == 0) {
426                         ao_rn_dbg("name is set\n");
427                         status = AO_RN_OK;
428                         break;
429                 }
430
431                 /* Make the command pin control command/data mode */
432                 ao_rn_send_cmd(AO_RN_SET_COMMAND_PIN, NULL);
433                 if (ao_rn_wait_status() != AO_RN_OK) {
434                         ao_rn_dbg("set command pin failed\n");
435                         continue;
436                 }
437
438                 /* Select 'fast' mode to ignore command sequence (more or less) */
439                 ao_rn_send_cmd(AO_RN_SET_FAST_MODE, NULL);
440                 if (ao_rn_wait_status() != AO_RN_OK) {
441                         ao_rn_dbg("set fast mode failed\n");
442                         continue;
443                 }
444
445                 /* Finally, set the name. Doing this last makes it possible to check
446                  * if the whole sequence has been done
447                  */
448                 if (ao_rn_set_name() != AO_RN_OK) {
449                         ao_rn_dbg("set name failed\n");
450                         continue;
451                 }
452
453                 /* After we've configured the device, go back around and reboot it
454                  * as that's how we get the new configuration to take effect
455                  */
456         }
457         ao_rn_dbg("ao_rn status %d\n", status);
458
459         if (status != AO_RN_OK)
460                 ao_bt_panic(4);
461
462         ao_gpio_set(AO_RN_CMD_PORT, AO_RN_CMD_PIN, foo, AO_RN_CMD_DATA);
463
464         /* Wait for the hardware to finish sending messages, then clear the queue */
465         ao_delay(AO_MS_TO_TICKS(200));
466         ao_rn_drain();
467
468         ao_exti_enable(AO_RN_CONNECTED_PORT, AO_RN_CONNECTED_PIN);
469
470 #if 1
471         ao_rn_stdio = ao_add_stdio(_ao_wrap_rn_pollchar,
472                                    ao_wrap_rn_putchar,
473                                    NULL);
474
475         ao_rn_echo(0);
476
477         ao_rn_check_link();
478
479         ao_rn_dbg("RN running\n");
480
481         /*
482          * Now just hang around and flash the blue LED when we've got
483          * a connection
484          */
485         for (;;) {
486                 ao_arch_block_interrupts();
487                 while (!ao_rn_connected)
488                         ao_sleep(&ao_rn_connected);
489                 ao_arch_release_interrupts();
490                 while (ao_rn_connected) {
491                         ao_led_for(AO_BT_LED, AO_MS_TO_TICKS(20));
492                         ao_delay(AO_SEC_TO_TICKS(3));
493                 }
494         }
495 #else
496
497         /*
498          * Separate debug code when things aren't working. Just dump
499          * inbound bluetooth characters to stdout
500          */
501         for (;;) {
502                 int     c;
503
504                 while (rn_cmd_running)
505                         ao_delay(AO_MS_TO_TICKS(1000));
506
507                 ao_arch_block_interrupts();
508                 while ((c = _ao_wrap_rn_pollchar()) == AO_READ_AGAIN)
509                         ao_sleep(&ao_serial_rn_rx_fifo);
510                 ao_arch_release_interrupts();
511                 putchar(c); flush();
512         }
513 #endif
514 }
515
516 static struct ao_task ao_rn_task;
517
518 static void
519 ao_rn_factory(void)
520 {
521         int     i;
522         int     v = 0;
523
524         /*
525          * Factory reset. Flip pin P3_1 5 times within the first five
526          * seconds of power-on
527          */
528
529         /* Select our target output pin */
530         ao_enable_output(AO_RN_P3_1_PORT, AO_RN_P3_1_PIN, foo, v);
531
532         /* Turn off the BT device using the SW_BTN pin */
533         printf("Power down BT\n"); flush();
534         ao_gpio_set(AO_RN_SW_BTN_PORT, AO_RN_SW_BTN_PIN, foo, 0);
535         ao_delay(AO_MS_TO_TICKS(1000));
536
537         /* And turn it back on */
538         printf("Power up BT\n"); flush();
539         ao_gpio_set(AO_RN_SW_BTN_PORT, AO_RN_SW_BTN_PIN, foo, 1);
540
541         /* Right after power on, poke P3_1 five times to force a
542          * factory reset
543          */
544         for (i = 0; i < 10; i++) {
545                 v = 1-v;
546                 ao_delay(AO_MS_TO_TICKS(100));
547                 ao_gpio_set(AO_RN_P3_1_PORT, AO_RN_P3_1_PIN, foo, v);
548                 ao_led_toggle(AO_BT_LED);
549         }
550
551         /* And let P3_1 float again */
552         ao_enable_input(AO_RN_P3_1_PORT, AO_RN_P3_1_PIN, AO_EXTI_MODE_PULL_NONE);
553 }
554
555 static const struct ao_cmds rn_cmds[] = {
556         { ao_rn_factory, "F\0Factory reset rn4678" },
557         { 0 },
558 };
559
560 void
561 ao_rn4678_init(void)
562 {
563         (void) ao_rn_set_name;
564
565         ao_serial_rn_set_speed(AO_SERIAL_SPEED_115200);
566
567         /* Reset line */
568         ao_enable_output(AO_RN_RST_N_PORT, AO_RN_RST_N_PIN, foo, 0);
569
570         /* SW_BTN */
571         ao_enable_output(AO_RN_SW_BTN_PORT, AO_RN_SW_BTN_PIN, foo, 1);
572
573         /* P3_7 command/data selector */
574         ao_enable_output(AO_RN_CMD_PORT, AO_RN_CMD_PIN, foo, AO_RN_CMD_CMD);
575
576         ao_enable_input(AO_RN_CONNECTED_PORT, AO_RN_CONNECTED_PIN, AO_EXTI_MODE_PULL_NONE);
577         ao_exti_setup(AO_RN_CONNECTED_PORT, AO_RN_CONNECTED_PIN,
578                       AO_EXTI_MODE_FALLING|AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_LOW,
579                       ao_rn_isr);
580
581         ao_cmd_register(rn_cmds);
582         ao_add_task(&ao_rn_task, ao_rn, "bluetooth");
583 }