Skip to content

Commit

Permalink
iwmbtfw(8): Improve Intel 7260/7265 adaptors handling
Browse files Browse the repository at this point in the history
- Allow firmware downloading for hw_variant #8;
- Enter manufacturer mode for setting of event mask;
- Handle multi-event response on HCI commands for 7260;
  This allows to remove kludge with skipping of 0xfc2f opcode.
- Disable patch and exit manufacturer mode on downloading failure;
- Use default firmware if correct firmware file is not found;

Reviewed by:	Philippe Michaud-Boudreault <pitwuu_AT_gmail_DOT_com>
Tested by:	arrowd
Differential revision:	https://reviews.freebsd.org/D30543

(cherry picked from commit da93a73)
  • Loading branch information
wulf7 committed Jun 13, 2021
1 parent a4a738b commit e9dd950
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 48 deletions.
14 changes: 14 additions & 0 deletions usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ char *
iwmbt_get_fwname(struct iwmbt_version *ver, struct iwmbt_boot_params *params,
const char *prefix, const char *suffix)
{
struct stat sb;
char *fwname;

switch (ver->hw_variant) {
case 0x07: /* 7260 */
case 0x08: /* 7265 */
asprintf(&fwname, "%s/ibt-hw-%x.%x.%x-fw-%x.%x.%x.%x.%x.%s",
prefix,
le16toh(ver->hw_platform),
Expand All @@ -131,6 +133,18 @@ iwmbt_get_fwname(struct iwmbt_version *ver, struct iwmbt_boot_params *params,
le16toh(ver->fw_build_ww),
le16toh(ver->fw_build_yy),
suffix);
/*
* Fallback to the default firmware patch if
* the correct firmware patch file is not found.
*/
if (stat(fwname, &sb) != 0 && errno == ENOENT) {
free(fwname);
asprintf(&fwname, "%s/ibt-hw-%x.%x.%s",
prefix,
le16toh(ver->hw_platform),
le16toh(ver->hw_variant),
suffix);
}
break;

case 0x0b: /* 8260 */
Expand Down
116 changes: 70 additions & 46 deletions usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,17 +134,18 @@ iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
struct iwmbt_firmware fw_job = *fw;
uint16_t cmd_opcode;
uint8_t cmd_length;
uint8_t cmd_buf[IWMBT_HCI_MAX_CMD_SIZE];
struct iwmbt_hci_cmd *cmd_buf;
uint8_t evt_code;
uint8_t evt_length;
uint8_t evt_buf[IWMBT_HCI_MAX_EVENT_SIZE];
int skip_patch = 0;
int activate_patch = 0;

for (;;) {
skip_patch = 0;

if (fw_job.len < 4)
break;
while (fw_job.len > 0) {
if (fw_job.len < 4) {
iwmbt_err("Invalid firmware, unexpected EOF in HCI "
"command header. Remains=%d", fw_job.len);
return (-1);
}

if (fw_job.buf[0] != 0x01) {
iwmbt_err("Invalid firmware, expected HCI command (%d)",
Expand All @@ -159,47 +160,61 @@ iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
/* Load in the HCI command to perform. */
cmd_opcode = le16dec(fw_job.buf);
cmd_length = fw_job.buf[2];
memcpy(cmd_buf, fw_job.buf, 3);
cmd_buf = (struct iwmbt_hci_cmd *)fw_job.buf;

iwmbt_debug("opcode=%04x, len=%02x", cmd_opcode, cmd_length);

/* For some reason the command 0xfc2f hangs up my card. */
if (cmd_opcode == 0xfc2f)
skip_patch = 1;
/*
* If there is a command that loads a patch in the
* firmware file, then activate the patch upon success,
* otherwise just disable the manufacturer mode.
*/
if (cmd_opcode == 0xfc8e)
activate_patch = 1;

/* Advance by three. */
fw_job.buf += 3;
fw_job.len -= 3;

if (fw_job.len < cmd_length)
cmd_length = fw_job.len;

/* Copy data to HCI command buffer. */
memcpy(cmd_buf + 3, fw_job.buf,
MIN(cmd_length, IWMBT_HCI_MAX_CMD_SIZE - 3));
if (fw_job.len < cmd_length) {
iwmbt_err("Invalid firmware, unexpected EOF in HCI "
"command data. len=%d, remains=%d",
cmd_length, fw_job.len);
return (-1);
}

/* Advance by data length. */
fw_job.buf += cmd_length;
fw_job.len -= cmd_length;

ret = libusb_control_transfer(hdl,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
0,
0,
0,
(uint8_t *)cmd_buf,
IWMBT_HCI_CMD_SIZE(cmd_buf),
IWMBT_HCI_CMD_TIMEOUT);

if (ret < 0) {
iwmbt_err("libusb_control_transfer() failed: err=%s",
libusb_strerror(ret));
return (-1);
}

/*
* Every command has its associated event: data must match
* what is recorded in the firmware file. Perform that check
* now.
*
* Some commands are mapped to more than one event sequence,
* in that case we can drop the non-patch commands, as we
* probably don't need them for operation of the card.
*
*/

for (;;) {
while (fw_job.len > 0 && fw_job.buf[0] == 0x02) {
/* Is this the end of the file? */
if (fw_job.len < 3)
break;

if (fw_job.buf[0] != 0x02)
break;
if (fw_job.len < 3) {
iwmbt_err("Invalid firmware, unexpected EOF in"
"event header. remains=%d", fw_job.len);
return (-1);
}

/* Advance by one. */
fw_job.buf++;
Expand All @@ -219,30 +234,39 @@ iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
iwmbt_debug("event=%04x, len=%02x",
evt_code, evt_length);

if (fw_job.len < evt_length) {
iwmbt_err("Invalid firmware, unexpected EOF in"
" event data. len=%d, remains=%d",
evt_length, fw_job.len);
return (-1);
}

ret = libusb_interrupt_transfer(hdl,
IWMBT_INTERRUPT_ENDPOINT_ADDR,
evt_buf,
IWMBT_HCI_MAX_EVENT_SIZE,
&transferred,
IWMBT_HCI_CMD_TIMEOUT);

if (ret < 0) {
iwmbt_err("libusb_interrupt_transfer() failed:"
" err=%s", libusb_strerror(ret));
return (-1);
}

if ((int)evt_length + 2 != transferred ||
memcmp(evt_buf + 2, fw_job.buf, evt_length) != 0) {
iwmbt_err("event does not match firmware");
return (-1);
}

/* Advance by data length. */
fw_job.buf += evt_length;
fw_job.len -= evt_length;

if (skip_patch == 0) {
ret = iwmbt_hci_command(hdl,
(struct iwmbt_hci_cmd *)cmd_buf,
evt_buf,
IWMBT_HCI_MAX_EVENT_SIZE,
&transferred,
IWMBT_HCI_CMD_TIMEOUT);

if (ret < 0) {
iwmbt_debug("Can't send patch: "
"code=%d, size=%d",
ret,
transferred);
return (-1);
}
}
}
}

return (0);
return (activate_patch);
}

int
Expand Down
9 changes: 7 additions & 2 deletions usr.sbin/bluetooth/iwmbtfw/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -441,13 +441,15 @@ main(int argc, char *argv[])
/* Download firmware and parse it for magic Intel Reset parameter */
r = iwmbt_patch_firmware(hdl, firmware_path);
free(firmware_path);
if (r < 0)
if (r < 0) {
(void)iwmbt_exit_manufacturer(hdl, 0x01);
goto shutdown;
}

iwmbt_info("Firmware download complete");

/* Exit manufacturer mode */
r = iwmbt_exit_manufacturer(hdl, 0x02);
r = iwmbt_exit_manufacturer(hdl, r == 0 ? 0x00 : 0x02);
if (r < 0) {
iwmbt_debug("iwmbt_exit_manufacturer() failed code %d", r);
goto shutdown;
Expand All @@ -462,9 +464,12 @@ main(int argc, char *argv[])
iwmbt_dump_version(&ver);

/* Set Intel Event mask */
if (iwmbt_enter_manufacturer(hdl) < 0)
goto reset;
r = iwmbt_set_event_mask(hdl);
if (r == 0)
iwmbt_info("Intel Event Mask is set");
(void)iwmbt_exit_manufacturer(hdl, 0x00);

} else {

Expand Down

0 comments on commit e9dd950

Please sign in to comment.