summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/bluetooth')
-rw-r--r--usr.sbin/bluetooth/ath3kfw/Makefile1
-rw-r--r--usr.sbin/bluetooth/ath3kfw/ath3k_dbg.h41
-rw-r--r--usr.sbin/bluetooth/ath3kfw/ath3k_fw.c114
-rw-r--r--usr.sbin/bluetooth/ath3kfw/ath3k_fw.h56
-rw-r--r--usr.sbin/bluetooth/ath3kfw/ath3k_hw.c358
-rw-r--r--usr.sbin/bluetooth/ath3kfw/ath3k_hw.h66
-rw-r--r--usr.sbin/bluetooth/ath3kfw/ath3kfw.832
-rw-r--r--usr.sbin/bluetooth/ath3kfw/ath3kfw.c297
-rw-r--r--usr.sbin/bluetooth/ath3kfw/main.c394
9 files changed, 1052 insertions, 307 deletions
diff --git a/usr.sbin/bluetooth/ath3kfw/Makefile b/usr.sbin/bluetooth/ath3kfw/Makefile
index 26ce06e..61a6f9c 100644
--- a/usr.sbin/bluetooth/ath3kfw/Makefile
+++ b/usr.sbin/bluetooth/ath3kfw/Makefile
@@ -3,5 +3,6 @@
PROG= ath3kfw
MAN= ath3kfw.8
LIBADD+= usb
+SRCS= main.c ath3k_fw.c ath3k_hw.c
.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/ath3kfw/ath3k_dbg.h b/usr.sbin/bluetooth/ath3kfw/ath3k_dbg.h
new file mode 100644
index 0000000..3aaf3b3
--- /dev/null
+++ b/usr.sbin/bluetooth/ath3kfw/ath3k_dbg.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
+ * All rights reserved.
+ *
+ * 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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+#ifndef __ATH3K_DEBUG_H__
+#define __ATH3K_DEBUG_H__
+
+extern int ath3k_do_debug;
+extern int ath3k_do_info;
+
+#define ath3k_debug(...) if (ath3k_do_debug) fprintf(stderr, __VA_ARGS__)
+#define ath3k_err(...) fprintf(stderr, __VA_ARGS__)
+#define ath3k_info(...) if (ath3k_do_info) fprintf(stdout, __VA_ARGS__)
+
+#endif
diff --git a/usr.sbin/bluetooth/ath3kfw/ath3k_fw.c b/usr.sbin/bluetooth/ath3kfw/ath3k_fw.c
new file mode 100644
index 0000000..2c48004
--- /dev/null
+++ b/usr.sbin/bluetooth/ath3kfw/ath3k_fw.c
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
+ * All rights reserved.
+ *
+ * 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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <err.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "ath3k_fw.h"
+#include "ath3k_dbg.h"
+
+int
+ath3k_fw_read(struct ath3k_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) {
+ fprintf(stderr, "%s: read len %d != file size %d\n",
+ __func__,
+ (int) r,
+ (int) sb.st_size);
+ free(buf);
+ close(fd);
+ return (0);
+ }
+
+ /* We have everything, so! */
+
+ bzero(fw, sizeof(*fw));
+
+ fw->fwname = strdup(fwname);
+ fw->len = sb.st_size;
+ fw->size = sb.st_size;
+ fw->buf = buf;
+
+ close(fd);
+ return (1);
+}
+
+void
+ath3k_fw_free(struct ath3k_firmware *fw)
+{
+ if (fw->fwname)
+ free(fw->fwname);
+ if (fw->buf)
+ free(fw->buf);
+ bzero(fw, sizeof(*fw));
+}
diff --git a/usr.sbin/bluetooth/ath3kfw/ath3k_fw.h b/usr.sbin/bluetooth/ath3kfw/ath3k_fw.h
new file mode 100644
index 0000000..eddc0b7
--- /dev/null
+++ b/usr.sbin/bluetooth/ath3kfw/ath3k_fw.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
+ * All rights reserved.
+ *
+ * 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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+#ifndef __ATH3K_FW_H__
+#define __ATH3K_FW_H__
+
+/*
+ * XXX TODO: ensure that the endian-ness of this stuff is
+ * correct!
+ */
+struct ath3k_version {
+ unsigned int rom_version;
+ unsigned int build_version;
+ unsigned int ram_version;
+ unsigned char ref_clock;
+ unsigned char reserved[0x07];
+};
+
+struct ath3k_firmware {
+ char *fwname;
+ int len; /* firmware length */
+ int size; /* buffer size */
+ unsigned char *buf;
+};
+
+extern int ath3k_fw_read(struct ath3k_firmware *fw, const char *fwname);
+extern void ath3k_fw_free(struct ath3k_firmware *fw);
+
+#endif
diff --git a/usr.sbin/bluetooth/ath3kfw/ath3k_hw.c b/usr.sbin/bluetooth/ath3kfw/ath3k_hw.c
new file mode 100644
index 0000000..890e2db
--- /dev/null
+++ b/usr.sbin/bluetooth/ath3kfw/ath3k_hw.c
@@ -0,0 +1,358 @@
+/*-
+ * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
+ * All rights reserved.
+ *
+ * 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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <err.h>
+#include <fcntl.h>
+#include <sys/endian.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <libusb.h>
+
+#include "ath3k_fw.h"
+#include "ath3k_hw.h"
+#include "ath3k_dbg.h"
+
+#define XMIN(x, y) ((x) < (y) ? (x) : (y))
+
+int
+ath3k_load_fwfile(struct libusb_device_handle *hdl,
+ const struct ath3k_firmware *fw)
+{
+ int size, count, sent = 0;
+ int ret, r;
+
+ count = fw->len;
+
+ size = XMIN(count, FW_HDR_SIZE);
+
+ ath3k_debug("%s: file=%s, size=%d\n",
+ __func__, fw->fwname, count);
+
+ /*
+ * Flip the device over to configuration mode.
+ */
+ ret = libusb_control_transfer(hdl,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT,
+ ATH3K_DNLOAD,
+ 0,
+ 0,
+ fw->buf + sent,
+ size,
+ 1000); /* XXX timeout */
+
+ if (ret != size) {
+ fprintf(stderr, "Can't switch to config mode; ret=%d\n",
+ ret);
+ return (-1);
+ }
+
+ sent += size;
+ count -= size;
+
+ /* Load in the rest of the data */
+ while (count) {
+ size = XMIN(count, BULK_SIZE);
+ ath3k_debug("%s: transferring %d bytes, offset %d\n",
+ __func__,
+ sent,
+ size);
+
+ ret = libusb_bulk_transfer(hdl,
+ 0x2,
+ fw->buf + sent,
+ size,
+ &r,
+ 1000);
+
+ if (ret < 0 || r != size) {
+ fprintf(stderr, "Can't load firmware: err=%s, size=%d\n",
+ libusb_strerror(ret),
+ size);
+ return (-1);
+ }
+ sent += size;
+ count -= size;
+ }
+ return (0);
+}
+
+int
+ath3k_get_state(struct libusb_device_handle *hdl, unsigned char *state)
+{
+ int ret;
+
+ ret = libusb_control_transfer(hdl,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN,
+ ATH3K_GETSTATE,
+ 0,
+ 0,
+ state,
+ 1,
+ 1000); /* XXX timeout */
+
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: libusb_control_transfer() failed: code=%d\n",
+ __func__,
+ ret);
+ return (0);
+ }
+
+ return (ret == 1);
+}
+
+int
+ath3k_get_version(struct libusb_device_handle *hdl,
+ struct ath3k_version *version)
+{
+ int ret;
+
+ ret = libusb_control_transfer(hdl,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN,
+ ATH3K_GETVERSION,
+ 0,
+ 0,
+ (unsigned char *) version,
+ sizeof(struct ath3k_version),
+ 1000); /* XXX timeout */
+
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: libusb_control_transfer() failed: code=%d\n",
+ __func__,
+ ret);
+ return (0);
+ }
+
+ /* XXX endian fix! */
+
+ return (ret == sizeof(struct ath3k_version));
+}
+
+int
+ath3k_load_patch(libusb_device_handle *hdl, const char *fw_path)
+{
+ int ret;
+ unsigned char fw_state;
+ struct ath3k_version fw_ver, pt_ver;
+ char fwname[FILENAME_MAX];
+ struct ath3k_firmware fw;
+ uint32_t tmp;
+
+ ret = ath3k_get_state(hdl, &fw_state);
+ if (ret < 0) {
+ ath3k_err("%s: Can't get state\n", __func__);
+ return (ret);
+ }
+
+ if (fw_state & ATH3K_PATCH_UPDATE) {
+ ath3k_info("%s: Patch already downloaded\n",
+ __func__);
+ return (0);
+ }
+
+ ret = ath3k_get_version(hdl, &fw_ver);
+ if (ret < 0) {
+ ath3k_debug("%s: Can't get version\n", __func__);
+ return (ret);
+ }
+
+ /* XXX path info? */
+ snprintf(fwname, FILENAME_MAX, "%s/ar3k/AthrBT_0x%08x.dfu",
+ fw_path,
+ fw_ver.rom_version);
+
+ /* Read in the firmware */
+ if (ath3k_fw_read(&fw, fwname) <= 0) {
+ ath3k_debug("%s: ath3k_fw_read() failed\n",
+ __func__);
+ return (-1);
+ }
+
+ /*
+ * Extract the ROM/build version from the patch file.
+ */
+ memcpy(&tmp, fw.buf + fw.len - 8, sizeof(tmp));
+ pt_ver.rom_version = le32toh(tmp);
+ memcpy(&tmp, fw.buf + fw.len - 4, sizeof(tmp));
+ pt_ver.build_version = le32toh(tmp);
+
+ ath3k_info("%s: file %s: rom_ver=%d, build_ver=%d\n",
+ __func__,
+ fwname,
+ (int) pt_ver.rom_version,
+ (int) pt_ver.build_version);
+
+ /* Check the ROM/build version against the firmware */
+ if ((pt_ver.rom_version != fw_ver.rom_version) ||
+ (pt_ver.build_version <= fw_ver.build_version)) {
+ ath3k_debug("Patch file version mismatch!\n");
+ ath3k_fw_free(&fw);
+ return (-1);
+ }
+
+ /* Load in the firmware */
+ ret = ath3k_load_fwfile(hdl, &fw);
+
+ /* free it */
+ ath3k_fw_free(&fw);
+
+ return (ret);
+}
+
+int
+ath3k_load_syscfg(libusb_device_handle *hdl, const char *fw_path)
+{
+ unsigned char fw_state;
+ char filename[FILENAME_MAX];
+ struct ath3k_firmware fw;
+ struct ath3k_version fw_ver;
+ int clk_value, ret;
+
+ ret = ath3k_get_state(hdl, &fw_state);
+ if (ret < 0) {
+ ath3k_err("Can't get state to change to load configuration err");
+ return (-EBUSY);
+ }
+
+ ret = ath3k_get_version(hdl, &fw_ver);
+ if (ret < 0) {
+ ath3k_err("Can't get version to change to load ram patch err");
+ return (ret);
+ }
+
+ switch (fw_ver.ref_clock) {
+ case ATH3K_XTAL_FREQ_26M:
+ clk_value = 26;
+ break;
+ case ATH3K_XTAL_FREQ_40M:
+ clk_value = 40;
+ break;
+ case ATH3K_XTAL_FREQ_19P2:
+ clk_value = 19;
+ break;
+ default:
+ clk_value = 0;
+ break;
+}
+
+ snprintf(filename, FILENAME_MAX, "%s/ar3k/ramps_0x%08x_%d%s",
+ fw_path,
+ fw_ver.rom_version,
+ clk_value,
+ ".dfu");
+
+ ath3k_info("%s: syscfg file = %s\n",
+ __func__,
+ filename);
+
+ /* Read in the firmware */
+ if (ath3k_fw_read(&fw, filename) <= 0) {
+ ath3k_err("%s: ath3k_fw_read() failed\n",
+ __func__);
+ return (-1);
+ }
+
+ ret = ath3k_load_fwfile(hdl, &fw);
+
+ ath3k_fw_free(&fw);
+ return (ret);
+}
+
+int
+ath3k_set_normal_mode(libusb_device_handle *hdl)
+{
+ int ret;
+ unsigned char fw_state;
+
+ ret = ath3k_get_state(hdl, &fw_state);
+ if (ret < 0) {
+ ath3k_err("%s: can't get state\n", __func__);
+ return (ret);
+ }
+
+ /*
+ * This isn't a fatal error - the device may have detached
+ * already.
+ */
+ if ((fw_state & ATH3K_MODE_MASK) == ATH3K_NORMAL_MODE) {
+ ath3k_debug("%s: firmware is already in normal mode\n",
+ __func__);
+ return (0);
+ }
+
+ ret = libusb_control_transfer(hdl,
+ LIBUSB_REQUEST_TYPE_VENDOR, /* XXX out direction? */
+ ATH3K_SET_NORMAL_MODE,
+ 0,
+ 0,
+ NULL,
+ 0,
+ 1000); /* XXX timeout */
+
+ if (ret < 0) {
+ ath3k_err("%s: libusb_control_transfer() failed: code=%d\n",
+ __func__,
+ ret);
+ return (0);
+ }
+
+ return (ret == 0);
+}
+
+int
+ath3k_switch_pid(libusb_device_handle *hdl)
+{
+ int ret;
+ ret = libusb_control_transfer(hdl,
+ LIBUSB_REQUEST_TYPE_VENDOR, /* XXX set an out flag? */
+ USB_REG_SWITCH_VID_PID,
+ 0,
+ 0,
+ NULL,
+ 0,
+ 1000); /* XXX timeout */
+
+ if (ret < 0) {
+ ath3k_debug("%s: libusb_control_transfer() failed: code=%d\n",
+ __func__,
+ ret);
+ return (0);
+ }
+
+ return (ret == 0);
+}
diff --git a/usr.sbin/bluetooth/ath3kfw/ath3k_hw.h b/usr.sbin/bluetooth/ath3kfw/ath3k_hw.h
new file mode 100644
index 0000000..2d72f3f
--- /dev/null
+++ b/usr.sbin/bluetooth/ath3kfw/ath3k_hw.h
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
+ * All rights reserved.
+ *
+ * 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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+#ifndef __ATH3K_HW_H__
+#define __ATH3K_HW_H__
+
+#define ATH3K_DNLOAD 0x01
+#define ATH3K_GETSTATE 0x05
+#define ATH3K_SET_NORMAL_MODE 0x07
+#define ATH3K_GETVERSION 0x09
+#define USB_REG_SWITCH_VID_PID 0x0a
+
+#define ATH3K_MODE_MASK 0x3F
+#define ATH3K_NORMAL_MODE 0x0E
+
+#define ATH3K_PATCH_UPDATE 0x80
+#define ATH3K_SYSCFG_UPDATE 0x40
+
+#define ATH3K_XTAL_FREQ_26M 0x00
+#define ATH3K_XTAL_FREQ_40M 0x01
+#define ATH3K_XTAL_FREQ_19P2 0x02
+#define ATH3K_NAME_LEN 0xFF
+
+#define USB_REQ_DFU_DNLOAD 1
+#define BULK_SIZE 4096
+#define FW_HDR_SIZE 20
+
+extern int ath3k_load_fwfile(struct libusb_device_handle *hdl,
+ const struct ath3k_firmware *fw);
+extern int ath3k_get_state(struct libusb_device_handle *hdl,
+ unsigned char *state);
+extern int ath3k_get_version(struct libusb_device_handle *hdl,
+ struct ath3k_version *version);
+extern int ath3k_load_patch(libusb_device_handle *hdl, const char *fw_path);
+extern int ath3k_load_syscfg(libusb_device_handle *hdl, const char *fw_path);
+extern int ath3k_set_normal_mode(libusb_device_handle *hdl);
+extern int ath3k_switch_pid(libusb_device_handle *hdl);
+
+#endif
diff --git a/usr.sbin/bluetooth/ath3kfw/ath3kfw.8 b/usr.sbin/bluetooth/ath3kfw/ath3kfw.8
index 3a37343..7485583 100644
--- a/usr.sbin/bluetooth/ath3kfw/ath3kfw.8
+++ b/usr.sbin/bluetooth/ath3kfw/ath3kfw.8
@@ -1,4 +1,5 @@
.\" Copyright (c) 2010 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" Copyright (c) 2013, 2016 Adrian Chadd <adrian@freebsd.org>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -24,16 +25,16 @@
.\"
.\" $FreeBSD$
.\"
-.Dd November 9, 2010
+.Dd June 4, 2016
.Dt ATH3KFW 8
.Os
.Sh NAME
.Nm ath3kfw
-.Nd firmware download utility for Atheros AR3011 chip based Bluetooth USB devices
+.Nd firmware download utility for Atheros AR3011/AR3012 chip based Bluetooth USB devices
.Sh SYNOPSIS
.Nm
.Fl d Ar device_name
-.Fl f Ar firmware_file_name
+.Fl f Ar firmware_path
.Nm
.Fl h
.Sh DESCRIPTION
@@ -45,23 +46,31 @@ device.
.Pp
This utility will
.Em only
-work with Atheros AR3011 chip based Bluetooth USB devices.
+work with Atheros AR3011 and AR3012 chip based Bluetooth USB devices.
The identification is currently based on USB vendor ID/product ID pair.
The vendor ID should be 0x0cf3
.Pq Dv USB_VENDOR_ATHEROS2
-and the product ID should be 0x3000.
+and the product ID should be one of the supported devices.
.Pp
-Firmware files ath3k-1.fw and ath3k-2.fw can be obtained from the
-linux-firmware RPM.
+Firmware files are available in the linux-firmware RPM.
+.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 f Ar firmware_file_name
-Specify firmware file name for download.
+.It 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
@@ -72,7 +81,10 @@ Display usage message and exit.
.Xr ugen 4 ,
.Xr devd 8
.Sh AUTHORS
-.An Maksim Yevmenkin Aq Mt m_evmenkin@yahoo.com
+The original utility was written by
+.An Maksim Yevmenkin Aq Mt m_evmenkin@yahoo.com .
+This was written based on Linux ath3k by
+.An Adrian Chadd Aq Mt adrian@freebsd.org .
.Sh BUGS
Most likely.
Please report if found.
diff --git a/usr.sbin/bluetooth/ath3kfw/ath3kfw.c b/usr.sbin/bluetooth/ath3kfw/ath3kfw.c
deleted file mode 100644
index 37191d1..0000000
--- a/usr.sbin/bluetooth/ath3kfw/ath3kfw.c
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * ath3kfw.c
- */
-
-/*-
- * Copyright (c) 2010 Maksim Yevmenkin <m_evmenkin@yahoo.com>
- * All rights reserved.
- *
- * 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 <errno.h>
-#include <fcntl.h>
-#include <libusb20_desc.h>
-#include <libusb20.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#define ATH3KFW "ath3kfw"
-#define ATH3KFW_VENDOR_ID 0x0cf3
-#define ATH3KFW_PRODUCT_ID 0x3000
-#define ATH3KFW_FW "/usr/local/etc/ath3k-1.fw"
-#define ATH3KFW_BULK_EP 0x02
-#define ATH3KFW_REQ_DFU_DNLOAD 1
-#define ATH3KFW_MAX_BSIZE 4096
-
-static int parse_ugen_name (char const *ugen, uint8_t *bus,
- uint8_t *addr);
-static int find_device (struct libusb20_backend *be,
- uint8_t bus, uint8_t addr,
- struct libusb20_device **dev);
-static int download_firmware (struct libusb20_device *dev,
- char const *firmware);
-static void usage (void);
-
-static int vendor_id = ATH3KFW_VENDOR_ID;
-static int product_id = ATH3KFW_PRODUCT_ID;
-
-/*
- * Firmware downloader for Atheros AR3011 based USB Bluetooth devices
- */
-
-int
-main(int argc, char **argv)
-{
- uint8_t bus, addr;
- char const *firmware;
- struct libusb20_backend *be;
- struct libusb20_device *dev;
- int n;
-
- openlog(ATH3KFW, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_USER);
-
- bus = 0;
- addr = 0;
- firmware = ATH3KFW_FW;
-
- while ((n = getopt(argc, argv, "d:f:hp:v:")) != -1) {
- switch (n) {
- case 'd': /* ugen device name */
- if (parse_ugen_name(optarg, &bus, &addr) < 0)
- usage();
- break;
-
- case 'f': /* firmware file */
- firmware = optarg;
- break;
- case 'p': /* product id */
- product_id = strtol(optarg, NULL, 0);
- break;
- case 'v': /* vendor id */
- vendor_id = strtol(optarg, NULL, 0);
- break;
- case 'h':
- default:
- usage();
- break;
- /* NOT REACHED */
- }
- }
-
- be = libusb20_be_alloc_default();
- if (be == NULL) {
- syslog(LOG_ERR, "libusb20_be_alloc_default() failed");
- return (-1);
- }
-
- if (find_device(be, bus, addr, &dev) < 0) {
- syslog(LOG_ERR, "ugen%d.%d is not recognized as " \
- "Atheros AR3011 based device " \
- "(possibly caused by lack of permissions)", bus, addr);
- return (-1);
- }
-
- if (download_firmware(dev, firmware) < 0) {
- syslog(LOG_ERR, "could not download %s firmare to ugen%d.%d",
- firmware, bus, addr);
- return (-1);
- }
-
- libusb20_be_free(be);
- closelog();
-
- return (0);
-}
-
-/*
- * 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);
-}
-
-/*
- * Find USB device
- */
-
-static int
-find_device(struct libusb20_backend *be, uint8_t bus, uint8_t addr,
- struct libusb20_device **dev)
-{
- struct LIBUSB20_DEVICE_DESC_DECODED *desc;
-
- *dev = NULL;
-
- while ((*dev = libusb20_be_device_foreach(be, *dev)) != NULL) {
- if (libusb20_dev_get_bus_number(*dev) != bus ||
- libusb20_dev_get_address(*dev) != addr)
- continue;
-
- desc = libusb20_dev_get_device_desc(*dev);
- if (desc == NULL)
- continue;
-
- if (desc->idVendor != vendor_id ||
- desc->idProduct != product_id)
- continue;
-
- break;
- }
-
- return ((*dev == NULL)? -1 : 0);
-}
-
-/*
- * Download firmware
- */
-
-static int
-download_firmware(struct libusb20_device *dev, char const *firmware)
-{
- struct libusb20_transfer *bulk;
- struct LIBUSB20_CONTROL_SETUP_DECODED req;
- int fd, n, error;
- uint8_t buf[ATH3KFW_MAX_BSIZE];
-
- error = -1;
-
- if (libusb20_dev_open(dev, 1) != 0) {
- syslog(LOG_ERR, "libusb20_dev_open() failed");
- return (error);
- }
-
- if ((bulk = libusb20_tr_get_pointer(dev, 0)) == NULL) {
- syslog(LOG_ERR, "libusb20_tr_get_pointer() failed");
- goto out;
- }
-
- if (libusb20_tr_open(bulk, ATH3KFW_MAX_BSIZE, 1, ATH3KFW_BULK_EP) != 0) {
- syslog(LOG_ERR, "libusb20_tr_open(%d, 1, %d) failed",
- ATH3KFW_MAX_BSIZE, ATH3KFW_BULK_EP);
- goto out;
- }
-
- if ((fd = open(firmware, O_RDONLY)) < 0) {
- syslog(LOG_ERR, "open(%s) failed. %s",
- firmware, strerror(errno));
- goto out1;
- }
-
- n = read(fd, buf, 20);
- if (n != 20) {
- syslog(LOG_ERR, "read(%s, 20) failed. %s",
- firmware, strerror(errno));
- goto out2;
- }
-
- LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req);
- req.bmRequestType = LIBUSB20_REQUEST_TYPE_VENDOR;
- req.bRequest = ATH3KFW_REQ_DFU_DNLOAD;
- req.wLength = 20;
-
- if (libusb20_dev_request_sync(dev, &req, buf, NULL, 5000, 0) != 0) {
- syslog(LOG_ERR, "libusb20_dev_request_sync() failed");
- goto out2;
- }
-
- for (;;) {
- n = read(fd, buf, sizeof(buf));
- if (n < 0) {
- syslog(LOG_ERR, "read(%s, %d) failed. %s",
- firmware, (int) sizeof(buf), strerror(errno));
- goto out2;
- }
- if (n == 0)
- break;
-
- libusb20_tr_setup_bulk(bulk, buf, n, 3000);
- libusb20_tr_start(bulk);
-
- while (libusb20_dev_process(dev) == 0) {
- if (libusb20_tr_pending(bulk) == 0)
- break;
-
- libusb20_dev_wait_process(dev, -1);
- }
-
- if (libusb20_tr_get_status(bulk) != 0) {
- syslog(LOG_ERR, "bulk transfer failed with status %d",
- libusb20_tr_get_status(bulk));
- goto out2;
- }
- }
-
- error = 0;
-out2:
- close(fd);
-out1:
- libusb20_tr_close(bulk);
-out:
- libusb20_dev_close(dev);
-
- return (error);
-}
-
-/*
- * Display usage and exit
- */
-
-static void
-usage(void)
-{
- printf(
-"Usage: %s -d ugenX.Y -f firmware_file\n"
-"Usage: %s -h\n" \
-"Where:\n" \
-"\t-d ugenX.Y ugen device name\n" \
-"\t-f firmware image firmware image file name for download\n" \
-"\t-v vendor_id vendor id\n" \
-"\t-p vendor_id product id\n" \
-"\t-h display this message\n", ATH3KFW, ATH3KFW);
-
- exit(255);
-}
-
diff --git a/usr.sbin/bluetooth/ath3kfw/main.c b/usr.sbin/bluetooth/ath3kfw/main.c
new file mode 100644
index 0000000..6ca5206
--- /dev/null
+++ b/usr.sbin/bluetooth/ath3kfw/main.c
@@ -0,0 +1,394 @@
+/*-
+ * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
+ * All rights reserved.
+ *
+ * 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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <err.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <libusb.h>
+
+#include "ath3k_fw.h"
+#include "ath3k_hw.h"
+#include "ath3k_dbg.h"
+
+#define _DEFAULT_ATH3K_FIRMWARE_PATH "/usr/share/firmware/ath3k/"
+
+int ath3k_do_debug = 0;
+int ath3k_do_info = 0;
+
+struct ath3k_devid {
+ uint16_t product_id;
+ uint16_t vendor_id;
+ int is_3012;
+};
+
+static struct ath3k_devid ath3k_list[] = {
+
+ /* Atheros AR3012 with sflash firmware*/
+ { .vendor_id = 0x0489, .product_id = 0xe04e, .is_3012 = 1 },
+ { .vendor_id = 0x0489, .product_id = 0xe04d, .is_3012 = 1 },
+ { .vendor_id = 0x0489, .product_id = 0xe056, .is_3012 = 1 },
+ { .vendor_id = 0x0489, .product_id = 0xe057, .is_3012 = 1 },
+ { .vendor_id = 0x0489, .product_id = 0xe05f, .is_3012 = 1 },
+ { .vendor_id = 0x04c5, .product_id = 0x1330, .is_3012 = 1 },
+ { .vendor_id = 0x04ca, .product_id = 0x3004, .is_3012 = 1 },
+ { .vendor_id = 0x04ca, .product_id = 0x3005, .is_3012 = 1 },
+ { .vendor_id = 0x04ca, .product_id = 0x3006, .is_3012 = 1 },
+ { .vendor_id = 0x04ca, .product_id = 0x3008, .is_3012 = 1 },
+ { .vendor_id = 0x04ca, .product_id = 0x300b, .is_3012 = 1 },
+ { .vendor_id = 0x0930, .product_id = 0x0219, .is_3012 = 1 },
+ { .vendor_id = 0x0930, .product_id = 0x0220, .is_3012 = 1 },
+ { .vendor_id = 0x0b05, .product_id = 0x17d0, .is_3012 = 1 },
+ { .vendor_id = 0x0CF3, .product_id = 0x0036, .is_3012 = 1 },
+ { .vendor_id = 0x0cf3, .product_id = 0x3004, .is_3012 = 1 },
+ { .vendor_id = 0x0cf3, .product_id = 0x3005, .is_3012 = 1 },
+ { .vendor_id = 0x0cf3, .product_id = 0x3008, .is_3012 = 1 },
+ { .vendor_id = 0x0cf3, .product_id = 0x311D, .is_3012 = 1 },
+ { .vendor_id = 0x0cf3, .product_id = 0x311E, .is_3012 = 1 },
+ { .vendor_id = 0x0cf3, .product_id = 0x311F, .is_3012 = 1 },
+ { .vendor_id = 0x0cf3, .product_id = 0x3121, .is_3012 = 1 },
+ { .vendor_id = 0x0CF3, .product_id = 0x817a, .is_3012 = 1 },
+ { .vendor_id = 0x0cf3, .product_id = 0xe004, .is_3012 = 1 },
+ { .vendor_id = 0x0cf3, .product_id = 0xe005, .is_3012 = 1 },
+ { .vendor_id = 0x0cf3, .product_id = 0xe003, .is_3012 = 1 },
+ { .vendor_id = 0x13d3, .product_id = 0x3362, .is_3012 = 1 },
+ { .vendor_id = 0x13d3, .product_id = 0x3375, .is_3012 = 1 },
+ { .vendor_id = 0x13d3, .product_id = 0x3393, .is_3012 = 1 },
+ { .vendor_id = 0x13d3, .product_id = 0x3402, .is_3012 = 1 },
+
+ /* Atheros AR5BBU22 with sflash firmware */
+ { .vendor_id = 0x0489, .product_id = 0xE036, .is_3012 = 1 },
+ { .vendor_id = 0x0489, .product_id = 0xE03C, .is_3012 = 1 },
+};
+
+static int
+ath3k_is_3012(struct libusb_device_descriptor *d)
+{
+ int i;
+
+ /* Search looking for whether it's an AR3012 */
+ for (i = 0; i < (int) nitems(ath3k_list); i++) {
+ if ((ath3k_list[i].product_id == d->idProduct) &&
+ (ath3k_list[i].vendor_id == d->idVendor)) {
+ fprintf(stderr, "%s: found AR3012\n", __func__);
+ return (ath3k_list[i].is_3012);
+ }
+ }
+
+ /* Not found */
+ return (0);
+}
+
+static libusb_device *
+ath3k_find_device(libusb_context *ctx, int bus_id, int dev_id)
+{
+ libusb_device **list, *dev = NULL, *found = NULL;
+ ssize_t cnt, i;
+
+ cnt = libusb_get_device_list(ctx, &list);
+ if (cnt < 0) {
+ ath3k_err("%s: libusb_get_device_list() failed: code %lld\n",
+ __func__,
+ (long long int) cnt);
+ return (NULL);
+ }
+
+ /*
+ * XXX TODO: match on the vendor/product id too!
+ */
+ for (i = 0; i < cnt; i++) {
+ dev = list[i];
+ if (bus_id == libusb_get_bus_number(dev) &&
+ dev_id == libusb_get_device_address(dev)) {
+ /*
+ * 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 int
+ath3k_init_ar3012(libusb_device_handle *hdl, const char *fw_path)
+{
+ int ret;
+
+ ret = ath3k_load_patch(hdl, fw_path);
+ if (ret < 0) {
+ ath3k_err("Loading patch file failed\n");
+ return (ret);
+ }
+
+ ret = ath3k_load_syscfg(hdl, fw_path);
+ if (ret < 0) {
+ ath3k_err("Loading sysconfig file failed\n");
+ return (ret);
+ }
+
+ ret = ath3k_set_normal_mode(hdl);
+ if (ret < 0) {
+ ath3k_err("Set normal mode failed\n");
+ return (ret);
+ }
+
+ ath3k_switch_pid(hdl);
+ return (0);
+}
+
+static int
+ath3k_init_firmware(libusb_device_handle *hdl, const char *file_prefix)
+{
+ struct ath3k_firmware fw;
+ char fwname[FILENAME_MAX];
+ int ret;
+
+ /* XXX path info? */
+ snprintf(fwname, FILENAME_MAX, "%s/ath3k-1.fw", file_prefix);
+
+ ath3k_debug("%s: loading ath3k-1.fw\n", __func__);
+
+ /* Read in the firmware */
+ if (ath3k_fw_read(&fw, fwname) <= 0) {
+ fprintf(stderr, "%s: ath3k_fw_read() failed\n",
+ __func__);
+ return (-1);
+ }
+
+ /* Load in the firmware */
+ ret = ath3k_load_fwfile(hdl, &fw);
+
+ /* free it */
+ ath3k_fw_free(&fw);
+
+ return (0);
+}
+
+/*
+ * 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: ath3kfw (-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[])
+{
+ struct libusb_device_descriptor d;
+ libusb_context *ctx;
+ libusb_device *dev;
+ libusb_device_handle *hdl;
+ unsigned char state;
+ struct ath3k_version ver;
+ int r;
+ uint8_t bus_id = 0, dev_id = 0;
+ int devid_set = 0;
+ int n;
+ char *firmware_path = NULL;
+ int is_3012 = 0;
+
+ /* libusb setup */
+ r = libusb_init(&ctx);
+ if (r != 0) {
+ ath3k_err("%s: libusb_init failed: code %d\n",
+ argv[0],
+ r);
+ exit(127);
+ }
+
+ /* Enable debugging, just because */
+ libusb_set_debug(ctx, 3);
+
+ /* 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':
+ ath3k_do_debug = 1;
+ break;
+ case 'f': /* firmware path */
+ if (firmware_path)
+ free(firmware_path);
+ firmware_path = strdup(optarg);
+ break;
+ case 'I':
+ ath3k_do_info = 1;
+ break;
+ case 'h':
+ default:
+ usage();
+ break;
+ /* NOT REACHED */
+ }
+ }
+
+ /* Ensure the devid was given! */
+ if (devid_set == 0) {
+ usage();
+ /* NOTREACHED */
+ }
+
+ ath3k_debug("%s: opening dev %d.%d\n",
+ basename(argv[0]),
+ (int) bus_id,
+ (int) dev_id);
+
+ /* Find a device based on the bus/dev id */
+ dev = ath3k_find_device(ctx, bus_id, dev_id);
+ if (dev == NULL) {
+ ath3k_err("%s: device not found\n", __func__);
+ /* XXX cleanup? */
+ exit(1);
+ }
+
+ /* Get the device descriptor for this device entry */
+ r = libusb_get_device_descriptor(dev, &d);
+ if (r != 0) {
+ warn("%s: libusb_get_device_descriptor: %s\n",
+ __func__,
+ libusb_strerror(r));
+ exit(1);
+ }
+
+ /* See if its an AR3012 */
+ if (ath3k_is_3012(&d)) {
+ is_3012 = 1;
+
+ /* If it's bcdDevice > 1, don't attach */
+ if (d.bcdDevice > 0x0001) {
+ ath3k_debug("%s: AR3012; bcdDevice=%d, exiting\n",
+ __func__,
+ d.bcdDevice);
+ exit(0);
+ }
+ }
+
+ /* 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) {
+ ath3k_err("%s: libusb_open() failed: code %d\n", __func__, r);
+ /* XXX cleanup? */
+ exit(1);
+ }
+
+ /*
+ * Get the initial NIC state.
+ */
+ r = ath3k_get_state(hdl, &state);
+ if (r == 0) {
+ ath3k_err("%s: ath3k_get_state() failed!\n", __func__);
+ /* XXX cleanup? */
+ exit(1);
+ }
+ ath3k_debug("%s: state=0x%02x\n",
+ __func__,
+ (int) state);
+
+ /* And the version */
+ r = ath3k_get_version(hdl, &ver);
+ if (r == 0) {
+ ath3k_err("%s: ath3k_get_version() failed!\n", __func__);
+ /* XXX cleanup? */
+ exit(1);
+ }
+ ath3k_info("ROM version: %d, build version: %d, ram version: %d, "
+ "ref clock=%d\n",
+ ver.rom_version,
+ ver.build_version,
+ ver.ram_version,
+ ver.ref_clock);
+
+ /* Default the firmware path */
+ if (firmware_path == NULL)
+ firmware_path = strdup(_DEFAULT_ATH3K_FIRMWARE_PATH);
+
+ if (is_3012) {
+ (void) ath3k_init_ar3012(hdl, firmware_path);
+ } else {
+ (void) ath3k_init_firmware(hdl, firmware_path);
+ }
+
+ /* Shutdown */
+ libusb_close(hdl);
+ hdl = NULL;
+
+ libusb_unref_device(dev);
+ dev = NULL;
+
+ libusb_exit(ctx);
+ ctx = NULL;
+}
OpenPOWER on IntegriCloud