diff --git a/libexec/rc/rc.d/bluetooth b/libexec/rc/rc.d/bluetooth --- a/libexec/rc/rc.d/bluetooth +++ b/libexec/rc/rc.d/bluetooth @@ -127,8 +127,17 @@ > /dev/null 2>&1 || return 1 # Initilalize HCI node - ${hccontrol} -n ${dev}hci reset \ - > /dev/null 2>&1 || return 1 + for loop in 1 2 3 + do + ${hccontrol} -n ${dev}hci reset \ + > /dev/null 2>1 && break + if [ ${loop} = 3 ] + then + warn Reset failed three times, giving up. + return 1 + fi + warn Reset failed, retrying. + done ${hccontrol} -n ${dev}hci read_bd_addr \ > /dev/null 2>&1 || return 1 diff --git a/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_intel.c b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_intel.c --- a/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_intel.c +++ b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_intel.c @@ -59,6 +59,7 @@ enum { UBT_INTEL_DEVICE_7260, UBT_INTEL_DEVICE_8260, + UBT_INTEL_DEVICE_TLV, }; struct ubt_intel_version_rp { @@ -74,6 +75,11 @@ uint8_t fw_patch_num; } __attribute__ ((packed)); +struct ubt_intel_tlv_field { + uint8_t type; + uint8_t len; + uint8_t val[]; +} __attribute__ ((packed)); static device_probe_t ubt_intel_probe; /* @@ -93,6 +99,8 @@ { USB_VPI(USB_VENDOR_INTEL2, 0x0025, UBT_INTEL_DEVICE_8260) }, { USB_VPI(USB_VENDOR_INTEL2, 0x0026, UBT_INTEL_DEVICE_8260) }, { USB_VPI(USB_VENDOR_INTEL2, 0x0029, UBT_INTEL_DEVICE_8260) }, + /* Intel AX210 */ + { USB_VPI(USB_VENDOR_INTEL2, 0x0032, UBT_INTEL_DEVICE_TLV) }, }; /* @@ -128,6 +136,50 @@ return (error); } +static bool +ubt_intel_tlv_is_firmware_running(struct usb_device *dev) +{ +#define TLV_BUFFER_SIZE 252 + uint8_t evtbuf[offsetof(struct ubt_hci_event_command_compl, data) + TLV_BUFFER_SIZE] = {0}; + struct ubt_hci_event_command_compl *evt = (struct ubt_hci_event_command_compl *)evtbuf; + uint8_t cmdbuf[offsetof(struct ubt_hci_cmd, data) + 1]; + struct ubt_hci_cmd *cmd = (struct ubt_hci_cmd *)cmdbuf; + usb_error_t error; + struct ubt_intel_tlv_field *f; + size_t pos; + + cmd->opcode = htole16(0xfc05), + cmd->length = 1, + cmd->data[0] = 0xff; + + evt->header.length = TLV_BUFFER_SIZE + UBT_HCI_EVENT_COMPL_HEAD_SIZE; + + error = ubt_do_hci_request(dev, cmd, evt, UBT_INTEL_HCICMD_TIMEOUT); + if (error != USB_ERR_NORMAL_COMPLETION) + return false; + + if (evt->header.event != NG_HCI_EVENT_COMMAND_COMPL) + return false; + + pos = offsetof(struct ubt_hci_event_command_compl, data); + + while ((pos + 2) < evt->header.length) { + f = (struct ubt_intel_tlv_field *) &evt->data[pos]; + if (pos + f->len >= evt->header.length) + break; + + if (f->type == 0x1c && f->len == 1) { + if (f->val[0] == 0x03) + return true; + return false; + } + + pos += offsetof(struct ubt_intel_tlv_field, val); + pos += f->len; + } + return false; +} + /* * Probe for a Intel Wireless Bluetooth device. */ @@ -193,6 +245,14 @@ return (ENXIO); break; + case UBT_INTEL_DEVICE_TLV: + /* + * Find if the device is in bootloader mode or is running + * operational firmware. + */ + if (!ubt_intel_tlv_is_firmware_running(uaa->device)) + return ENXIO; + break; default: KASSERT(0 == 1, ("Unknown DRIVER_INFO")); } diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h @@ -42,6 +42,37 @@ uint8_t fw_patch_num; } __attribute__ ((packed)); +struct iwmbt_tlv { + uint8_t status; +#define IWMBT_TLV_MAX 4096 + uint8_t tlv_data[IWMBT_TLV_MAX]; + /* Must be at end (not part of transferred data) */ + uint16_t tlv_len; +} __attribute__ ((packed)); + +struct iwmbt_tlv_field { + uint8_t type; + uint8_t len; + uint8_t val[]; +} __attribute__ ((packed)); + +enum types { + IWMBT_TLV_TYPE_FNAME_FIRST = 0x10, + IWMBT_TLV_TYPE_FNAME_SECOND = 0x11, + IWMBT_TLV_TYPE_HW_VERSION = 0x12, + IWMBT_TLV_TYPE_RUNNING = 0x1e, + IWMBT_TLV_TYPE_SIGTYPE = 0x2f, +}; + +struct iwmbt_parsed_tlv { + uint32_t parse_state; + uint32_t fn_first; + uint32_t fn_second; + uint32_t hw_version; + uint8_t running; + uint8_t sigtype; +}; + struct iwmbt_boot_params { uint8_t status; uint8_t otp_format; @@ -73,5 +104,8 @@ extern char *iwmbt_get_fwname(struct iwmbt_version *ver, struct iwmbt_boot_params *params, const char *prefix, const char *suffix); +extern char *iwmbt_get_fwname_tlv(const char *prefix, + struct iwmbt_parsed_tlv *ptlv, + const char *suffix); #endif diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c @@ -170,3 +170,23 @@ return (fwname); } + +static uint16_t +iwmbt_mangle_tlv_value(uint32_t val) +{ + uint16_t ret = ((val & 0xfff) << 4) | ((val & 0x0f000000) >> 24); + return bswap16(ret); +} + +char * +iwmbt_get_fwname_tlv(const char *prefix, struct iwmbt_parsed_tlv *ptlv, const char *suffix) +{ + char *fwname; + + asprintf(&fwname, "%s/ibt-%04x-%04x.%s", + prefix, + iwmbt_mangle_tlv_value(ptlv->fn_first), + iwmbt_mangle_tlv_value(ptlv->fn_second), + suffix); + return fwname; +} diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h @@ -27,6 +27,8 @@ #ifndef __IWMBT_HW_H__ #define __IWMBT_HW_H__ +#include + /* USB control request (HCI command) structure */ struct iwmbt_hci_cmd { uint16_t opcode; @@ -74,16 +76,18 @@ 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); + const struct iwmbt_firmware *fw, uint32_t *boot_param, bool is_tlv, uint8_t sigtype); 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_tlv(struct libusb_device_handle *hdl, + struct iwmbt_tlv *tlv); 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); + uint32_t boot_param, bool hard); 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); diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c @@ -269,7 +269,7 @@ int iwmbt_load_fwfile(struct libusb_device_handle *hdl, - const struct iwmbt_firmware *fw, uint32_t *boot_param) + const struct iwmbt_firmware *fw, uint32_t *boot_param, bool is_tlv, uint8_t sigtype) { int ready = 0, sent = 0; int ret, transferred; @@ -300,15 +300,26 @@ 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"); + if (sigtype == 0 || !is_tlv) { + 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; + /* skip 4 bytes */ + sent += 4; - IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1"); - IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2"); + IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1"); + IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2"); + + if (is_tlv) + sent += 320; + } else { + sent += 644; + + IWMBT_SEND_FRAGMENT(0x00, 128, "CSS segment"); + IWMBT_SEND_FRAGMENT(0x03, 96, "public key?"); + IWMBT_SEND_FRAGMENT(0x02, 96, "signature?"); + } /* * Send firmware chunks. Chunk len must be 4 byte aligned. @@ -460,6 +471,46 @@ return (0); } +int +iwmbt_get_tlv(struct libusb_device_handle *hdl, + struct iwmbt_tlv *tlv) +{ + int ret, transferred; + struct iwmbt_hci_event_cmd_compl*event; + uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_tlv) - sizeof(tlv->tlv_len)]; + uint8_t cmdbuf[offsetof(struct iwmbt_hci_cmd, data) + 1]; + struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *) cmdbuf; + cmd->opcode = htole16(0xfc05), + cmd->length = 1, + cmd->data[0] = 0xff; + + memset(buf, 0, sizeof(buf)); + + ret = iwmbt_hci_command(hdl, + cmd, + buf, + sizeof(buf), + &transferred, + IWMBT_HCI_CMD_TIMEOUT); + + if (ret < 0 || transferred < 1) { + iwmbt_debug("Can't get TLV: : code=%d, size=%d", + ret, + transferred); + return (-1); + } + tlv->tlv_len = transferred - offsetof(struct iwmbt_tlv, tlv_data) - offsetof(struct iwmbt_hci_event_cmd_compl, data); + + event = (struct iwmbt_hci_event_cmd_compl *)buf; + if (event->data[0]) { + iwmbt_debug("Bad status: : status=%02x", event->data[0]); + return -1; + } + memcpy(tlv, event->data, transferred); + + return (0); +} + int iwmbt_get_boot_params(struct libusb_device_handle *hdl, struct iwmbt_boot_params *params) @@ -495,7 +546,7 @@ } int -iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param) +iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param, bool hard) { int ret, transferred = 0; struct iwmbt_hci_event *event; @@ -504,6 +555,17 @@ .length = 8, .data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 }, }; + if (hard) { + cmd.data[0] = 0x01; + cmd.data[1] = 0x01; + cmd.data[2] = 0x01; + cmd.data[3] = 0x00; + } else { + cmd.data[0] = 0x00; + cmd.data[1] = 0x00; + cmd.data[2] = 0x00; + cmd.data[3] = 0x01; + } uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE]; le32enc(cmd.data + 4, boot_param); @@ -514,20 +576,30 @@ buf, sizeof(buf), &transferred, - IWMBT_HCI_CMD_TIMEOUT); + IWMBT_LOADCMPL_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); - } + if (hard) { + if (ret < 0) { + iwmbt_debug("Intel Hard Reset command failed: code=%d, size=%d", + ret, + transferred); + return (ret); + } + usleep(150000); + } else { + 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); + /* 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. event: %02x, data[0]: %02x", event->header.event, event->data[0]); + return (-1); + } } return (0); diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8 b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8 --- a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8 +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8 @@ -28,7 +28,7 @@ .Os .Sh NAME .Nm iwmbtfw -.Nd firmware download utility for Intel Wireless 7260/8260/8265 chip based Bluetooth +.Nd firmware download utility for Intel Wireless 7260/8260/8265/AX210 chip based Bluetooth USB devices .Sh SYNOPSIS .Nm @@ -45,7 +45,7 @@ .Pp This utility will .Em only -work with Intel Wireless 7260/8260/8265 chip based Bluetooth USB devices and some of +work with Intel Wireless 7260/8260/8265/AX210 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 diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf --- a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf @@ -6,6 +6,6 @@ match "subsystem" "DEVICE"; match "type" "ATTACH"; match "vendor" "0x8087"; - match "product" "(0x07dc|0x0a2a|0x0aa7|0x0a2b|0x0aaa|0x0025|0x0026|0x0029)"; + match "product" "(0x07dc|0x0a2a|0x0aa7|0x0a2b|0x0aaa|0x0025|0x0026|0x0029|0x0032)"; action "/usr/sbin/iwmbtfw -d $cdev -f /usr/local/share/iwmbt-firmware"; }; diff --git a/usr.sbin/bluetooth/iwmbtfw/main.c b/usr.sbin/bluetooth/iwmbtfw/main.c --- a/usr.sbin/bluetooth/iwmbtfw/main.c +++ b/usr.sbin/bluetooth/iwmbtfw/main.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -73,6 +74,12 @@ { .vendor_id = 0x8087, .product_id = 0x0029 }, }; +static struct iwmbt_devid iwmbt_list_tlv[] = { + + { .vendor_id = 0x8087, .product_id = 0x0032 }, + +}; + static int iwmbt_is_7260(struct libusb_device_descriptor *d) { @@ -109,9 +116,33 @@ return (0); } +static int +iwmbt_is_tlv(struct libusb_device_descriptor *d) +{ + int i; + + /* Search looking for whether it's a TLV device (ie: AX210) */ + for (i = 0; i < (int) nitems(iwmbt_list_tlv); i++) { + if ((iwmbt_list_tlv[i].product_id == d->idProduct) && + (iwmbt_list_tlv[i].vendor_id == d->idVendor)) { + iwmbt_info("found TLV"); + return (1); + } + } + + /* Not found */ + return (0); +} + +enum use_method { + IWMBT_METHOD_OLD, + IWMBT_METHOD_8260, + IWMBT_METHOD_TLV, +}; + static libusb_device * iwmbt_find_device(libusb_context *ctx, int bus_id, int dev_id, - int *iwmbt_use_old_method) + enum use_method *iwmbt_use_method) { libusb_device **list, *dev = NULL, *found = NULL; struct libusb_device_descriptor d; @@ -146,7 +177,7 @@ * Take a reference so it's not freed later on. */ found = libusb_ref_device(dev); - *iwmbt_use_old_method = 1; + *iwmbt_use_method = IWMBT_METHOD_OLD; break; } else if (iwmbt_is_8260(&d)) { @@ -154,7 +185,15 @@ * Take a reference so it's not freed later on. */ found = libusb_ref_device(dev); - *iwmbt_use_old_method = 0; + *iwmbt_use_method = IWMBT_METHOD_8260; + break; + } else + if (iwmbt_is_tlv(&d)) { + /* + * Take a reference so it's not freed later on. + */ + found = libusb_ref_device(dev); + *iwmbt_use_method = IWMBT_METHOD_TLV; break; } } @@ -200,6 +239,117 @@ params->otp_bdaddr[0]); } +static int +iwmbt_tlv_foreach(struct iwmbt_tlv *tlv, int (*cb)(struct iwmbt_tlv_field *, void *), void *cbdata) +{ + struct iwmbt_tlv_field *f; + size_t pos = 0; + int rc; + + while ((pos + 2) < tlv->tlv_len) { + f = (struct iwmbt_tlv_field *) &tlv->tlv_data[pos]; + if (pos + f->len >= tlv->tlv_len) + break; + rc = cb(f, cbdata); + if (rc) + return rc; + pos += offsetof(struct iwmbt_tlv_field, val); + pos += f->len; + } + return 0; +} + +static int +iwmbt_dump_one_tlv(struct iwmbt_tlv_field *f, void *cbdata __unused) +{ + uint32_t tmp; + + switch (f->len) { + case 1: + iwmbt_info("TLV: 0x%02x = 0x%02x", f->type, f->val[0]); + break; + case 2: + memcpy(&tmp, f->val, f->len); + iwmbt_info("TLV: 0x%02x = 0x%04x", f->type, le16toh(*(uint16_t *)&tmp)); + break; + case 4: + memcpy(&tmp, f->val, f->len); + iwmbt_info("TLV: 0x%02x = 0x%08x", f->type, le32toh(*(uint16_t *)&tmp)); + break; + default: + iwmbt_info("TLV: 0x%02x has unhandled length of %d", f->type, f->len); + break; + } + return 0; +} + +static void +iwmbt_dump_tlv(struct iwmbt_tlv *tlv) +{ + iwmbt_tlv_foreach(tlv, iwmbt_dump_one_tlv, NULL); +} + +static int +iwmbt_parse_one_tlv(struct iwmbt_tlv_field *f, void *cbdata) +{ + struct iwmbt_parsed_tlv *parsed = cbdata; + + switch (f->type) { + case IWMBT_TLV_TYPE_FNAME_FIRST: + if (f->len != sizeof(parsed->fn_first)) { + iwmbt_err("Invalid fn_first length %d (expected %zu)", f->len, sizeof(parsed->fn_first)); + return -1; + } + memcpy(&parsed->fn_first, f->val, f->len); + parsed->parse_state |= 1; + break; + case IWMBT_TLV_TYPE_FNAME_SECOND: + if (f->len != sizeof(parsed->fn_second)) { + iwmbt_err("Invalid fn_second length %d (expected %zu)", f->len, sizeof(parsed->fn_second)); + return -1; + } + memcpy(&parsed->fn_second, f->val, f->len); + parsed->parse_state |= 2; + break; + case IWMBT_TLV_TYPE_HW_VERSION: + if (f->len != sizeof(parsed->hw_version)) { + iwmbt_err("Invalid hw_version length %d (expected %zu)", f->len, sizeof(parsed->fn_second)); + return -1; + } + memcpy(&parsed->hw_version, f->val, f->len); + parsed->parse_state |= 4; + break; + case IWMBT_TLV_TYPE_RUNNING: + if (f->len != sizeof(parsed->running)) { + iwmbt_err("Invalid running length %d (expected %zu)", f->len, sizeof(parsed->fn_second)); + return -1; + } + memcpy(&parsed->running, f->val, f->len); + parsed->parse_state |= 8; + break; + case IWMBT_TLV_TYPE_SIGTYPE: + if (f->len != sizeof(parsed->sigtype)) { + iwmbt_err("Invalid sigtype length %d (expected %zu)", f->len, sizeof(parsed->fn_second)); + return -1; + } + memcpy(&parsed->sigtype, f->val, f->len); + parsed->parse_state |= 16; + break; + } + return 0; +} + +static int +iwmbt_parse_tlv(struct iwmbt_tlv *tlv, struct iwmbt_parsed_tlv *parsed) +{ + parsed->parse_state = 0; + if (iwmbt_tlv_foreach(tlv, iwmbt_parse_one_tlv, parsed)) + return -1; + if (parsed->parse_state != 31) + return -1; + return 0; +} + static int iwmbt_patch_firmware(libusb_device_handle *hdl, const char *firmware_path) { @@ -227,7 +377,7 @@ static int iwmbt_init_firmware(libusb_device_handle *hdl, const char *firmware_path, - uint32_t *boot_param) + uint32_t *boot_param, bool is_tlv, uint8_t sigtype) { struct iwmbt_firmware fw; int ret; @@ -241,7 +391,7 @@ } /* Load in the firmware */ - ret = iwmbt_load_fwfile(hdl, &fw, boot_param); + ret = iwmbt_load_fwfile(hdl, &fw, boot_param, is_tlv, sigtype); if (ret < 0) iwmbt_debug("Loading firmware file failed"); @@ -317,6 +467,8 @@ libusb_context *ctx = NULL; libusb_device *dev = NULL; libusb_device_handle *hdl = NULL; + struct iwmbt_tlv tlv; + struct iwmbt_parsed_tlv ptlv; static struct iwmbt_version ver; static struct iwmbt_boot_params params; uint32_t boot_param; @@ -327,7 +479,7 @@ char *firmware_dir = NULL; char *firmware_path = NULL; int retcode = 1; - int iwmbt_use_old_method = 0; + enum use_method iwmbt_use_method = IWMBT_METHOD_8260; /* Parse command line arguments */ while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) { @@ -372,7 +524,7 @@ 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, &iwmbt_use_old_method); + dev = iwmbt_find_device(ctx, bus_id, dev_id, &iwmbt_use_method); if (dev == NULL) { iwmbt_err("device not found"); goto shutdown; @@ -402,15 +554,29 @@ } /* Get Intel version */ - r = iwmbt_get_version(hdl, &ver); + if (iwmbt_use_method == IWMBT_METHOD_TLV) + r = iwmbt_get_tlv(hdl, &tlv); + else + r = iwmbt_get_version(hdl, &ver); if (r < 0) { iwmbt_debug("iwmbt_get_version() failed code %d", r); goto shutdown; } + + /* Parse TLV and extract ver stuff from it */ + if (iwmbt_use_method == IWMBT_METHOD_TLV) { + /* TODO: Parse in get? */ + iwmbt_dump_tlv(&tlv); + if (iwmbt_parse_tlv(&tlv, &ptlv)) + goto shutdown; + ver.hw_platform = (ptlv.hw_version & 0x0000ff00) >> 8; + ver.hw_variant = (ptlv.hw_version & 0x003f0000) >> 16; + } + iwmbt_dump_version(&ver); iwmbt_debug("fw_variant=0x%02x", (int) ver.fw_variant); - if (iwmbt_use_old_method) { + if (iwmbt_use_method == IWMBT_METHOD_OLD) { /* fw_patch_num = >0 operational mode */ if (ver.fw_patch_num > 0x00) { @@ -469,7 +635,8 @@ iwmbt_info("Intel Event Mask is set"); (void)iwmbt_exit_manufacturer(hdl, 0x00); - } else { + } else + if (iwmbt_use_method == IWMBT_METHOD_8260) { /* fw_variant = 0x06 bootloader mode / 0x23 operational mode */ if (ver.fw_variant == 0x23) { @@ -509,14 +676,14 @@ 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); + r = iwmbt_init_firmware(hdl, firmware_path, &boot_param, false, 0); free(firmware_path); if (r < 0) goto shutdown; iwmbt_info("Firmware download complete"); - r = iwmbt_intel_reset(hdl, boot_param); + r = iwmbt_intel_reset(hdl, boot_param, false); if (r < 0) { iwmbt_debug("iwmbt_intel_reset() failed!"); goto shutdown; @@ -542,6 +709,67 @@ free(firmware_path); } + /* Set Intel Event mask */ + r = iwmbt_set_event_mask(hdl); + if (r == 0) + iwmbt_info("Intel Event Mask is set"); + } else { + + switch (ver.hw_variant) { + case 0x17: + break; + default: + iwmbt_err("unknown hw_variant 0x%02x", (int) ver.hw_variant); + goto shutdown; + } + + if (ptlv.running != 0x01) { + /* Reset to bootloader */ + // TODO: Possibly always exit here... to reset: + r = iwmbt_intel_reset(hdl, 0, true); + if (r < 0) { + iwmbt_debug("iwmbt_intel_reset() failed!"); + goto shutdown; + } + } + + firmware_path = iwmbt_get_fwname_tlv(firmware_dir, &ptlv, "sfi"); + 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, true, ptlv.sigtype); + free(firmware_path); + if (r < 0) + goto shutdown; + + iwmbt_info("Firmware download complete"); + + r = iwmbt_intel_reset(hdl, boot_param, false); + 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_tlv(firmware_dir, &ptlv, "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)