Path: blob/main/usr.sbin/bluetooth/iwmbtfw/main.c
105642 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2013 Adrian Chadd <[email protected]>4* Copyright (c) 2019 Vladimir Kondratyev <[email protected]>5* Copyright (c) 2023 Future Crew LLC.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions9* are met:10* 1. Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829#include <sys/param.h>30#include <sys/stat.h>31#include <sys/endian.h>3233#include <err.h>34#include <errno.h>35#include <fcntl.h>36#include <libgen.h>37#include <stdio.h>38#include <stdlib.h>39#include <string.h>40#include <unistd.h>4142#include <libusb.h>4344#include "iwmbt_fw.h"45#include "iwmbt_hw.h"46#include "iwmbt_dbg.h"4748#define _DEFAULT_IWMBT_FIRMWARE_PATH "/usr/share/firmware/intel"4950int iwmbt_do_debug = 0;51int iwmbt_do_info = 0;5253enum iwmbt_device {54IWMBT_DEVICE_UNKNOWN,55IWMBT_DEVICE_7260,56IWMBT_DEVICE_8260,57IWMBT_DEVICE_9260,58};5960struct iwmbt_devid {61uint16_t product_id;62uint16_t vendor_id;63enum iwmbt_device device;64};6566static struct iwmbt_devid iwmbt_list[] = {6768/* Intel Wireless 7260/7265 and successors */69{ .vendor_id = 0x8087, .product_id = 0x07dc, .device = IWMBT_DEVICE_7260 },70{ .vendor_id = 0x8087, .product_id = 0x0a2a, .device = IWMBT_DEVICE_7260 },71{ .vendor_id = 0x8087, .product_id = 0x0aa7, .device = IWMBT_DEVICE_7260 },7273/* Intel Wireless 8260/8265 and successors */74{ .vendor_id = 0x8087, .product_id = 0x0a2b, .device = IWMBT_DEVICE_8260 },75{ .vendor_id = 0x8087, .product_id = 0x0aaa, .device = IWMBT_DEVICE_8260 },76{ .vendor_id = 0x8087, .product_id = 0x0025, .device = IWMBT_DEVICE_8260 },77{ .vendor_id = 0x8087, .product_id = 0x0026, .device = IWMBT_DEVICE_8260 },78{ .vendor_id = 0x8087, .product_id = 0x0029, .device = IWMBT_DEVICE_8260 },7980/* Intel Wireless 9260/9560 and successors */81{ .vendor_id = 0x8087, .product_id = 0x0032, .device = IWMBT_DEVICE_9260 },82{ .vendor_id = 0x8087, .product_id = 0x0033, .device = IWMBT_DEVICE_9260 },83};8485static enum iwmbt_device86iwmbt_is_supported(struct libusb_device_descriptor *d)87{88int i;8990/* Search looking for whether it's an 7260/7265 */91for (i = 0; i < (int) nitems(iwmbt_list); i++) {92if ((iwmbt_list[i].product_id == d->idProduct) &&93(iwmbt_list[i].vendor_id == d->idVendor)) {94iwmbt_info("found iwmbtfw compatible");95return (iwmbt_list[i].device);96}97}9899/* Not found */100return (IWMBT_DEVICE_UNKNOWN);101}102103static libusb_device *104iwmbt_find_device(libusb_context *ctx, int bus_id, int dev_id,105enum iwmbt_device *iwmbt_device)106{107libusb_device **list, *dev = NULL, *found = NULL;108struct libusb_device_descriptor d;109enum iwmbt_device device;110ssize_t cnt, i;111int r;112113cnt = libusb_get_device_list(ctx, &list);114if (cnt < 0) {115iwmbt_err("libusb_get_device_list() failed: code %lld",116(long long int) cnt);117return (NULL);118}119120/*121* Scan through USB device list.122*/123for (i = 0; i < cnt; i++) {124dev = list[i];125if (bus_id == libusb_get_bus_number(dev) &&126dev_id == libusb_get_device_address(dev)) {127/* Get the device descriptor for this device entry */128r = libusb_get_device_descriptor(dev, &d);129if (r != 0) {130iwmbt_err("libusb_get_device_descriptor: %s",131libusb_strerror(r));132break;133}134135/* Match on the vendor/product id */136device = iwmbt_is_supported(&d);137if (device != IWMBT_DEVICE_UNKNOWN) {138/*139* Take a reference so it's not freed later on.140*/141found = libusb_ref_device(dev);142*iwmbt_device = device;143break;144}145}146}147148libusb_free_device_list(list, 1);149return (found);150}151152static void153iwmbt_dump_version(struct iwmbt_version *ver)154{155iwmbt_info("status 0x%02x", ver->status);156iwmbt_info("hw_platform 0x%02x", ver->hw_platform);157iwmbt_info("hw_variant 0x%02x", ver->hw_variant);158iwmbt_info("hw_revision 0x%02x", ver->hw_revision);159iwmbt_info("fw_variant 0x%02x", ver->fw_variant);160iwmbt_info("fw_revision 0x%02x", ver->fw_revision);161iwmbt_info("fw_build_num 0x%02x", ver->fw_build_num);162iwmbt_info("fw_build_ww 0x%02x", ver->fw_build_ww);163iwmbt_info("fw_build_yy 0x%02x", ver->fw_build_yy);164iwmbt_info("fw_patch_num 0x%02x", ver->fw_patch_num);165}166167static void168iwmbt_dump_boot_params(struct iwmbt_boot_params *params)169{170iwmbt_info("Device revision: %u", le16toh(params->dev_revid));171iwmbt_info("Secure Boot: %s", params->secure_boot ? "on" : "off");172iwmbt_info("OTP lock: %s", params->otp_lock ? "on" : "off");173iwmbt_info("API lock: %s", params->api_lock ? "on" : "off");174iwmbt_info("Debug lock: %s", params->debug_lock ? "on" : "off");175iwmbt_info("Minimum firmware build %u week %u year %u",176params->min_fw_build_nn,177params->min_fw_build_cw,1782000 + params->min_fw_build_yy);179iwmbt_info("OTC BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x",180params->otp_bdaddr[5],181params->otp_bdaddr[4],182params->otp_bdaddr[3],183params->otp_bdaddr[2],184params->otp_bdaddr[1],185params->otp_bdaddr[0]);186}187188static void189iwmbt_dump_version_tlv(struct iwmbt_version_tlv *ver)190{191iwmbt_info("cnvi_top 0x%08x", ver->cnvi_top);192iwmbt_info("cnvr_top 0x%08x", ver->cnvr_top);193iwmbt_info("cnvi_bt 0x%08x", ver->cnvi_bt);194iwmbt_info("cnvr_bt 0x%08x", ver->cnvr_bt);195iwmbt_info("dev_rev_id 0x%04x", ver->dev_rev_id);196iwmbt_info("img_type 0x%02x", ver->img_type);197iwmbt_info("timestamp 0x%04x", ver->timestamp);198iwmbt_info("build_type 0x%02x", ver->build_type);199iwmbt_info("build_num 0x%08x", ver->build_num);200iwmbt_info("Secure Boot: %s", ver->secure_boot ? "on" : "off");201iwmbt_info("OTP lock: %s", ver->otp_lock ? "on" : "off");202iwmbt_info("API lock: %s", ver->api_lock ? "on" : "off");203iwmbt_info("Debug lock: %s", ver->debug_lock ? "on" : "off");204iwmbt_info("Minimum firmware build %u week %u year %u",205ver->min_fw_build_nn,206ver->min_fw_build_cw,2072000 + ver->min_fw_build_yy);208iwmbt_info("limited_cce 0x%02x", ver->limited_cce);209iwmbt_info("sbe_type 0x%02x", ver->sbe_type);210iwmbt_info("OTC BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x",211ver->otp_bd_addr.b[5],212ver->otp_bd_addr.b[4],213ver->otp_bd_addr.b[3],214ver->otp_bd_addr.b[2],215ver->otp_bd_addr.b[1],216ver->otp_bd_addr.b[0]);217if (ver->img_type == TLV_IMG_TYPE_BOOTLOADER ||218ver->img_type == TLV_IMG_TYPE_OPERATIONAL)219iwmbt_info("%s timestamp %u.%u buildtype %u build %u",220(ver->img_type == TLV_IMG_TYPE_BOOTLOADER ?221"Bootloader" : "Firmware"),2222000 + (ver->timestamp >> 8),223ver->timestamp & 0xff,224ver->build_type,225ver->build_num);226}227228229static int230iwmbt_init_firmware(libusb_device_handle *hdl, const char *firmware_path,231uint32_t *boot_param, uint8_t hw_variant, uint8_t sbe_type)232{233struct iwmbt_firmware fw;234int header_len, ret = -1;235236iwmbt_debug("loading %s", firmware_path);237238/* Read in the firmware */239if (iwmbt_fw_read(&fw, firmware_path) <= 0) {240iwmbt_debug("iwmbt_fw_read() failed");241return (-1);242}243244iwmbt_debug("Firmware file size=%d", fw.len);245246if (hw_variant <= 0x14) {247/*248* Hardware variants 0x0b, 0x0c, 0x11 - 0x14 .sfi file have249* a RSA header of 644 bytes followed by Command Buffer.250*/251header_len = RSA_HEADER_LEN;252if (fw.len < header_len) {253iwmbt_err("Invalid size of firmware file (%d)", fw.len);254ret = -1;255goto exit;256}257258/* Check if the CSS Header version is RSA(0x00010000) */259if (le32dec(fw.buf + CSS_HEADER_OFFSET) != 0x00010000) {260iwmbt_err("Invalid CSS Header version");261ret = -1;262goto exit;263}264265/* Only RSA secure boot engine supported */266if (sbe_type != 0x00) {267iwmbt_err("Invalid SBE type for hardware variant (%d)",268hw_variant);269ret = -1;270goto exit;271}272273} else if (hw_variant >= 0x17) {274/*275* Hardware variants 0x17, 0x18 onwards support both RSA and276* ECDSA secure boot engine. As a result, the corresponding sfi277* file will have RSA header of 644, ECDSA header of 320 bytes278* followed by Command Buffer.279*/280header_len = ECDSA_OFFSET + ECDSA_HEADER_LEN;281if (fw.len < header_len) {282iwmbt_err("Invalid size of firmware file (%d)", fw.len);283ret = -1;284goto exit;285}286287/* Check if CSS header for ECDSA follows the RSA header */288if (fw.buf[ECDSA_OFFSET] != 0x06) {289ret = -1;290goto exit;291}292293/* Check if the CSS Header version is ECDSA(0x00020000) */294if (le32dec(fw.buf + ECDSA_OFFSET + CSS_HEADER_OFFSET) != 0x00020000) {295iwmbt_err("Invalid CSS Header version");296ret = -1;297goto exit;298}299}300301/* Load in the CSS header */302if (sbe_type == 0x00)303ret = iwmbt_load_rsa_header(hdl, &fw);304else if (sbe_type == 0x01)305ret = iwmbt_load_ecdsa_header(hdl, &fw);306if (ret < 0)307goto exit;308309/* Load in the Command Buffer */310ret = iwmbt_load_fwfile(hdl, &fw, boot_param, header_len);311312exit:313/* free firmware */314iwmbt_fw_free(&fw);315316return (ret);317}318319static int320iwmbt_init_ddc(libusb_device_handle *hdl, const char *ddc_path)321{322struct iwmbt_firmware ddc;323int ret;324325iwmbt_debug("loading %s", ddc_path);326327/* Read in the DDC file */328if (iwmbt_fw_read(&ddc, ddc_path) <= 0) {329iwmbt_debug("iwmbt_fw_read() failed");330return (-1);331}332333/* Load in the DDC file */334ret = iwmbt_load_ddc(hdl, &ddc);335if (ret < 0)336iwmbt_debug("Loading DDC file failed");337338/* free it */339iwmbt_fw_free(&ddc);340341return (ret);342}343344/*345* Parse ugen name and extract device's bus and address346*/347348static int349parse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr)350{351char *ep;352353if (strncmp(ugen, "ugen", 4) != 0)354return (-1);355356*bus = (uint8_t) strtoul(ugen + 4, &ep, 10);357if (*ep != '.')358return (-1);359360*addr = (uint8_t) strtoul(ep + 1, &ep, 10);361if (*ep != '\0')362return (-1);363364return (0);365}366367static void368usage(void)369{370fprintf(stderr,371"Usage: iwmbtfw [-DI] -d ugenX.Y [-f firmware path]\n");372fprintf(stderr, " -D: enable debugging\n");373fprintf(stderr, " -d: device to operate upon\n");374fprintf(stderr, " -f: firmware path (defaults to %s)\n",375_DEFAULT_IWMBT_FIRMWARE_PATH);376fprintf(stderr, " -I: enable informational output\n");377exit(127);378}379380381382/*383* Returns 0 on success.384*/385static int386handle_7260(libusb_device_handle *hdl, char *firmware_dir)387{388int r;389char *firmware_path;390struct iwmbt_version ver;391struct iwmbt_firmware fw;392393r = iwmbt_get_version(hdl, &ver);394if (r < 0) {395iwmbt_debug("iwmbt_get_version() failed code %d", r);396return 1;397}398iwmbt_dump_version(&ver);399iwmbt_debug("fw_patch_num=0x%02x", (int) ver.fw_patch_num);400401/* fw_patch_num = >0 operational mode */402if (ver.fw_patch_num > 0x00) {403iwmbt_info("Firmware has already been downloaded");404return 0;405}406407firmware_path = iwmbt_get_fwname(&ver, NULL, firmware_dir, "bseq");408if (firmware_path == NULL)409return 1;410iwmbt_debug("firmware_path = %s", firmware_path);411412r = iwmbt_fw_read(&fw, firmware_path);413free(firmware_path);414if (r <= 0) {415iwmbt_debug("iwmbt_fw_read() failed");416return 1;417}418419r = iwmbt_enter_manufacturer(hdl);420if (r < 0) {421iwmbt_debug("iwmbt_enter_manufacturer() failed code %d", r);422iwmbt_fw_free(&fw);423return 1;424}425426/* Download firmware */427r = iwmbt_patch_fwfile(hdl, &fw);428iwmbt_fw_free(&fw);429if (r < 0) {430iwmbt_debug("Loading firmware file failed");431(void)iwmbt_exit_manufacturer(hdl, IWMBT_MM_EXIT_COLD_RESET);432return 1;433}434435iwmbt_info("Firmware download complete");436437r = iwmbt_exit_manufacturer(hdl,438(r == 0 ? IWMBT_MM_EXIT_ONLY : IWMBT_MM_EXIT_WARM_RESET));439if (r < 0) {440iwmbt_debug("iwmbt_exit_manufacturer() failed code %d", r);441return 1;442}443444/* Once device is running in operational mode we can ignore failures */445446/* Dump actual controller version */447r = iwmbt_get_version(hdl, &ver);448if (r == 0)449iwmbt_dump_version(&ver);450451if (iwmbt_enter_manufacturer(hdl) < 0)452return 0;453r = iwmbt_set_event_mask(hdl);454if (r == 0)455iwmbt_info("Intel Event Mask is set");456(void)iwmbt_exit_manufacturer(hdl, IWMBT_MM_EXIT_ONLY);457458return 0;459}460461462/*463* Returns 0 on success.464*/465static int466handle_8260(libusb_device_handle *hdl, char *firmware_dir)467{468int r;469uint32_t boot_param;470struct iwmbt_version ver;471struct iwmbt_boot_params params;472char *firmware_path = NULL;473474r = iwmbt_get_version(hdl, &ver);475if (r < 0) {476iwmbt_debug("iwmbt_get_version() failed code %d", r);477return 1;478}479iwmbt_dump_version(&ver);480iwmbt_debug("fw_variant=0x%02x", (int) ver.fw_variant);481482if (ver.fw_variant == FW_VARIANT_OPERATIONAL) {483iwmbt_info("Firmware has already been downloaded");484return 0;485}486487if (ver.fw_variant != FW_VARIANT_BOOTLOADER){488iwmbt_err("unknown fw_variant 0x%02x", (int) ver.fw_variant);489return 1;490}491492/* Read Intel Secure Boot Params */493r = iwmbt_get_boot_params(hdl, ¶ms);494if (r < 0) {495iwmbt_debug("iwmbt_get_boot_params() failed!");496return 1;497}498iwmbt_dump_boot_params(¶ms);499500/* Check if firmware fragments are ACKed with a cmd complete event */501if (params.limited_cce != 0x00) {502iwmbt_err("Unsupported Intel firmware loading method (%u)",503params.limited_cce);504return 1;505}506507firmware_path = iwmbt_get_fwname(&ver, ¶ms, firmware_dir, "sfi");508if (firmware_path == NULL)509return 1;510iwmbt_debug("firmware_path = %s", firmware_path);511512/* Download firmware and parse it for magic Intel Reset parameter */513r = iwmbt_init_firmware(hdl, firmware_path, &boot_param, 0, 0);514free(firmware_path);515if (r < 0)516return 1;517518iwmbt_info("Firmware download complete");519520r = iwmbt_intel_reset(hdl, boot_param);521if (r < 0) {522iwmbt_debug("iwmbt_intel_reset() failed!");523return 1;524}525526iwmbt_info("Firmware operational");527528/* Once device is running in operational mode we can ignore failures */529530/* Dump actual controller version */531r = iwmbt_get_version(hdl, &ver);532if (r == 0)533iwmbt_dump_version(&ver);534535/* Apply the device configuration (DDC) parameters */536firmware_path = iwmbt_get_fwname(&ver, ¶ms, firmware_dir, "ddc");537iwmbt_debug("ddc_path = %s", firmware_path);538if (firmware_path != NULL) {539r = iwmbt_init_ddc(hdl, firmware_path);540if (r == 0)541iwmbt_info("DDC download complete");542free(firmware_path);543}544545r = iwmbt_set_event_mask(hdl);546if (r == 0)547iwmbt_info("Intel Event Mask is set");548549return 0;550}551552553static int554handle_9260(libusb_device_handle *hdl, char *firmware_dir)555{556int r;557uint32_t boot_param;558struct iwmbt_version vl;559struct iwmbt_version_tlv vt;560char *firmware_path = NULL;561562r = iwmbt_get_version_tlv(hdl, &vt);563if (r < 0) {564iwmbt_debug("iwmbt_get_version_tlv() failed code %d", r);565return 1;566}567iwmbt_dump_version_tlv(&vt);568iwmbt_debug("img_type=0x%02x", (int) vt.img_type);569570if (vt.img_type == TLV_IMG_TYPE_OPERATIONAL) {571iwmbt_info("Firmware has already been downloaded");572return 0;573}574575if (vt.img_type != TLV_IMG_TYPE_BOOTLOADER) {576iwmbt_err("unknown img_type 0x%02x", (int) vt.img_type);577return 1;578}579580/* Check if firmware fragments are ACKed with a cmd complete event */581if (vt.limited_cce != 0x00) {582iwmbt_err("Unsupported Intel firmware loading method (%u)",583vt.limited_cce);584return 1;585}586587/* Check if secure boot engine is supported: 1 (ECDSA) or 0 (RSA) */588if (vt.sbe_type > 0x01) {589iwmbt_err("Unsupported secure boot engine (%u)",590vt.sbe_type);591return 1;592}593594firmware_path = iwmbt_get_fwname_tlv(&vt, firmware_dir, "sfi");595if (firmware_path == NULL)596return 1;597iwmbt_debug("firmware_path = %s", firmware_path);598599/* Download firmware and parse it for magic Intel Reset parameter */600r = iwmbt_init_firmware(hdl, firmware_path, &boot_param,601vt.cnvi_bt >> 16 & 0x3f, vt.sbe_type);602free(firmware_path);603if (r < 0)604return 1;605606iwmbt_info("Firmware download complete");607608r = iwmbt_intel_reset(hdl, boot_param);609if (r < 0) {610iwmbt_debug("iwmbt_intel_reset() failed!");611return 1;612}613614iwmbt_info("Firmware operational");615616/* Once device is running in operational mode we can ignore failures */617618r = iwmbt_get_version(hdl, &vl);619if (r == 0)620iwmbt_dump_version(&vl);621622/* Apply the device configuration (DDC) parameters */623firmware_path = iwmbt_get_fwname_tlv(&vt, firmware_dir, "ddc");624iwmbt_debug("ddc_path = %s", firmware_path);625if (firmware_path != NULL) {626r = iwmbt_init_ddc(hdl, firmware_path);627if (r == 0)628iwmbt_info("DDC download complete");629free(firmware_path);630}631632r = iwmbt_set_event_mask(hdl);633if (r == 0)634iwmbt_info("Intel Event Mask is set");635636return 0;637}638639640int641main(int argc, char *argv[])642{643libusb_context *ctx = NULL;644libusb_device *dev = NULL;645libusb_device_handle *hdl = NULL;646int r;647uint8_t bus_id = 0, dev_id = 0;648int devid_set = 0;649int n;650char *firmware_dir = NULL;651int retcode = 1;652enum iwmbt_device iwmbt_device;653654/* Parse command line arguments */655while ((n = getopt(argc, argv, "Dd:f:hI")) != -1) {656switch (n) {657case 'd': /* ugen device name */658devid_set = 1;659if (parse_ugen_name(optarg, &bus_id, &dev_id) < 0)660usage();661break;662case 'D':663iwmbt_do_debug = 1;664break;665case 'f': /* firmware dir */666if (firmware_dir)667free(firmware_dir);668firmware_dir = strdup(optarg);669break;670case 'I':671iwmbt_do_info = 1;672break;673case 'h':674default:675usage();676break;677/* NOT REACHED */678}679}680681/* Ensure the devid was given! */682if (devid_set == 0) {683usage();684/* NOTREACHED */685}686687/* Default the firmware path */688if (firmware_dir == NULL)689firmware_dir = strdup(_DEFAULT_IWMBT_FIRMWARE_PATH);690691/* libusb setup */692r = libusb_init(&ctx);693if (r != 0) {694iwmbt_err("libusb_init failed: code %d", r);695exit(127);696}697698iwmbt_debug("opening dev %d.%d", (int) bus_id, (int) dev_id);699700/* Find a device based on the bus/dev id */701dev = iwmbt_find_device(ctx, bus_id, dev_id, &iwmbt_device);702if (dev == NULL) {703iwmbt_err("device not found");704goto shutdown;705}706707/* XXX enforce that bInterfaceNumber is 0 */708709/* XXX enforce the device/product id if they're non-zero */710711/* Grab device handle */712r = libusb_open(dev, &hdl);713if (r != 0) {714iwmbt_err("libusb_open() failed: code %d", r);715goto shutdown;716}717718/* Check if ng_ubt is attached */719r = libusb_kernel_driver_active(hdl, 0);720if (r < 0) {721iwmbt_err("libusb_kernel_driver_active() failed: code %d", r);722goto shutdown;723}724if (r > 0) {725iwmbt_info("Firmware has already been downloaded");726retcode = 0;727goto shutdown;728}729730switch(iwmbt_device) {731case IWMBT_DEVICE_7260:732retcode = handle_7260(hdl, firmware_dir);733break;734case IWMBT_DEVICE_8260:735retcode = handle_8260(hdl, firmware_dir);736break;737case IWMBT_DEVICE_9260:738retcode = handle_9260(hdl, firmware_dir);739break;740default:741iwmbt_err("FIXME: unknown iwmbt type %d", (int)iwmbt_device);742retcode = 1;743}744745if (retcode == 0) {746/* Ask kernel driver to probe and attach device again */747r = libusb_reset_device(hdl);748if (r != 0)749iwmbt_err("libusb_reset_device() failed: %s",750libusb_strerror(r));751}752753shutdown:754if (hdl != NULL)755libusb_close(hdl);756757if (dev != NULL)758libusb_unref_device(dev);759760if (ctx != NULL)761libusb_exit(ctx);762763if (retcode == 0)764iwmbt_info("Firmware download is successful!");765else766iwmbt_err("Firmware download failed!");767768return (retcode);769}770771772