1/* $NetBSD: init_bcm43xx.c,v 1.6 2023/02/07 20:45:44 mlelstv Exp $ */ 2 3/*- 4 * Copyright (c) 2017 Nathanial Sloss <nathanialsloss@yahoo.com.au> 5 * All rights reserved. 6 * 7 * Copyright (c) 2008 Iain Hibbert 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31/* 32 * init information in this file gleaned from hciattach(8) 33 * command from BlueZ for Linux - see http://www.bluez.org/ 34 */ 35 36#include <sys/cdefs.h> 37__RCSID("$NetBSD: init_bcm43xx.c,v 1.6 2023/02/07 20:45:44 mlelstv Exp $"); 38 39#include <sys/param.h> 40 41#include <bluetooth.h> 42#include <err.h> 43#include <errno.h> 44#include <fcntl.h> 45#include <stdlib.h> 46#include <string.h> 47#include <termios.h> 48#include <unistd.h> 49 50#include "btattach.h" 51#include "firmload.h" 52 53#define HCI_CMD_BCM43XX_SET_UART_BAUD_RATE \ 54 HCI_OPCODE(HCI_OGF_VENDOR, 0x018) 55 56#define HCI_CMD_BCM43XX_SET_BDADDR \ 57 HCI_OPCODE(HCI_OGF_VENDOR, 0x006) 58 59#define HCI_CMD_BCM43XX_SET_CLOCK \ 60 HCI_OPCODE(HCI_OGF_VENDOR, 0x045) 61 62#define HCI_CMD_43XXFWDN \ 63 HCI_OPCODE(HCI_OGF_VENDOR, 0x02e) 64 65#define HCI_CMD_GET_LOCAL_NAME 0x0c14 66 67#define BCM43XX_CLK_48 1 68#define BCM43XX_CLK_24 2 69 70static int 71bcm43xx_get_local_name(int fd, char *name, size_t namelen) 72{ 73 char buf[256]; 74 size_t len; 75 76 memset(buf, 0, sizeof(buf)); 77 78 uart_send_cmd(fd, HCI_CMD_GET_LOCAL_NAME, NULL, 0); 79 len = uart_recv_cc(fd, HCI_CMD_GET_LOCAL_NAME, buf, sizeof(buf)); 80 if (len == 0) 81 return EIO; 82 83 strlcpy(name, &buf[1], MIN(len - 1, namelen)); 84 85 if (strlen(name) == 0) 86 return EIO; 87 88 return 0; 89} 90 91void 92init_bcm43xx(int fd, unsigned int speed) 93{ 94 uint8_t rate[6], clock; 95 uint8_t fw_buf[1024]; 96 int fwfd, fw_len; 97 uint8_t resp[7]; 98 uint16_t fw_cmd; 99 char local_name[256]; 100 char fw[260]; 101 102 memset(rate, 0, sizeof(rate)); 103 memset(local_name, 0, sizeof(local_name)); 104 105 if (uart_send_cmd(fd, HCI_CMD_RESET, NULL, 0)) 106 return; 107 uart_recv_cc(fd, HCI_CMD_RESET, &resp, sizeof(resp)); 108 /* assume it succeeded? */ 109 110 if (bcm43xx_get_local_name(fd, local_name, sizeof(local_name)) != 0) { 111 fprintf(stderr, "Couldn't read local name\n"); 112 return; 113 } 114 snprintf(fw, sizeof(fw), "%s.hcd", local_name); 115 116 fwfd = firmware_open("bcm43xx", fw); 117 if (fwfd < 0) { 118 fprintf(stderr, "Unable to open firmware bcm43xx/%s: %s\n", 119 fw, strerror(errno)); 120 return; 121 } 122 123 uart_send_cmd(fd, HCI_CMD_43XXFWDN, NULL, 0); 124 uart_recv_cc(fd, HCI_CMD_43XXFWDN, &resp, sizeof(resp)); 125 sleep(1); 126 127 while (read(fwfd, &fw_buf[1], 3) == 3) { 128 fw_buf[0] = HCI_CMD_PKT; 129 fw_len = fw_buf[3]; 130 if (read(fwfd, &fw_buf[4], fw_len) != fw_len) 131 break; 132 fw_cmd = fw_buf[2] << 8 | fw_buf[1]; 133 uart_send_cmd(fd, fw_cmd, &fw_buf[4], fw_len); 134 uart_recv_cc(fd, fw_cmd, &resp, sizeof(resp)); 135 } 136 137 close(fwfd); 138 139 sleep(4); 140 uart_send_cmd(fd, HCI_CMD_RESET, NULL, 0); 141 uart_recv_cc(fd, HCI_CMD_RESET, &resp, sizeof(resp)); 142 /* assume it succeeded? */ 143 144 if (speed >= 3000000) 145 clock = BCM43XX_CLK_48; 146 else 147 clock = BCM43XX_CLK_24; 148 149 uart_send_cmd(fd, HCI_CMD_BCM43XX_SET_CLOCK, &clock, sizeof(clock)); 150 uart_recv_cc(fd, HCI_CMD_BCM43XX_SET_CLOCK, &resp, sizeof(resp)); 151 152 rate[2] = speed; 153 rate[3] = speed >> 8; 154 rate[4] = speed >> 16; 155 rate[5] = speed >> 24; 156 157 uart_send_cmd(fd, HCI_CMD_BCM43XX_SET_UART_BAUD_RATE, &rate, sizeof(rate)); 158 uart_recv_cc(fd, HCI_CMD_BCM43XX_SET_UART_BAUD_RATE, &resp, sizeof(resp)); 159} 160