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