1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org> 5 * Copyright (c) 2023 Future Crew LLC. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/endian.h> 31 #include <sys/stat.h> 32 33 #include <netgraph/bluetooth/include/ng_hci.h> 34 35 #include <err.h> 36 #include <errno.h> 37 #include <stddef.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <time.h> 42 #include <unistd.h> 43 44 #include <libusb.h> 45 46 #include "rtlbt_fw.h" 47 #include "rtlbt_hw.h" 48 #include "rtlbt_dbg.h" 49 50 static int 51 rtlbt_hci_command(struct libusb_device_handle *hdl, struct rtlbt_hci_cmd *cmd, 52 void *event, int size, int *transferred, int timeout) 53 { 54 struct timespec to, now, remains; 55 int ret; 56 57 ret = libusb_control_transfer(hdl, 58 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE, 59 0, 60 0, 61 0, 62 (uint8_t *)cmd, 63 RTLBT_HCI_CMD_SIZE(cmd), 64 timeout); 65 66 if (ret < 0) { 67 rtlbt_err("libusb_control_transfer() failed: err=%s", 68 libusb_strerror(ret)); 69 return (ret); 70 } 71 72 clock_gettime(CLOCK_MONOTONIC, &now); 73 to = RTLBT_MSEC2TS(timeout); 74 timespecadd(&to, &now, &to); 75 76 do { 77 timespecsub(&to, &now, &remains); 78 ret = libusb_interrupt_transfer(hdl, 79 RTLBT_INTERRUPT_ENDPOINT_ADDR, 80 event, 81 size, 82 transferred, 83 RTLBT_TS2MSEC(remains) + 1); 84 85 if (ret < 0) { 86 rtlbt_err("libusb_interrupt_transfer() failed: err=%s", 87 libusb_strerror(ret)); 88 return (ret); 89 } 90 91 switch (((struct rtlbt_hci_event *)event)->header.event) { 92 case NG_HCI_EVENT_COMMAND_COMPL: 93 if (*transferred < 94 (int)offsetof(struct rtlbt_hci_event_cmd_compl, data)) 95 break; 96 if (cmd->opcode != 97 ((struct rtlbt_hci_event_cmd_compl *)event)->opcode) 98 break; 99 return (0); 100 default: 101 break; 102 } 103 rtlbt_debug("Stray HCI event: %x", 104 ((struct rtlbt_hci_event *)event)->header.event); 105 } while (timespeccmp(&to, &now, >)); 106 107 rtlbt_err("libusb_interrupt_transfer() failed: err=%s", 108 libusb_strerror(LIBUSB_ERROR_TIMEOUT)); 109 110 return (LIBUSB_ERROR_TIMEOUT); 111 } 112 113 int 114 rtlbt_read_local_ver(struct libusb_device_handle *hdl, 115 ng_hci_read_local_ver_rp *ver) 116 { 117 int ret, transferred; 118 struct rtlbt_hci_event_cmd_compl *event; 119 struct rtlbt_hci_cmd cmd = { 120 .opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_INFO, 121 NG_HCI_OCF_READ_LOCAL_VER)), 122 .length = 0, 123 }; 124 uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(ng_hci_read_local_ver_rp)]; 125 126 memset(buf, 0, sizeof(buf)); 127 128 ret = rtlbt_hci_command(hdl, 129 &cmd, 130 buf, 131 sizeof(buf), 132 &transferred, 133 RTLBT_HCI_CMD_TIMEOUT); 134 135 if (ret < 0 || transferred != sizeof(buf)) { 136 rtlbt_debug("Can't read local version: code=%d, size=%d", 137 ret, 138 transferred); 139 return (-1); 140 } 141 142 event = (struct rtlbt_hci_event_cmd_compl *)buf; 143 memcpy(ver, event->data, sizeof(ng_hci_read_local_ver_rp)); 144 145 return (0); 146 } 147 148 int 149 rtlbt_read_rom_ver(struct libusb_device_handle *hdl, uint8_t *ver) 150 { 151 int ret, transferred; 152 struct rtlbt_hci_event_cmd_compl *event; 153 struct rtlbt_hci_cmd cmd = { 154 .opcode = htole16(0xfc6d), 155 .length = 0, 156 }; 157 uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_rom_ver_rp)]; 158 159 memset(buf, 0, sizeof(buf)); 160 161 ret = rtlbt_hci_command(hdl, 162 &cmd, 163 buf, 164 sizeof(buf), 165 &transferred, 166 RTLBT_HCI_CMD_TIMEOUT); 167 168 if (ret < 0 || transferred != sizeof(buf)) { 169 rtlbt_debug("Can't read ROM version: code=%d, size=%d", 170 ret, 171 transferred); 172 return (-1); 173 } 174 175 event = (struct rtlbt_hci_event_cmd_compl *)buf; 176 *ver = ((struct rtlbt_rom_ver_rp *)event->data)->version; 177 178 return (0); 179 } 180 181 int 182 rtlbt_load_fwfile(struct libusb_device_handle *hdl, 183 const struct rtlbt_firmware *fw) 184 { 185 uint8_t cmd_buf[RTLBT_HCI_MAX_CMD_SIZE]; 186 struct rtlbt_hci_cmd *cmd = (struct rtlbt_hci_cmd *)cmd_buf; 187 struct rtlbt_hci_dl_cmd *dl_cmd = (struct rtlbt_hci_dl_cmd *)cmd->data; 188 uint8_t evt_buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_hci_dl_rp)]; 189 uint8_t *data = fw->buf; 190 int frag_num = fw->len / RTLBT_MAX_CMD_DATA_LEN + 1; 191 int frag_len = RTLBT_MAX_CMD_DATA_LEN; 192 int i; 193 int ret, transferred; 194 195 for (i = 0; i < frag_num; i++) { 196 197 rtlbt_debug("download fw (%d/%d)", i + 1, frag_num); 198 199 memset(cmd_buf, 0, sizeof(cmd_buf)); 200 cmd->opcode = htole16(0xfc20); 201 if (i > 0x7f) 202 dl_cmd->index = (i & 0x7f) + 1; 203 else 204 dl_cmd->index = i; 205 206 if (i == (frag_num - 1)) { 207 dl_cmd->index |= 0x80; /* data end */ 208 frag_len = fw->len % RTLBT_MAX_CMD_DATA_LEN; 209 } 210 cmd->length = frag_len + 1; 211 memcpy(dl_cmd->data, data, frag_len); 212 213 /* Send download command */ 214 ret = rtlbt_hci_command(hdl, 215 cmd, 216 evt_buf, 217 sizeof(evt_buf), 218 &transferred, 219 RTLBT_HCI_CMD_TIMEOUT); 220 if (ret < 0) { 221 rtlbt_err("download fw command failed (%d)", errno); 222 goto out; 223 } 224 if (transferred != sizeof(evt_buf)) { 225 rtlbt_err("download fw event length mismatch"); 226 errno = EIO; 227 ret = -1; 228 goto out; 229 } 230 231 data += RTLBT_MAX_CMD_DATA_LEN; 232 } 233 234 out: 235 return (ret); 236 } 237