iwmbtfw: Firmware loader for Intel Wireless 8260 based Bluetooth USB devices
Currently supported models are: 8260, 8265, 9560, 9260 and 22161. Firmware files can be installed with comms/iwmbt-firmware port. PR: 237083 Reviewed by: hps, emax X-MFC with: r351196 Differential Revision: https://reviews.freebsd.org/D21071
This commit is contained in:
parent
3544d43bb1
commit
7f32f0e227
@ -443,6 +443,7 @@ DIRDEPS+= \
|
||||
usr.sbin/bluetooth/hccontrol \
|
||||
usr.sbin/bluetooth/hcsecd \
|
||||
usr.sbin/bluetooth/hcseriald \
|
||||
usr.sbin/bluetooth/iwmbtfw \
|
||||
usr.sbin/bluetooth/l2control \
|
||||
usr.sbin/bluetooth/l2ping \
|
||||
usr.sbin/bluetooth/rfcomm_pppd \
|
||||
|
@ -471,6 +471,7 @@ OLD_FILES+=etc/bluetooth/hcsecd.conf
|
||||
OLD_FILES+=etc/bluetooth/hosts
|
||||
OLD_FILES+=etc/bluetooth/protocols
|
||||
OLD_FILES+=etc/defaults/bluetooth.device.conf
|
||||
OLD_FILES+=etc/devd/iwmbtfw.conf
|
||||
OLD_DIRS+=etc/bluetooth
|
||||
OLD_FILES+=etc/rc.d/bluetooth
|
||||
OLD_FILES+=etc/rc.d/bthidd
|
||||
@ -524,6 +525,7 @@ OLD_FILES+=usr/sbin/btpand
|
||||
OLD_FILES+=usr/sbin/hccontrol
|
||||
OLD_FILES+=usr/sbin/hcsecd
|
||||
OLD_FILES+=usr/sbin/hcseriald
|
||||
OLD_FILES+=usr/sbin/iwmbtfw
|
||||
OLD_FILES+=usr/sbin/l2control
|
||||
OLD_FILES+=usr/sbin/l2ping
|
||||
OLD_FILES+=usr/sbin/rfcomm_pppd
|
||||
@ -601,6 +603,7 @@ OLD_FILES+=usr/share/man/man8/btpand.8.gz
|
||||
OLD_FILES+=usr/share/man/man8/hccontrol.8.gz
|
||||
OLD_FILES+=usr/share/man/man8/hcsecd.8.gz
|
||||
OLD_FILES+=usr/share/man/man8/hcseriald.8.gz
|
||||
OLD_FILES+=usr/share/man/man8/iwmbtfw.8.gz
|
||||
OLD_FILES+=usr/share/man/man8/l2control.8.gz
|
||||
OLD_FILES+=usr/share/man/man8/l2ping.8.gz
|
||||
OLD_FILES+=usr/share/man/man8/rfcomm_pppd.8.gz
|
||||
|
@ -21,6 +21,7 @@ SUBDIR+= ath3kfw
|
||||
SUBDIR+= bcmfw
|
||||
SUBDIR+= bthidcontrol
|
||||
SUBDIR+= bthidd
|
||||
SUBDIR+= iwmbtfw
|
||||
.endif
|
||||
|
||||
.include <bsd.subdir.mk>
|
||||
|
11
usr.sbin/bluetooth/iwmbtfw/Makefile
Normal file
11
usr.sbin/bluetooth/iwmbtfw/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PACKAGE= bluetooth
|
||||
CONFS= iwmbtfw.conf
|
||||
CONFSDIR= /etc/devd
|
||||
PROG= iwmbtfw
|
||||
MAN= iwmbtfw.8
|
||||
LIBADD+= usb
|
||||
SRCS= main.c iwmbt_fw.c iwmbt_hw.c
|
||||
|
||||
.include <bsd.prog.mk>
|
47
usr.sbin/bluetooth/iwmbtfw/iwmbt_dbg.h
Normal file
47
usr.sbin/bluetooth/iwmbtfw/iwmbt_dbg.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
|
||||
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#ifndef __IWMBT_DEBUG_H__
|
||||
#define __IWMBT_DEBUG_H__
|
||||
|
||||
extern int iwmbt_do_debug;
|
||||
extern int iwmbt_do_info;
|
||||
|
||||
#define iwmbt_err(fmt, ...) \
|
||||
fprintf(stderr, "iwmbtfw: %s: "fmt"\n", __func__, ##__VA_ARGS__)
|
||||
#define iwmbt_info(fmt, ...) do { \
|
||||
if (iwmbt_do_info) \
|
||||
fprintf(stderr, "%s: "fmt"\n", __func__, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
#define iwmbt_debug(fmt, ...) do { \
|
||||
if (iwmbt_do_debug) \
|
||||
fprintf(stderr, "%s: "fmt"\n", __func__, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#endif
|
148
usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
Normal file
148
usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
Normal file
@ -0,0 +1,148 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
|
||||
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "iwmbt_fw.h"
|
||||
#include "iwmbt_dbg.h"
|
||||
|
||||
int
|
||||
iwmbt_fw_read(struct iwmbt_firmware *fw, const char *fwname)
|
||||
{
|
||||
int fd;
|
||||
struct stat sb;
|
||||
unsigned char *buf;
|
||||
ssize_t r;
|
||||
int i;
|
||||
|
||||
fd = open(fwname, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
warn("%s: open: %s", __func__, fwname);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (fstat(fd, &sb) != 0) {
|
||||
warn("%s: stat: %s", __func__, fwname);
|
||||
close(fd);
|
||||
return (0);
|
||||
}
|
||||
|
||||
buf = calloc(1, sb.st_size);
|
||||
if (buf == NULL) {
|
||||
warn("%s: calloc", __func__);
|
||||
close(fd);
|
||||
return (0);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
/* XXX handle partial reads */
|
||||
r = read(fd, buf, sb.st_size);
|
||||
if (r < 0) {
|
||||
warn("%s: read", __func__);
|
||||
free(buf);
|
||||
close(fd);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (r != sb.st_size) {
|
||||
iwmbt_err("read len %d != file size %d",
|
||||
(int) r,
|
||||
(int) sb.st_size);
|
||||
free(buf);
|
||||
close(fd);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* We have everything, so! */
|
||||
|
||||
memset(fw, 0, sizeof(*fw));
|
||||
|
||||
fw->fwname = strdup(fwname);
|
||||
fw->len = sb.st_size;
|
||||
fw->buf = buf;
|
||||
|
||||
close(fd);
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
iwmbt_fw_free(struct iwmbt_firmware *fw)
|
||||
{
|
||||
if (fw->fwname)
|
||||
free(fw->fwname);
|
||||
if (fw->buf)
|
||||
free(fw->buf);
|
||||
memset(fw, 0, sizeof(*fw));
|
||||
}
|
||||
|
||||
char *
|
||||
iwmbt_get_fwname(struct iwmbt_version *ver, struct iwmbt_boot_params *params,
|
||||
const char *prefix, const char *suffix)
|
||||
{
|
||||
char *fwname;
|
||||
|
||||
switch (ver->hw_variant) {
|
||||
case 0x0b: /* 8260 */
|
||||
case 0x0c: /* 8265 */
|
||||
asprintf(&fwname, "%s/ibt-%u-%u.%s",
|
||||
prefix,
|
||||
le16toh(ver->hw_variant),
|
||||
le16toh(params->dev_revid),
|
||||
suffix);
|
||||
break;
|
||||
|
||||
case 0x11: /* 9560 */
|
||||
case 0x12: /* 9260 */
|
||||
case 0x13:
|
||||
case 0x14: /* 22161 */
|
||||
asprintf(&fwname, "%s/ibt-%u-%u-%u.%s",
|
||||
prefix,
|
||||
le16toh(ver->hw_variant),
|
||||
le16toh(ver->hw_revision),
|
||||
le16toh(ver->fw_revision),
|
||||
suffix);
|
||||
break;
|
||||
|
||||
default:
|
||||
fwname = NULL;
|
||||
}
|
||||
|
||||
return (fwname);
|
||||
}
|
79
usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h
Normal file
79
usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
|
||||
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __IWMBT_FW_H__
|
||||
#define __IWMBT_FW_H__
|
||||
|
||||
struct iwmbt_version {
|
||||
uint8_t status;
|
||||
uint8_t hw_platform;
|
||||
uint8_t hw_variant;
|
||||
uint8_t hw_revision;
|
||||
uint8_t fw_variant;
|
||||
uint8_t fw_revision;
|
||||
uint8_t fw_build_num;
|
||||
uint8_t fw_build_ww;
|
||||
uint8_t fw_build_yy;
|
||||
uint8_t fw_patch_num;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwmbt_boot_params {
|
||||
uint8_t status;
|
||||
uint8_t otp_format;
|
||||
uint8_t otp_content;
|
||||
uint8_t otp_patch;
|
||||
uint16_t dev_revid;
|
||||
uint8_t secure_boot;
|
||||
uint8_t key_from_hdr;
|
||||
uint8_t key_type;
|
||||
uint8_t otp_lock;
|
||||
uint8_t api_lock;
|
||||
uint8_t debug_lock;
|
||||
uint8_t otp_bdaddr[6];
|
||||
uint8_t min_fw_build_nn;
|
||||
uint8_t min_fw_build_cw;
|
||||
uint8_t min_fw_build_yy;
|
||||
uint8_t limited_cce;
|
||||
uint8_t unlocked_state;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwmbt_firmware {
|
||||
char *fwname;
|
||||
int len;
|
||||
unsigned char *buf;
|
||||
};
|
||||
|
||||
extern int iwmbt_fw_read(struct iwmbt_firmware *fw, const char *fwname);
|
||||
extern void iwmbt_fw_free(struct iwmbt_firmware *fw);
|
||||
extern char *iwmbt_get_fwname(struct iwmbt_version *ver,
|
||||
struct iwmbt_boot_params *params, const char *prefix,
|
||||
const char *suffix);
|
||||
|
||||
#endif
|
392
usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
Normal file
392
usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
Normal file
@ -0,0 +1,392 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libusb.h>
|
||||
|
||||
#include "iwmbt_fw.h"
|
||||
#include "iwmbt_hw.h"
|
||||
#include "iwmbt_dbg.h"
|
||||
|
||||
#define XMIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
static int
|
||||
iwmbt_send_fragment(struct libusb_device_handle *hdl,
|
||||
uint8_t fragment_type, const void *data, uint8_t len, int timeout)
|
||||
{
|
||||
int ret, transferred;
|
||||
uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
|
||||
struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *) buf;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
cmd->opcode = htole16(0xfc09),
|
||||
cmd->length = len + 1,
|
||||
cmd->data[0] = fragment_type;
|
||||
memcpy(cmd->data + 1, data, len);
|
||||
|
||||
ret = libusb_bulk_transfer(hdl,
|
||||
IWMBT_BULK_OUT_ENDPOINT_ADDR,
|
||||
(uint8_t *)cmd,
|
||||
IWMBT_HCI_CMD_SIZE(cmd),
|
||||
&transferred,
|
||||
timeout);
|
||||
|
||||
if (ret < 0 || transferred != IWMBT_HCI_CMD_SIZE(cmd)) {
|
||||
iwmbt_err("libusb_bulk_transfer() failed: err=%s, size=%zu",
|
||||
libusb_strerror(ret),
|
||||
IWMBT_HCI_CMD_SIZE(cmd));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
ret = libusb_bulk_transfer(hdl,
|
||||
IWMBT_BULK_IN_ENDPOINT_ADDR,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
&transferred,
|
||||
timeout);
|
||||
|
||||
if (ret < 0) {
|
||||
iwmbt_err("libusb_bulk_transfer() failed: err=%s",
|
||||
libusb_strerror(ret));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
|
||||
void *event, int size, int *transferred, int timeout)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = libusb_control_transfer(hdl,
|
||||
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
(uint8_t *)cmd,
|
||||
IWMBT_HCI_CMD_SIZE(cmd),
|
||||
timeout);
|
||||
|
||||
if (ret < 0) {
|
||||
iwmbt_err("libusb_control_transfer() failed: err=%s",
|
||||
libusb_strerror(ret));
|
||||
return (ret);
|
||||
}
|
||||
|
||||
ret = libusb_interrupt_transfer(hdl,
|
||||
IWMBT_INTERRUPT_ENDPOINT_ADDR,
|
||||
event,
|
||||
size,
|
||||
transferred,
|
||||
timeout);
|
||||
|
||||
if (ret < 0)
|
||||
iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
|
||||
libusb_strerror(ret));
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
iwmbt_load_fwfile(struct libusb_device_handle *hdl,
|
||||
const struct iwmbt_firmware *fw, uint32_t *boot_param)
|
||||
{
|
||||
int ready = 0, sent = 0;
|
||||
int ret, transferred;
|
||||
struct iwmbt_hci_cmd *cmd;
|
||||
struct iwmbt_hci_event *event;
|
||||
uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
|
||||
|
||||
#define IWMBT_SEND_FRAGMENT(fragment_type, size, msg) do { \
|
||||
iwmbt_debug("transferring %d bytes, offset %d", size, sent); \
|
||||
\
|
||||
ret = iwmbt_send_fragment(hdl, \
|
||||
fragment_type, \
|
||||
fw->buf + sent, \
|
||||
XMIN(size, fw->len - sent), \
|
||||
IWMBT_HCI_CMD_TIMEOUT); \
|
||||
\
|
||||
if (ret < 0) { \
|
||||
iwmbt_debug("Failed to send "msg": code=%d", ret); \
|
||||
return (-1); \
|
||||
} \
|
||||
sent += size; \
|
||||
} while (0)
|
||||
|
||||
if (fw->len < 644) {
|
||||
iwmbt_err("Invalid size of firmware file (%d)", fw->len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
iwmbt_debug("file=%s, size=%d", fw->fwname, fw->len);
|
||||
|
||||
IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment");
|
||||
IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 1");
|
||||
IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 2");
|
||||
|
||||
/* skip 4 bytes */
|
||||
sent += 4;
|
||||
|
||||
IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1");
|
||||
IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2");
|
||||
|
||||
/*
|
||||
* Send firmware chunks. Chunk len must be 4 byte aligned.
|
||||
* multiple commands can be combined
|
||||
*/
|
||||
while (fw->len - sent - ready >= (int) sizeof(struct iwmbt_hci_cmd)) {
|
||||
cmd = (struct iwmbt_hci_cmd *)(fw->buf + sent + ready);
|
||||
/* Parse firmware for Intel Reset HCI command parameter */
|
||||
if (cmd->opcode == htole16(0xfc0e)) {
|
||||
*boot_param = le32dec(cmd->data);
|
||||
iwmbt_debug("boot_param=0x%08x", *boot_param);
|
||||
}
|
||||
ready += IWMBT_HCI_CMD_SIZE(cmd);
|
||||
while (ready >= 0xFC) {
|
||||
IWMBT_SEND_FRAGMENT(0x01, 0xFC, "firmware chunk");
|
||||
ready -= 0xFC;
|
||||
}
|
||||
if (ready > 0 && ready % 4 == 0) {
|
||||
IWMBT_SEND_FRAGMENT(0x01, ready, "firmware chunk");
|
||||
ready = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for firmware download completion event */
|
||||
ret = libusb_interrupt_transfer(hdl,
|
||||
IWMBT_INTERRUPT_ENDPOINT_ADDR,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
&transferred,
|
||||
IWMBT_LOADCMPL_TIMEOUT);
|
||||
|
||||
if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
|
||||
iwmbt_err("libusb_interrupt_transfer() failed: "
|
||||
"err=%s, size=%d",
|
||||
libusb_strerror(ret),
|
||||
transferred);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Expect Vendor Specific Event 0x06 */
|
||||
event = (struct iwmbt_hci_event *)buf;
|
||||
if (event->header.event != 0xFF || event->data[0] != 0x06) {
|
||||
iwmbt_err("firmware download completion event missed");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
iwmbt_get_version(struct libusb_device_handle *hdl,
|
||||
struct iwmbt_version *version)
|
||||
{
|
||||
int ret, transferred;
|
||||
struct iwmbt_hci_event_cmd_compl*event;
|
||||
struct iwmbt_hci_cmd cmd = {
|
||||
.opcode = htole16(0xfc05),
|
||||
.length = 0,
|
||||
};
|
||||
uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_version)];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
ret = iwmbt_hci_command(hdl,
|
||||
&cmd,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
&transferred,
|
||||
IWMBT_HCI_CMD_TIMEOUT);
|
||||
|
||||
if (ret < 0 || transferred != sizeof(buf)) {
|
||||
iwmbt_debug("Can't get version: : code=%d, size=%d",
|
||||
ret,
|
||||
transferred);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
event = (struct iwmbt_hci_event_cmd_compl *)buf;
|
||||
memcpy(version, event->data, sizeof(struct iwmbt_version));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
iwmbt_get_boot_params(struct libusb_device_handle *hdl,
|
||||
struct iwmbt_boot_params *params)
|
||||
{
|
||||
int ret, transferred = 0;
|
||||
struct iwmbt_hci_event_cmd_compl *event;
|
||||
struct iwmbt_hci_cmd cmd = {
|
||||
.opcode = htole16(0xfc0d),
|
||||
.length = 0,
|
||||
};
|
||||
uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_boot_params)];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
ret = iwmbt_hci_command(hdl,
|
||||
&cmd,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
&transferred,
|
||||
IWMBT_HCI_CMD_TIMEOUT);
|
||||
|
||||
if (ret < 0 || transferred != sizeof(buf)) {
|
||||
iwmbt_debug("Can't get boot params: code=%d, size=%d",
|
||||
ret,
|
||||
transferred);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
event = (struct iwmbt_hci_event_cmd_compl *)buf;
|
||||
memcpy(params, event->data, sizeof(struct iwmbt_boot_params));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param)
|
||||
{
|
||||
int ret, transferred = 0;
|
||||
struct iwmbt_hci_event *event;
|
||||
static struct iwmbt_hci_cmd cmd = {
|
||||
.opcode = htole16(0xfc01),
|
||||
.length = 8,
|
||||
.data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 },
|
||||
};
|
||||
uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
|
||||
|
||||
le32enc(cmd.data + 4, boot_param);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
ret = iwmbt_hci_command(hdl,
|
||||
&cmd,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
&transferred,
|
||||
IWMBT_HCI_CMD_TIMEOUT);
|
||||
|
||||
if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
|
||||
iwmbt_debug("Intel Reset command failed: code=%d, size=%d",
|
||||
ret,
|
||||
transferred);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/* expect Vendor Specific Event 0x02 */
|
||||
event = (struct iwmbt_hci_event *)buf;
|
||||
if (event->header.event != 0xFF || event->data[0] != 0x02) {
|
||||
iwmbt_err("Intel Reset completion event missed");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
iwmbt_load_ddc(struct libusb_device_handle *hdl,
|
||||
const struct iwmbt_firmware *ddc)
|
||||
{
|
||||
int size, sent = 0;
|
||||
int ret, transferred;
|
||||
uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
|
||||
struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf;
|
||||
|
||||
size = ddc->len;
|
||||
|
||||
iwmbt_debug("file=%s, size=%d", ddc->fwname, size);
|
||||
|
||||
while (size > 0) {
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
cmd->opcode = htole16(0xfc8b);
|
||||
cmd->length = ddc->buf[sent] + 1;
|
||||
memcpy(cmd->data, ddc->buf + sent, XMIN(ddc->buf[sent], size));
|
||||
|
||||
iwmbt_debug("transferring %d bytes, offset %d",
|
||||
cmd->length,
|
||||
sent);
|
||||
|
||||
size -= cmd->length;
|
||||
sent += cmd->length;
|
||||
|
||||
ret = iwmbt_hci_command(hdl,
|
||||
cmd,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
&transferred,
|
||||
IWMBT_HCI_CMD_TIMEOUT);
|
||||
|
||||
if (ret < 0) {
|
||||
iwmbt_debug("Intel Write DDC failed: code=%d", ret);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
iwmbt_set_event_mask(struct libusb_device_handle *hdl)
|
||||
{
|
||||
int ret, transferred = 0;
|
||||
static struct iwmbt_hci_cmd cmd = {
|
||||
.opcode = htole16(0xfc52),
|
||||
.length = 8,
|
||||
.data = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 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("Intel Set Event Mask failed: code=%d", ret);
|
||||
|
||||
return (ret);
|
||||
}
|
88
usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h
Normal file
88
usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#ifndef __IWMBT_HW_H__
|
||||
#define __IWMBT_HW_H__
|
||||
|
||||
/* USB control request (HCI command) structure */
|
||||
struct iwmbt_hci_cmd {
|
||||
uint16_t opcode;
|
||||
uint8_t length;
|
||||
uint8_t data[];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define IWMBT_HCI_CMD_SIZE(cmd) \
|
||||
((cmd)->length + offsetof(struct iwmbt_hci_cmd, data))
|
||||
|
||||
/* USB interrupt transfer HCI event header structure */
|
||||
struct iwmbt_hci_evhdr {
|
||||
uint8_t event;
|
||||
uint8_t length;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* USB interrupt transfer (generic HCI event) structure */
|
||||
struct iwmbt_hci_event {
|
||||
struct iwmbt_hci_evhdr header;
|
||||
uint8_t data[];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* USB interrupt transfer (HCI command completion event) structure */
|
||||
struct iwmbt_hci_event_cmd_compl {
|
||||
struct iwmbt_hci_evhdr header;
|
||||
uint8_t numpkt;
|
||||
uint16_t opcode;
|
||||
uint8_t data[];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define IWMBT_HCI_EVT_COMPL_SIZE(payload) \
|
||||
(offsetof(struct iwmbt_hci_event_cmd_compl, data) + sizeof(payload))
|
||||
|
||||
#define IWMBT_CONTROL_ENDPOINT_ADDR 0x00
|
||||
#define IWMBT_INTERRUPT_ENDPOINT_ADDR 0x81
|
||||
#define IWMBT_BULK_IN_ENDPOINT_ADDR 0x82
|
||||
#define IWMBT_BULK_OUT_ENDPOINT_ADDR 0x02
|
||||
|
||||
#define IWMBT_HCI_MAX_CMD_SIZE 256
|
||||
#define IWMBT_HCI_MAX_EVENT_SIZE 16
|
||||
|
||||
#define IWMBT_HCI_CMD_TIMEOUT 2000 /* ms */
|
||||
#define IWMBT_LOADCMPL_TIMEOUT 5000 /* ms */
|
||||
|
||||
extern int iwmbt_load_fwfile(struct libusb_device_handle *hdl,
|
||||
const struct iwmbt_firmware *fw, uint32_t *boot_param);
|
||||
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,
|
||||
struct iwmbt_boot_params *params);
|
||||
extern int iwmbt_intel_reset(struct libusb_device_handle *hdl,
|
||||
uint32_t boot_param);
|
||||
extern int iwmbt_load_ddc(struct libusb_device_handle *hdl,
|
||||
const struct iwmbt_firmware *ddc);
|
||||
extern int iwmbt_set_event_mask(struct libusb_device_handle *hdl);
|
||||
|
||||
#endif
|
96
usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8
Normal file
96
usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8
Normal file
@ -0,0 +1,96 @@
|
||||
.\" Copyright (c) 2013, 2016 Adrian Chadd <adrian@freebsd.org>
|
||||
.\" Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd June 4, 2019
|
||||
.Dt IWMBTFW 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm iwmbtfw
|
||||
.Nd firmware download utility for Intel Wireless 8260/8265 chip based Bluetooth
|
||||
USB devices
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Fl d Ar device_name
|
||||
.Fl f Ar firmware_path
|
||||
.Nm
|
||||
.Fl h
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility downloads the specified firmware file to the specified
|
||||
.Xr ugen 4
|
||||
device.
|
||||
.Pp
|
||||
This utility will
|
||||
.Em only
|
||||
work with Intel Wireless 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
|
||||
.Pq Dv USB_VENDOR_INTEL2
|
||||
and the product ID should be one of the supported devices.
|
||||
.Pp
|
||||
Firmware files are available in the
|
||||
.Pa comms/iwmbt-firmware
|
||||
port.
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
utility will query the device to determine which firmware image and board
|
||||
configuration to load in at runtime.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width indent
|
||||
.It Fl D
|
||||
Enable verbose debugging.
|
||||
.It Fl d Ar device_name
|
||||
Specify
|
||||
.Xr ugen 4
|
||||
device name.
|
||||
.It Fl I
|
||||
Enable informational debugging.
|
||||
.It Fl f Ar firmware_path
|
||||
Specify the directory containing the firmware files to search and upload.
|
||||
.It Fl h
|
||||
Display usage message and exit.
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
.Sh SEE ALSO
|
||||
.Xr libusb 3 ,
|
||||
.Xr ugen 4 ,
|
||||
.Xr devd 8
|
||||
.Sh AUTHORS
|
||||
.Nm
|
||||
is based on
|
||||
.Xr ath3kfw 8
|
||||
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 .
|
||||
.Sh BUGS
|
||||
Most likely.
|
||||
Please report if found.
|
12
usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf
Normal file
12
usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf
Normal file
@ -0,0 +1,12 @@
|
||||
# $FreeBSD$
|
||||
#
|
||||
# Download Intel Wireless 8260/8265 bluetooth adaptor firmware
|
||||
|
||||
notify 100 {
|
||||
match "system" "USB";
|
||||
match "subsystem" "DEVICE";
|
||||
match "type" "ATTACH";
|
||||
match "vendor" "0x8087";
|
||||
match "product" "(0x0a2b|0x0aaa|0x0025|0x0026|0x0029)";
|
||||
action "/usr/sbin/iwmbtfw -d $cdev -f /usr/local/share/iwmbt-firmware";
|
||||
};
|
456
usr.sbin/bluetooth/iwmbtfw/main.c
Normal file
456
usr.sbin/bluetooth/iwmbtfw/main.c
Normal file
@ -0,0 +1,456 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
|
||||
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/endian.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libusb.h>
|
||||
|
||||
#include "iwmbt_fw.h"
|
||||
#include "iwmbt_hw.h"
|
||||
#include "iwmbt_dbg.h"
|
||||
|
||||
#define _DEFAULT_IWMBT_FIRMWARE_PATH "/usr/share/firmware/intel"
|
||||
|
||||
int iwmbt_do_debug = 0;
|
||||
int iwmbt_do_info = 0;
|
||||
|
||||
struct iwmbt_devid {
|
||||
uint16_t product_id;
|
||||
uint16_t vendor_id;
|
||||
};
|
||||
|
||||
static struct iwmbt_devid iwmbt_list[] = {
|
||||
|
||||
/* Intel Wireless 8260/8265 and successors */
|
||||
{ .vendor_id = 0x8087, .product_id = 0x0a2b },
|
||||
{ .vendor_id = 0x8087, .product_id = 0x0aaa },
|
||||
{ .vendor_id = 0x8087, .product_id = 0x0025 },
|
||||
{ .vendor_id = 0x8087, .product_id = 0x0026 },
|
||||
{ .vendor_id = 0x8087, .product_id = 0x0029 },
|
||||
};
|
||||
|
||||
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)) {
|
||||
iwmbt_info("found 8260/8265");
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return (0);
|
||||
}
|
||||
|
||||
static libusb_device *
|
||||
iwmbt_find_device(libusb_context *ctx, int bus_id, int dev_id)
|
||||
{
|
||||
libusb_device **list, *dev = NULL, *found = NULL;
|
||||
struct libusb_device_descriptor d;
|
||||
ssize_t cnt, i;
|
||||
int r;
|
||||
|
||||
cnt = libusb_get_device_list(ctx, &list);
|
||||
if (cnt < 0) {
|
||||
iwmbt_err("libusb_get_device_list() failed: code %lld",
|
||||
(long long int) cnt);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan through USB device list.
|
||||
*/
|
||||
for (i = 0; i < cnt; i++) {
|
||||
dev = list[i];
|
||||
if (bus_id == libusb_get_bus_number(dev) &&
|
||||
dev_id == libusb_get_device_address(dev)) {
|
||||
/* Get the device descriptor for this device entry */
|
||||
r = libusb_get_device_descriptor(dev, &d);
|
||||
if (r != 0) {
|
||||
iwmbt_err("libusb_get_device_descriptor: %s",
|
||||
libusb_strerror(r));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Match on the vendor/product id */
|
||||
if (iwmbt_is_8260(&d)) {
|
||||
/*
|
||||
* Take a reference so it's not freed later on.
|
||||
*/
|
||||
found = libusb_ref_device(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
libusb_free_device_list(list, 1);
|
||||
return (found);
|
||||
}
|
||||
|
||||
static void
|
||||
iwmbt_dump_version(struct iwmbt_version *ver)
|
||||
{
|
||||
iwmbt_info("status 0x%02x", ver->status);
|
||||
iwmbt_info("hw_platform 0x%02x", ver->hw_platform);
|
||||
iwmbt_info("hw_variant 0x%02x", ver->hw_variant);
|
||||
iwmbt_info("hw_revision 0x%02x", ver->hw_revision);
|
||||
iwmbt_info("fw_variant 0x%02x", ver->fw_variant);
|
||||
iwmbt_info("fw_revision 0x%02x", ver->fw_revision);
|
||||
iwmbt_info("fw_build_num 0x%02x", ver->fw_build_num);
|
||||
iwmbt_info("fw_build_ww 0x%02x", ver->fw_build_ww);
|
||||
iwmbt_info("fw_build_yy 0x%02x", ver->fw_build_yy);
|
||||
iwmbt_info("fw_patch_num 0x%02x", ver->fw_patch_num);
|
||||
}
|
||||
|
||||
static void
|
||||
iwmbt_dump_boot_params(struct iwmbt_boot_params *params)
|
||||
{
|
||||
iwmbt_info("Device revision: %u", le16toh(params->dev_revid));
|
||||
iwmbt_info("Secure Boot: %s", params->secure_boot ? "on" : "off");
|
||||
iwmbt_info("OTP lock: %s", params->otp_lock ? "on" : "off");
|
||||
iwmbt_info("API lock: %s", params->api_lock ? "on" : "off");
|
||||
iwmbt_info("Debug lock: %s", params->debug_lock ? "on" : "off");
|
||||
iwmbt_info("Minimum firmware build %u week %u year %u",
|
||||
params->min_fw_build_nn,
|
||||
params->min_fw_build_cw,
|
||||
2000 + params->min_fw_build_yy);
|
||||
iwmbt_info("OTC BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
params->otp_bdaddr[5],
|
||||
params->otp_bdaddr[4],
|
||||
params->otp_bdaddr[3],
|
||||
params->otp_bdaddr[2],
|
||||
params->otp_bdaddr[1],
|
||||
params->otp_bdaddr[0]);
|
||||
}
|
||||
|
||||
static int
|
||||
iwmbt_init_firmware(libusb_device_handle *hdl, const char *firmware_path,
|
||||
uint32_t *boot_param)
|
||||
{
|
||||
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_load_fwfile(hdl, &fw, boot_param);
|
||||
if (ret < 0)
|
||||
iwmbt_debug("Loading firmware file failed");
|
||||
|
||||
/* free it */
|
||||
iwmbt_fw_free(&fw);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
iwmbt_init_ddc(libusb_device_handle *hdl, const char *ddc_path)
|
||||
{
|
||||
struct iwmbt_firmware ddc;
|
||||
int ret;
|
||||
|
||||
iwmbt_debug("loading %s", ddc_path);
|
||||
|
||||
/* Read in the DDC file */
|
||||
if (iwmbt_fw_read(&ddc, ddc_path) <= 0) {
|
||||
iwmbt_debug("iwmbt_fw_read() failed");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Load in the DDC file */
|
||||
ret = iwmbt_load_ddc(hdl, &ddc);
|
||||
if (ret < 0)
|
||||
iwmbt_debug("Loading DDC file failed");
|
||||
|
||||
/* free it */
|
||||
iwmbt_fw_free(&ddc);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse ugen name and extract device's bus and address
|
||||
*/
|
||||
|
||||
static int
|
||||
parse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr)
|
||||
{
|
||||
char *ep;
|
||||
|
||||
if (strncmp(ugen, "ugen", 4) != 0)
|
||||
return (-1);
|
||||
|
||||
*bus = (uint8_t) strtoul(ugen + 4, &ep, 10);
|
||||
if (*ep != '.')
|
||||
return (-1);
|
||||
|
||||
*addr = (uint8_t) strtoul(ep + 1, &ep, 10);
|
||||
if (*ep != '\0')
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: iwmbtfw (-D) -d ugenX.Y (-f firmware path) (-I)\n");
|
||||
fprintf(stderr, " -D: enable debugging\n");
|
||||
fprintf(stderr, " -d: device to operate upon\n");
|
||||
fprintf(stderr, " -f: firmware path, if not default\n");
|
||||
fprintf(stderr, " -I: enable informational output\n");
|
||||
exit(127);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
libusb_context *ctx = NULL;
|
||||
libusb_device *dev = NULL;
|
||||
libusb_device_handle *hdl = NULL;
|
||||
static struct iwmbt_version ver;
|
||||
static struct iwmbt_boot_params params;
|
||||
uint32_t boot_param;
|
||||
int r;
|
||||
uint8_t bus_id = 0, dev_id = 0;
|
||||
int devid_set = 0;
|
||||
int n;
|
||||
char *firmware_dir = NULL;
|
||||
char *firmware_path = NULL;
|
||||
int retcode = 1;
|
||||
|
||||
/* Parse command line arguments */
|
||||
while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) {
|
||||
switch (n) {
|
||||
case 'd': /* ugen device name */
|
||||
devid_set = 1;
|
||||
if (parse_ugen_name(optarg, &bus_id, &dev_id) < 0)
|
||||
usage();
|
||||
break;
|
||||
case 'D':
|
||||
iwmbt_do_debug = 1;
|
||||
break;
|
||||
case 'f': /* firmware dir */
|
||||
if (firmware_dir)
|
||||
free(firmware_dir);
|
||||
firmware_dir = strdup(optarg);
|
||||
break;
|
||||
case 'I':
|
||||
iwmbt_do_info = 1;
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
/* NOT REACHED */
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure the devid was given! */
|
||||
if (devid_set == 0) {
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
/* libusb setup */
|
||||
r = libusb_init(&ctx);
|
||||
if (r != 0) {
|
||||
iwmbt_err("libusb_init failed: code %d", r);
|
||||
exit(127);
|
||||
}
|
||||
|
||||
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);
|
||||
if (dev == NULL) {
|
||||
iwmbt_err("device not found");
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
/* XXX enforce that bInterfaceNumber is 0 */
|
||||
|
||||
/* XXX enforce the device/product id if they're non-zero */
|
||||
|
||||
/* Grab device handle */
|
||||
r = libusb_open(dev, &hdl);
|
||||
if (r != 0) {
|
||||
iwmbt_err("libusb_open() failed: code %d", r);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
/* Check if ng_ubt is attached */
|
||||
r = libusb_kernel_driver_active(hdl, 0);
|
||||
if (r < 0) {
|
||||
iwmbt_err("libusb_kernel_driver_active() failed: code %d", r);
|
||||
goto shutdown;
|
||||
}
|
||||
if (r > 0) {
|
||||
iwmbt_info("Firmware has already been downloaded");
|
||||
retcode = 0;
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
/* Get Intel version */
|
||||
r = iwmbt_get_version(hdl, &ver);
|
||||
if (r < 0) {
|
||||
iwmbt_debug("iwmbt_get_version() failedL code %d", r);
|
||||
goto shutdown;
|
||||
}
|
||||
iwmbt_dump_version(&ver);
|
||||
iwmbt_debug("fw_variant=0x%02x", (int) ver.fw_variant);
|
||||
|
||||
/* fw_variant = 0x06 bootloader mode / 0x23 operational mode */
|
||||
if (ver.fw_variant == 0x23) {
|
||||
iwmbt_info("Firmware has already been downloaded");
|
||||
retcode = 0;
|
||||
goto reset;
|
||||
}
|
||||
|
||||
if (ver.fw_variant != 0x06){
|
||||
iwmbt_err("unknown fw_variant 0x%02x", (int) ver.fw_variant);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
/* Read Intel Secure Boot Params */
|
||||
r = iwmbt_get_boot_params(hdl, ¶ms);
|
||||
if (r < 0) {
|
||||
iwmbt_debug("iwmbt_get_boot_params() failed!");
|
||||
goto shutdown;
|
||||
}
|
||||
iwmbt_dump_boot_params(¶ms);
|
||||
|
||||
/* Check if firmware fragments are ACKed with a cmd complete event */
|
||||
if (params.limited_cce != 0x00) {
|
||||
iwmbt_err("Unsupported Intel firmware loading method (%u)",
|
||||
params.limited_cce);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
/* Default the firmware path */
|
||||
if (firmware_dir == NULL)
|
||||
firmware_dir = strdup(_DEFAULT_IWMBT_FIRMWARE_PATH);
|
||||
|
||||
firmware_path = iwmbt_get_fwname(&ver, ¶ms, firmware_dir, "sfi");
|
||||
if (firmware_path == NULL)
|
||||
goto shutdown;
|
||||
|
||||
iwmbt_debug("firmware_path = %s", firmware_path);
|
||||
|
||||
/* Download firmware and parse it for magic Intel Reset parameter */
|
||||
r = iwmbt_init_firmware(hdl, firmware_path, &boot_param);
|
||||
free(firmware_path);
|
||||
if (r < 0)
|
||||
goto shutdown;
|
||||
|
||||
iwmbt_info("Firmware download complete");
|
||||
|
||||
r = iwmbt_intel_reset(hdl, boot_param);
|
||||
if (r < 0) {
|
||||
iwmbt_debug("iwmbt_intel_reset() failed!");
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
iwmbt_info("Firmware operational");
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Apply the device configuration (DDC) parameters */
|
||||
firmware_path = iwmbt_get_fwname(&ver, ¶ms, firmware_dir, "ddc");
|
||||
iwmbt_debug("ddc_path = %s", firmware_path);
|
||||
if (firmware_path != NULL) {
|
||||
r = iwmbt_init_ddc(hdl, firmware_path);
|
||||
if (r == 0)
|
||||
iwmbt_info("DDC download complete");
|
||||
free(firmware_path);
|
||||
}
|
||||
|
||||
/* Set Intel Event mask */
|
||||
r = iwmbt_set_event_mask(hdl);
|
||||
if (r == 0)
|
||||
iwmbt_info("Intel Event Mask is set");
|
||||
|
||||
reset:
|
||||
|
||||
/* Ask kernel driver to probe and attach device again */
|
||||
r = libusb_reset_device(hdl);
|
||||
if (r != 0)
|
||||
iwmbt_err("libusb_reset_device() failed: %s",
|
||||
libusb_strerror(r));
|
||||
|
||||
shutdown:
|
||||
|
||||
/* Shutdown */
|
||||
|
||||
if (hdl != NULL)
|
||||
libusb_close(hdl);
|
||||
|
||||
if (dev != NULL)
|
||||
libusb_unref_device(dev);
|
||||
|
||||
if (ctx != NULL)
|
||||
libusb_exit(ctx);
|
||||
|
||||
if (retcode == 0)
|
||||
iwmbt_info("Firmware download is succesful!");
|
||||
else
|
||||
iwmbt_err("Firmware download failed!");
|
||||
|
||||
return (retcode);
|
||||
}
|
Loading…
Reference in New Issue
Block a user