iwmbtfw(8): Improve Intel 7260/7265 adaptors handling

- 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 da93a73f83)
This commit is contained in:
Vladimir Kondratyev 2021-05-31 22:32:08 +03:00
parent a4a738ba75
commit e9dd950507
3 changed files with 91 additions and 48 deletions

View File

@ -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),
@ -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 */

View File

@ -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)",
@ -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++;
@ -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

View File

@ -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;
@ -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 {