Path: blob/main/usr.sbin/bluetooth/ath3kfw/main.c
105240 views
/*-1* Copyright (c) 2013 Adrian Chadd <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer,9* without modification.10* 2. Redistributions in binary form must reproduce at minimum a disclaimer11* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any12* redistribution must be conditioned upon including a substantially13* similar Disclaimer requirement for further binary redistribution.14*15* NO WARRANTY16* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY19* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL20* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,21* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF22* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS23* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER24* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)25* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF26* THE POSSIBILITY OF SUCH DAMAGES.27*/2829#include <stdio.h>30#include <stdlib.h>31#include <unistd.h>32#include <errno.h>33#include <string.h>34#include <err.h>35#include <fcntl.h>36#include <libgen.h>37#include <sys/stat.h>38#include <sys/param.h>3940#include <libusb.h>4142#include "ath3k_fw.h"43#include "ath3k_hw.h"44#include "ath3k_dbg.h"4546#define _DEFAULT_ATH3K_FIRMWARE_PATH "/usr/share/firmware/ath3k/"4748int ath3k_do_debug = 0;49int ath3k_do_info = 0;5051struct ath3k_devid {52uint16_t product_id;53uint16_t vendor_id;54int is_3012;55};5657static struct ath3k_devid ath3k_list[] = {5859/* Atheros AR3012 with sflash firmware*/60{ .vendor_id = 0x0489, .product_id = 0xe04e, .is_3012 = 1 },61{ .vendor_id = 0x0489, .product_id = 0xe04d, .is_3012 = 1 },62{ .vendor_id = 0x0489, .product_id = 0xe056, .is_3012 = 1 },63{ .vendor_id = 0x0489, .product_id = 0xe057, .is_3012 = 1 },64{ .vendor_id = 0x0489, .product_id = 0xe05f, .is_3012 = 1 },65{ .vendor_id = 0x04c5, .product_id = 0x1330, .is_3012 = 1 },66{ .vendor_id = 0x04ca, .product_id = 0x3004, .is_3012 = 1 },67{ .vendor_id = 0x04ca, .product_id = 0x3005, .is_3012 = 1 },68{ .vendor_id = 0x04ca, .product_id = 0x3006, .is_3012 = 1 },69{ .vendor_id = 0x04ca, .product_id = 0x3008, .is_3012 = 1 },70{ .vendor_id = 0x04ca, .product_id = 0x300b, .is_3012 = 1 },71{ .vendor_id = 0x0930, .product_id = 0x0219, .is_3012 = 1 },72{ .vendor_id = 0x0930, .product_id = 0x0220, .is_3012 = 1 },73{ .vendor_id = 0x0b05, .product_id = 0x17d0, .is_3012 = 1 },74{ .vendor_id = 0x0CF3, .product_id = 0x0036, .is_3012 = 1 },75{ .vendor_id = 0x0cf3, .product_id = 0x3004, .is_3012 = 1 },76{ .vendor_id = 0x0cf3, .product_id = 0x3005, .is_3012 = 1 },77{ .vendor_id = 0x0cf3, .product_id = 0x3008, .is_3012 = 1 },78{ .vendor_id = 0x0cf3, .product_id = 0x311D, .is_3012 = 1 },79{ .vendor_id = 0x0cf3, .product_id = 0x311E, .is_3012 = 1 },80{ .vendor_id = 0x0cf3, .product_id = 0x311F, .is_3012 = 1 },81{ .vendor_id = 0x0cf3, .product_id = 0x3121, .is_3012 = 1 },82{ .vendor_id = 0x0CF3, .product_id = 0x817a, .is_3012 = 1 },83{ .vendor_id = 0x0cf3, .product_id = 0xe004, .is_3012 = 1 },84{ .vendor_id = 0x0cf3, .product_id = 0xe005, .is_3012 = 1 },85{ .vendor_id = 0x0cf3, .product_id = 0xe003, .is_3012 = 1 },86{ .vendor_id = 0x13d3, .product_id = 0x3362, .is_3012 = 1 },87{ .vendor_id = 0x13d3, .product_id = 0x3375, .is_3012 = 1 },88{ .vendor_id = 0x13d3, .product_id = 0x3393, .is_3012 = 1 },89{ .vendor_id = 0x13d3, .product_id = 0x3402, .is_3012 = 1 },9091/* Atheros AR5BBU22 with sflash firmware */92{ .vendor_id = 0x0489, .product_id = 0xE036, .is_3012 = 1 },93{ .vendor_id = 0x0489, .product_id = 0xE03C, .is_3012 = 1 },94};9596static int97ath3k_is_3012(struct libusb_device_descriptor *d)98{99int i;100101/* Search looking for whether it's an AR3012 */102for (i = 0; i < (int) nitems(ath3k_list); i++) {103if ((ath3k_list[i].product_id == d->idProduct) &&104(ath3k_list[i].vendor_id == d->idVendor)) {105fprintf(stderr, "%s: found AR3012\n", __func__);106return (ath3k_list[i].is_3012);107}108}109110/* Not found */111return (0);112}113114static libusb_device *115ath3k_find_device(libusb_context *ctx, int bus_id, int dev_id)116{117libusb_device **list, *dev = NULL, *found = NULL;118ssize_t cnt, i;119120cnt = libusb_get_device_list(ctx, &list);121if (cnt < 0) {122ath3k_err("%s: libusb_get_device_list() failed: code %lld\n",123__func__,124(long long int) cnt);125return (NULL);126}127128/*129* XXX TODO: match on the vendor/product id too!130*/131for (i = 0; i < cnt; i++) {132dev = list[i];133if (bus_id == libusb_get_bus_number(dev) &&134dev_id == libusb_get_device_address(dev)) {135/*136* Take a reference so it's not freed later on.137*/138found = libusb_ref_device(dev);139break;140}141}142143libusb_free_device_list(list, 1);144return (found);145}146147static int148ath3k_init_ar3012(libusb_device_handle *hdl, const char *fw_path)149{150int ret;151152ret = ath3k_load_patch(hdl, fw_path);153if (ret < 0) {154ath3k_err("Loading patch file failed\n");155return (ret);156}157158ret = ath3k_load_syscfg(hdl, fw_path);159if (ret < 0) {160ath3k_err("Loading sysconfig file failed\n");161return (ret);162}163164ret = ath3k_set_normal_mode(hdl);165if (ret < 0) {166ath3k_err("Set normal mode failed\n");167return (ret);168}169170ath3k_switch_pid(hdl);171return (0);172}173174static int175ath3k_init_firmware(libusb_device_handle *hdl, const char *file_prefix)176{177struct ath3k_firmware fw;178char fwname[FILENAME_MAX];179int ret;180181/* XXX path info? */182snprintf(fwname, FILENAME_MAX, "%s/ath3k-1.fw", file_prefix);183184ath3k_debug("%s: loading ath3k-1.fw\n", __func__);185186/* Read in the firmware */187if (ath3k_fw_read(&fw, fwname) <= 0) {188fprintf(stderr, "%s: ath3k_fw_read() failed\n",189__func__);190return (-1);191}192193/* Load in the firmware */194ret = ath3k_load_fwfile(hdl, &fw);195196/* free it */197ath3k_fw_free(&fw);198199return (ret);200}201202/*203* Parse ugen name and extract device's bus and address204*/205206static int207parse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr)208{209char *ep;210211if (strncmp(ugen, "ugen", 4) != 0)212return (-1);213214*bus = (uint8_t) strtoul(ugen + 4, &ep, 10);215if (*ep != '.')216return (-1);217218*addr = (uint8_t) strtoul(ep + 1, &ep, 10);219if (*ep != '\0')220return (-1);221222return (0);223}224225static void226usage(void)227{228fprintf(stderr,229"Usage: ath3kfw (-D) -d ugenX.Y (-f firmware path) (-I)\n");230fprintf(stderr, " -D: enable debugging\n");231fprintf(stderr, " -d: device to operate upon\n");232fprintf(stderr, " -f: firmware path, if not default\n");233fprintf(stderr, " -I: enable informational output\n");234exit(127);235}236237int238main(int argc, char *argv[])239{240struct libusb_device_descriptor d;241libusb_context *ctx;242libusb_device *dev;243libusb_device_handle *hdl;244unsigned char state;245struct ath3k_version ver;246int r;247uint8_t bus_id = 0, dev_id = 0;248int devid_set = 0;249int n;250char *firmware_path = NULL;251int is_3012 = 0;252253/* libusb setup */254r = libusb_init(&ctx);255if (r != 0) {256ath3k_err("%s: libusb_init failed: code %d\n",257argv[0],258r);259exit(127);260}261262/* Enable debugging, just because */263libusb_set_debug(ctx, 3);264265/* Parse command line arguments */266while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) {267switch (n) {268case 'd': /* ugen device name */269devid_set = 1;270if (parse_ugen_name(optarg, &bus_id, &dev_id) < 0)271usage();272break;273case 'D':274ath3k_do_debug = 1;275break;276case 'f': /* firmware path */277if (firmware_path)278free(firmware_path);279firmware_path = strdup(optarg);280break;281case 'I':282ath3k_do_info = 1;283break;284case 'h':285default:286usage();287break;288/* NOT REACHED */289}290}291292/* Ensure the devid was given! */293if (devid_set == 0) {294usage();295/* NOTREACHED */296}297298ath3k_debug("%s: opening dev %d.%d\n",299basename(argv[0]),300(int) bus_id,301(int) dev_id);302303/* Find a device based on the bus/dev id */304dev = ath3k_find_device(ctx, bus_id, dev_id);305if (dev == NULL) {306ath3k_err("%s: device not found\n", __func__);307/* XXX cleanup? */308exit(1);309}310311/* Get the device descriptor for this device entry */312r = libusb_get_device_descriptor(dev, &d);313if (r != 0) {314warn("%s: libusb_get_device_descriptor: %s\n",315__func__,316libusb_strerror(r));317exit(1);318}319320/* See if its an AR3012 */321if (ath3k_is_3012(&d)) {322is_3012 = 1;323324/* If it's bcdDevice > 1, don't attach */325if (d.bcdDevice > 0x0001) {326ath3k_debug("%s: AR3012; bcdDevice=%d, exiting\n",327__func__,328d.bcdDevice);329exit(0);330}331}332333/* XXX enforce that bInterfaceNumber is 0 */334335/* XXX enforce the device/product id if they're non-zero */336337/* Grab device handle */338r = libusb_open(dev, &hdl);339if (r != 0) {340ath3k_err("%s: libusb_open() failed: code %d\n", __func__, r);341/* XXX cleanup? */342exit(1);343}344345/*346* Get the initial NIC state.347*/348r = ath3k_get_state(hdl, &state);349if (r == 0) {350ath3k_err("%s: ath3k_get_state() failed!\n", __func__);351/* XXX cleanup? */352exit(1);353}354ath3k_debug("%s: state=0x%02x\n",355__func__,356(int) state);357358/* And the version */359r = ath3k_get_version(hdl, &ver);360if (r == 0) {361ath3k_err("%s: ath3k_get_version() failed!\n", __func__);362/* XXX cleanup? */363exit(1);364}365ath3k_info("ROM version: %d, build version: %d, ram version: %d, "366"ref clock=%d\n",367ver.rom_version,368ver.build_version,369ver.ram_version,370ver.ref_clock);371372/* Default the firmware path */373if (firmware_path == NULL)374firmware_path = strdup(_DEFAULT_ATH3K_FIRMWARE_PATH);375376if (is_3012) {377(void) ath3k_init_ar3012(hdl, firmware_path);378} else {379(void) ath3k_init_firmware(hdl, firmware_path);380}381382/* Shutdown */383libusb_close(hdl);384hdl = NULL;385386libusb_unref_device(dev);387dev = NULL;388389libusb_exit(ctx);390ctx = NULL;391}392393394