1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30 #include <sys/types.h> 31 #include <sys/endian.h> 32 #include <sys/stat.h> 33 34 #include <err.h> 35 #include <errno.h> 36 #include <stddef.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 #include <libusb.h> 43 44 #include "iwmbt_fw.h" 45 #include "iwmbt_hw.h" 46 #include "iwmbt_dbg.h" 47 48 #define XMIN(x, y) ((x) < (y) ? (x) : (y)) 49 50 static int 51 iwmbt_send_fragment(struct libusb_device_handle *hdl, 52 uint8_t fragment_type, const void *data, uint8_t len, int timeout) 53 { 54 int ret, transferred; 55 uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE]; 56 struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *) buf; 57 58 memset(buf, 0, sizeof(buf)); 59 cmd->opcode = htole16(0xfc09), 60 cmd->length = len + 1, 61 cmd->data[0] = fragment_type; 62 memcpy(cmd->data + 1, data, len); 63 64 ret = libusb_bulk_transfer(hdl, 65 IWMBT_BULK_OUT_ENDPOINT_ADDR, 66 (uint8_t *)cmd, 67 IWMBT_HCI_CMD_SIZE(cmd), 68 &transferred, 69 timeout); 70 71 if (ret < 0 || transferred != (int)IWMBT_HCI_CMD_SIZE(cmd)) { 72 iwmbt_err("libusb_bulk_transfer() failed: err=%s, size=%zu", 73 libusb_strerror(ret), 74 IWMBT_HCI_CMD_SIZE(cmd)); 75 return (-1); 76 } 77 78 ret = libusb_bulk_transfer(hdl, 79 IWMBT_BULK_IN_ENDPOINT_ADDR, 80 buf, 81 sizeof(buf), 82 &transferred, 83 timeout); 84 85 if (ret < 0) { 86 iwmbt_err("libusb_bulk_transfer() failed: err=%s", 87 libusb_strerror(ret)); 88 return (-1); 89 } 90 91 return (0); 92 } 93 94 static int 95 iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd, 96 void *event, int size, int *transferred, int timeout) 97 { 98 int ret; 99 100 ret = libusb_control_transfer(hdl, 101 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE, 102 0, 103 0, 104 0, 105 (uint8_t *)cmd, 106 IWMBT_HCI_CMD_SIZE(cmd), 107 timeout); 108 109 if (ret < 0) { 110 iwmbt_err("libusb_control_transfer() failed: err=%s", 111 libusb_strerror(ret)); 112 return (ret); 113 } 114 115 ret = libusb_interrupt_transfer(hdl, 116 IWMBT_INTERRUPT_ENDPOINT_ADDR, 117 event, 118 size, 119 transferred, 120 timeout); 121 122 if (ret < 0) 123 iwmbt_err("libusb_interrupt_transfer() failed: err=%s", 124 libusb_strerror(ret)); 125 126 return (ret); 127 } 128 129 int 130 iwmbt_load_fwfile(struct libusb_device_handle *hdl, 131 const struct iwmbt_firmware *fw, uint32_t *boot_param) 132 { 133 int ready = 0, sent = 0; 134 int ret, transferred; 135 struct iwmbt_hci_cmd *cmd; 136 struct iwmbt_hci_event *event; 137 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE]; 138 139 #define IWMBT_SEND_FRAGMENT(fragment_type, size, msg) do { \ 140 iwmbt_debug("transferring %d bytes, offset %d", size, sent); \ 141 \ 142 ret = iwmbt_send_fragment(hdl, \ 143 fragment_type, \ 144 fw->buf + sent, \ 145 XMIN(size, fw->len - sent), \ 146 IWMBT_HCI_CMD_TIMEOUT); \ 147 \ 148 if (ret < 0) { \ 149 iwmbt_debug("Failed to send "msg": code=%d", ret); \ 150 return (-1); \ 151 } \ 152 sent += size; \ 153 } while (0) 154 155 if (fw->len < 644) { 156 iwmbt_err("Invalid size of firmware file (%d)", fw->len); 157 return (-1); 158 } 159 160 iwmbt_debug("file=%s, size=%d", fw->fwname, fw->len); 161 162 IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment"); 163 IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 1"); 164 IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 2"); 165 166 /* skip 4 bytes */ 167 sent += 4; 168 169 IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1"); 170 IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2"); 171 172 /* 173 * Send firmware chunks. Chunk len must be 4 byte aligned. 174 * multiple commands can be combined 175 */ 176 while (fw->len - sent - ready >= (int) sizeof(struct iwmbt_hci_cmd)) { 177 cmd = (struct iwmbt_hci_cmd *)(fw->buf + sent + ready); 178 /* Parse firmware for Intel Reset HCI command parameter */ 179 if (cmd->opcode == htole16(0xfc0e)) { 180 *boot_param = le32dec(cmd->data); 181 iwmbt_debug("boot_param=0x%08x", *boot_param); 182 } 183 ready += IWMBT_HCI_CMD_SIZE(cmd); 184 while (ready >= 0xFC) { 185 IWMBT_SEND_FRAGMENT(0x01, 0xFC, "firmware chunk"); 186 ready -= 0xFC; 187 } 188 if (ready > 0 && ready % 4 == 0) { 189 IWMBT_SEND_FRAGMENT(0x01, ready, "firmware chunk"); 190 ready = 0; 191 } 192 } 193 194 /* Wait for firmware download completion event */ 195 ret = libusb_interrupt_transfer(hdl, 196 IWMBT_INTERRUPT_ENDPOINT_ADDR, 197 buf, 198 sizeof(buf), 199 &transferred, 200 IWMBT_LOADCMPL_TIMEOUT); 201 202 if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) { 203 iwmbt_err("libusb_interrupt_transfer() failed: " 204 "err=%s, size=%d", 205 libusb_strerror(ret), 206 transferred); 207 return (-1); 208 } 209 210 /* Expect Vendor Specific Event 0x06 */ 211 event = (struct iwmbt_hci_event *)buf; 212 if (event->header.event != 0xFF || event->data[0] != 0x06) { 213 iwmbt_err("firmware download completion event missed"); 214 return (-1); 215 } 216 217 return (0); 218 } 219 220 int 221 iwmbt_get_version(struct libusb_device_handle *hdl, 222 struct iwmbt_version *version) 223 { 224 int ret, transferred; 225 struct iwmbt_hci_event_cmd_compl*event; 226 struct iwmbt_hci_cmd cmd = { 227 .opcode = htole16(0xfc05), 228 .length = 0, 229 }; 230 uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_version)]; 231 232 memset(buf, 0, sizeof(buf)); 233 234 ret = iwmbt_hci_command(hdl, 235 &cmd, 236 buf, 237 sizeof(buf), 238 &transferred, 239 IWMBT_HCI_CMD_TIMEOUT); 240 241 if (ret < 0 || transferred != sizeof(buf)) { 242 iwmbt_debug("Can't get version: : code=%d, size=%d", 243 ret, 244 transferred); 245 return (-1); 246 } 247 248 event = (struct iwmbt_hci_event_cmd_compl *)buf; 249 memcpy(version, event->data, sizeof(struct iwmbt_version)); 250 251 return (0); 252 } 253 254 int 255 iwmbt_get_boot_params(struct libusb_device_handle *hdl, 256 struct iwmbt_boot_params *params) 257 { 258 int ret, transferred = 0; 259 struct iwmbt_hci_event_cmd_compl *event; 260 struct iwmbt_hci_cmd cmd = { 261 .opcode = htole16(0xfc0d), 262 .length = 0, 263 }; 264 uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_boot_params)]; 265 266 memset(buf, 0, sizeof(buf)); 267 268 ret = iwmbt_hci_command(hdl, 269 &cmd, 270 buf, 271 sizeof(buf), 272 &transferred, 273 IWMBT_HCI_CMD_TIMEOUT); 274 275 if (ret < 0 || transferred != sizeof(buf)) { 276 iwmbt_debug("Can't get boot params: code=%d, size=%d", 277 ret, 278 transferred); 279 return (-1); 280 } 281 282 event = (struct iwmbt_hci_event_cmd_compl *)buf; 283 memcpy(params, event->data, sizeof(struct iwmbt_boot_params)); 284 285 return (0); 286 } 287 288 int 289 iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param) 290 { 291 int ret, transferred = 0; 292 struct iwmbt_hci_event *event; 293 static struct iwmbt_hci_cmd cmd = { 294 .opcode = htole16(0xfc01), 295 .length = 8, 296 .data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 }, 297 }; 298 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE]; 299 300 le32enc(cmd.data + 4, boot_param); 301 memset(buf, 0, sizeof(buf)); 302 303 ret = iwmbt_hci_command(hdl, 304 &cmd, 305 buf, 306 sizeof(buf), 307 &transferred, 308 IWMBT_HCI_CMD_TIMEOUT); 309 310 if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) { 311 iwmbt_debug("Intel Reset command failed: code=%d, size=%d", 312 ret, 313 transferred); 314 return (ret); 315 } 316 317 /* expect Vendor Specific Event 0x02 */ 318 event = (struct iwmbt_hci_event *)buf; 319 if (event->header.event != 0xFF || event->data[0] != 0x02) { 320 iwmbt_err("Intel Reset completion event missed"); 321 return (-1); 322 } 323 324 return (0); 325 } 326 327 int 328 iwmbt_load_ddc(struct libusb_device_handle *hdl, 329 const struct iwmbt_firmware *ddc) 330 { 331 int size, sent = 0; 332 int ret, transferred; 333 uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE]; 334 struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf; 335 336 size = ddc->len; 337 338 iwmbt_debug("file=%s, size=%d", ddc->fwname, size); 339 340 while (size > 0) { 341 342 memset(buf, 0, sizeof(buf)); 343 cmd->opcode = htole16(0xfc8b); 344 cmd->length = ddc->buf[sent] + 1; 345 memcpy(cmd->data, ddc->buf + sent, XMIN(ddc->buf[sent], size)); 346 347 iwmbt_debug("transferring %d bytes, offset %d", 348 cmd->length, 349 sent); 350 351 size -= cmd->length; 352 sent += cmd->length; 353 354 ret = iwmbt_hci_command(hdl, 355 cmd, 356 buf, 357 sizeof(buf), 358 &transferred, 359 IWMBT_HCI_CMD_TIMEOUT); 360 361 if (ret < 0) { 362 iwmbt_debug("Intel Write DDC failed: code=%d", ret); 363 return (-1); 364 } 365 } 366 367 return (0); 368 } 369 370 int 371 iwmbt_set_event_mask(struct libusb_device_handle *hdl) 372 { 373 int ret, transferred = 0; 374 static struct iwmbt_hci_cmd cmd = { 375 .opcode = htole16(0xfc52), 376 .length = 8, 377 .data = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 378 }; 379 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE]; 380 381 ret = iwmbt_hci_command(hdl, 382 &cmd, 383 buf, 384 sizeof(buf), 385 &transferred, 386 IWMBT_HCI_CMD_TIMEOUT); 387 388 if (ret < 0) 389 iwmbt_debug("Intel Set Event Mask failed: code=%d", ret); 390 391 return (ret); 392 } 393