2 * SPDX-License-Identifier: BSD-2-Clause
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
28 #include <sys/param.h>
29 #include <sys/endian.h>
44 #include "iwmbt_dbg.h"
46 #define XMIN(x, y) ((x) < (y) ? (x) : (y))
49 iwmbt_send_fragment(struct libusb_device_handle *hdl,
50 uint8_t fragment_type, const void *data, uint8_t len, int timeout)
53 uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
54 struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *) buf;
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);
62 ret = libusb_bulk_transfer(hdl,
63 IWMBT_BULK_OUT_ENDPOINT_ADDR,
65 IWMBT_HCI_CMD_SIZE(cmd),
69 if (ret < 0 || transferred != (int)IWMBT_HCI_CMD_SIZE(cmd)) {
70 iwmbt_err("libusb_bulk_transfer() failed: err=%s, size=%zu",
72 IWMBT_HCI_CMD_SIZE(cmd));
76 ret = libusb_bulk_transfer(hdl,
77 IWMBT_BULK_IN_ENDPOINT_ADDR,
84 iwmbt_err("libusb_bulk_transfer() failed: err=%s",
85 libusb_strerror(ret));
93 iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
94 void *event, int size, int *transferred, int timeout)
98 ret = libusb_control_transfer(hdl,
99 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
104 IWMBT_HCI_CMD_SIZE(cmd),
108 iwmbt_err("libusb_control_transfer() failed: err=%s",
109 libusb_strerror(ret));
113 ret = libusb_interrupt_transfer(hdl,
114 IWMBT_INTERRUPT_ENDPOINT_ADDR,
121 iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
122 libusb_strerror(ret));
128 iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
129 const struct iwmbt_firmware *fw)
131 int ret, transferred;
132 struct iwmbt_firmware fw_job = *fw;
135 struct iwmbt_hci_cmd *cmd_buf;
138 uint8_t evt_buf[IWMBT_HCI_MAX_EVENT_SIZE];
139 int activate_patch = 0;
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);
148 if (fw_job.buf[0] != 0x01) {
149 iwmbt_err("Invalid firmware, expected HCI command (%d)",
154 /* Advance by one. */
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;
163 iwmbt_debug("opcode=%04x, len=%02x", cmd_opcode, cmd_length);
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.
170 if (cmd_opcode == 0xfc8e)
173 /* Advance by three. */
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);
184 /* Advance by data length. */
185 fw_job.buf += cmd_length;
186 fw_job.len -= cmd_length;
188 ret = libusb_control_transfer(hdl,
189 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
194 IWMBT_HCI_CMD_SIZE(cmd_buf),
195 IWMBT_HCI_CMD_TIMEOUT);
198 iwmbt_err("libusb_control_transfer() failed: err=%s",
199 libusb_strerror(ret));
204 * Every command has its associated event: data must match
205 * what is recorded in the firmware file. Perform that check
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);
217 /* Advance by one. */
221 /* Load in the HCI event. */
222 evt_code = fw_job.buf[0];
223 evt_length = fw_job.buf[1];
225 /* Advance by two. */
229 /* Prepare HCI event buffer. */
230 memset(evt_buf, 0, IWMBT_HCI_MAX_EVENT_SIZE);
232 iwmbt_debug("event=%04x, len=%02x",
233 evt_code, evt_length);
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);
242 ret = libusb_interrupt_transfer(hdl,
243 IWMBT_INTERRUPT_ENDPOINT_ADDR,
245 IWMBT_HCI_MAX_EVENT_SIZE,
247 IWMBT_HCI_CMD_TIMEOUT);
250 iwmbt_err("libusb_interrupt_transfer() failed:"
251 " err=%s", libusb_strerror(ret));
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");
261 /* Advance by data length. */
262 fw_job.buf += evt_length;
263 fw_job.len -= evt_length;
267 return (activate_patch);
271 iwmbt_load_fwfile(struct libusb_device_handle *hdl,
272 const struct iwmbt_firmware *fw, uint32_t *boot_param)
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];
280 #define IWMBT_SEND_FRAGMENT(fragment_type, size, msg) do { \
281 iwmbt_debug("transferring %d bytes, offset %d", size, sent); \
283 ret = iwmbt_send_fragment(hdl, \
286 XMIN(size, fw->len - sent), \
287 IWMBT_HCI_CMD_TIMEOUT); \
290 iwmbt_debug("Failed to send "msg": code=%d", ret); \
297 iwmbt_err("Invalid size of firmware file (%d)", fw->len);
301 iwmbt_debug("file=%s, size=%d", fw->fwname, fw->len);
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");
310 IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1");
311 IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2");
314 * Send firmware chunks. Chunk len must be 4 byte aligned.
315 * multiple commands can be combined
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);
324 ready += IWMBT_HCI_CMD_SIZE(cmd);
325 while (ready >= 0xFC) {
326 IWMBT_SEND_FRAGMENT(0x01, 0xFC, "firmware chunk");
329 if (ready > 0 && ready % 4 == 0) {
330 IWMBT_SEND_FRAGMENT(0x01, ready, "firmware chunk");
335 /* Wait for firmware download completion event */
336 ret = libusb_interrupt_transfer(hdl,
337 IWMBT_INTERRUPT_ENDPOINT_ADDR,
341 IWMBT_LOADCMPL_TIMEOUT);
343 if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
344 iwmbt_err("libusb_interrupt_transfer() failed: "
346 libusb_strerror(ret),
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");
362 iwmbt_enter_manufacturer(struct libusb_device_handle *hdl)
364 int ret, transferred;
365 static struct iwmbt_hci_cmd cmd = {
366 .opcode = htole16(0xfc11),
368 .data = { 0x01, 0x00 },
370 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
372 ret = iwmbt_hci_command(hdl,
377 IWMBT_HCI_CMD_TIMEOUT);
380 iwmbt_debug("Can't enter manufacturer mode: code=%d, size=%d",
390 iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, int mode)
392 int ret, transferred;
393 static struct iwmbt_hci_cmd cmd = {
394 .opcode = htole16(0xfc11),
396 .data = { 0x00, 0x00 },
398 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
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
407 iwmbt_debug("iwmbt_exit_manufacturer(): unknown mode (%d)",
412 ret = iwmbt_hci_command(hdl,
417 IWMBT_HCI_CMD_TIMEOUT);
420 iwmbt_debug("Can't exit manufacturer mode: code=%d, size=%d",
430 iwmbt_get_version(struct libusb_device_handle *hdl,
431 struct iwmbt_version *version)
433 int ret, transferred;
434 struct iwmbt_hci_event_cmd_compl*event;
435 struct iwmbt_hci_cmd cmd = {
436 .opcode = htole16(0xfc05),
439 uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_version)];
441 memset(buf, 0, sizeof(buf));
443 ret = iwmbt_hci_command(hdl,
448 IWMBT_HCI_CMD_TIMEOUT);
450 if (ret < 0 || transferred != sizeof(buf)) {
451 iwmbt_debug("Can't get version: : code=%d, size=%d",
457 event = (struct iwmbt_hci_event_cmd_compl *)buf;
458 memcpy(version, event->data, sizeof(struct iwmbt_version));
464 iwmbt_get_boot_params(struct libusb_device_handle *hdl,
465 struct iwmbt_boot_params *params)
467 int ret, transferred = 0;
468 struct iwmbt_hci_event_cmd_compl *event;
469 struct iwmbt_hci_cmd cmd = {
470 .opcode = htole16(0xfc0d),
473 uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_boot_params)];
475 memset(buf, 0, sizeof(buf));
477 ret = iwmbt_hci_command(hdl,
482 IWMBT_HCI_CMD_TIMEOUT);
484 if (ret < 0 || transferred != sizeof(buf)) {
485 iwmbt_debug("Can't get boot params: code=%d, size=%d",
491 event = (struct iwmbt_hci_event_cmd_compl *)buf;
492 memcpy(params, event->data, sizeof(struct iwmbt_boot_params));
498 iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param)
500 int ret, transferred = 0;
501 struct iwmbt_hci_event *event;
502 static struct iwmbt_hci_cmd cmd = {
503 .opcode = htole16(0xfc01),
505 .data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 },
507 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
509 le32enc(cmd.data + 4, boot_param);
510 memset(buf, 0, sizeof(buf));
512 ret = iwmbt_hci_command(hdl,
517 IWMBT_HCI_CMD_TIMEOUT);
519 if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
520 iwmbt_debug("Intel Reset command failed: code=%d, size=%d",
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");
537 iwmbt_load_ddc(struct libusb_device_handle *hdl,
538 const struct iwmbt_firmware *ddc)
541 int ret, transferred;
542 uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
543 struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf;
547 iwmbt_debug("file=%s, size=%d", ddc->fwname, size);
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));
556 iwmbt_debug("transferring %d bytes, offset %d",
563 ret = iwmbt_hci_command(hdl,
568 IWMBT_HCI_CMD_TIMEOUT);
571 iwmbt_debug("Intel Write DDC failed: code=%d", ret);
580 iwmbt_set_event_mask(struct libusb_device_handle *hdl)
582 int ret, transferred = 0;
583 static struct iwmbt_hci_cmd cmd = {
584 .opcode = htole16(0xfc52),
586 .data = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
588 uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
590 ret = iwmbt_hci_command(hdl,
595 IWMBT_HCI_CMD_TIMEOUT);
598 iwmbt_debug("Intel Set Event Mask failed: code=%d", ret);