├── .gitignore ├── Makefile ├── README.md ├── ap6212hciattach.c ├── debian ├── changelog ├── compat ├── control ├── copyright ├── install ├── nanopi-bluetooth.default ├── nanopi-bluetooth.service ├── rules └── source │ └── format ├── hciattach.h ├── hciattach_bcm43xx.c ├── lib └── firmware │ └── brcm │ └── bcm43438a0.hcd └── tty.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | /ap6212hciattach 3 | *.debhelper.log 4 | *.debhelper 5 | *.substvars 6 | debhelper-build-stamp 7 | /debian/files 8 | /debian/nanopi-bluetooth/ 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OBJS = ap6212hciattach.o hciattach_bcm43xx.o 2 | 3 | ap6212hciattach: $(OBJS) 4 | gcc $(OBJS) -o ap6212hciattach 5 | 6 | .c.o: 7 | gcc -O -c -Wall $< 8 | 9 | clean: 10 | rm -f $(OBJS) ap6212hciattach 11 | 12 | $(OBJS): hciattach.h tty.h 13 | 14 | deb: 15 | dpkg-buildpackage -b -uc 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## nanopi-bluetooth 2 | 3 | Enables bluetooth on NanoPi M3, on the onboard AMPAK ap6212 wifi/bt 4 | device. 5 | 6 | 7 | -------------------------------------------------------------------------------- /ap6212hciattach.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * BlueZ - Bluetooth protocol stack for Linux 4 | * 5 | * Copyright (C) 2000-2001 Qualcomm Incorporated 6 | * Copyright (C) 2002-2003 Maxim Krasnyansky 7 | * Copyright (C) 2002-2010 Marcel Holtmann 8 | * 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 | * 24 | */ 25 | 26 | #ifdef HAVE_CONFIG_H 27 | #include 28 | #endif 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include "tty.h" 46 | #include "hciattach.h" 47 | 48 | struct uart_t { 49 | int proto; 50 | int init_speed; 51 | int speed; 52 | int flags; 53 | int pm; 54 | char *bdaddr; 55 | }; 56 | 57 | #define FLOW_CTL 0x0001 58 | #define AMP_DEV 0x0002 59 | #define ENABLE_PM 1 60 | #define DISABLE_PM 0 61 | 62 | static volatile sig_atomic_t __io_canceled = 0; 63 | 64 | static void sig_hup(int sig) 65 | { 66 | } 67 | 68 | static void sig_term(int sig) 69 | { 70 | __io_canceled = 1; 71 | } 72 | 73 | static void sig_alarm(int sig) 74 | { 75 | fprintf(stderr, "Initialization timed out.\n"); 76 | exit(1); 77 | } 78 | 79 | int set_speed(int fd, struct termios *ti, int speed) 80 | { 81 | if (cfsetospeed(ti, tty_get_speed(speed)) < 0) 82 | return -errno; 83 | 84 | if (cfsetispeed(ti, tty_get_speed(speed)) < 0) 85 | return -errno; 86 | 87 | if (tcsetattr(fd, TCSANOW, ti) < 0) 88 | return -errno; 89 | 90 | return 0; 91 | } 92 | 93 | /* 94 | * Read an HCI event from the given file descriptor. 95 | */ 96 | int read_hci_event(int fd, unsigned char* buf, int size) 97 | { 98 | int remain, r; 99 | int count = 0; 100 | 101 | if (size <= 0) 102 | return -1; 103 | 104 | /* The first byte identifies the packet type. For HCI event packets, it 105 | * should be 0x04, so we read until we get to the 0x04. */ 106 | while (1) { 107 | r = read(fd, buf, 1); 108 | if (r <= 0) 109 | return -1; 110 | if (buf[0] == 0x04) 111 | break; 112 | } 113 | count++; 114 | 115 | /* The next two bytes are the event code and parameter total length. */ 116 | while (count < 3) { 117 | r = read(fd, buf + count, 3 - count); 118 | if (r <= 0) 119 | return -1; 120 | count += r; 121 | } 122 | 123 | /* Now we read the parameters. */ 124 | if (buf[2] < (size - 3)) 125 | remain = buf[2]; 126 | else 127 | remain = size - 3; 128 | 129 | while ((count - 3) < remain) { 130 | r = read(fd, buf + count, remain - (count - 3)); 131 | if (r <= 0) 132 | return -1; 133 | count += r; 134 | } 135 | return count; 136 | } 137 | 138 | /* Initialize UART driver */ 139 | static int init_uart(char *dev, const struct uart_t *u, int send_break, int raw) 140 | { 141 | struct termios ti; 142 | int fd, i; 143 | unsigned long flags = 0; 144 | 145 | if (raw) 146 | flags |= 1 << HCI_UART_RAW_DEVICE; 147 | 148 | if (u->flags & AMP_DEV) 149 | flags |= 1 << HCI_UART_CREATE_AMP; 150 | 151 | fd = open(dev, O_RDWR | O_NOCTTY); 152 | if (fd < 0) { 153 | perror("Can't open serial port"); 154 | return -1; 155 | } 156 | 157 | tcflush(fd, TCIOFLUSH); 158 | 159 | if (tcgetattr(fd, &ti) < 0) { 160 | perror("Can't get port settings"); 161 | goto fail; 162 | } 163 | 164 | cfmakeraw(&ti); 165 | 166 | ti.c_cflag |= CLOCAL; 167 | // hangup at reset workaround: turn CRTSCTS off, 168 | // then turn turn on after delay 169 | ti.c_cflag &= ~CRTSCTS; 170 | if (tcsetattr(fd, TCSANOW, &ti) < 0) { 171 | perror("Can't set port settings"); 172 | goto fail; 173 | } 174 | 175 | if (u->flags & FLOW_CTL) { 176 | usleep(10000); 177 | ti.c_cflag |= CRTSCTS; 178 | if (tcsetattr(fd, TCSANOW, &ti) < 0) { 179 | perror("Can't set port settings"); 180 | goto fail; 181 | } 182 | } 183 | 184 | /* Set initial baudrate */ 185 | if (set_speed(fd, &ti, u->init_speed) < 0) { 186 | perror("Can't set initial baud rate"); 187 | goto fail; 188 | } 189 | 190 | tcflush(fd, TCIOFLUSH); 191 | 192 | if (send_break) { 193 | tcsendbreak(fd, 0); 194 | usleep(500000); 195 | } 196 | 197 | if( bcm43xx_init(fd, u->init_speed, u->speed, &ti, u->bdaddr) < 0 ) 198 | goto fail; 199 | 200 | tcflush(fd, TCIOFLUSH); 201 | 202 | /* Set actual baudrate */ 203 | if (set_speed(fd, &ti, u->speed) < 0) { 204 | perror("Can't set baud rate"); 205 | goto fail; 206 | } 207 | 208 | /* Set TTY to N_HCI line discipline */ 209 | i = N_HCI; 210 | if (ioctl(fd, TIOCSETD, &i) < 0) { 211 | perror("Can't set line discipline"); 212 | goto fail; 213 | } 214 | 215 | if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) { 216 | perror("Can't set UART flags"); 217 | goto fail; 218 | } 219 | 220 | if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) { 221 | perror("Can't set device"); 222 | goto fail; 223 | } 224 | 225 | return fd; 226 | 227 | fail: 228 | close(fd); 229 | return -1; 230 | } 231 | 232 | static void usage(void) 233 | { 234 | printf("ap6212attach - bluetooth initialization utility for ap6212\n"); 235 | printf("Usage:\n"); 236 | printf("\tap6212attach [-n] [-p] [-b] [-r] [-t timeout] [-s initial_speed]" 237 | " [speed] [flow|noflow]" 238 | " [sleep|nosleep] [bdaddr]\n"); 239 | } 240 | 241 | int main(int argc, char *argv[]) 242 | { 243 | int detach, printpid, raw, opt, i, n, ld, err; 244 | int to = 10; 245 | int init_speed = 0; 246 | int send_break = 0; 247 | pid_t pid; 248 | struct sigaction sa; 249 | struct pollfd p; 250 | sigset_t sigs; 251 | char dev[PATH_MAX]; 252 | struct uart_t uart_bcm43xx = { 253 | HCI_UART_H4, 115200, 3000000, FLOW_CTL, DISABLE_PM, NULL 254 | }; 255 | 256 | setlinebuf(stdout); 257 | detach = 1; 258 | printpid = 0; 259 | raw = 0; 260 | 261 | while ((opt=getopt(argc, argv, "bnpt:s:r")) != EOF) { 262 | switch(opt) { 263 | case 'b': 264 | send_break = 1; 265 | break; 266 | 267 | case 'n': 268 | detach = 0; 269 | break; 270 | 271 | case 'p': 272 | printpid = 1; 273 | break; 274 | 275 | case 't': 276 | to = atoi(optarg); 277 | break; 278 | 279 | case 's': 280 | init_speed = atoi(optarg); 281 | break; 282 | 283 | case 'r': 284 | raw = 1; 285 | break; 286 | 287 | default: 288 | usage(); 289 | exit(1); 290 | } 291 | } 292 | 293 | n = argc - optind; 294 | if (n < 2) { 295 | usage(); 296 | exit(1); 297 | } 298 | 299 | for (n = 0; optind < argc; n++, optind++) { 300 | char *opt; 301 | 302 | opt = argv[optind]; 303 | 304 | switch(n) { 305 | case 0: 306 | dev[0] = 0; 307 | if (!strchr(opt, '/')) 308 | strcpy(dev, "/dev/"); 309 | 310 | if (strlen(opt) > PATH_MAX - (strlen(dev) + 1)) { 311 | fprintf(stderr, "Invalid serial device\n"); 312 | exit(1); 313 | } 314 | 315 | strcat(dev, opt); 316 | break; 317 | 318 | case 1: 319 | uart_bcm43xx.speed = atoi(argv[optind]); 320 | break; 321 | 322 | case 2: 323 | if (!strcmp("flow", argv[optind])) 324 | uart_bcm43xx.flags |= FLOW_CTL; 325 | else 326 | uart_bcm43xx.flags &= ~FLOW_CTL; 327 | break; 328 | 329 | case 3: 330 | if (!strcmp("sleep", argv[optind])) 331 | uart_bcm43xx.pm = ENABLE_PM; 332 | else 333 | uart_bcm43xx.pm = DISABLE_PM; 334 | break; 335 | 336 | case 4: 337 | uart_bcm43xx.bdaddr = argv[optind]; 338 | break; 339 | } 340 | } 341 | 342 | /* If user specified a initial speed, use that instead of 343 | the hardware's default */ 344 | if (init_speed) 345 | uart_bcm43xx.init_speed = init_speed; 346 | 347 | memset(&sa, 0, sizeof(sa)); 348 | sa.sa_flags = SA_NOCLDSTOP; 349 | sa.sa_handler = sig_alarm; 350 | sigaction(SIGALRM, &sa, NULL); 351 | 352 | /* 10 seconds should be enough for initialization */ 353 | alarm(to); 354 | 355 | n = init_uart(dev, &uart_bcm43xx, send_break, raw); 356 | if (n < 0) { 357 | perror("Can't initialize device"); 358 | exit(1); 359 | } 360 | 361 | printf("Device setup complete\n"); 362 | 363 | alarm(0); 364 | 365 | memset(&sa, 0, sizeof(sa)); 366 | sa.sa_flags = SA_NOCLDSTOP; 367 | sa.sa_handler = SIG_IGN; 368 | sigaction(SIGCHLD, &sa, NULL); 369 | sigaction(SIGPIPE, &sa, NULL); 370 | 371 | sa.sa_handler = sig_term; 372 | sigaction(SIGTERM, &sa, NULL); 373 | sigaction(SIGINT, &sa, NULL); 374 | 375 | sa.sa_handler = sig_hup; 376 | sigaction(SIGHUP, &sa, NULL); 377 | 378 | if (detach) { 379 | if ((pid = fork())) { 380 | if (printpid) 381 | printf("%d\n", pid); 382 | return 0; 383 | } 384 | 385 | for (i = 0; i < 20; i++) 386 | if (i != n) 387 | close(i); 388 | } 389 | 390 | p.fd = n; 391 | p.events = POLLERR | POLLHUP; 392 | 393 | sigfillset(&sigs); 394 | sigdelset(&sigs, SIGCHLD); 395 | sigdelset(&sigs, SIGPIPE); 396 | sigdelset(&sigs, SIGTERM); 397 | sigdelset(&sigs, SIGINT); 398 | sigdelset(&sigs, SIGHUP); 399 | 400 | while (!__io_canceled) { 401 | p.revents = 0; 402 | err = ppoll(&p, 1, NULL, &sigs); 403 | if (err < 0 && errno == EINTR) 404 | continue; 405 | if (err) 406 | break; 407 | } 408 | 409 | /* Restore TTY line discipline */ 410 | ld = N_TTY; 411 | if (ioctl(n, TIOCSETD, &ld) < 0) { 412 | perror("Can't restore line discipline"); 413 | } 414 | 415 | return 0; 416 | } 417 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | nanopi-bluetooth (3) unstable; urgency=medium 2 | 3 | * Do not set bdaddr by default 4 | 5 | -- Rafal Thu, 23 Nov 2017 09:31:42 +0100 6 | 7 | nanopi-bluetooth (2) unstable; urgency=low 8 | 9 | * try reset hangup problem workaround: set CRTSCTS "off" and then "on" after 10ms delay 10 | 11 | -- Rafal Fri, 11 Aug 2017 20:45:11 +0200 12 | 13 | nanopi-bluetooth (1) unstable; urgency=low 14 | 15 | * Initial release 16 | 17 | -- Rafal Tue, 01 Aug 2017 21:50:19 +0200 18 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: nanopi-bluetooth 2 | Section: admin 3 | Priority: optional 4 | Maintainer: Rafal 5 | Build-Depends: debhelper (>= 9) 6 | Standards-Version: 3.9.8 7 | Homepage: https://github.com/rafaello7/nanopi-bluetooth 8 | 9 | Package: nanopi-bluetooth 10 | Architecture: any 11 | Depends: ${shlibs:Depends}, ${misc:Depends} 12 | Description: Enables bluetooth on NanoPi M3 device 13 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Source: 3 | 4 | Files: * 5 | Copyright: 2017 Rafal 6 | License: GPL-2+ 7 | This package is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | . 12 | This package is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | . 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see 19 | . 20 | On Debian systems, the complete text of the GNU General 21 | Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". 22 | 23 | -------------------------------------------------------------------------------- /debian/install: -------------------------------------------------------------------------------- 1 | ap6212hciattach usr/lib/nanopi-bluetooth 2 | lib/firmware/brcm/bcm43438a0.hcd 3 | -------------------------------------------------------------------------------- /debian/nanopi-bluetooth.default: -------------------------------------------------------------------------------- 1 | # Defaults for nanopi-bluetooth initscript 2 | 3 | # bluetooth device address 4 | #BDADDR="bdaddr 11:22:33:44:55:66" 5 | -------------------------------------------------------------------------------- /debian/nanopi-bluetooth.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=enable bluetooth on ap6212 3 | 4 | [Service] 5 | EnvironmentFile=/etc/default/nanopi-bluetooth 6 | ExecStart=/usr/lib/nanopi-bluetooth/ap6212hciattach -n /dev/ttySAC1 115200 flow $BDADDR 7 | KillMode=process 8 | Restart=on-failure 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | %: 4 | dh $@ --with systemd 5 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /hciattach.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * BlueZ - Bluetooth protocol stack for Linux 4 | * 5 | * Copyright (C) 2003-2010 Marcel Holtmann 6 | * 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 | * 22 | */ 23 | 24 | #include 25 | 26 | #ifndef N_HCI 27 | #define N_HCI 15 28 | #endif 29 | 30 | #define HCIUARTSETPROTO _IOW('U', 200, int) 31 | #define HCIUARTGETPROTO _IOR('U', 201, int) 32 | #define HCIUARTGETDEVICE _IOR('U', 202, int) 33 | #define HCIUARTSETFLAGS _IOW('U', 203, int) 34 | #define HCIUARTGETFLAGS _IOR('U', 204, int) 35 | 36 | #define HCI_UART_H4 0 37 | #define HCI_UART_BCSP 1 38 | #define HCI_UART_3WIRE 2 39 | #define HCI_UART_H4DS 3 40 | #define HCI_UART_LL 4 41 | #define HCI_UART_ATH3K 5 42 | #define HCI_UART_INTEL 6 43 | #define HCI_UART_BCM 7 44 | #define HCI_UART_QCA 8 45 | #define HCI_UART_AG6XX 9 46 | #define HCI_UART_NOKIA 10 47 | #define HCI_UART_MRVL 11 48 | 49 | #define HCI_UART_RAW_DEVICE 0 50 | #define HCI_UART_RESET_ON_INIT 1 51 | #define HCI_UART_CREATE_AMP 2 52 | #define HCI_UART_INIT_PENDING 3 53 | #define HCI_UART_EXT_CONFIG 4 54 | #define HCI_UART_VND_DETECT 5 55 | 56 | int read_hci_event(int fd, unsigned char *buf, int size); 57 | int set_speed(int fd, struct termios *ti, int speed); 58 | 59 | int bcm43xx_init(int fd, int def_speed, int speed, struct termios *ti, 60 | const char *bdaddr); 61 | -------------------------------------------------------------------------------- /hciattach_bcm43xx.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * BlueZ - Bluetooth protocol stack for Linux 4 | * 5 | * Copyright (C) 2014 Intel Corporation. All rights reserved. 6 | * 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 | * 22 | */ 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include 26 | #endif 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include "hciattach.h" 41 | 42 | #define HCI_COMMAND_PKT 0x01 43 | /* BD Address */ 44 | typedef struct { 45 | uint8_t b[6]; 46 | } __attribute__((packed)) bdaddr_t; 47 | 48 | #define BCM43XX_CLOCK_48 1 49 | #define BCM43XX_CLOCK_24 2 50 | 51 | #define CMD_SUCCESS 0x00 52 | 53 | #define CC_MIN_SIZE 7 54 | 55 | #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) 56 | 57 | 58 | static int bcm43xx_reset(int fd) 59 | { 60 | unsigned char cmd[] = { HCI_COMMAND_PKT, 0x03, 0x0C, 0x00 }; 61 | unsigned char resp[CC_MIN_SIZE]; 62 | struct pollfd pfd; 63 | int i, res = 0; 64 | 65 | for(i = 0; i < 3 && res == 0; ++i) { 66 | if( i ) 67 | printf("reset retry\n"); 68 | if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) { 69 | fprintf(stderr, "Failed to write reset command\n"); 70 | return -1; 71 | } 72 | pfd.fd = fd; 73 | pfd.events = POLLIN; 74 | pfd.revents = 0; 75 | 76 | res = poll(&pfd, 1, 2000); 77 | } 78 | if( res < 0 ) { 79 | fprintf(stderr, "poll error\n"); 80 | return -1; 81 | } 82 | if( res == 0 ) { 83 | fprintf(stderr, "reset fail\n"); 84 | return -1; 85 | } 86 | 87 | if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) { 88 | fprintf(stderr, "Failed to reset chip, invalid HCI event\n"); 89 | return -1; 90 | } 91 | 92 | if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) { 93 | fprintf(stderr, "Failed to reset chip, command failure\n"); 94 | return -1; 95 | } 96 | 97 | return 0; 98 | } 99 | 100 | int bachk(const char *str) 101 | { 102 | if (!str) 103 | return -1; 104 | 105 | if (strlen(str) != 17) 106 | return -1; 107 | 108 | while (*str) { 109 | if (!isxdigit(*str++)) 110 | return -1; 111 | 112 | if (!isxdigit(*str++)) 113 | return -1; 114 | 115 | if (*str == 0) 116 | break; 117 | 118 | if (*str++ != ':') 119 | return -1; 120 | } 121 | 122 | return 0; 123 | } 124 | 125 | int str2ba(const char *str, bdaddr_t *ba) 126 | { 127 | int i; 128 | 129 | if (bachk(str) < 0) { 130 | memset(ba, 0, sizeof(*ba)); 131 | return -1; 132 | } 133 | 134 | for (i = 5; i >= 0; i--, str += 3) 135 | ba->b[i] = strtol(str, NULL, 16); 136 | 137 | return 0; 138 | } 139 | 140 | static int bcm43xx_set_bdaddr(int fd, const char *bdaddr) 141 | { 142 | unsigned char cmd[] = 143 | { HCI_COMMAND_PKT, 0x01, 0xfc, 0x06, 0x00, 0x00, 144 | 0x00, 0x00, 0x00, 0x00 }; 145 | unsigned char resp[CC_MIN_SIZE]; 146 | 147 | printf("Set BDADDR UART: %s\n", bdaddr); 148 | 149 | if (str2ba(bdaddr, (bdaddr_t *) (&cmd[4])) < 0) { 150 | fprintf(stderr, "Incorrect bdaddr\n"); 151 | return -1; 152 | } 153 | 154 | tcflush(fd, TCIOFLUSH); 155 | 156 | if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) { 157 | fprintf(stderr, "Failed to write set bdaddr command\n"); 158 | return -1; 159 | } 160 | 161 | if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) { 162 | fprintf(stderr, "Failed to set bdaddr, invalid HCI event\n"); 163 | return -1; 164 | } 165 | 166 | if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) { 167 | fprintf(stderr, "Failed to set bdaddr, command failure\n"); 168 | return -1; 169 | } 170 | 171 | return 0; 172 | } 173 | 174 | static int bcm43xx_set_clock(int fd, unsigned char clock) 175 | { 176 | unsigned char cmd[] = { HCI_COMMAND_PKT, 0x45, 0xfc, 0x01, 0x00 }; 177 | unsigned char resp[CC_MIN_SIZE]; 178 | 179 | printf("Set Controller clock (%d)\n", clock); 180 | 181 | cmd[4] = clock; 182 | 183 | tcflush(fd, TCIOFLUSH); 184 | 185 | if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) { 186 | fprintf(stderr, "Failed to write update clock command\n"); 187 | return -1; 188 | } 189 | 190 | if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) { 191 | fprintf(stderr, "Failed to update clock, invalid HCI event\n"); 192 | return -1; 193 | } 194 | 195 | if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) { 196 | fprintf(stderr, "Failed to update clock, command failure\n"); 197 | return -1; 198 | } 199 | 200 | return 0; 201 | } 202 | 203 | static int bcm43xx_set_speed(int fd, struct termios *ti, uint32_t speed) 204 | { 205 | unsigned char cmd[] = 206 | { HCI_COMMAND_PKT, 0x18, 0xfc, 0x06, 0x00, 0x00, 207 | 0x00, 0x00, 0x00, 0x00 }; 208 | unsigned char resp[CC_MIN_SIZE]; 209 | 210 | if (speed > 3000000 && bcm43xx_set_clock(fd, BCM43XX_CLOCK_48)) 211 | return -1; 212 | 213 | printf("Set Controller UART speed to %d bit/s\n", speed); 214 | 215 | cmd[6] = (uint8_t) (speed); 216 | cmd[7] = (uint8_t) (speed >> 8); 217 | cmd[8] = (uint8_t) (speed >> 16); 218 | cmd[9] = (uint8_t) (speed >> 24); 219 | 220 | tcflush(fd, TCIOFLUSH); 221 | 222 | if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) { 223 | fprintf(stderr, "Failed to write update baudrate command\n"); 224 | return -1; 225 | } 226 | 227 | if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) { 228 | fprintf(stderr, "Failed to update baudrate, invalid HCI event\n"); 229 | return -1; 230 | } 231 | 232 | if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) { 233 | fprintf(stderr, "Failed to update baudrate, command failure\n"); 234 | return -1; 235 | } 236 | 237 | if (set_speed(fd, ti, speed) < 0) { 238 | perror("Can't set host baud rate"); 239 | return -1; 240 | } 241 | 242 | return 0; 243 | } 244 | 245 | static int bcm43xx_load_firmware(int fd, const char *fw) 246 | { 247 | unsigned char cmd[] = { HCI_COMMAND_PKT, 0x2e, 0xfc, 0x00 }; 248 | struct timespec tm_mode = { 0, 50000 }; 249 | struct timespec tm_ready = { 0, 100000000 }; 250 | unsigned char resp[CC_MIN_SIZE]; 251 | unsigned char tx_buf[1024]; 252 | int len, fd_fw, n; 253 | 254 | printf("Flash firmware %s\n", fw); 255 | 256 | fd_fw = open(fw, O_RDONLY); 257 | if (fd_fw < 0) { 258 | fprintf(stderr, "Unable to open firmware (%s)\n", fw); 259 | return -1; 260 | } 261 | 262 | tcflush(fd, TCIOFLUSH); 263 | 264 | if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) { 265 | fprintf(stderr, "Failed to write download mode command\n"); 266 | goto fail; 267 | } 268 | 269 | if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) { 270 | fprintf(stderr, "Failed to load firmware, invalid HCI event\n"); 271 | goto fail; 272 | } 273 | 274 | if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) { 275 | fprintf(stderr, "Failed to load firmware, command failure\n"); 276 | goto fail; 277 | } 278 | 279 | /* Wait 50ms to let the firmware placed in download mode */ 280 | nanosleep(&tm_mode, NULL); 281 | 282 | tcflush(fd, TCIOFLUSH); 283 | 284 | while ((n = read(fd_fw, &tx_buf[1], 3))) { 285 | if (n < 0) { 286 | fprintf(stderr, "Failed to read firmware\n"); 287 | goto fail; 288 | } 289 | 290 | tx_buf[0] = HCI_COMMAND_PKT; 291 | 292 | len = tx_buf[3]; 293 | 294 | if (read(fd_fw, &tx_buf[4], len) < 0) { 295 | fprintf(stderr, "Failed to read firmware\n"); 296 | goto fail; 297 | } 298 | 299 | if (write(fd, tx_buf, len + 4) != (len + 4)) { 300 | fprintf(stderr, "Failed to write firmware\n"); 301 | goto fail; 302 | } 303 | 304 | read_hci_event(fd, resp, sizeof(resp)); 305 | tcflush(fd, TCIOFLUSH); 306 | } 307 | 308 | /* Wait for firmware ready */ 309 | nanosleep(&tm_ready, NULL); 310 | 311 | close(fd_fw); 312 | return 0; 313 | 314 | fail: 315 | close(fd_fw); 316 | return -1; 317 | } 318 | 319 | int bcm43xx_init(int fd, int def_speed, int speed, struct termios *ti, 320 | const char *bdaddr) 321 | { 322 | char fw_path[] = "/lib/firmware/brcm/bcm43438a0.hcd"; 323 | 324 | printf("bcm43xx_init\n"); 325 | if (bcm43xx_reset(fd)) 326 | return -1; 327 | 328 | if (bcm43xx_set_speed(fd, ti, speed)) 329 | return -1; 330 | 331 | if (bcm43xx_load_firmware(fd, fw_path)) 332 | return -1; 333 | 334 | /* Controller speed has been reset to def speed */ 335 | if (set_speed(fd, ti, def_speed) < 0) { 336 | perror("Can't set host baud rate"); 337 | return -1; 338 | } 339 | 340 | if (bcm43xx_reset(fd)) 341 | return -1; 342 | 343 | if (bdaddr) 344 | bcm43xx_set_bdaddr(fd, bdaddr); 345 | 346 | if (bcm43xx_set_speed(fd, ti, speed)) 347 | return -1; 348 | 349 | return 0; 350 | } 351 | -------------------------------------------------------------------------------- /lib/firmware/brcm/bcm43438a0.hcd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaello7/nanopi-bluetooth/62787df6be326272c5a8066f58e9da2f33018cd4/lib/firmware/brcm/bcm43438a0.hcd -------------------------------------------------------------------------------- /tty.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * BlueZ - Bluetooth protocol stack for Linux 4 | * 5 | * Copyright (C) 2016 Intel Corporation. All rights reserved. 6 | * 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 | * 22 | */ 23 | 24 | #include 25 | 26 | static inline unsigned int tty_get_speed(int speed) 27 | { 28 | switch (speed) { 29 | case 9600: 30 | return B9600; 31 | case 19200: 32 | return B19200; 33 | case 38400: 34 | return B38400; 35 | case 57600: 36 | return B57600; 37 | case 115200: 38 | return B115200; 39 | case 230400: 40 | return B230400; 41 | case 460800: 42 | return B460800; 43 | case 500000: 44 | return B500000; 45 | case 576000: 46 | return B576000; 47 | case 921600: 48 | return B921600; 49 | case 1000000: 50 | return B1000000; 51 | case 1152000: 52 | return B1152000; 53 | case 1500000: 54 | return B1500000; 55 | case 2000000: 56 | return B2000000; 57 | #ifdef B2500000 58 | case 2500000: 59 | return B2500000; 60 | #endif 61 | #ifdef B3000000 62 | case 3000000: 63 | return B3000000; 64 | #endif 65 | #ifdef B3500000 66 | case 3500000: 67 | return B3500000; 68 | #endif 69 | #ifdef B3710000 70 | case 3710000: 71 | return B3710000; 72 | #endif 73 | #ifdef B4000000 74 | case 4000000: 75 | return B4000000; 76 | #endif 77 | } 78 | 79 | return 0; 80 | } 81 | --------------------------------------------------------------------------------