2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/param.h>
31 #include <sys/endian.h>
46 #include "iwmbt_dbg.h"
48 #define XMIN(x, y) ((x) < (y) ? (x) : (y))
51 iwmbt_send_fragment(struct libusb_device_handle *hdl,
52 uint8_t fragment_type, const void *data, uint8_t len, int timeout)
55 uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
56 struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *) buf;
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);
64 ret = libusb_bulk_transfer(hdl,
65 IWMBT_BULK_OUT_ENDPOINT_ADDR,
67 IWMBT_HCI_CMD_SIZE(cmd),
71 if (ret < 0 || transferred != (int)IWMBT_HCI_CMD_SIZE(cmd)) {
72 iwmbt_err("libusb_bulk_transfer() failed: err=%s, size=%zu",
74 IWMBT_HCI_CMD_SIZE(cmd));
78 ret = libusb_bulk_transfer(hdl,
79 IWMBT_BULK_IN_ENDPOINT_ADDR,
86 iwmbt_err("libusb_bulk_transfer() failed: err=%s",
87 libusb_strerror(ret));
95 iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
96 void *event, int size, int *transferred, int timeout)
100 ret = libusb_control_transfer(hdl,
101 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
106 IWMBT_HCI_CMD_SIZE(cmd),
110 iwmbt_err("libusb_control_transfer() failed: err=%s",
111 libusb_strerror(ret));
115 ret = libusb_interrupt_transfer(hdl,
116 IWMBT_INTERRUPT_ENDPOINT_ADDR,
123 iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
124 libusb_strerror(ret));
130 iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
131 const struct iwmbt_firmware *fw)
133 int ret, transferred;
134 struct iwmbt_firmware fw_job = *fw;
137 struct iwmbt_hci_cmd *cmd_buf;
140 uint8_t evt_buf[IWMBT_HCI_MAX_EVENT_SIZE];
141 int activate_patch = 0;
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);
150 if (fw_job.buf[0] != 0x01) {
151 iwmbt_err("Invalid firmware, expected HCI command (%d)",
156 /* Advance by one. */
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;
165 iwmbt_debug("opcode=%04x, len=%02x", cmd_opcode, cmd_length);
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.
172 if (cmd_opcode == 0xfc8e)
175 /* Advance by three. */
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);
186 /* Advance by data length. */
187 fw_job.buf += cmd_length;
188 fw_job.len -= cmd_length;
190 ret = libusb_control_transfer(hdl,
191 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
196 IWMBT_HCI_CMD_SIZE(cmd_buf),
197 IWMBT_HCI_CMD_TIMEOUT);
200 iwmbt_err("libusb_control_transfer() failed: err=%s",
201 libusb_strerror(ret));
206 * Every command has its associated event: data must match
207 * what is recorded in the firmware file. Perform that check
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);
219 /* Advance by one. */
223 /* Load in the HCI event. */
224 evt_code = fw_job.buf[0];
225 evt_length = fw_job.buf[1];
227 /* Advance by two. */
231 /* Prepare HCI event buffer. */
232 memset(evt_buf, 0, IWMBT_HCI_MAX_EVENT_SIZE);
234 iwmbt_debug("event=%04x, len=%02x",
235 evt_code, evt_length);
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);
244 ret = libusb_interrupt_transfer(hdl,
245 IWMBT_INTERRUPT_ENDPOINT_ADDR,
247 IWMBT_HCI_MAX_EVENT_SIZE,
249 IWMBT_HCI_CMD_TIMEOUT);
252 iwmbt_err("libusb_interrupt_transfer() failed:"
253 " err=%s", libusb_strerror(ret));
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");
263 /* Advance by data length. */
264 fw_job.buf += evt_length;
265 fw_job.len -= evt_length;
269 return (activate_patch);
273 iwmbt_load_fwfile(struct libusb_device_handle *hdl,
274 const struct iwmbt_firmware *fw, uint32_t *boot_param)
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];
282 #define IWMBT_SEND_FRAGMENT(fragment_type, size, msg) do { \
283 iwmbt_debug("transferring %d bytes, offset %d", size, sent); \
285 ret = iwmbt_send_fragment(hdl, \
288 XMIN(size, fw->len - sent), \
289 IWMBT_HCI_CMD_TIMEOUT); \
292 iwmbt_debug("Failed to send "msg": code=%d", ret); \
299 iwmbt_err("Invalid size of firmware file (%d)", fw->len);
303 iwmbt_debug("file=%s, size=%d", fw->fwname, fw->len);
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");
312 IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1");
313 IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2");
316 * Send firmware chunks. Chunk len must be 4 byte aligned.
317 * multiple commands can be combined
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);
326 ready += IWMBT_HCI_CMD_SIZE(cmd);
327 while (ready >= 0xFC) {
328 IWMBT_SEND_FRAGMENT(0x01, 0xFC, "firmware chunk");
331 if (ready > 0 && ready % 4 == 0) {
332 IWMBT_SEND_FRAGMENT(0x01, ready, "firmware chunk");
337 /* Wait for firmware download completion event */
338 ret = libusb_interrupt_transfer(hdl,
339 IWMBT_INTERRUPT_ENDPOINT_ADDR,
343 IWMBT_LOADCMPL_TIMEOUT);
345 if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
346 iwmbt_err("libusb_interrupt_transfer() failed: "
348 libusb_strerror(ret),
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");
364 iwmbt_enter_manufacturer(struct libusb_device_handle *hdl)
366 int ret, transferred;
367 static struct iwmbt_hci_cmd cmd = {
368 .opcode = htole16(0xfc11),
370 .data = { 0x01, 0x00 },
372 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
374 ret = iwmbt_hci_command(hdl,
379 IWMBT_HCI_CMD_TIMEOUT);
382 iwmbt_debug("Can't enter manufacturer mode: code=%d, size=%d",
392 iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, int mode)
394 int ret, transferred;
395 static struct iwmbt_hci_cmd cmd = {
396 .opcode = htole16(0xfc11),
398 .data = { 0x00, 0x00 },
400 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
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
409 iwmbt_debug("iwmbt_exit_manufacturer(): unknown mode (%d)",
414 ret = iwmbt_hci_command(hdl,
419 IWMBT_HCI_CMD_TIMEOUT);
422 iwmbt_debug("Can't exit manufacturer mode: code=%d, size=%d",
432 iwmbt_get_version(struct libusb_device_handle *hdl,
433 struct iwmbt_version *version)
435 int ret, transferred;
436 struct iwmbt_hci_event_cmd_compl*event;
437 struct iwmbt_hci_cmd cmd = {
438 .opcode = htole16(0xfc05),
441 uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_version)];
443 memset(buf, 0, sizeof(buf));
445 ret = iwmbt_hci_command(hdl,
450 IWMBT_HCI_CMD_TIMEOUT);
452 if (ret < 0 || transferred != sizeof(buf)) {
453 iwmbt_debug("Can't get version: : code=%d, size=%d",
459 event = (struct iwmbt_hci_event_cmd_compl *)buf;
460 memcpy(version, event->data, sizeof(struct iwmbt_version));
466 iwmbt_get_boot_params(struct libusb_device_handle *hdl,
467 struct iwmbt_boot_params *params)
469 int ret, transferred = 0;
470 struct iwmbt_hci_event_cmd_compl *event;
471 struct iwmbt_hci_cmd cmd = {
472 .opcode = htole16(0xfc0d),
475 uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_boot_params)];
477 memset(buf, 0, sizeof(buf));
479 ret = iwmbt_hci_command(hdl,
484 IWMBT_HCI_CMD_TIMEOUT);
486 if (ret < 0 || transferred != sizeof(buf)) {
487 iwmbt_debug("Can't get boot params: code=%d, size=%d",
493 event = (struct iwmbt_hci_event_cmd_compl *)buf;
494 memcpy(params, event->data, sizeof(struct iwmbt_boot_params));
500 iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param)
502 int ret, transferred = 0;
503 struct iwmbt_hci_event *event;
504 static struct iwmbt_hci_cmd cmd = {
505 .opcode = htole16(0xfc01),
507 .data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 },
509 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
511 le32enc(cmd.data + 4, boot_param);
512 memset(buf, 0, sizeof(buf));
514 ret = iwmbt_hci_command(hdl,
519 IWMBT_HCI_CMD_TIMEOUT);
521 if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
522 iwmbt_debug("Intel Reset command failed: code=%d, size=%d",
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");
539 iwmbt_load_ddc(struct libusb_device_handle *hdl,
540 const struct iwmbt_firmware *ddc)
543 int ret, transferred;
544 uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
545 struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf;
549 iwmbt_debug("file=%s, size=%d", ddc->fwname, size);
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));
558 iwmbt_debug("transferring %d bytes, offset %d",
565 ret = iwmbt_hci_command(hdl,
570 IWMBT_HCI_CMD_TIMEOUT);
573 iwmbt_debug("Intel Write DDC failed: code=%d", ret);
582 iwmbt_set_event_mask(struct libusb_device_handle *hdl)
584 int ret, transferred = 0;
585 static struct iwmbt_hci_cmd cmd = {
586 .opcode = htole16(0xfc52),
588 .data = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
590 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
592 ret = iwmbt_hci_command(hdl,
597 IWMBT_HCI_CMD_TIMEOUT);
600 iwmbt_debug("Intel Set Event Mask failed: code=%d", ret);