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:
parent
a4a738ba75
commit
e9dd950507
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user