Module Name:    src
Committed By:   aymeric
Date:           Thu May  9 12:44:31 UTC 2013

Modified Files:
        src/sys/dev/usb: aubtfwl.c
Added Files:
        src/sys/dev/usb: aubtfwlreg.h

Log Message:
. add support for loading code + config of AR3012 based chips
. make it easy to add vendor and product ids for similar hardware


To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 src/sys/dev/usb/aubtfwl.c
cvs rdiff -u -r0 -r1.1 src/sys/dev/usb/aubtfwlreg.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/usb/aubtfwl.c
diff -u src/sys/dev/usb/aubtfwl.c:1.4 src/sys/dev/usb/aubtfwl.c:1.5
--- src/sys/dev/usb/aubtfwl.c:1.4	Thu Dec 27 16:42:32 2012
+++ src/sys/dev/usb/aubtfwl.c	Thu May  9 12:44:31 2013
@@ -1,4 +1,4 @@
-/* $NetBSD: aubtfwl.c,v 1.4 2012/12/27 16:42:32 skrll Exp $ */
+/* $NetBSD: aubtfwl.c,v 1.5 2013/05/09 12:44:31 aymeric Exp $ */
 
 /*
  * Copyright (c) 2011 Jonathan A. Kollasch
@@ -27,16 +27,18 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: aubtfwl.c,v 1.4 2012/12/27 16:42:32 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: aubtfwl.c,v 1.5 2013/05/09 12:44:31 aymeric Exp $");
 
 #include <sys/param.h>
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdevs.h>
 #include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
 #include <dev/usb/usbdi_util.h>
 #include <dev/firmload.h>
 
-#define AR3K_FIRMWARE_HEADER_SIZE 20
+#include <dev/usb/aubtfwlreg.h>
+
 #define AR3K_FIRMWARE_CHUNK_SIZE 4096
 
 static int aubtfwl_match(device_t, cfdata_t, void *);
@@ -46,19 +48,33 @@ static void aubtfwl_attach_hook(device_t
 
 struct aubtfwl_softc {
 	usbd_device_handle sc_udev;
+	int sc_flags;
+#define AUBT_IS_AR3012		1
 };
 
 CFATTACH_DECL_NEW(aubtfwl, sizeof(struct aubtfwl_softc), aubtfwl_match, aubtfwl_attach, aubtfwl_detach, NULL);
 
+static const struct usb_devno ar3k_devs[] = {
+	{ USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR3011 },
+};
+
+static const struct usb_devno ar3k12_devs[] = {
+	{ USB_VENDOR_FOXCONN, USB_PRODUCT_FOXCONN_AR3012 },
+};
+
 static int
 aubtfwl_match(device_t parent, cfdata_t match, void *aux)
 {
 	const struct usb_attach_arg * const uaa = aux;
 
-	if (uaa->vendor == USB_VENDOR_ATHEROS2 &&
-	    uaa->product == USB_PRODUCT_ATHEROS2_AR3011)
+	if (usb_lookup(ar3k_devs, uaa->vendor, uaa->product))
 		return UMATCH_VENDOR_PRODUCT;
 
+	if (usb_lookup(ar3k12_devs, uaa->vendor, uaa->product)) {
+		return (UGETW(uaa->device->ddesc.bcdDevice) > 1)?
+			UMATCH_NONE : UMATCH_VENDOR_PRODUCT;
+	}
+
 	return UMATCH_NONE;
 }
 
@@ -70,6 +86,10 @@ aubtfwl_attach(device_t parent, device_t
 	aprint_naive("\n");
 	aprint_normal("\n");
 	sc->sc_udev = uaa->device;
+	sc->sc_flags = 0;
+
+	if (usb_lookup(ar3k12_devs, uaa->vendor, uaa->product))
+		sc->sc_flags |= AUBT_IS_AR3012;
 
 	config_mountroot(self, aubtfwl_attach_hook);
 }
@@ -84,27 +104,27 @@ aubtfwl_detach(device_t self, int flags)
 	return 0;
 }
 
-static void
-aubtfwl_attach_hook(device_t self)
-{
+/* Returns 0 if firmware was correctly loaded */
+static int
+aubtfwl_firmware_load(device_t self, const char *name) {
 	struct aubtfwl_softc * const sc = device_private(self);
 	usbd_interface_handle iface;
 	usbd_pipe_handle pipe;
 	usbd_xfer_handle xfer;
 	void *buf;
 	usb_device_request_t req;
-	int error;
+	int error = 0;
 	firmware_handle_t fwh;
 	size_t fws;
 	size_t fwo = 0;
 	uint32_t n;
-	
-	memset(&req, 0, sizeof(req));
 
-	error = firmware_open("ubt", "ath3k-1.fw", &fwh); /* XXX revisit name */
+	memset(&req, 0, sizeof req);
+
+	error = firmware_open("ubt", name, &fwh);
 	if (error != 0) {
-		aprint_error_dev(self, "ath3k-1.fw open fail %d\n", error);
-		return;
+		aprint_error_dev(self, "'%s' open fail %d\n", name, error);
+		return error;
 	}
 	fws = firmware_get_size(fwh);
 
@@ -132,12 +152,14 @@ aubtfwl_attach_hook(device_t self)
 	xfer = usbd_alloc_xfer(sc->sc_udev);
 	if (xfer == NULL) {
 		aprint_error_dev(self, "failed to alloc xfer\n");
+		error = 1;
 		goto out_pipe;
 	}
 
-	buf = usbd_alloc_buffer(xfer, 4096);
+	buf = usbd_alloc_buffer(xfer, AR3K_FIRMWARE_CHUNK_SIZE);
 	if (buf == NULL) {
 		aprint_error_dev(self, "failed to alloc buffer\n");
+		error = 1;
 		goto out_xfer;
 	}
 
@@ -147,7 +169,7 @@ aubtfwl_attach_hook(device_t self)
 		goto out_xfer;
 	}
 
-	req.bRequest = 1;
+	req.bRequest = AR3K_SEND_FIRMWARE;
 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
 	USETW(req.wValue, 0);
 	USETW(req.wIndex, 0);
@@ -158,7 +180,7 @@ aubtfwl_attach_hook(device_t self)
 	error = usbd_do_request(sc->sc_udev, &req, buf);
 	if (error != 0) {
 		aprint_error_dev(self, "%s\n", usbd_errstr(error));
-		return;
+		return error;
 	}
 	fwo = AR3K_FIRMWARE_HEADER_SIZE;
 
@@ -166,7 +188,7 @@ aubtfwl_attach_hook(device_t self)
 		n = min(AR3K_FIRMWARE_CHUNK_SIZE, fws - fwo);
 		error = firmware_read(fwh, fwo, buf, n);
 		if (error != 0) {
-			break;;
+			break;
 		}
 		error = usbd_bulk_transfer(xfer, pipe,
 		    USBD_NO_COPY, USBD_DEFAULT_TIMEOUT,
@@ -174,11 +196,13 @@ aubtfwl_attach_hook(device_t self)
 		if (error != USBD_NORMAL_COMPLETION) {
 			aprint_error_dev(self, "xfer failed, %s\n",
 			   usbd_errstr(error));
-			break;;
+			break;
 		}
 		fwo += n;
 	}
-	aprint_verbose_dev(self, "firmware load complete\n");
+
+	if (error == 0)
+		aprint_verbose_dev(self, "firmware load complete\n");
 
 out_xfer:
 	usbd_free_xfer(xfer);
@@ -187,5 +211,133 @@ out_pipe:
 out_firmware:
 	firmware_close(fwh);
 
+	return !!error;
+}
+
+static int
+aubtfwl_get_state(struct aubtfwl_softc *sc, uint8_t *state) {
+	usb_device_request_t req;
+	int error = 0;
+
+	memset(&req, 0, sizeof req);
+
+	req.bRequest = AR3K_GET_STATE;
+	req.bmRequestType = UT_READ_VENDOR_DEVICE;
+	USETW(req.wValue, 0);
+	USETW(req.wIndex, 0);
+	USETW(req.wLength, sizeof *state);
+
+	error = usbd_do_request(sc->sc_udev, &req, state);
+
+	return error;
+}
+
+static int
+aubtfwl_get_version(struct aubtfwl_softc *sc, struct ar3k_version *ver) {
+	usb_device_request_t req;
+	int error = 0;
+
+	memset(&req, 0, sizeof req);
+
+	req.bRequest = AR3K_GET_VERSION;
+	req.bmRequestType = UT_READ_VENDOR_DEVICE;
+	USETW(req.wValue, 0);
+	USETW(req.wIndex, 0);
+	USETW(req.wLength, sizeof *ver);
+
+	error = usbd_do_request(sc->sc_udev, &req, ver);
+
+#if BYTE_ORDER == BIG_ENDIAN
+	if (error == USBD_NORMAL_COMPLETION) {
+		ver->rom = bswap32(ver->rom);
+		ver->build = bswap32(ver->build);
+		ver->ram = bswap32(ver->ram);
+	}
+#endif
+	return error;
+}
+
+static int
+aubtfwl_send_command(struct aubtfwl_softc *sc, uByte cmd) {
+	usb_device_request_t req;
+	int error = 0;
+
+	memset(&req, 0, sizeof req);
+
+	req.bRequest = cmd;
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	USETW(req.wValue, 0);
+	USETW(req.wIndex, 0);
+	USETW(req.wLength, 0);
+
+	error = usbd_do_request(sc->sc_udev, &req, NULL);
+
+	return error;
+}
+
+static void
+aubtfwl_attach_hook(device_t self)
+{
+	struct aubtfwl_softc * const sc = device_private(self);
+	char firmware_name[MAXPATHLEN+1];
+	struct ar3k_version ver;
+	uint8_t state;
+	int clock = 0;
+	int error = 0;
+
+	if (sc->sc_flags & AUBT_IS_AR3012) {
+		error = aubtfwl_get_version(sc, &ver);
+		if (!error)
+			error = aubtfwl_get_state(sc, &state);
+
+		if (error) {
+			aprint_error_dev(self,
+				"couldn't get version or state\n");
+			return;
+		}
+
+		aprint_verbose_dev(self, "state is 0x%02x\n", state);
+
+		if (!(state & AR3K_STATE_IS_PATCHED)) {
+			snprintf(firmware_name, sizeof firmware_name,
+				"ar3k/AthrBT_0x%08x.dfu", ver.rom);
+			error = aubtfwl_firmware_load(self, firmware_name);
+
+			if (error)
+				return;
+		}
+
+		switch (ver.clock) {
+		case AR3K_CLOCK_19M:
+			clock = 19;
+			break;
+		case AR3K_CLOCK_26M:
+			clock = 26;
+			break;
+		case AR3K_CLOCK_40M:
+			clock = 40;
+			break;
+		}
+
+		snprintf(firmware_name, sizeof firmware_name,
+			"ar3k/ramps_0x%08x_%d.dfu", ver.rom, clock);
+		aubtfwl_firmware_load(self, firmware_name);
+
+		if ((state & AR3K_STATE_MODE_MASK) != AR3K_STATE_MODE_NORMAL) {
+			error = aubtfwl_send_command(sc, AR3K_SET_NORMAL_MODE);
+			if (error) {
+				aprint_error_dev(self,
+					"couldn't set normal mode: %s",
+					usbd_errstr(error));
+				return;
+			}
+		}
+
+		/* Apparently some devices will fail this, so ignore result */
+		(void) aubtfwl_send_command(sc, AR3K_SWITCH_VID_PID);
+	} else {
+		aubtfwl_firmware_load(self, "ath3k-1.fw");
+	}
+
 	return;
 }

Added files:

Index: src/sys/dev/usb/aubtfwlreg.h
diff -u /dev/null src/sys/dev/usb/aubtfwlreg.h:1.1
--- /dev/null	Thu May  9 12:44:31 2013
+++ src/sys/dev/usb/aubtfwlreg.h	Thu May  9 12:44:31 2013
@@ -0,0 +1,24 @@
+
+#define AR3K_FIRMWARE_HEADER_SIZE 20
+
+#define AR3K_SEND_FIRMWARE	1
+#define AR3K_GET_STATE		5
+#define AR3K_SET_NORMAL_MODE	7
+#define AR3K_GET_VERSION	9
+#define AR3K_SWITCH_VID_PID	10
+
+#define AR3K_STATE_MODE_MASK	0x3f
+#define AR3K_STATE_MODE_NORMAL	14
+#define AR3K_STATE_IS_SYSCFGED	0x40
+#define AR3K_STATE_IS_PATCHED	0x80
+
+struct ar3k_version {
+	uint32_t rom;
+	uint32_t build;
+	uint32_t ram;
+	uint8_t clock;
+#define AR3K_CLOCK_26M		0
+#define AR3K_CLOCK_40M		1
+#define AR3K_CLOCK_19M		2
+	uint8_t pad[7];
+};

Reply via email to