Description: Patches for compatibility with on-board Bluetooth on RPi 3
Author: Simon Long <simon@raspberrypi.org>
Last-Update: 2017-04-05

--- a/tools/hciattach.c
+++ b/tools/hciattach.c
@@ -1091,6 +1091,9 @@
 	{ "bcm43xx",    0x0000, 0x0000, HCI_UART_H4,   115200, 3000000,
 				FLOW_CTL, DISABLE_PM, NULL, bcm43xx, NULL  },
 
+	{ "bcm43xx-3wire",    0x0000, 0x0000, HCI_UART_3WIRE,   115200, 3000000,
+				0, DISABLE_PM, NULL, bcm43xx, NULL  },
+
 	{ "ath3k",    0x0000, 0x0000, HCI_UART_ATH3K, 115200, 115200,
 			FLOW_CTL, DISABLE_PM, NULL, ath3k_ps, ath3k_pm  },
 
@@ -1237,7 +1240,7 @@
 {
 	struct uart_t *u = NULL;
 	int detach, printpid, raw, opt, i, n, ld, err;
-	int to = 10;
+	int to = 30;
 	int init_speed = 0;
 	int send_break = 0;
 	pid_t pid;
--- a/tools/hciattach.h
+++ b/tools/hciattach.h
@@ -44,7 +45,7 @@
 #define HCI_UART_VND_DETECT	5
 
 #ifndef FIRMWARE_DIR
-#define FIRMWARE_DIR "/etc/firmware"
+#define FIRMWARE_DIR "/lib/firmware"
 #endif
 
 int read_hci_event(int fd, unsigned char *buf, int size);
--- a/tools/hciattach_bcm43xx.c
+++ b/tools/hciattach_bcm43xx.c
@@ -36,6 +36,7 @@
 #include <dirent.h>
 #include <time.h>
 #include <limits.h>
+#include <sys/stat.h>
 
 #include "lib/bluetooth.h"
 #include "lib/hci.h"
@@ -303,9 +304,23 @@
 static int bcm43xx_locate_patch(const char *dir_name,
 		const char *chip_name, char *location)
 {
+	struct stat statbuf;
+	char path[PATH_MAX];
 	DIR *dir;
 	int ret = -1;
 
+	/* Try an exact match before scanning, otherwise a board without a specific
+	 * firmware/link may end up loading a specific firmware for another board
+	 * just because it is encountered first.
+	 */
+	if (snprintf(path, PATH_MAX, "%s/%s%s", dir_name, chip_name, FW_EXT) < 0)
+		return -1;
+	if ((stat(path, &statbuf) == 0) && S_ISREG(statbuf.st_mode)) {
+		strcpy(location, path);
+		return 0;
+	}
+
+	/* Now search subdirectories and files with suffixes */
 	dir = opendir(dir_name);
 	if (!dir) {
 		fprintf(stderr, "Cannot open directory '%s': %s\n",
@@ -320,8 +335,6 @@
 			break;
 
 		if (entry->d_type & DT_DIR) {
-			char path[PATH_MAX];
-
 			if (!strcmp(entry->d_name, "..") || !strcmp(entry->d_name, "."))
 				continue;
 
@@ -341,8 +354,10 @@
 				break;
 
 			/* found */
-			snprintf(location, PATH_MAX, "%s/%s", dir_name, entry->d_name);
-			ret = 0;
+			if (snprintf(location, PATH_MAX, "%s/%s", dir_name, entry->d_name) < 0)
+				ret = -1;
+			else
+				ret = 0;
 			break;
 		}
 	}
@@ -352,11 +367,32 @@
 	return ret;
 }
 
+static int get_board_type(char *buf, int buf_size)
+{
+	int bytes_read;
+	int len;
+	int fd;
+
+	fd = open("/sys/firmware/devicetree/base/compatible", O_RDONLY);
+	if (fd < 0)
+		return -1;
+
+	bytes_read = read(fd, buf, buf_size);
+	close(fd);
+	if (bytes_read < 0)
+		return -1;
+
+	len = strnlen(buf, buf_size);
+	return (len < buf_size) ? len : -1;
+}
+
 int bcm43xx_init(int fd, int def_speed, int speed, struct termios *ti,
 		const char *bdaddr)
 {
 	char chip_name[20];
+	char board_specific_name[sizeof(chip_name) + 64];
 	char fw_path[PATH_MAX];
+	int chip_name_len;
 
 	printf("bcm43xx_init\n");
 
@@ -366,12 +402,17 @@
 	if (bcm43xx_read_local_name(fd, chip_name, sizeof(chip_name)))
 		return -1;
 
-	if (bcm43xx_locate_patch(FIRMWARE_DIR, chip_name, fw_path)) {
-		fprintf(stderr, "Patch not found, continue anyway\n");
+	/* Grab the board compatible string from Device Tree */
+	chip_name_len = strlen(chip_name);
+	memcpy(board_specific_name, chip_name, chip_name_len);
+	board_specific_name[chip_name_len++] = '.';
+
+	if (((get_board_type(board_specific_name + chip_name_len,
+			     sizeof(board_specific_name) - chip_name_len) < 0) ||
+	     bcm43xx_locate_patch(FIRMWARE_DIR, board_specific_name, fw_path)) &&
+	    bcm43xx_locate_patch(FIRMWARE_DIR, chip_name, fw_path)) {
+		fprintf(stderr, "Patch not found for %s, continue anyway\n", chip_name);
 	} else {
-		if (bcm43xx_set_speed(fd, ti, speed))
-			return -1;
-
 		if (bcm43xx_load_firmware(fd, fw_path))
 			return -1;
 
@@ -381,6 +422,7 @@
 			return -1;
 		}
 
+		sleep(1);
 		if (bcm43xx_reset(fd))
 			return -1;
 	}
