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/param.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_patch_fwfile(struct libusb_device_handle *hdl, 131 const struct iwmbt_firmware *fw) 132 { 133 int ret, transferred; 134 struct iwmbt_firmware fw_job = *fw; 135 uint16_t cmd_opcode; 136 uint8_t cmd_length; 137 struct iwmbt_hci_cmd *cmd_buf; 138 uint8_t evt_code; 139 uint8_t evt_length; 140 uint8_t evt_buf[IWMBT_HCI_MAX_EVENT_SIZE]; 141 int activate_patch = 0; 142 143 while (fw_job.len > 0) { 144 if (fw_job.len < 4) { 145 iwmbt_err("Invalid firmware, unexpected EOF in HCI " 146 "command header. Remains=%d", fw_job.len); 147 return (-1); 148 } 149 150 if (fw_job.buf[0] != 0x01) { 151 iwmbt_err("Invalid firmware, expected HCI command (%d)", 152 fw_job.buf[0]); 153 return (-1); 154 } 155 156 /* Advance by one. */ 157 fw_job.buf++; 158 fw_job.len--; 159 160 /* Load in the HCI command to perform. */ 161 cmd_opcode = le16dec(fw_job.buf); 162 cmd_length = fw_job.buf[2]; 163 cmd_buf = (struct iwmbt_hci_cmd *)fw_job.buf; 164 165 iwmbt_debug("opcode=%04x, len=%02x", cmd_opcode, cmd_length); 166 167 /* 168 * If there is a command that loads a patch in the 169 * firmware file, then activate the patch upon success, 170 * otherwise just disable the manufacturer mode. 171 */ 172 if (cmd_opcode == 0xfc8e) 173 activate_patch = 1; 174 175 /* Advance by three. */ 176 fw_job.buf += 3; 177 fw_job.len -= 3; 178 179 if (fw_job.len < cmd_length) { 180 iwmbt_err("Invalid firmware, unexpected EOF in HCI " 181 "command data. len=%d, remains=%d", 182 cmd_length, fw_job.len); 183 return (-1); 184 } 185 186 /* Advance by data length. */ 187 fw_job.buf += cmd_length; 188 fw_job.len -= cmd_length; 189 190 ret = libusb_control_transfer(hdl, 191 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE, 192 0, 193 0, 194 0, 195 (uint8_t *)cmd_buf, 196 IWMBT_HCI_CMD_SIZE(cmd_buf), 197 IWMBT_HCI_CMD_TIMEOUT); 198 199 if (ret < 0) { 200 iwmbt_err("libusb_control_transfer() failed: err=%s", 201 libusb_strerror(ret)); 202 return (-1); 203 } 204 205 /* 206 * Every command has its associated event: data must match 207 * what is recorded in the firmware file. Perform that check 208 * now. 209 */ 210 211 while (fw_job.len > 0 && fw_job.buf[0] == 0x02) { 212 /* Is this the end of the file? */ 213 if (fw_job.len < 3) { 214 iwmbt_err("Invalid firmware, unexpected EOF in" 215 "event header. remains=%d", fw_job.len); 216 return (-1); 217 } 218 219 /* Advance by one. */ 220 fw_job.buf++; 221 fw_job.len--; 222 223 /* Load in the HCI event. */ 224 evt_code = fw_job.buf[0]; 225 evt_length = fw_job.buf[1]; 226 227 /* Advance by two. */ 228 fw_job.buf += 2; 229 fw_job.len -= 2; 230 231 /* Prepare HCI event buffer. */ 232 memset(evt_buf, 0, IWMBT_HCI_MAX_EVENT_SIZE); 233 234 iwmbt_debug("event=%04x, len=%02x", 235 evt_code, evt_length); 236 237 if (fw_job.len < evt_length) { 238 iwmbt_err("Invalid firmware, unexpected EOF in" 239 " event data. len=%d, remains=%d", 240 evt_length, fw_job.len); 241 return (-1); 242 } 243 244 ret = libusb_interrupt_transfer(hdl, 245 IWMBT_INTERRUPT_ENDPOINT_ADDR, 246 evt_buf, 247 IWMBT_HCI_MAX_EVENT_SIZE, 248 &transferred, 249 IWMBT_HCI_CMD_TIMEOUT); 250 251 if (ret < 0) { 252 iwmbt_err("libusb_interrupt_transfer() failed:" 253 " err=%s", libusb_strerror(ret)); 254 return (-1); 255 } 256 257 if ((int)evt_length + 2 != transferred || 258 memcmp(evt_buf + 2, fw_job.buf, evt_length) != 0) { 259 iwmbt_err("event does not match firmware"); 260 return (-1); 261 } 262 263 /* Advance by data length. */ 264 fw_job.buf += evt_length; 265 fw_job.len -= evt_length; 266 } 267 } 268 269 return (activate_patch); 270 } 271 272 int 273 iwmbt_load_fwfile(struct libusb_device_handle *hdl, 274 const struct iwmbt_firmware *fw, uint32_t *boot_param) 275 { 276 int ready = 0, sent = 0; 277 int ret, transferred; 278 struct iwmbt_hci_cmd *cmd; 279 struct iwmbt_hci_event *event; 280 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE]; 281 282 #define IWMBT_SEND_FRAGMENT(fragment_type, size, msg) do { \ 283 iwmbt_debug("transferring %d bytes, offset %d", size, sent); \ 284 \ 285 ret = iwmbt_send_fragment(hdl, \ 286 fragment_type, \ 287 fw->buf + sent, \ 288 XMIN(size, fw->len - sent), \ 289 IWMBT_HCI_CMD_TIMEOUT); \ 290 \ 291 if (ret < 0) { \ 292 iwmbt_debug("Failed to send "msg": code=%d", ret); \ 293 return (-1); \ 294 } \ 295 sent += size; \ 296 } while (0) 297 298 if (fw->len < 644) { 299 iwmbt_err("Invalid size of firmware file (%d)", fw->len); 300 return (-1); 301 } 302 303 iwmbt_debug("file=%s, size=%d", fw->fwname, fw->len); 304 305 IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment"); 306 IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 1"); 307 IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 2"); 308 309 /* skip 4 bytes */ 310 sent += 4; 311 312 IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1"); 313 IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2"); 314 315 /* 316 * Send firmware chunks. Chunk len must be 4 byte aligned. 317 * multiple commands can be combined 318 */ 319 while (fw->len - sent - ready >= (int) sizeof(struct iwmbt_hci_cmd)) { 320 cmd = (struct iwmbt_hci_cmd *)(fw->buf + sent + ready); 321 /* Parse firmware for Intel Reset HCI command parameter */ 322 if (cmd->opcode == htole16(0xfc0e)) { 323 *boot_param = le32dec(cmd->data); 324 iwmbt_debug("boot_param=0x%08x", *boot_param); 325 } 326 ready += IWMBT_HCI_CMD_SIZE(cmd); 327 while (ready >= 0xFC) { 328 IWMBT_SEND_FRAGMENT(0x01, 0xFC, "firmware chunk"); 329 ready -= 0xFC; 330 } 331 if (ready > 0 && ready % 4 == 0) { 332 IWMBT_SEND_FRAGMENT(0x01, ready, "firmware chunk"); 333 ready = 0; 334 } 335 } 336 337 /* Wait for firmware download completion event */ 338 ret = libusb_interrupt_transfer(hdl, 339 IWMBT_INTERRUPT_ENDPOINT_ADDR, 340 buf, 341 sizeof(buf), 342 &transferred, 343 IWMBT_LOADCMPL_TIMEOUT); 344 345 if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) { 346 iwmbt_err("libusb_interrupt_transfer() failed: " 347 "err=%s, size=%d", 348 libusb_strerror(ret), 349 transferred); 350 return (-1); 351 } 352 353 /* Expect Vendor Specific Event 0x06 */ 354 event = (struct iwmbt_hci_event *)buf; 355 if (event->header.event != 0xFF || event->data[0] != 0x06) { 356 iwmbt_err("firmware download completion event missed"); 357 return (-1); 358 } 359 360 return (0); 361 } 362 363 int 364 iwmbt_enter_manufacturer(struct libusb_device_handle *hdl) 365 { 366 int ret, transferred; 367 static struct iwmbt_hci_cmd cmd = { 368 .opcode = htole16(0xfc11), 369 .length = 2, 370 .data = { 0x01, 0x00 }, 371 }; 372 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE]; 373 374 ret = iwmbt_hci_command(hdl, 375 &cmd, 376 buf, 377 sizeof(buf), 378 &transferred, 379 IWMBT_HCI_CMD_TIMEOUT); 380 381 if (ret < 0) { 382 iwmbt_debug("Can't enter manufacturer mode: code=%d, size=%d", 383 ret, 384 transferred); 385 return (-1); 386 } 387 388 return (0); 389 } 390 391 int 392 iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, int mode) 393 { 394 int ret, transferred; 395 static struct iwmbt_hci_cmd cmd = { 396 .opcode = htole16(0xfc11), 397 .length = 2, 398 .data = { 0x00, 0x00 }, 399 }; 400 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE]; 401 402 /* 403 * The mode sets the type of reset we want to perform: 404 * 0x00: simply exit manufacturer mode without a reset. 405 * 0x01: exit manufacturer mode with a reset and patches disabled 406 * 0x02: exit manufacturer mode with a reset and patches enabled 407 */ 408 if (mode > 2) { 409 iwmbt_debug("iwmbt_exit_manufacturer(): unknown mode (%d)", 410 mode); 411 } 412 cmd.data[1] = mode; 413 414 ret = iwmbt_hci_command(hdl, 415 &cmd, 416 buf, 417 sizeof(buf), 418 &transferred, 419 IWMBT_HCI_CMD_TIMEOUT); 420 421 if (ret < 0) { 422 iwmbt_debug("Can't exit manufacturer mode: code=%d, size=%d", 423 ret, 424 transferred); 425 return (-1); 426 } 427 428 return (0); 429 } 430 431 int 432 iwmbt_get_version(struct libusb_device_handle *hdl, 433 struct iwmbt_version *version) 434 { 435 int ret, transferred; 436 struct iwmbt_hci_event_cmd_compl*event; 437 struct iwmbt_hci_cmd cmd = { 438 .opcode = htole16(0xfc05), 439 .length = 0, 440 }; 441 uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_version)]; 442 443 memset(buf, 0, sizeof(buf)); 444 445 ret = iwmbt_hci_command(hdl, 446 &cmd, 447 buf, 448 sizeof(buf), 449 &transferred, 450 IWMBT_HCI_CMD_TIMEOUT); 451 452 if (ret < 0 || transferred != sizeof(buf)) { 453 iwmbt_debug("Can't get version: : code=%d, size=%d", 454 ret, 455 transferred); 456 return (-1); 457 } 458 459 event = (struct iwmbt_hci_event_cmd_compl *)buf; 460 memcpy(version, event->data, sizeof(struct iwmbt_version)); 461 462 return (0); 463 } 464 465 int 466 iwmbt_get_boot_params(struct libusb_device_handle *hdl, 467 struct iwmbt_boot_params *params) 468 { 469 int ret, transferred = 0; 470 struct iwmbt_hci_event_cmd_compl *event; 471 struct iwmbt_hci_cmd cmd = { 472 .opcode = htole16(0xfc0d), 473 .length = 0, 474 }; 475 uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_boot_params)]; 476 477 memset(buf, 0, sizeof(buf)); 478 479 ret = iwmbt_hci_command(hdl, 480 &cmd, 481 buf, 482 sizeof(buf), 483 &transferred, 484 IWMBT_HCI_CMD_TIMEOUT); 485 486 if (ret < 0 || transferred != sizeof(buf)) { 487 iwmbt_debug("Can't get boot params: code=%d, size=%d", 488 ret, 489 transferred); 490 return (-1); 491 } 492 493 event = (struct iwmbt_hci_event_cmd_compl *)buf; 494 memcpy(params, event->data, sizeof(struct iwmbt_boot_params)); 495 496 return (0); 497 } 498 499 int 500 iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param) 501 { 502 int ret, transferred = 0; 503 struct iwmbt_hci_event *event; 504 static struct iwmbt_hci_cmd cmd = { 505 .opcode = htole16(0xfc01), 506 .length = 8, 507 .data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 }, 508 }; 509 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE]; 510 511 le32enc(cmd.data + 4, boot_param); 512 memset(buf, 0, sizeof(buf)); 513 514 ret = iwmbt_hci_command(hdl, 515 &cmd, 516 buf, 517 sizeof(buf), 518 &transferred, 519 IWMBT_HCI_CMD_TIMEOUT); 520 521 if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) { 522 iwmbt_debug("Intel Reset command failed: code=%d, size=%d", 523 ret, 524 transferred); 525 return (ret); 526 } 527 528 /* expect Vendor Specific Event 0x02 */ 529 event = (struct iwmbt_hci_event *)buf; 530 if (event->header.event != 0xFF || event->data[0] != 0x02) { 531 iwmbt_err("Intel Reset completion event missed"); 532 return (-1); 533 } 534 535 return (0); 536 } 537 538 int 539 iwmbt_load_ddc(struct libusb_device_handle *hdl, 540 const struct iwmbt_firmware *ddc) 541 { 542 int size, sent = 0; 543 int ret, transferred; 544 uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE]; 545 struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf; 546 547 size = ddc->len; 548 549 iwmbt_debug("file=%s, size=%d", ddc->fwname, size); 550 551 while (size > 0) { 552 553 memset(buf, 0, sizeof(buf)); 554 cmd->opcode = htole16(0xfc8b); 555 cmd->length = ddc->buf[sent] + 1; 556 memcpy(cmd->data, ddc->buf + sent, XMIN(ddc->buf[sent], size)); 557 558 iwmbt_debug("transferring %d bytes, offset %d", 559 cmd->length, 560 sent); 561 562 size -= cmd->length; 563 sent += cmd->length; 564 565 ret = iwmbt_hci_command(hdl, 566 cmd, 567 buf, 568 sizeof(buf), 569 &transferred, 570 IWMBT_HCI_CMD_TIMEOUT); 571 572 if (ret < 0) { 573 iwmbt_debug("Intel Write DDC failed: code=%d", ret); 574 return (-1); 575 } 576 } 577 578 return (0); 579 } 580 581 int 582 iwmbt_set_event_mask(struct libusb_device_handle *hdl) 583 { 584 int ret, transferred = 0; 585 static struct iwmbt_hci_cmd cmd = { 586 .opcode = htole16(0xfc52), 587 .length = 8, 588 .data = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 589 }; 590 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE]; 591 592 ret = iwmbt_hci_command(hdl, 593 &cmd, 594 buf, 595 sizeof(buf), 596 &transferred, 597 IWMBT_HCI_CMD_TIMEOUT); 598 599 if (ret < 0) 600 iwmbt_debug("Intel Set Event Mask failed: code=%d", ret); 601 602 return (ret); 603 } 604