iwmbtfw(8): Add support for Intel 7260/7265 bluetooth adapter firmwares
To use it comms/iwmbt-firmware port should be updated to 20210315 version. Submitted by: Philippe Michaud-Boudreault <pitwuu@gmail.com> Tested by: Helge Oldach <freebsd@oldach.net> Reviewed by: wulf PR: 228787 MFC after: 2 weeks
This commit is contained in:
parent
15c0aaf517
commit
fe70d7b26d
@ -119,6 +119,20 @@ iwmbt_get_fwname(struct iwmbt_version *ver, struct iwmbt_boot_params *params,
|
||||
char *fwname;
|
||||
|
||||
switch (ver->hw_variant) {
|
||||
case 0x07: /* 7260 */
|
||||
asprintf(&fwname, "%s/ibt-hw-%x.%x.%x-fw-%x.%x.%x.%x.%x.%s",
|
||||
prefix,
|
||||
le16toh(ver->hw_platform),
|
||||
le16toh(ver->hw_variant),
|
||||
le16toh(ver->hw_revision),
|
||||
le16toh(ver->fw_variant),
|
||||
le16toh(ver->fw_revision),
|
||||
le16toh(ver->fw_build_num),
|
||||
le16toh(ver->fw_build_ww),
|
||||
le16toh(ver->fw_build_yy),
|
||||
suffix);
|
||||
break;
|
||||
|
||||
case 0x0b: /* 8260 */
|
||||
case 0x0c: /* 8265 */
|
||||
asprintf(&fwname, "%s/ibt-%u-%u.%s",
|
||||
|
@ -27,7 +27,7 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
@ -126,6 +126,125 @@ iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
|
||||
const struct iwmbt_firmware *fw)
|
||||
{
|
||||
int ret, transferred;
|
||||
struct iwmbt_firmware fw_job = *fw;
|
||||
uint16_t cmd_opcode;
|
||||
uint8_t cmd_length;
|
||||
uint8_t cmd_buf[IWMBT_HCI_MAX_CMD_SIZE];
|
||||
uint8_t evt_code;
|
||||
uint8_t evt_length;
|
||||
uint8_t evt_buf[IWMBT_HCI_MAX_EVENT_SIZE];
|
||||
int skip_patch = 0;
|
||||
|
||||
for (;;) {
|
||||
skip_patch = 0;
|
||||
|
||||
if (fw_job.len < 4)
|
||||
break;
|
||||
|
||||
if (fw_job.buf[0] != 0x01) {
|
||||
iwmbt_err("Invalid firmware, expected HCI command (%d)",
|
||||
fw_job.buf[0]);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Advance by one. */
|
||||
fw_job.buf++;
|
||||
fw_job.len--;
|
||||
|
||||
/* 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);
|
||||
|
||||
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;
|
||||
|
||||
/* 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));
|
||||
|
||||
/* Advance by data length. */
|
||||
fw_job.buf += cmd_length;
|
||||
fw_job.len -= cmd_length;
|
||||
|
||||
/*
|
||||
* 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 (;;) {
|
||||
/* Is this the end of the file? */
|
||||
if (fw_job.len < 3)
|
||||
break;
|
||||
|
||||
if (fw_job.buf[0] != 0x02)
|
||||
break;
|
||||
|
||||
/* Advance by one. */
|
||||
fw_job.buf++;
|
||||
fw_job.len--;
|
||||
|
||||
/* Load in the HCI event. */
|
||||
evt_code = fw_job.buf[0];
|
||||
evt_length = fw_job.buf[1];
|
||||
|
||||
/* Advance by two. */
|
||||
fw_job.buf += 2;
|
||||
fw_job.len -= 2;
|
||||
|
||||
/* Prepare HCI event buffer. */
|
||||
memset(evt_buf, 0, IWMBT_HCI_MAX_EVENT_SIZE);
|
||||
|
||||
iwmbt_debug("event=%04x, len=%02x",
|
||||
evt_code, evt_length);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
int
|
||||
iwmbt_load_fwfile(struct libusb_device_handle *hdl,
|
||||
const struct iwmbt_firmware *fw, uint32_t *boot_param)
|
||||
@ -217,6 +336,74 @@ iwmbt_load_fwfile(struct libusb_device_handle *hdl,
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
iwmbt_enter_manufacturer(struct libusb_device_handle *hdl)
|
||||
{
|
||||
int ret, transferred;
|
||||
static struct iwmbt_hci_cmd cmd = {
|
||||
.opcode = htole16(0xfc11),
|
||||
.length = 2,
|
||||
.data = { 0x01, 0x00 },
|
||||
};
|
||||
uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
|
||||
|
||||
ret = iwmbt_hci_command(hdl,
|
||||
&cmd,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
&transferred,
|
||||
IWMBT_HCI_CMD_TIMEOUT);
|
||||
|
||||
if (ret < 0) {
|
||||
iwmbt_debug("Can't enter manufacturer mode: code=%d, size=%d",
|
||||
ret,
|
||||
transferred);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, int mode)
|
||||
{
|
||||
int ret, transferred;
|
||||
static struct iwmbt_hci_cmd cmd = {
|
||||
.opcode = htole16(0xfc11),
|
||||
.length = 2,
|
||||
.data = { 0x00, 0x00 },
|
||||
};
|
||||
uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
|
||||
|
||||
/*
|
||||
* The mode sets the type of reset we want to perform:
|
||||
* 0x00: simply exit manufacturer mode without a reset.
|
||||
* 0x01: exit manufacturer mode with a reset and patches disabled
|
||||
* 0x02: exit manufacturer mode with a reset and patches enabled
|
||||
*/
|
||||
if (mode > 2) {
|
||||
iwmbt_debug("iwmbt_exit_manufacturer(): unknown mode (%d)",
|
||||
mode);
|
||||
}
|
||||
cmd.data[1] = mode;
|
||||
|
||||
ret = iwmbt_hci_command(hdl,
|
||||
&cmd,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
&transferred,
|
||||
IWMBT_HCI_CMD_TIMEOUT);
|
||||
|
||||
if (ret < 0) {
|
||||
iwmbt_debug("Can't exit manufacturer mode: code=%d, size=%d",
|
||||
ret,
|
||||
transferred);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
iwmbt_get_version(struct libusb_device_handle *hdl,
|
||||
struct iwmbt_version *version)
|
||||
|
@ -73,8 +73,13 @@ struct iwmbt_hci_event_cmd_compl {
|
||||
#define IWMBT_HCI_CMD_TIMEOUT 2000 /* ms */
|
||||
#define IWMBT_LOADCMPL_TIMEOUT 5000 /* ms */
|
||||
|
||||
extern int iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
|
||||
const struct iwmbt_firmware *fw);
|
||||
extern int iwmbt_load_fwfile(struct libusb_device_handle *hdl,
|
||||
const struct iwmbt_firmware *fw, uint32_t *boot_param);
|
||||
extern int iwmbt_enter_manufacturer(struct libusb_device_handle *hdl);
|
||||
extern int iwmbt_exit_manufacturer(struct libusb_device_handle *hdl,
|
||||
int mode);
|
||||
extern int iwmbt_get_version(struct libusb_device_handle *hdl,
|
||||
struct iwmbt_version *version);
|
||||
extern int iwmbt_get_boot_params(struct libusb_device_handle *hdl,
|
||||
|
@ -1,5 +1,6 @@
|
||||
.\" Copyright (c) 2013, 2016 Adrian Chadd <adrian@freebsd.org>
|
||||
.\" Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
|
||||
.\" Copyright (c) 2021 Philippe Michaud-Boudreault <pitwuu@gmail.com>
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
@ -24,12 +25,12 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd June 4, 2019
|
||||
.Dd May 3, 2021
|
||||
.Dt IWMBTFW 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm iwmbtfw
|
||||
.Nd firmware download utility for Intel Wireless 8260/8265 chip based Bluetooth
|
||||
.Nd firmware download utility for Intel Wireless 7260/8260/8265 chip based Bluetooth
|
||||
USB devices
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
@ -46,7 +47,7 @@ device.
|
||||
.Pp
|
||||
This utility will
|
||||
.Em only
|
||||
work with Intel Wireless 8260/8265 chip based Bluetooth USB devices and some of
|
||||
work with Intel Wireless 7260/8260/8265 chip based Bluetooth USB devices and some of
|
||||
their successors.
|
||||
The identification is currently based on USB vendor ID/product ID pair.
|
||||
The vendor ID should be 0x8087
|
||||
@ -91,6 +92,9 @@ utility used as firmware downloader template and on Linux btintel driver
|
||||
source code.
|
||||
It is written by
|
||||
.An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org .
|
||||
.Pp
|
||||
Support for the 7260 card added by
|
||||
.An Philippe Michaud-Boudreault Aq Mt pitwuu@gmail.com .
|
||||
.Sh BUGS
|
||||
Most likely.
|
||||
Please report if found.
|
||||
|
@ -7,6 +7,6 @@ notify 100 {
|
||||
match "subsystem" "DEVICE";
|
||||
match "type" "ATTACH";
|
||||
match "vendor" "0x8087";
|
||||
match "product" "(0x0a2b|0x0aaa|0x0025|0x0026|0x0029)";
|
||||
match "product" "(0x07dc|0x0a2a|0x0aa7|0x0a2b|0x0aaa|0x0025|0x0026|0x0029)";
|
||||
action "/usr/sbin/iwmbtfw -d $cdev -f /usr/local/share/iwmbt-firmware";
|
||||
};
|
||||
|
@ -57,7 +57,15 @@ struct iwmbt_devid {
|
||||
uint16_t vendor_id;
|
||||
};
|
||||
|
||||
static struct iwmbt_devid iwmbt_list[] = {
|
||||
static struct iwmbt_devid iwmbt_list_72xx[] = {
|
||||
|
||||
/* Intel Wireless 7260/7265 and successors */
|
||||
{ .vendor_id = 0x8087, .product_id = 0x07dc },
|
||||
{ .vendor_id = 0x8087, .product_id = 0x0a2a },
|
||||
{ .vendor_id = 0x8087, .product_id = 0x0aa7 },
|
||||
};
|
||||
|
||||
static struct iwmbt_devid iwmbt_list_82xx[] = {
|
||||
|
||||
/* Intel Wireless 8260/8265 and successors */
|
||||
{ .vendor_id = 0x8087, .product_id = 0x0a2b },
|
||||
@ -67,15 +75,33 @@ static struct iwmbt_devid iwmbt_list[] = {
|
||||
{ .vendor_id = 0x8087, .product_id = 0x0029 },
|
||||
};
|
||||
|
||||
static int
|
||||
iwmbt_is_7260(struct libusb_device_descriptor *d)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Search looking for whether it's an 7260/7265 */
|
||||
for (i = 0; i < (int) nitems(iwmbt_list_72xx); i++) {
|
||||
if ((iwmbt_list_72xx[i].product_id == d->idProduct) &&
|
||||
(iwmbt_list_72xx[i].vendor_id == d->idVendor)) {
|
||||
iwmbt_info("found 7260/7265");
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
iwmbt_is_8260(struct libusb_device_descriptor *d)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Search looking for whether it's an 8260/8265 */
|
||||
for (i = 0; i < (int) nitems(iwmbt_list); i++) {
|
||||
if ((iwmbt_list[i].product_id == d->idProduct) &&
|
||||
(iwmbt_list[i].vendor_id == d->idVendor)) {
|
||||
for (i = 0; i < (int) nitems(iwmbt_list_82xx); i++) {
|
||||
if ((iwmbt_list_82xx[i].product_id == d->idProduct) &&
|
||||
(iwmbt_list_82xx[i].vendor_id == d->idVendor)) {
|
||||
iwmbt_info("found 8260/8265");
|
||||
return (1);
|
||||
}
|
||||
@ -86,7 +112,8 @@ iwmbt_is_8260(struct libusb_device_descriptor *d)
|
||||
}
|
||||
|
||||
static libusb_device *
|
||||
iwmbt_find_device(libusb_context *ctx, int bus_id, int dev_id)
|
||||
iwmbt_find_device(libusb_context *ctx, int bus_id, int dev_id,
|
||||
int *iwmbt_use_old_method)
|
||||
{
|
||||
libusb_device **list, *dev = NULL, *found = NULL;
|
||||
struct libusb_device_descriptor d;
|
||||
@ -116,11 +143,20 @@ iwmbt_find_device(libusb_context *ctx, int bus_id, int dev_id)
|
||||
}
|
||||
|
||||
/* Match on the vendor/product id */
|
||||
if (iwmbt_is_7260(&d)) {
|
||||
/*
|
||||
* Take a reference so it's not freed later on.
|
||||
*/
|
||||
found = libusb_ref_device(dev);
|
||||
*iwmbt_use_old_method = 1;
|
||||
break;
|
||||
} else
|
||||
if (iwmbt_is_8260(&d)) {
|
||||
/*
|
||||
* Take a reference so it's not freed later on.
|
||||
*/
|
||||
found = libusb_ref_device(dev);
|
||||
*iwmbt_use_old_method = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -166,6 +202,31 @@ iwmbt_dump_boot_params(struct iwmbt_boot_params *params)
|
||||
params->otp_bdaddr[0]);
|
||||
}
|
||||
|
||||
static int
|
||||
iwmbt_patch_firmware(libusb_device_handle *hdl, const char *firmware_path)
|
||||
{
|
||||
struct iwmbt_firmware fw;
|
||||
int ret;
|
||||
|
||||
iwmbt_debug("loading %s", firmware_path);
|
||||
|
||||
/* Read in the firmware */
|
||||
if (iwmbt_fw_read(&fw, firmware_path) <= 0) {
|
||||
iwmbt_debug("iwmbt_fw_read() failed");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Load in the firmware */
|
||||
ret = iwmbt_patch_fwfile(hdl, &fw);
|
||||
if (ret < 0)
|
||||
iwmbt_debug("Loading firmware file failed");
|
||||
|
||||
/* free it */
|
||||
iwmbt_fw_free(&fw);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
iwmbt_init_firmware(libusb_device_handle *hdl, const char *firmware_path,
|
||||
uint32_t *boot_param)
|
||||
@ -268,6 +329,7 @@ main(int argc, char *argv[])
|
||||
char *firmware_dir = NULL;
|
||||
char *firmware_path = NULL;
|
||||
int retcode = 1;
|
||||
int iwmbt_use_old_method = 0;
|
||||
|
||||
/* Parse command line arguments */
|
||||
while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) {
|
||||
@ -312,7 +374,7 @@ main(int argc, char *argv[])
|
||||
iwmbt_debug("opening dev %d.%d", (int) bus_id, (int) dev_id);
|
||||
|
||||
/* Find a device based on the bus/dev id */
|
||||
dev = iwmbt_find_device(ctx, bus_id, dev_id);
|
||||
dev = iwmbt_find_device(ctx, bus_id, dev_id, &iwmbt_use_old_method);
|
||||
if (dev == NULL) {
|
||||
iwmbt_err("device not found");
|
||||
goto shutdown;
|
||||
@ -344,12 +406,68 @@ main(int argc, char *argv[])
|
||||
/* Get Intel version */
|
||||
r = iwmbt_get_version(hdl, &ver);
|
||||
if (r < 0) {
|
||||
iwmbt_debug("iwmbt_get_version() failedL code %d", r);
|
||||
iwmbt_debug("iwmbt_get_version() failed code %d", r);
|
||||
goto shutdown;
|
||||
}
|
||||
iwmbt_dump_version(&ver);
|
||||
iwmbt_debug("fw_variant=0x%02x", (int) ver.fw_variant);
|
||||
|
||||
if (iwmbt_use_old_method) {
|
||||
|
||||
/* fw_patch_num = >0 operational mode */
|
||||
if (ver.fw_patch_num > 0x00) {
|
||||
iwmbt_info("Firmware has already been downloaded");
|
||||
retcode = 0;
|
||||
goto reset;
|
||||
}
|
||||
|
||||
/* Default the firmware path */
|
||||
if (firmware_dir == NULL)
|
||||
firmware_dir = strdup(_DEFAULT_IWMBT_FIRMWARE_PATH);
|
||||
|
||||
firmware_path = iwmbt_get_fwname(&ver, ¶ms, firmware_dir, "bseq");
|
||||
if (firmware_path == NULL)
|
||||
goto shutdown;
|
||||
|
||||
iwmbt_debug("firmware_path = %s", firmware_path);
|
||||
|
||||
/* Enter manufacturer mode */
|
||||
r = iwmbt_enter_manufacturer(hdl);
|
||||
if (r < 0) {
|
||||
iwmbt_debug("iwmbt_enter_manufacturer() failed code %d", r);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
/* Download firmware and parse it for magic Intel Reset parameter */
|
||||
r = iwmbt_patch_firmware(hdl, firmware_path);
|
||||
free(firmware_path);
|
||||
if (r < 0)
|
||||
goto shutdown;
|
||||
|
||||
iwmbt_info("Firmware download complete");
|
||||
|
||||
/* Exit manufacturer mode */
|
||||
r = iwmbt_exit_manufacturer(hdl, 0x02);
|
||||
if (r < 0) {
|
||||
iwmbt_debug("iwmbt_exit_manufacturer() failed code %d", r);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
/* Once device is running in operational mode we can ignore failures */
|
||||
retcode = 0;
|
||||
|
||||
/* Execute Read Intel Version one more time */
|
||||
r = iwmbt_get_version(hdl, &ver);
|
||||
if (r == 0)
|
||||
iwmbt_dump_version(&ver);
|
||||
|
||||
/* Set Intel Event mask */
|
||||
r = iwmbt_set_event_mask(hdl);
|
||||
if (r == 0)
|
||||
iwmbt_info("Intel Event Mask is set");
|
||||
|
||||
} else {
|
||||
|
||||
/* fw_variant = 0x06 bootloader mode / 0x23 operational mode */
|
||||
if (ver.fw_variant == 0x23) {
|
||||
iwmbt_info("Firmware has already been downloaded");
|
||||
@ -425,6 +543,7 @@ main(int argc, char *argv[])
|
||||
r = iwmbt_set_event_mask(hdl);
|
||||
if (r == 0)
|
||||
iwmbt_info("Intel Event Mask is set");
|
||||
}
|
||||
|
||||
reset:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user