. add support for loading code + config of AR3012 based chips
. make it easy to add vendor and product ids for similar hardware
This commit is contained in:
parent
bf96118089
commit
65b7149d79
@ -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 self, void *aux)
|
||||
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;
|
||||
}
|
||||
|
24
sys/dev/usb/aubtfwlreg.h
Normal file
24
sys/dev/usb/aubtfwlreg.h
Normal file
@ -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];
|
||||
};
|
Loading…
Reference in New Issue
Block a user