All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] Driver for the ar5523 chipset
@ 2012-10-11 20:01 Pontus Fuchs
  2012-10-11 20:01 ` [PATCH 1/4] ar5523: Add main driver file Pontus Fuchs
                   ` (5 more replies)
  0 siblings, 6 replies; 27+ messages in thread
From: Pontus Fuchs @ 2012-10-11 20:01 UTC (permalink / raw)
  To: gregkh; +Cc: linux-wireless, hch, s.L-H, Pontus Fuchs

Some time ago I found an old USB wireless dongle in my drawer. I was
surprised it wasn't supported in Linux. Google gave me an old
unfinished driver created by Christoph Hellwig. Armed with some
spare time and naivity I picked up where Christoph left in 2007 and
now it's actually working pretty fine. It does not crash for me and
throughput is decent.

It's based on the FreeBSD driver, which in turn is based on the
reverse engineered Windows driver, hence a very limited feature set.
Not even HW crypto offload is supported. But at least there is a
firmware which has a suitable license:

http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/contrib/dev/uath/ar5523.bin.uu

Pontus Fuchs (4):
  ar5523: Add main driver file
  ar5523: Add driver header file
  ar5523: Add Firmware API header file
  ar5523: Add Kconfig and Makefile

 drivers/staging/Kconfig            |    2 +
 drivers/staging/Makefile           |    1 +
 drivers/staging/ar5523/Kconfig     |    7 +
 drivers/staging/ar5523/Makefile    |    1 +
 drivers/staging/ar5523/ar5523.c    | 1824 ++++++++++++++++++++++++++++++++++++
 drivers/staging/ar5523/ar5523.h    |  164 ++++
 drivers/staging/ar5523/ar5523_hw.h |  447 +++++++++
 7 files changed, 2446 insertions(+)
 create mode 100644 drivers/staging/ar5523/Kconfig
 create mode 100644 drivers/staging/ar5523/Makefile
 create mode 100644 drivers/staging/ar5523/ar5523.c
 create mode 100644 drivers/staging/ar5523/ar5523.h
 create mode 100644 drivers/staging/ar5523/ar5523_hw.h

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 1/4] ar5523: Add main driver file
  2012-10-11 20:01 [PATCH 0/4] Driver for the ar5523 chipset Pontus Fuchs
@ 2012-10-11 20:01 ` Pontus Fuchs
  2012-10-15 20:54   ` Luis R. Rodriguez
  2012-10-11 20:01 ` [PATCH 2/4] ar5523: Add driver header file Pontus Fuchs
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 27+ messages in thread
From: Pontus Fuchs @ 2012-10-11 20:01 UTC (permalink / raw)
  To: gregkh; +Cc: linux-wireless, hch, s.L-H, Pontus Fuchs

This driver is for the AR5523 chipset from Atheros. It was created
in 2007 by Christoph Hellwig but it was never finished. I found it a
couple if months ago and after some polishing it's working pretty
fine.

The driver was written with the FreeBSD driver (uath) as reference,
which was written with the reverse-engineered windows driver as
reference, hence the feature set is very limited. Station mode
only, no HW crypto offload.

Signed-off-by: Pontus Fuchs <pontus.fuchs@gmail.com>
---
 drivers/staging/ar5523/ar5523.c | 1824 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 1824 insertions(+)
 create mode 100644 drivers/staging/ar5523/ar5523.c

diff --git a/drivers/staging/ar5523/ar5523.c b/drivers/staging/ar5523/ar5523.c
new file mode 100644
index 0000000..56f3ace
--- /dev/null
+++ b/drivers/staging/ar5523/ar5523.c
@@ -0,0 +1,1824 @@
+/*
+ * Copyright (c) 2012 Pontus Fuchs <pontus.fuchs@gmail.com>
+ *
+ *  This file is free software: you may copy, redistribute and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation, either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ *  This file is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Driver for Atheros AR5005UG/AR5005UX chipsets.
+ *
+ * Copyright (c) 2006
+ *	Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2007
+ *	Christoph Hellwig <hch@lst.de>
+ *
+ * Copyright (c) 2006 Sam Leffler, Errno Consulting
+ * Copyright (c) 2008-2009 Weongyo Jeong <weongyo@freebsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This driver is based on the uath driver written by Damien Bergamini for
+ * OpenBSD, who did black-box analysis of the Windows binary driver to find
+ * out how the hardware works.  It contains a lot magic numbers because of
+ * that and only has minimal functionality.
+ */
+#include <linux/compiler.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/completion.h>
+#include <linux/firmware.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <net/mac80211.h>
+
+#include "ar5523.h"
+#include "ar5523_hw.h"
+
+/*
+ * Various supported device vendors/products.
+ * UB51: AR5005UG 802.11b/g, UB52: AR5005UX 802.11a/b/g
+ */
+
+static int ar5523_submit_rx_cmd(struct ar5523 *ar);
+static void ar5523_data_tx_pkt_put(struct ar5523 *ar);
+
+static void ar5523_read_reply(struct ar5523 *ar, struct ar5523_cmd_hdr *hdr,
+			      struct ar5523_tx_cmd *cmd)
+{
+	int dlen, olen;
+	u32 *rp;
+
+	dlen = hdr->len - sizeof(*hdr);
+
+	if (dlen < 0) {
+		WARN_ON(1);
+		goto out;
+	}
+
+	ar5523_dbg(ar, "Code = %d len = %d\n", hdr->code & 0xff, dlen);
+
+	rp = (u32 *)(hdr + 1);
+	if (dlen >= sizeof(u32)) {
+		olen = be32_to_cpu(rp[0]);
+		dlen -= sizeof(u32);
+		if (olen == 0) {
+			/* convention is 0 =>'s one word */
+			olen = sizeof(u32);
+		}
+	} else
+		olen = 0;
+
+	if (cmd->odata) {
+		if (cmd->olen < olen) {
+			ar5523_err(ar, "olen to small %d < %d\n",
+				   cmd->olen, olen);
+			cmd->olen = 0;
+			cmd->res = -EOVERFLOW;
+		} else {
+			cmd->olen = olen;
+			memcpy(cmd->odata, &rp[1], olen);
+			cmd->res = 0;
+		}
+	}
+
+out:
+	complete(&cmd->done);
+}
+
+static void ar5523_cmd_rx_cb(struct urb *urb)
+{
+	struct ar5523 *ar = urb->context;
+	struct ar5523_tx_cmd *cmd = &ar->tx_cmd;
+	struct ar5523_cmd_hdr *hdr = ar->rx_cmd_buf;
+	int dlen;
+
+	if (urb->status) {
+		if (urb->status != -ESHUTDOWN)
+			ar5523_err(ar, "RX USB error %d.\n", urb->status);
+		goto skip;
+	}
+
+	if (urb->actual_length < sizeof(struct ar5523_cmd_hdr)) {
+		ar5523_err(ar, "RX USB to short.\n");
+		goto skip;
+	}
+
+	ar5523_dbg(ar, "%s code %02x priv %d\n", __func__,
+		   be32_to_cpu(hdr->code) & 0xff, hdr->priv);
+
+	hdr->code = be32_to_cpu(hdr->code);
+	hdr->len = be32_to_cpu(hdr->len);
+
+	switch (hdr->code & 0xff) {
+	default:
+		/* reply to a read command */
+		if (hdr->priv != AR5523_CMD_ID) {
+			ar5523_err(ar, "Unexpected command id: %02x\n",
+				   hdr->code & 0xff);
+			goto skip;
+		}
+		ar5523_read_reply(ar, hdr, cmd);
+		break;
+
+	case WDCMSG_DEVICE_AVAIL:
+		ar5523_dbg(ar, "WDCMSG_DEVICE_AVAIL\n");
+		cmd->res = 0;
+		cmd->olen = 0;
+		complete(&cmd->done);
+		break;
+
+	case WDCMSG_SEND_COMPLETE:
+		ar5523_dbg(ar, "WDCMSG_SEND_COMPLETE: %d pending\n",
+			atomic_read(&ar->tx_nr_pending));
+		if (!test_bit(AR5523_HW_UP, &ar->flags))
+			ar5523_dbg(ar, "Unexpected WDCMSG_SEND_COMPLETE\n");
+		else {
+			mod_timer(&ar->tx_wd_timer,
+				  jiffies + AR5523_TX_WD_TIMEOUT);
+			ar5523_data_tx_pkt_put(ar);
+
+		}
+		break;
+
+	case WDCMSG_TARGET_START:
+		/* This command returns a bogus id so it needs special
+		   handling */
+		dlen = hdr->len - sizeof(*hdr);
+		if (dlen != (int)sizeof(u32)) {
+			ar5523_err(ar, "Invalid reply to WDCMSG_TARGET_START");
+			return;
+		}
+		memcpy(cmd->odata, hdr + 1, sizeof(u32));
+		cmd->olen = sizeof(u32);
+		cmd->res = 0;
+		complete(&cmd->done);
+		break;
+
+	case WDCMSG_STATS_UPDATE:
+		ar5523_dbg(ar, "WDCMSG_STATS_UPDATE\n");
+		break;
+	}
+
+skip:
+	ar5523_submit_rx_cmd(ar);
+}
+
+static int ar5523_alloc_rx_cmd(struct ar5523 *ar)
+{
+	ar->rx_cmd_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ar->rx_cmd_urb)
+		return -ENOMEM;
+
+	ar->rx_cmd_buf = usb_alloc_coherent(ar->dev, AR5523_MAX_RXCMDSZ,
+					    GFP_KERNEL,
+					    &ar->rx_cmd_urb->transfer_dma);
+	if (!ar->rx_cmd_buf) {
+		usb_free_urb(ar->rx_cmd_urb);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static void ar5523_cancel_rx_cmd(struct ar5523 *ar)
+{
+	usb_kill_urb(ar->rx_cmd_urb);
+}
+
+static void ar5523_free_rx_cmd(struct ar5523 *ar)
+{
+	usb_free_coherent(ar->dev, AR5523_MAX_RXCMDSZ,
+			  ar->rx_cmd_buf, ar->rx_cmd_urb->transfer_dma);
+	usb_free_urb(ar->rx_cmd_urb);
+}
+
+static int ar5523_submit_rx_cmd(struct ar5523 *ar)
+{
+	int error;
+
+	usb_fill_bulk_urb(ar->rx_cmd_urb, ar->dev,
+			  ar5523_cmd_rx_pipe(ar->dev), ar->rx_cmd_buf,
+			  AR5523_MAX_RXCMDSZ, ar5523_cmd_rx_cb, ar);
+	ar->rx_cmd_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	error = usb_submit_urb(ar->rx_cmd_urb, GFP_ATOMIC);
+	if (error) {
+		if (error != -ENODEV)
+			ar5523_err(ar, "error %d when submitting rx urb\n",
+				   error);
+		return error;
+	}
+	return 0;
+}
+
+/*
+ * Command submitted cb
+ */
+static void ar5523_cmd_tx_cb(struct urb *urb)
+{
+	struct ar5523_tx_cmd *cmd = urb->context;
+	struct ar5523 *ar = cmd->ar;
+
+	if (urb->status) {
+		ar5523_err(ar, "Failed to TX command. Status = %d\n",
+			   urb->status);
+		cmd->res = urb->status;
+		complete(&cmd->done);
+		return;
+	}
+
+	if (!(cmd->flags & AR5523_CMD_FLAG_READ)) {
+		cmd->res = 0;
+		complete(&cmd->done);
+	}
+}
+
+static int ar5523_cmd(struct ar5523 *ar, u32 code, const void *idata,
+		      int ilen, void *odata, int olen, int flags)
+{
+	struct ar5523_cmd_hdr *hdr;
+	struct ar5523_tx_cmd *cmd = &ar->tx_cmd;
+	int xferlen, error;
+
+	/* always bulk-out a multiple of 4 bytes */
+	xferlen = (sizeof(struct ar5523_cmd_hdr) + ilen + 3) & ~3;
+
+	hdr = (struct ar5523_cmd_hdr *)cmd->buf_tx;
+	memset(hdr, 0, sizeof(struct ar5523_cmd_hdr));
+	hdr->len  = cpu_to_be32(xferlen);
+	hdr->code = cpu_to_be32(code);
+	hdr->priv = AR5523_CMD_ID;
+
+	if (flags & AR5523_CMD_FLAG_MAGIC)
+		hdr->magic = cpu_to_be32(1 << 24);
+	memcpy(hdr + 1, idata, ilen);
+
+	cmd->odata = odata;
+	cmd->olen = olen;
+	cmd->flags = flags;
+
+	ar5523_dbg(ar, "do cmd %02x\n", code);
+
+	usb_fill_bulk_urb(cmd->urb_tx, ar->dev, ar5523_cmd_tx_pipe(ar->dev),
+			  cmd->buf_tx, xferlen, ar5523_cmd_tx_cb, cmd);
+	cmd->urb_tx->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	error = usb_submit_urb(cmd->urb_tx, GFP_KERNEL);
+	if (error) {
+		ar5523_err(ar, "could not send command 0x%x, error=%d\n",
+			   code, error);
+		return error;
+	}
+
+	if (!wait_for_completion_timeout(&cmd->done, 2 * HZ)) {
+		cmd->odata = NULL;
+		ar5523_err(ar, "timeout waiting for command %02x reply\n",
+			   code);
+		cmd->res = -ETIMEDOUT;
+	}
+	return cmd->res;
+}
+
+static int ar5523_cmd_write(struct ar5523 *ar, u32 code, const void *data,
+			    int len, int flags)
+{
+	flags &= ~AR5523_CMD_FLAG_READ;
+	return ar5523_cmd(ar, code, data, len, NULL, 0, flags);
+}
+
+static int ar5523_cmd_read(struct ar5523 *ar, u32 code, const void *idata,
+			   int ilen, void *odata, int olen, int flags)
+{
+	flags |= AR5523_CMD_FLAG_READ;
+	return ar5523_cmd(ar, code, idata, ilen, odata, olen, flags);
+}
+
+static int ar5523_config(struct ar5523 *ar, u32 reg, u32 val)
+{
+	struct ar5523_write_mac write;
+	int error;
+
+	write.reg = cpu_to_be32(reg);
+	write.len = cpu_to_be32(0);	/* 0 = single write */
+	*(u32 *)write.data = cpu_to_be32(val);
+
+	error = ar5523_cmd_write(ar, WDCMSG_TARGET_SET_CONFIG, &write,
+				 3 * sizeof(u32), 0);
+	if (error != 0)
+		ar5523_err(ar, "could not write register 0x%02x\n", reg);
+	return error;
+}
+
+static int ar5523_config_multi(struct ar5523 *ar, u32 reg, const void *data,
+			       int len)
+{
+	struct ar5523_write_mac write;
+	int error;
+
+	write.reg = cpu_to_be32(reg);
+	write.len = cpu_to_be32(len);
+	memcpy(write.data, data, len);
+
+	/* properly handle the case where len is zero (reset) */
+	error = ar5523_cmd_write(ar, WDCMSG_TARGET_SET_CONFIG, &write,
+	    (len == 0) ? sizeof(u32) : 2 * sizeof(u32) + len, 0);
+	if (error != 0)
+		ar5523_err(ar, "could not write %d bytes to register 0x%02x\n",
+			   len, reg);
+	return error;
+}
+
+static int ar5523_get_status(struct ar5523 *ar, u32 which, void *odata,
+			     int olen)
+{
+	int error;
+
+	which = cpu_to_be32(which);
+	error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_STATUS,
+	    &which, sizeof(which), odata, olen, AR5523_CMD_FLAG_MAGIC);
+	if (error != 0)
+		ar5523_err(ar, "could not read EEPROM offset 0x%02x\n",
+			   be32_to_cpu(which));
+	return error;
+}
+
+static int ar5523_get_capability(struct ar5523 *ar, u32 cap, u32 *val)
+{
+	int error;
+
+	cap = cpu_to_be32(cap);
+	error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_CAPABILITY,
+	    &cap, sizeof(cap), val, sizeof(u32), AR5523_CMD_FLAG_MAGIC);
+	if (error != 0) {
+		ar5523_err(ar, "could not read capability %u\n",
+			   be32_to_cpu(cap));
+		return error;
+	}
+	*val = be32_to_cpu(*val);
+	return error;
+}
+
+static int ar5523_get_devcap(struct ar5523 *ar)
+{
+#define	GETCAP(x) do {				\
+	error = ar5523_get_capability(ar, x, &cap);		\
+	if (error != 0)					\
+		return error;				\
+	ar5523_info(ar, "Cap: "			\
+	    "%s=0x%08x\n", #x, cap);	\
+} while (0)
+	int error;
+	u32 cap;
+
+	/* collect device capabilities */
+	GETCAP(CAP_TARGET_VERSION);
+	GETCAP(CAP_TARGET_REVISION);
+	GETCAP(CAP_MAC_VERSION);
+	GETCAP(CAP_MAC_REVISION);
+	GETCAP(CAP_PHY_REVISION);
+	GETCAP(CAP_ANALOG_5GHz_REVISION);
+	GETCAP(CAP_ANALOG_2GHz_REVISION);
+
+	GETCAP(CAP_REG_DOMAIN);
+	GETCAP(CAP_REG_CAP_BITS);
+	GETCAP(CAP_WIRELESS_MODES);
+	GETCAP(CAP_CHAN_SPREAD_SUPPORT);
+	GETCAP(CAP_COMPRESS_SUPPORT);
+	GETCAP(CAP_BURST_SUPPORT);
+	GETCAP(CAP_FAST_FRAMES_SUPPORT);
+	GETCAP(CAP_CHAP_TUNING_SUPPORT);
+	GETCAP(CAP_TURBOG_SUPPORT);
+	GETCAP(CAP_TURBO_PRIME_SUPPORT);
+	GETCAP(CAP_DEVICE_TYPE);
+	GETCAP(CAP_WME_SUPPORT);
+	GETCAP(CAP_TOTAL_QUEUES);
+	GETCAP(CAP_CONNECTION_ID_MAX);
+
+	GETCAP(CAP_LOW_5GHZ_CHAN);
+	GETCAP(CAP_HIGH_5GHZ_CHAN);
+	GETCAP(CAP_LOW_2GHZ_CHAN);
+	GETCAP(CAP_HIGH_2GHZ_CHAN);
+	GETCAP(CAP_TWICE_ANTENNAGAIN_5G);
+	GETCAP(CAP_TWICE_ANTENNAGAIN_2G);
+
+	GETCAP(CAP_CIPHER_AES_CCM);
+	GETCAP(CAP_CIPHER_TKIP);
+	GETCAP(CAP_MIC_TKIP);
+	return 0;
+}
+
+static int ar5523_set_ledsteady(struct ar5523 *ar, int lednum, int ledmode)
+{
+	struct ar5523_cmd_ledsteady led;
+
+	led.lednum = cpu_to_be32(lednum);
+	led.ledmode = cpu_to_be32(ledmode);
+
+	ar5523_dbg(ar, "set %s led %s (steady)\n",
+		   (lednum == UATH_LED_LINK) ? "link" : "activity",
+		   ledmode ? "on" : "off");
+	return ar5523_cmd_write(ar, WDCMSG_SET_LED_STEADY, &led, sizeof(led),
+				 0);
+}
+
+static int ar5523_set_rxfilter(struct ar5523 *ar, u32 bits, u32 op)
+{
+	struct ar5523_cmd_rx_filter rxfilter;
+
+	rxfilter.bits = cpu_to_be32(bits);
+	rxfilter.op = cpu_to_be32(op);
+
+	ar5523_dbg(ar, "setting Rx filter=0x%x flags=0x%x\n", bits, op);
+	return ar5523_cmd_write(ar, WDCMSG_RX_FILTER, &rxfilter,
+				 sizeof(rxfilter), 0);
+}
+
+static int ar5523_reset_tx_queues(struct ar5523 *ar)
+{
+	__be32 qid = cpu_to_be32(0);
+
+	ar5523_dbg(ar, "resetting Tx queue\n");
+	return ar5523_cmd_write(ar, WDCMSG_RELEASE_TX_QUEUE,
+				 &qid, sizeof(qid), 0);
+}
+
+static int ar5523_set_chan(struct ar5523 *ar)
+{
+	struct ieee80211_conf *conf = &ar->hw->conf;
+
+	struct ar5523_cmd_reset reset;
+
+	memset(&reset, 0, sizeof(reset));
+	reset.flags |= cpu_to_be32(UATH_CHAN_2GHZ);
+	reset.flags |= cpu_to_be32(UATH_CHAN_OFDM);
+	reset.freq = cpu_to_be32(conf->channel->center_freq);
+	reset.maxrdpower = cpu_to_be32(50);	/* XXX */
+	reset.channelchange = cpu_to_be32(1);
+	reset.keeprccontent = cpu_to_be32(0);
+
+	ar5523_dbg(ar, "set chan flags 0x%x freq %d\n",
+		   be32_to_cpu(reset.flags),
+		   conf->channel->center_freq);
+	return ar5523_cmd_write(ar, WDCMSG_RESET, &reset, sizeof(reset), 0);
+}
+
+static int ar5523_queue_init(struct ar5523 *ar)
+{
+	struct ar5523_cmd_txq_setup qinfo;
+
+	ar5523_dbg(ar, "setting up Tx queue\n");
+	qinfo.qid	     = cpu_to_be32(0);
+	qinfo.len	     = cpu_to_be32(sizeof(qinfo.attr));
+	qinfo.attr.priority  = cpu_to_be32(0);	/* XXX */
+	qinfo.attr.aifs	     = cpu_to_be32(3);
+	qinfo.attr.logcwmin  = cpu_to_be32(4);
+	qinfo.attr.logcwmax  = cpu_to_be32(10);
+	qinfo.attr.bursttime = cpu_to_be32(0);
+	qinfo.attr.mode	     = cpu_to_be32(0);
+	qinfo.attr.qflags    = cpu_to_be32(1);	/* XXX? */
+	return ar5523_cmd_write(ar, WDCMSG_SETUP_TX_QUEUE, &qinfo,
+				 sizeof(qinfo), 0);
+}
+
+static int ar5523_switch_chan(struct ar5523 *ar)
+{
+	int error;
+
+	error = ar5523_set_chan(ar);
+	if (error) {
+		ar5523_err(ar, "could not set chan, error %d\n", error);
+		goto out_err;
+	}
+
+	/* reset Tx rings */
+	error = ar5523_reset_tx_queues(ar);
+	if (error) {
+		ar5523_err(ar, "could not reset Tx queues, error %d\n",
+			   error);
+		goto out_err;
+	}
+	/* set Tx rings WME properties */
+	error = ar5523_queue_init(ar);
+	if (error)
+		ar5523_err(ar, "could not init wme, error %d\n", error);
+
+out_err:
+	return error;
+}
+
+static void ar5523_rx_data_put(struct ar5523 *ar,
+				struct ar5523_rx_data *data)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&ar->rx_data_list_lock, flags);
+	list_move(&data->list, &ar->rx_data_free);
+	spin_unlock_irqrestore(&ar->rx_data_list_lock, flags);
+}
+
+static void ar5523_data_rx_cb(struct urb *urb)
+{
+	struct ar5523_rx_data *data = urb->context;
+	struct ar5523 *ar = data->ar;
+	struct ar5523_rx_desc *desc;
+	struct ar5523_chunk *chunk;
+	struct ieee80211_hw *hw = ar->hw;
+	struct ieee80211_rx_status *rx_status;
+	u32 rxlen;
+	int usblen = urb->actual_length;
+	int hdrlen, pad;
+
+	ar5523_dbg(ar, "%s\n", __func__);
+	/* sync/async unlink faults aren't errors */
+	if (urb->status) {
+		if (urb->status != -ESHUTDOWN)
+			ar5523_err(ar, "%s: USB err: %d\n", __func__,
+				   urb->status);
+		goto skip;
+	}
+
+	if (usblen < AR5523_MIN_RXBUFSZ) {
+		ar5523_err(ar, "RX: wrong xfer size (usblen=%d)\n", usblen);
+		goto skip;
+	}
+
+	chunk = (struct ar5523_chunk *) data->skb->data;
+
+	if (((chunk->flags & UATH_CFLAGS_FINAL) == 0) ||
+		chunk->seqnum != 0) {
+		ar5523_dbg(ar, "RX: No final flag. s: %d f: %02x l: %d\n",
+			   chunk->seqnum, chunk->flags,
+			   be16_to_cpu(chunk->length));
+		goto skip;
+	}
+
+	/* Rx descriptor is located at the end, 32-bit aligned */
+	desc = (struct ar5523_rx_desc *)
+		(data->skb->data + usblen - sizeof(struct ar5523_rx_desc));
+
+	rxlen = be32_to_cpu(desc->len);
+	if (rxlen > ar->rxbufsz) {
+		ar5523_dbg(ar, "RX: Bad descriptor (len=%d)\n",
+			   be32_to_cpu(desc->len));
+		goto skip;
+	}
+
+	if (!rxlen) {
+		ar5523_dbg(ar, "RX: rxlen is 0\n");
+		goto skip;
+	}
+
+	if (be32_to_cpu(desc->status) != 0) {
+		ar5523_dbg(ar, "Bad RX status (0x%x len = %d). Skip\n",
+			   be32_to_cpu(desc->status), be32_to_cpu(desc->len));
+		goto skip;
+	}
+
+	skb_reserve(data->skb, sizeof(*chunk));
+	skb_put(data->skb, rxlen - sizeof(struct ar5523_rx_desc));
+
+	hdrlen = ieee80211_get_hdrlen_from_skb(data->skb);
+	if (hdrlen & 3) {
+		ar5523_dbg(ar, "eek, alignment workaround activated\n");
+		pad = hdrlen % 4;
+		memmove(data->skb->data + pad, data->skb->data, hdrlen);
+		skb_pull(data->skb, pad);
+	}
+
+	rx_status = IEEE80211_SKB_RXCB(data->skb);
+	memset(rx_status, 0, sizeof(*rx_status));
+	rx_status->freq = be32_to_cpu(desc->channel);
+	rx_status->band = hw->conf.channel->band;
+	rx_status->signal = -95 + be32_to_cpu(desc->rssi);
+
+	ieee80211_rx_irqsafe(hw, data->skb);
+	data->skb = NULL;
+
+skip:
+	if (data->skb) {
+		dev_kfree_skb_irq(data->skb);
+		data->skb = NULL;
+	}
+
+	ar5523_rx_data_put(ar, data);
+	if (atomic_inc_return(&ar->rx_data_free_cnt) >=
+	    AR5523_RX_DATA_REFILL_COUNT &&
+	    test_bit(AR5523_HW_UP, &ar->flags))
+		queue_work(ar->wq, &ar->rx_refill_work);
+}
+
+static void ar5523_rx_refill_work(struct work_struct *work)
+{
+	struct ar5523 *ar = container_of(work, struct ar5523, rx_refill_work);
+	struct ar5523_rx_data *data;
+	unsigned long flags;
+	int error;
+
+	ar5523_dbg(ar, "%s\n", __func__);
+	do {
+		spin_lock_irqsave(&ar->rx_data_list_lock, flags);
+
+		if (!list_empty(&ar->rx_data_free))
+			data = (struct ar5523_rx_data *) ar->rx_data_free.next;
+		else
+			data = NULL;
+		spin_unlock_irqrestore(&ar->rx_data_list_lock, flags);
+
+		if (!data)
+			goto done;
+
+		data->skb = alloc_skb(ar->rxbufsz, GFP_KERNEL);
+		if (!data->skb) {
+			ar5523_err(ar, "could not allocate rx skbuff\n");
+			return;
+		}
+
+		usb_fill_bulk_urb(data->urb, ar->dev,
+				  ar5523_data_rx_pipe(ar->dev), data->skb->data,
+				  ar->rxbufsz, ar5523_data_rx_cb, data);
+
+		spin_lock_irqsave(&ar->rx_data_list_lock, flags);
+		list_move(&data->list, &ar->rx_data_used);
+		spin_unlock_irqrestore(&ar->rx_data_list_lock, flags);
+		atomic_dec(&ar->rx_data_free_cnt);
+
+		error = usb_submit_urb(data->urb, GFP_KERNEL);
+		if (error) {
+			kfree_skb(data->skb);
+			if (error != -ENODEV)
+				ar5523_err(ar, "Err sending rx data urb %d\n",
+					   error);
+			ar5523_rx_data_put(ar, data);
+			atomic_inc(&ar->rx_data_free_cnt);
+			return;
+		}
+
+	} while (true);
+done:
+	return;
+}
+
+static void ar5523_cancel_rx_bufs(struct ar5523 *ar)
+{
+	struct ar5523_rx_data *data;
+	unsigned long flags;
+
+	do {
+		spin_lock_irqsave(&ar->rx_data_list_lock, flags);
+		if (!list_empty(&ar->rx_data_used))
+			data = (struct ar5523_rx_data *) ar->rx_data_used.next;
+		else
+			data = NULL;
+		spin_unlock_irqrestore(&ar->rx_data_list_lock, flags);
+
+		if (!data)
+			break;
+
+		usb_kill_urb(data->urb);
+		list_move(&data->list, &ar->rx_data_free);
+		atomic_inc(&ar->rx_data_free_cnt);
+	} while (data);
+}
+
+static void ar5523_free_rx_bufs(struct ar5523 *ar)
+{
+	struct ar5523_rx_data *data;
+
+	ar5523_cancel_rx_bufs(ar);
+	while (!list_empty(&ar->rx_data_free)) {
+		data = (struct ar5523_rx_data *) ar->rx_data_free.next;
+		list_del(&data->list);
+		usb_free_urb(data->urb);
+	}
+}
+
+static int ar5523_alloc_rx_bufs(struct ar5523 *ar)
+{
+	int i;
+
+	for (i = 0; i < AR5523_RX_DATA_COUNT; i++) {
+		struct ar5523_rx_data *data = &ar->rx_data[i];
+
+		data->ar = ar;
+		data->urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!data->urb) {
+			ar5523_err(ar, "could not allocate rx data urb\n");
+			goto err;
+		}
+		list_add_tail(&data->list, &ar->rx_data_free);
+		atomic_inc(&ar->rx_data_free_cnt);
+	}
+	return 0;
+
+err:
+	ar5523_free_rx_bufs(ar);
+	return -ENOMEM;
+}
+
+static void ar5523_data_tx_pkt_put(struct ar5523 *ar)
+{
+	atomic_dec(&ar->tx_nr_total);
+	if (!atomic_dec_return(&ar->tx_nr_pending)) {
+		del_timer(&ar->tx_wd_timer);
+		wake_up(&ar->tx_flush_waitq);
+	}
+
+	if (atomic_read(&ar->tx_nr_total) < AR5523_TX_DATA_RESTART_COUNT) {
+		ar5523_dbg(ar, "restart tx queue\n");
+		ieee80211_wake_queues(ar->hw);
+	}
+}
+
+static void ar5523_data_tx_cb(struct urb *urb)
+{
+	struct sk_buff *skb = urb->context;
+	struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
+	struct ar5523_tx_data *data = (struct ar5523_tx_data *)
+				       txi->driver_data;
+	struct ar5523 *ar = data->ar;
+	unsigned long flags;
+
+	ar5523_dbg(ar, "data tx urb completed: %d\n", urb->status);
+
+	spin_lock_irqsave(&ar->tx_data_list_lock, flags);
+	list_del(&data->list);
+	spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
+
+	if (urb->status) {
+		ar5523_dbg(ar, "%s: urb status: %d\n", __func__, urb->status);
+		ar5523_data_tx_pkt_put(ar);
+		ieee80211_free_txskb(ar->hw, skb);
+	} else {
+		skb_pull(skb, sizeof(struct ar5523_tx_desc) + sizeof(__be32));
+		ieee80211_tx_status_irqsafe(ar->hw, skb);
+	}
+	usb_free_urb(urb);
+}
+
+static void ar5523_tx(struct ieee80211_hw *hw,
+		       struct ieee80211_tx_control *control,
+		       struct sk_buff *skb)
+{
+	struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
+	struct ar5523_tx_data *data = (struct ar5523_tx_data *)
+					txi->driver_data;
+	struct ar5523 *ar = hw->priv;
+	unsigned long flags;
+
+	ar5523_dbg(ar, "tx called\n");
+	if (atomic_inc_return(&ar->tx_nr_total) >= AR5523_TX_DATA_COUNT) {
+		ar5523_dbg(ar, "tx queue full\n");
+		ar5523_dbg(ar, "stop queues (tot %d pend %d)\n",
+			   atomic_read(&ar->tx_nr_total),
+			   atomic_read(&ar->tx_nr_pending));
+		ieee80211_stop_queues(hw);
+	}
+
+	data->skb = skb;
+
+	spin_lock_irqsave(&ar->tx_data_list_lock, flags);
+	list_add_tail(&data->list, &ar->tx_queue_pending);
+	spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
+
+	ieee80211_queue_work(ar->hw, &ar->tx_work);
+}
+
+static void ar5523_tx_work_locked(struct ar5523 *ar)
+{
+	struct ar5523_tx_data *data;
+	struct ar5523_tx_desc *desc;
+	struct ar5523_chunk *chunk;
+	struct ieee80211_tx_info *txi;
+	struct urb *urb;
+	struct sk_buff *skb;
+	int error = 0, paylen;
+	u32 txqid;
+	unsigned long flags;
+
+	BUILD_BUG_ON(sizeof(struct ar5523_tx_data) >
+		     IEEE80211_TX_INFO_DRIVER_DATA_SIZE);
+
+	ar5523_dbg(ar, "%s\n", __func__);
+	do {
+		spin_lock_irqsave(&ar->tx_data_list_lock, flags);
+		if (!list_empty(&ar->tx_queue_pending)) {
+			data = (struct ar5523_tx_data *)
+				ar->tx_queue_pending.next;
+			list_del(&data->list);
+		} else
+			data = NULL;
+		spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
+
+		if (!data)
+			break;
+
+		skb = data->skb;
+		txqid = 0;
+		txi = IEEE80211_SKB_CB(skb);
+		paylen = skb->len;
+		urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!urb) {
+			ar5523_err(ar, "Failed to allocate TX urb\n");
+			ieee80211_free_txskb(ar->hw, skb);
+			continue;
+		}
+
+		data->ar = ar;
+		data->urb = urb;
+
+		desc = (struct ar5523_tx_desc *)skb_push(skb, sizeof(*desc));
+		chunk = (struct ar5523_chunk *)skb_push(skb, sizeof(*chunk));
+
+		chunk->seqnum = 0;
+		chunk->flags = UATH_CFLAGS_FINAL;
+		chunk->length = cpu_to_be16(skb->len);
+
+		desc->msglen = cpu_to_be32(skb->len);
+		desc->msgid  = AR5523_DATA_ID;
+		desc->buflen = cpu_to_be32(paylen);
+		desc->type   = cpu_to_be32(WDCMSG_SEND);
+		desc->flags  = cpu_to_be32(UATH_TX_NOTIFY);
+
+		if (test_bit(AR5523_CONNECTED, &ar->flags))
+			desc->connid = cpu_to_be32(AR5523_ID_BSS);
+		else
+			desc->connid = cpu_to_be32(AR5523_ID_BROADCAST);
+
+		if (txi->flags & IEEE80211_TX_CTL_USE_MINRATE)
+			txqid |= UATH_TXQID_MINRATE;
+
+		desc->txqid = cpu_to_be32(txqid);
+
+		usb_fill_bulk_urb(urb, ar->dev, ar5523_data_tx_pipe(ar->dev),
+				  skb->data, skb->len, ar5523_data_tx_cb, skb);
+
+		spin_lock_irqsave(&ar->tx_data_list_lock, flags);
+		list_add_tail(&data->list, &ar->tx_queue_submitted);
+		spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
+		mod_timer(&ar->tx_wd_timer, jiffies + AR5523_TX_WD_TIMEOUT);
+		atomic_inc(&ar->tx_nr_pending);
+
+		ar5523_dbg(ar, "TX Frame (%d pending)\n",
+			   atomic_read(&ar->tx_nr_pending));
+		error = usb_submit_urb(urb, GFP_KERNEL);
+		if (error) {
+			ar5523_err(ar, "error %d when submitting tx urb\n",
+				   error);
+			spin_lock_irqsave(&ar->tx_data_list_lock, flags);
+			list_del(&data->list);
+			spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
+			atomic_dec(&ar->tx_nr_pending);
+			ar5523_data_tx_pkt_put(ar);
+			usb_free_urb(urb);
+			ieee80211_free_txskb(ar->hw, skb);
+		}
+	} while (true);
+}
+
+static void ar5523_tx_work(struct work_struct *work)
+{
+	struct ar5523 *ar = container_of(work, struct ar5523, tx_work);
+
+	ar5523_dbg(ar, "%s\n", __func__);
+	mutex_lock(&ar->mutex);
+	ar5523_tx_work_locked(ar);
+	mutex_unlock(&ar->mutex);
+}
+
+static void ar5523_tx_wd_timer(unsigned long arg)
+{
+	struct ar5523 *ar = (struct ar5523 *) arg;
+
+	ar5523_dbg(ar, "TX watchdog timer triggered\n");
+	ieee80211_queue_work(ar->hw, &ar->tx_wd_work);
+}
+
+static void ar5523_tx_wd_work(struct work_struct *work)
+{
+	struct ar5523 *ar = container_of(work, struct ar5523, tx_wd_work);
+
+	/* Occasionally the TX queues stop responding. The only way to
+	 * recover seems to be to reset the dongle.
+	 */
+
+	mutex_lock(&ar->mutex);
+	ar5523_err(ar, "TX queue stuck (tot %d pend %d)\n",
+		   atomic_read(&ar->tx_nr_total),
+		   atomic_read(&ar->tx_nr_pending));
+
+	ar5523_err(ar, "Will restart dongle.\n");
+	ar5523_cmd_write(ar, WDCMSG_TARGET_RESET, NULL, 0, 0);
+	mutex_unlock(&ar->mutex);
+}
+
+static void ar5523_flush_tx(struct ar5523 *ar)
+{
+	ar5523_tx_work_locked(ar);
+
+	/* Don't waste time trying to flush if USB is disconnected */
+	if (test_bit(AR5523_USB_DISCONNECTED, &ar->flags))
+		return;
+	if (!wait_event_timeout(ar->tx_flush_waitq,
+	    !atomic_read(&ar->tx_nr_pending), AR5523_FLUSH_TIMEOUT))
+		ar5523_err(ar, "flush timeout (tot %d pend %d)\n",
+			   atomic_read(&ar->tx_nr_total),
+			   atomic_read(&ar->tx_nr_pending));
+}
+
+static void ar5523_free_tx_cmd(struct ar5523 *ar)
+{
+	struct ar5523_tx_cmd *cmd = &ar->tx_cmd;
+
+	usb_free_coherent(ar->dev, AR5523_MAX_RXCMDSZ, cmd->buf_tx,
+			  cmd->urb_tx->transfer_dma);
+	usb_free_urb(cmd->urb_tx);
+}
+
+static int ar5523_alloc_tx_cmd(struct ar5523 *ar)
+{
+	struct ar5523_tx_cmd *cmd = &ar->tx_cmd;
+
+	cmd->ar = ar;
+	init_completion(&cmd->done);
+
+	cmd->urb_tx = usb_alloc_urb(0, GFP_KERNEL);
+	if (!cmd->urb_tx) {
+		ar5523_err(ar, "could not allocate urb\n");
+		return -ENOMEM;
+	}
+	cmd->buf_tx = usb_alloc_coherent(ar->dev, AR5523_MAX_TXCMDSZ,
+					 GFP_KERNEL,
+					 &cmd->urb_tx->transfer_dma);
+	if (!cmd->buf_tx) {
+		usb_free_urb(cmd->urb_tx);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+/*
+ * This function is called periodically (every second) when associated to
+ * query device statistics.
+ */
+static void ar5523_stat_work(struct work_struct *work)
+{
+	struct ar5523 *ar = container_of(work, struct ar5523, stat_work.work);
+	int error;
+
+	ar5523_dbg(ar, "%s\n", __func__);
+	mutex_lock(&ar->mutex);
+
+	/*
+	 * Send request for statistics asynchronously once a second. This
+	 * seems to be important. Throughput is a lot better if this is done.
+	 */
+	error = ar5523_cmd_write(ar, WDCMSG_TARGET_GET_STATS, NULL, 0, 0);
+	if (error)
+		ar5523_err(ar, "could not query stats, error %d\n", error);
+	mutex_unlock(&ar->mutex);
+	ieee80211_queue_delayed_work(ar->hw, &ar->stat_work, HZ);
+}
+
+/*
+ * Interface routines to the mac80211 stack.
+ */
+static int ar5523_start(struct ieee80211_hw *hw)
+{
+	struct ar5523 *ar = hw->priv;
+	int error;
+	__be32 val;
+
+	ar5523_dbg(ar, "start called\n");
+
+	mutex_lock(&ar->mutex);
+	val = cpu_to_be32(0);
+	ar5523_cmd_write(ar, WDCMSG_BIND, &val, sizeof(val), 0);
+
+	/* set MAC address */
+	ar5523_config_multi(ar, CFG_MAC_ADDR, &ar->hw->wiphy->perm_addr,
+			    ETH_ALEN);
+
+	/* XXX honor net80211 state */
+	ar5523_config(ar, CFG_RATE_CONTROL_ENABLE, 0x00000001);
+	ar5523_config(ar, CFG_DIVERSITY_CTL, 0x00000001);
+	ar5523_config(ar, CFG_ABOLT, 0x0000003f);
+	ar5523_config(ar, CFG_WME_ENABLED, 0x00000000);
+
+	ar5523_config(ar, CFG_SERVICE_TYPE, 1);
+	ar5523_config(ar, CFG_TP_SCALE, 0x00000000);
+	ar5523_config(ar, CFG_TPC_HALF_DBM5, 0x0000003c);
+	ar5523_config(ar, CFG_TPC_HALF_DBM2, 0x0000003c);
+	ar5523_config(ar, CFG_OVERRD_TX_POWER, 0x00000000);
+	ar5523_config(ar, CFG_GMODE_PROTECTION, 0x00000000);
+	ar5523_config(ar, CFG_GMODE_PROTECT_RATE_INDEX, 0x00000003);
+	ar5523_config(ar, CFG_PROTECTION_TYPE, 0x00000000);
+	ar5523_config(ar, CFG_MODE_CTS, 0x00000002);
+
+	error = ar5523_cmd_read(ar, WDCMSG_TARGET_START, NULL, 0,
+	    &val, sizeof(val), AR5523_CMD_FLAG_MAGIC);
+	if (error) {
+		ar5523_dbg(ar, "could not start target, error %d\n", error);
+		goto err;
+	}
+	ar5523_dbg(ar, "WDCMSG_TARGET_START returns handle: 0x%x\n",
+		   be32_to_cpu(val));
+
+	ar5523_switch_chan(ar);
+
+	val = cpu_to_be32(TARGET_DEVICE_AWAKE);
+	ar5523_cmd_write(ar, WDCMSG_SET_PWR_MODE, &val, sizeof(val), 0);
+	/* XXX? check */
+	ar5523_cmd_write(ar, WDCMSG_RESET_KEY_CACHE, NULL, 0, 0);
+
+	set_bit(AR5523_HW_UP, &ar->flags);
+	queue_work(ar->wq, &ar->rx_refill_work);
+
+	/* enable Rx */
+	ar5523_set_rxfilter(ar, 0, UATH_FILTER_OP_INIT);
+	ar5523_set_rxfilter(ar,
+			    UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST |
+			    UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON,
+			    UATH_FILTER_OP_SET);
+
+	ar5523_set_ledsteady(ar, UATH_LED_ACTIVITY, UATH_LED_ON);
+	ar5523_dbg(ar, "start OK\n");
+
+err:
+	mutex_unlock(&ar->mutex);
+	return error;
+}
+
+static void ar5523_stop(struct ieee80211_hw *hw)
+{
+	struct ar5523 *ar = hw->priv;
+
+	ar5523_dbg(ar, "stop called\n");
+
+	cancel_delayed_work_sync(&ar->stat_work);
+	mutex_lock(&ar->mutex);
+	clear_bit(AR5523_HW_UP, &ar->flags);
+
+	ar5523_set_ledsteady(ar, UATH_LED_LINK, UATH_LED_OFF);
+	ar5523_set_ledsteady(ar, UATH_LED_ACTIVITY, UATH_LED_OFF);
+
+	ar5523_cmd_write(ar, WDCMSG_TARGET_STOP, NULL, 0, 0);
+
+	del_timer_sync(&ar->tx_wd_timer);
+	cancel_work_sync(&ar->tx_wd_work);
+	cancel_work_sync(&ar->rx_refill_work);
+	ar5523_cancel_rx_bufs(ar);
+	mutex_unlock(&ar->mutex);
+}
+
+static int ar5523_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	struct ar5523 *ar = hw->priv;
+	int ret;
+
+	ar5523_dbg(ar, "set_rts_threshold called\n");
+	mutex_lock(&ar->mutex);
+
+	ret = ar5523_config(ar, CFG_USER_RTS_THRESHOLD, value);
+
+	mutex_unlock(&ar->mutex);
+	return ret;
+}
+
+static void ar5523_flush(struct ieee80211_hw *hw, bool drop)
+{
+	struct ar5523 *ar = hw->priv;
+
+	ar5523_dbg(ar, "flush called\n");
+	ar5523_flush_tx(ar);
+}
+
+static int ar5523_add_interface(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif)
+{
+	struct ar5523 *ar = hw->priv;
+
+	ar5523_dbg(ar, "add interface called\n");
+
+	if (ar->vif) {
+		ar5523_dbg(ar, "invalid add_interface\n");
+		return -EOPNOTSUPP;
+	}
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_STATION:
+		ar->vif = vif;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
+
+static void ar5523_remove_interface(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif)
+{
+	struct ar5523 *ar = hw->priv;
+
+	ar5523_dbg(ar, "remove interface called\n");
+	ar->vif = NULL;
+}
+
+static int ar5523_hwconfig(struct ieee80211_hw *hw, u32 changed)
+{
+	struct ar5523 *ar = hw->priv;
+
+	ar5523_dbg(ar, "config called\n");
+	mutex_lock(&ar->mutex);
+	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+		ar5523_dbg(ar, "Do channel switch\n");
+		ar5523_flush_tx(ar);
+		ar5523_switch_chan(ar);
+	}
+	mutex_unlock(&ar->mutex);
+	return 0;
+}
+
+static int ar5523_get_wlan_mode(struct ar5523 *ar,
+				struct ieee80211_bss_conf *bss_conf)
+{
+	struct ieee80211_supported_band *band;
+	int bit;
+	struct ieee80211_sta *sta;
+	u32 sta_rate_set;
+
+	band = ar->hw->wiphy->bands[ar->hw->conf.channel->band];
+	sta = ieee80211_find_sta(ar->vif, bss_conf->bssid);
+	if (!sta) {
+		ar5523_info(ar, "STA not found!\n");
+		return WLAN_MODE_11b;
+	}
+	sta_rate_set = sta->supp_rates[ar->hw->conf.channel->band];
+
+	for (bit = 0; bit < band->n_bitrates; bit++) {
+		if (sta_rate_set & 1) {
+			int rate = band->bitrates[bit].bitrate;
+			switch (rate) {
+			case 60:
+			case 90:
+			case 120:
+			case 180:
+			case 240:
+			case 360:
+			case 480:
+			case 540:
+				return WLAN_MODE_11g;
+			}
+		}
+		sta_rate_set >>= 1;
+	}
+	return WLAN_MODE_11b;
+}
+
+static void ar5523_create_rateset(struct ar5523 *ar,
+				  struct ieee80211_bss_conf *bss_conf,
+				  struct ar5523_cmd_rateset *rs,
+				  bool basic)
+{
+	struct ieee80211_supported_band *band;
+	struct ieee80211_sta *sta;
+	int bit, i = 0;
+	u32 sta_rate_set, basic_rate_set;
+
+	sta = ieee80211_find_sta(ar->vif, bss_conf->bssid);
+	basic_rate_set = bss_conf->basic_rates;
+	if (!sta) {
+		ar5523_info(ar, "STA not found. Cannot set rates\n");
+		sta_rate_set = bss_conf->basic_rates;
+	}
+	sta_rate_set = sta->supp_rates[ar->hw->conf.channel->band];
+
+	ar5523_dbg(ar, "sta rate_set = %08x\n", sta_rate_set);
+
+	band = ar->hw->wiphy->bands[ar->hw->conf.channel->band];
+	for (bit = 0; bit < band->n_bitrates; bit++) {
+		BUG_ON(i >= AR5523_MAX_NRATES);
+		ar5523_dbg(ar, "Considering rate %d : %d\n",
+			   band->bitrates[bit].hw_value, sta_rate_set & 1);
+		if (sta_rate_set & 1) {
+			rs->set[i] = band->bitrates[bit].hw_value;
+			if (basic_rate_set & 1 && basic)
+				rs->set[i] |= 0x80;
+			i++;
+		}
+		sta_rate_set >>= 1;
+		basic_rate_set >>= 1;
+	}
+
+	rs->length = i;
+}
+
+static int ar5523_set_basic_rates(struct ar5523 *ar,
+				  struct ieee80211_bss_conf *bss)
+{
+	struct ar5523_cmd_rates rates;
+
+	memset(&rates, 0, sizeof(rates));
+	rates.connid = cpu_to_be32(2);		/* XXX */
+	rates.size   = cpu_to_be32(sizeof(struct ar5523_cmd_rateset));
+	ar5523_create_rateset(ar, bss, &rates.rateset, true);
+
+	return ar5523_cmd_write(ar, WDCMSG_SET_BASIC_RATE, &rates,
+				sizeof(rates), 0);
+}
+
+static int ar5523_create_connection(struct ar5523 *ar,
+				    struct ieee80211_vif *vif,
+				    struct ieee80211_bss_conf *bss)
+{
+	struct ar5523_cmd_create_connection create;
+	int wlan_mode;
+
+	memset(&create, 0, sizeof(create));
+	create.connid = cpu_to_be32(2);
+	create.bssid = cpu_to_be32(0);
+	/* XXX packed or not?  */
+	create.size = cpu_to_be32(sizeof(struct ar5523_cmd_rateset));
+
+	ar5523_create_rateset(ar, bss, &create.connattr.rateset, false);
+
+	wlan_mode = ar5523_get_wlan_mode(ar, bss);
+	create.connattr.wlanmode = cpu_to_be32(wlan_mode);
+
+	return ar5523_cmd_write(ar, WDCMSG_CREATE_CONNECTION, &create,
+				sizeof(create), 0);
+}
+
+static int ar5523_write_associd(struct ar5523 *ar,
+				struct ieee80211_bss_conf *bss)
+{
+	struct ar5523_cmd_set_associd associd;
+
+	memset(&associd, 0, sizeof(associd));
+	associd.defaultrateix = cpu_to_be32(0);	/* XXX */
+	associd.associd = cpu_to_be32(bss->aid);
+	associd.timoffset = cpu_to_be32(0x3b);	/* XXX */
+	memcpy(associd.bssid, bss->bssid, ETH_ALEN);
+	return ar5523_cmd_write(ar, WDCMSG_WRITE_ASSOCID, &associd,
+				sizeof(associd), 0);
+}
+
+static void ar5523_bss_info_changed(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    struct ieee80211_bss_conf *bss,
+				    u32 changed)
+{
+	struct ar5523 *ar = hw->priv;
+	int error;
+
+	ar5523_dbg(ar, "bss_info_changed called\n");
+	mutex_lock(&ar->mutex);
+
+	if (!(changed & BSS_CHANGED_ASSOC))
+		goto out_unlock;
+
+	if (bss->assoc) {
+		error = ar5523_create_connection(ar, vif, bss);
+		if (error) {
+			ar5523_err(ar, "could not create connection\n");
+			goto out_unlock;
+		}
+
+		error = ar5523_set_basic_rates(ar, bss);
+		if (error) {
+			ar5523_err(ar, "could not set negotiated rate set\n");
+			goto out_unlock;
+		}
+
+		error = ar5523_write_associd(ar, bss);
+		if (error) {
+			ar5523_err(ar, "could not set association\n");
+			goto out_unlock;
+		}
+
+		/* turn link LED on */
+		ar5523_set_ledsteady(ar, UATH_LED_LINK, UATH_LED_ON);
+		set_bit(AR5523_CONNECTED, &ar->flags);
+		ieee80211_queue_delayed_work(hw, &ar->stat_work, HZ);
+
+	} else {
+		cancel_delayed_work(&ar->stat_work);
+		clear_bit(AR5523_CONNECTED, &ar->flags);
+		ar5523_set_ledsteady(ar, UATH_LED_LINK, UATH_LED_OFF);
+	}
+
+out_unlock:
+	mutex_unlock(&ar->mutex);
+
+}
+
+#define AR5523_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
+				  FIF_ALLMULTI | \
+				  FIF_FCSFAIL | \
+				  FIF_OTHER_BSS)
+
+static void ar5523_configure_filter(struct ieee80211_hw *hw,
+				    unsigned int changed_flags,
+				    unsigned int *total_flags,
+				    u64 multicast)
+{
+	struct ar5523 *ar = hw->priv;
+	u32 filter = 0;
+
+	ar5523_dbg(ar, "configure_filter called\n");
+	mutex_lock(&ar->mutex);
+	ar5523_flush_tx(ar);
+
+	*total_flags &= AR5523_SUPPORTED_FILTERS;
+
+	if (*total_flags & FIF_PROMISC_IN_BSS || *total_flags & FIF_OTHER_BSS)
+		filter |= UATH_FILTER_RX_PROM;
+
+	filter |= UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST |
+		  UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON;
+
+	ar5523_set_rxfilter(ar, 0, UATH_FILTER_OP_INIT);
+	ar5523_set_rxfilter(ar, filter, UATH_FILTER_OP_SET);
+
+	mutex_unlock(&ar->mutex);
+}
+
+static const struct ieee80211_ops ar5523_ops = {
+	.start			= ar5523_start,
+	.stop			= ar5523_stop,
+	.tx			= ar5523_tx,
+	.set_rts_threshold	= ar5523_set_rts_threshold,
+	.add_interface		= ar5523_add_interface,
+	.remove_interface	= ar5523_remove_interface,
+	.config			= ar5523_hwconfig,
+	.bss_info_changed	= ar5523_bss_info_changed,
+	.configure_filter	= ar5523_configure_filter,
+	.flush			= ar5523_flush,
+};
+
+static int ar5523_host_available(struct ar5523 *ar)
+{
+	struct ar5523_cmd_host_available setup;
+
+	/* inform target the host is available */
+	setup.sw_ver_major = cpu_to_be32(ATH_SW_VER_MAJOR);
+	setup.sw_ver_minor = cpu_to_be32(ATH_SW_VER_MINOR);
+	setup.sw_ver_patch = cpu_to_be32(ATH_SW_VER_PATCH);
+	setup.sw_ver_build = cpu_to_be32(ATH_SW_VER_BUILD);
+	return ar5523_cmd_read(ar, WDCMSG_HOST_AVAILABLE,
+			       &setup, sizeof(setup), NULL, 0, 0);
+}
+
+static int ar5523_get_devstatus(struct ar5523 *ar)
+{
+	u8 macaddr[ETH_ALEN];
+	int error;
+
+	/* retrieve MAC address */
+	error = ar5523_get_status(ar, ST_MAC_ADDR, macaddr, ETH_ALEN);
+	if (error) {
+		ar5523_err(ar, "could not read MAC address\n");
+		return error;
+	}
+
+	SET_IEEE80211_PERM_ADDR(ar->hw, macaddr);
+
+	error = ar5523_get_status(ar, ST_SERIAL_NUMBER,
+	    &ar->serial[0], sizeof(ar->serial));
+	if (error) {
+		ar5523_err(ar, "could not read device serial number\n");
+		return error;
+	}
+	return 0;
+}
+
+#define AR5523_SANE_RXBUFSZ 2000
+
+static int ar5523_get_max_rxsz(struct ar5523 *ar)
+{
+	int error;
+	__be32 rxsize;
+
+	/* Get max rx size */
+	error = ar5523_get_status(ar, ST_WDC_TRANSPORT_CHUNK_SIZE, &rxsize,
+				  sizeof(rxsize));
+	if (error != 0) {
+		ar5523_err(ar, "could not read max RX size\n");
+		return error;
+	}
+
+	ar->rxbufsz = be32_to_cpu(rxsize);
+
+	if (!ar->rxbufsz || ar->rxbufsz > AR5523_SANE_RXBUFSZ) {
+		ar5523_err(ar, "Bad rxbufsz from device. Using %d instead\n",
+			   AR5523_SANE_RXBUFSZ);
+		ar->rxbufsz = AR5523_SANE_RXBUFSZ;
+	}
+
+	ar5523_dbg(ar, "Max RX buf size: %d\n", ar->rxbufsz);
+	return 0;
+}
+
+/*
+ * This is copied from rtl818x, but we should probably move this
+ * to common code as in OpenBSD.
+ */
+static const struct ieee80211_rate ar5523_rates[] = {
+	{ .bitrate = 10, .hw_value = 2, },
+	{ .bitrate = 20, .hw_value = 4 },
+	{ .bitrate = 55, .hw_value = 11, },
+	{ .bitrate = 110, .hw_value = 22, },
+	{ .bitrate = 60, .hw_value = 12, },
+	{ .bitrate = 90, .hw_value = 18, },
+	{ .bitrate = 120, .hw_value = 24, },
+	{ .bitrate = 180, .hw_value = 36, },
+	{ .bitrate = 240, .hw_value = 48, },
+	{ .bitrate = 360, .hw_value = 72, },
+	{ .bitrate = 480, .hw_value = 96, },
+	{ .bitrate = 540, .hw_value = 108, },
+};
+
+static const struct ieee80211_channel ar5523_channels[] = {
+	{ .center_freq = 2412 },
+	{ .center_freq = 2417 },
+	{ .center_freq = 2422 },
+	{ .center_freq = 2427 },
+	{ .center_freq = 2432 },
+	{ .center_freq = 2437 },
+	{ .center_freq = 2442 },
+	{ .center_freq = 2447 },
+	{ .center_freq = 2452 },
+	{ .center_freq = 2457 },
+	{ .center_freq = 2462 },
+	{ .center_freq = 2467 },
+	{ .center_freq = 2472 },
+	{ .center_freq = 2484 },
+};
+
+static int ar5523_init_modes(struct ar5523 *ar)
+{
+	BUILD_BUG_ON(sizeof(ar->channels) != sizeof(ar5523_channels));
+	BUILD_BUG_ON(sizeof(ar->rates) != sizeof(ar5523_rates));
+
+	memcpy(ar->channels, ar5523_channels, sizeof(ar5523_channels));
+	memcpy(ar->rates, ar5523_rates, sizeof(ar5523_rates));
+
+	ar->band.band = IEEE80211_BAND_2GHZ;
+	ar->band.channels = ar->channels;
+	ar->band.n_channels = ARRAY_SIZE(ar5523_channels);
+	ar->band.bitrates = ar->rates;
+	ar->band.n_bitrates = ARRAY_SIZE(ar5523_rates);
+	ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &ar->band;
+	return 0;
+}
+
+/*
+ * Load the MIPS R4000 microcode into the device.  Once the image is loaded,
+ * the device will detach itself from the bus and reattach later with a new
+ * product Id (a la ezusb).
+ */
+static int ar5523_load_firmware(struct usb_device *dev)
+{
+	struct ar5523_fwblock *txblock, *rxblock;
+	const struct firmware *fw;
+	void *fwbuf;
+	int len, offset;
+	int foolen; /* XXX(hch): handle short transfers */
+	int error = -ENXIO;
+
+	if (request_firmware(&fw, AR5523_FIRMWARE_FILE, &dev->dev)) {
+		dev_err(&dev->dev, "no firmware found: %s\n",
+			AR5523_FIRMWARE_FILE);
+		return -ENOENT;
+	}
+
+	txblock = kmalloc(sizeof(*txblock), GFP_KERNEL);
+	if (!txblock)
+		goto out;
+
+	rxblock = kmalloc(sizeof(*rxblock), GFP_KERNEL);
+	if (!rxblock)
+		goto out_free_txblock;
+
+	fwbuf = kmalloc(AR5523_MAX_FWBLOCK_SIZE, GFP_KERNEL);
+	if (!fwbuf)
+		goto out_free_rxblock;
+
+	memset(txblock, 0, sizeof(struct ar5523_fwblock));
+	txblock->flags = cpu_to_be32(AR5523_WRITE_BLOCK);
+	txblock->total = cpu_to_be32(fw->size);
+
+	offset = 0;
+	len = fw->size;
+	while (len > 0) {
+		int mlen = min(len, AR5523_MAX_FWBLOCK_SIZE);
+
+		txblock->remain = cpu_to_be32(len - mlen);
+		txblock->len = cpu_to_be32(mlen);
+
+		/* send firmware block meta-data */
+		error = usb_bulk_msg(dev, ar5523_cmd_tx_pipe(dev),
+				     txblock, sizeof(*txblock), &foolen,
+				     AR5523_CMD_TIMEOUT);
+		if (error) {
+			dev_err(&dev->dev,
+				"could not send firmware block info\n");
+			goto out_free_fwbuf;
+		}
+
+		/* send firmware block data */
+		memcpy(fwbuf, fw->data + offset, mlen);
+		error = usb_bulk_msg(dev, ar5523_data_tx_pipe(dev),
+				     fwbuf, mlen, &foolen,
+				     AR5523_DATA_TIMEOUT);
+		if (error) {
+			dev_err(&dev->dev,
+				"could not send firmware block data\n");
+			goto out_free_fwbuf;
+		}
+
+		/* wait for ack from firmware */
+		error = usb_bulk_msg(dev, ar5523_cmd_rx_pipe(dev),
+				     rxblock, sizeof(*rxblock), &foolen,
+				     AR5523_CMD_TIMEOUT);
+		if (error) {
+			dev_err(&dev->dev,
+				"could not read firmware answer\n");
+			goto out_free_fwbuf;
+		}
+
+		len -= mlen;
+		offset += mlen;
+	}
+
+	/*
+	 * Set the error to -ENXIO to make sure we continue probing for
+	 * a driver.
+	 */
+	error = -ENXIO;
+
+ out_free_fwbuf:
+	kfree(fwbuf);
+ out_free_rxblock:
+	kfree(rxblock);
+ out_free_txblock:
+	kfree(txblock);
+ out:
+	release_firmware(fw);
+	return error;
+}
+
+static int ar5523_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct ieee80211_hw *hw;
+	struct ar5523 *ar;
+	int error = -ENOMEM;
+
+	/*
+	 * Load firmware if the device requires it.  This will return
+	 * -ENXIO on success and we'll get called back afer the usb
+	 * id changes to indicate that the firmware is present.
+	 */
+	if (id->driver_info & AR5523_FLAG_PRE_FIRMWARE)
+		return ar5523_load_firmware(dev);
+
+
+	hw = ieee80211_alloc_hw(sizeof(*ar), &ar5523_ops);
+	if (!hw)
+		goto out;
+	SET_IEEE80211_DEV(hw, &intf->dev);
+
+	ar = hw->priv;
+	ar->hw = hw;
+	ar->dev = dev;
+	mutex_init(&ar->mutex);
+
+	INIT_DELAYED_WORK(&ar->stat_work, ar5523_stat_work);
+	init_timer(&ar->tx_wd_timer);
+	setup_timer(&ar->tx_wd_timer, ar5523_tx_wd_timer, (unsigned long) ar);
+	INIT_WORK(&ar->tx_wd_work, ar5523_tx_wd_work);
+	INIT_WORK(&ar->tx_work, ar5523_tx_work);
+	INIT_LIST_HEAD(&ar->tx_queue_pending);
+	INIT_LIST_HEAD(&ar->tx_queue_submitted);
+	spin_lock_init(&ar->tx_data_list_lock);
+	atomic_set(&ar->tx_nr_total, 0);
+	atomic_set(&ar->tx_nr_pending, 0);
+	init_waitqueue_head(&ar->tx_flush_waitq);
+
+	atomic_set(&ar->rx_data_free_cnt, 0);
+	INIT_WORK(&ar->rx_refill_work, ar5523_rx_refill_work);
+	INIT_LIST_HEAD(&ar->rx_data_free);
+	INIT_LIST_HEAD(&ar->rx_data_used);
+	spin_lock_init(&ar->rx_data_list_lock);
+
+	ar->wq = create_singlethread_workqueue("ar5523");
+	if (!ar->wq) {
+		ar5523_err(ar, "Could not create wq\n");
+		goto out_free_ar;
+	}
+
+	error = ar5523_alloc_rx_bufs(ar);
+	if (error) {
+		ar5523_err(ar, "Could not allocate rx buffers\n");
+		goto out_free_wq;
+	}
+
+	error = ar5523_alloc_rx_cmd(ar);
+	if (error) {
+		ar5523_err(ar, "Could not allocate rx command buffers\n");
+		goto out_free_rx_bufs;
+	}
+
+	error = ar5523_alloc_tx_cmd(ar);
+	if (error) {
+		ar5523_err(ar, "Could not allocate tx command buffers\n");
+		goto out_free_rx_cmd;
+	}
+
+	error = ar5523_submit_rx_cmd(ar);
+	if (error) {
+		ar5523_err(ar, "Failed to submit rx cmd\n");
+		goto out_free_tx_cmd;
+	}
+
+	/*
+	 * We're now ready to send/receive firmware commands.
+	 */
+	error = ar5523_host_available(ar);
+	if (error) {
+		ar5523_err(ar, "could not initialize adapter\n");
+		goto out_cancel_rx_cmd;
+	}
+
+	error = ar5523_get_max_rxsz(ar);
+	if (error) {
+		ar5523_err(ar, "could not get caps from adapter\n");
+		goto out_cancel_rx_cmd;
+	}
+
+	error = ar5523_get_devcap(ar);
+	if (error) {
+		ar5523_err(ar, "could not get caps from adapter\n");
+		goto out_cancel_rx_cmd;
+	}
+
+	error = ar5523_get_devstatus(ar);
+	if (error != 0) {
+		ar5523_err(ar, "could not get device status\n");
+		goto out_cancel_rx_cmd;
+	}
+
+	ar5523_info(ar, "MAC/BBP AR5523, RF AR%c112\n",
+			(id->driver_info & AR5523_FLAG_ABG) ? '5' : '2');
+
+	ar->vif = NULL;
+	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+		    IEEE80211_HW_SIGNAL_DBM |
+		    IEEE80211_HW_HAS_RATE_CONTROL;
+	hw->extra_tx_headroom = sizeof(struct ar5523_tx_desc) +
+				sizeof(struct ar5523_chunk);
+	hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+	hw->queues = 1;
+
+	error = ar5523_init_modes(ar);
+	if (error)
+		goto out_cancel_rx_cmd;
+
+	usb_set_intfdata(intf, hw);
+
+	error = ieee80211_register_hw(hw);
+	if (error) {
+		ar5523_err(ar, "could not register device\n");
+		goto out_cancel_rx_cmd;
+	}
+
+	ar5523_info(ar, "Found and initialized AR5523 device\n");
+	return 0;
+
+out_cancel_rx_cmd:
+	ar5523_cancel_rx_cmd(ar);
+out_free_tx_cmd:
+	ar5523_free_tx_cmd(ar);
+out_free_rx_cmd:
+	ar5523_free_rx_cmd(ar);
+out_free_rx_bufs:
+	ar5523_free_rx_bufs(ar);
+out_free_wq:
+	destroy_workqueue(ar->wq);
+out_free_ar:
+	ieee80211_free_hw(hw);
+out:
+	return error;
+}
+
+static void ar5523_disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct ar5523 *ar = hw->priv;
+
+	ar5523_dbg(ar, "detaching\n");
+	set_bit(AR5523_USB_DISCONNECTED, &ar->flags);
+
+	ieee80211_unregister_hw(hw);
+
+	ar5523_cancel_rx_cmd(ar);
+	ar5523_free_tx_cmd(ar);
+	ar5523_free_rx_cmd(ar);
+	ar5523_free_rx_bufs(ar);
+
+	destroy_workqueue(ar->wq);
+
+	ieee80211_free_hw(hw);
+	usb_set_intfdata(intf, NULL);
+}
+
+#define AR5523_DEVICE_UG(vendor, device) \
+	{ USB_DEVICE((vendor), (device)) }, \
+	{ USB_DEVICE((vendor), (device) + 1), \
+		.driver_info = AR5523_FLAG_PRE_FIRMWARE }
+#define AR5523_DEVICE_UX(vendor, device) \
+	{ USB_DEVICE((vendor), (device)), \
+		.driver_info = AR5523_FLAG_ABG }, \
+	{ USB_DEVICE((vendor), (device) + 1), \
+		.driver_info = AR5523_FLAG_ABG|AR5523_FLAG_PRE_FIRMWARE }
+
+static struct usb_device_id ar5523_id_table[] = {
+	AR5523_DEVICE_UG(0x168c, 0x0001),	/* Atheros / AR5523 */
+	AR5523_DEVICE_UG(0x0cf3, 0x0001),	/* Atheros2 / AR5523_1 */
+	AR5523_DEVICE_UG(0x0cf3, 0x0003),	/* Atheros2 / AR5523_2 */
+	AR5523_DEVICE_UX(0x0cf3, 0x0005),	/* Atheros2 / AR5523_3 */
+	AR5523_DEVICE_UG(0x0d8e, 0x7801),	/* Conceptronic / AR5523_1 */
+	AR5523_DEVICE_UX(0x0d8e, 0x7811),	/* Conceptronic / AR5523_2 */
+	AR5523_DEVICE_UX(0x2001, 0x3a00),	/* Dlink / DWLAG132 */
+	AR5523_DEVICE_UG(0x2001, 0x3a02),	/* Dlink / DWLG132 */
+	AR5523_DEVICE_UX(0x2001, 0x3a04),	/* Dlink / DWLAG122 */
+	AR5523_DEVICE_UG(0x1690, 0x0712),	/* Gigaset / AR5523 */
+	AR5523_DEVICE_UG(0x1690, 0x0710),	/* Gigaset / SMCWUSBTG */
+	AR5523_DEVICE_UG(0x129b, 0x160c),	/* Gigaset / USB stick 108
+						   (CyberTAN Technology) */
+	AR5523_DEVICE_UG(0x16ab, 0x7801),	/* Globalsun / AR5523_1 */
+	AR5523_DEVICE_UX(0x16ab, 0x7811),	/* Globalsun / AR5523_2 */
+	AR5523_DEVICE_UG(0x0d8e, 0x7802),	/* Globalsun / AR5523_3 */
+	AR5523_DEVICE_UX(0x0846, 0x4300),	/* Netgear / WG111U */
+	AR5523_DEVICE_UG(0x0846, 0x4250),	/* Netgear / WG111T */
+	AR5523_DEVICE_UG(0x0846, 0x5f00),	/* Netgear / WPN111 */
+	AR5523_DEVICE_UG(0x157e, 0x3006),	/* Umedia / AR5523_1 */
+	AR5523_DEVICE_UX(0x157e, 0x3205),	/* Umedia / AR5523_2 */
+	AR5523_DEVICE_UG(0x157e, 0x3006),	/* Umedia / TEW444UBEU */
+	AR5523_DEVICE_UG(0x1435, 0x0826),	/* Wistronneweb / AR5523_1 */
+	AR5523_DEVICE_UX(0x1435, 0x0828),	/* Wistronneweb / AR5523_2 */
+	AR5523_DEVICE_UG(0x0cde, 0x0012),	/* Zcom / AR5523 */
+	AR5523_DEVICE_UG(0x1385, 0x4250),	/* Netgear3 / WG111T (2) */
+	AR5523_DEVICE_UG(0x1385, 0x5f00),	/* Netgear / WPN111 */
+	AR5523_DEVICE_UG(0x1385, 0x5f02),	/* Netgear / WPN111 */
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, ar5523_id_table);
+
+static struct usb_driver ar5523_driver = {
+	.name		= "ar5523",
+	.id_table	= ar5523_id_table,
+	.probe		= ar5523_probe,
+	.disconnect	= ar5523_disconnect,
+};
+
+static int __init ar5523_init(void)
+{
+	return usb_register(&ar5523_driver);
+}
+
+static void __exit ar5523_exit(void)
+{
+	usb_deregister(&ar5523_driver);
+}
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_FIRMWARE(AR5523_FIRMWARE_FILE);
+
+module_init(ar5523_init);
+module_exit(ar5523_exit);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 2/4] ar5523: Add driver header file
  2012-10-11 20:01 [PATCH 0/4] Driver for the ar5523 chipset Pontus Fuchs
  2012-10-11 20:01 ` [PATCH 1/4] ar5523: Add main driver file Pontus Fuchs
@ 2012-10-11 20:01 ` Pontus Fuchs
  2012-10-13  5:41   ` Kalle Valo
  2012-10-11 20:01 ` [PATCH 3/4] ar5523: Add Firmware API " Pontus Fuchs
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 27+ messages in thread
From: Pontus Fuchs @ 2012-10-11 20:01 UTC (permalink / raw)
  To: gregkh; +Cc: linux-wireless, hch, s.L-H, Pontus Fuchs

Header file containing structures and defines used by the ar5523
driver.

Signed-off-by: Pontus Fuchs <pontus.fuchs@gmail.com>
---
 drivers/staging/ar5523/ar5523.h |  164 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 164 insertions(+)
 create mode 100644 drivers/staging/ar5523/ar5523.h

diff --git a/drivers/staging/ar5523/ar5523.h b/drivers/staging/ar5523/ar5523.h
new file mode 100644
index 0000000..119f9ed
--- /dev/null
+++ b/drivers/staging/ar5523/ar5523.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2012 Pontus Fuchs <pontus.fuchs@gmail.com>
+ *
+ *  This file is free software: you may copy, redistribute and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation, either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ *  This file is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (c) 2006
+ *	Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 Sam Leffler, Errno Consulting
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define AR5523_FLAG_PRE_FIRMWARE	(1 << 0)
+#define AR5523_FLAG_ABG			(1 << 1)
+
+#define AR5523_FIRMWARE_FILE	"ar5523.bin"
+
+#define AR5523_CMD_TX_PIPE	0x01
+#define	AR5523_DATA_TX_PIPE	0x02
+#define	AR5523_CMD_RX_PIPE	0x81
+#define	AR5523_DATA_RX_PIPE	0x82
+
+#define ar5523_cmd_tx_pipe(dev) \
+	usb_sndbulkpipe((dev), AR5523_CMD_TX_PIPE)
+#define ar5523_data_tx_pipe(dev) \
+	usb_sndbulkpipe((dev), AR5523_DATA_TX_PIPE)
+#define ar5523_cmd_rx_pipe(dev) \
+	usb_rcvbulkpipe((dev), AR5523_CMD_RX_PIPE)
+#define ar5523_data_rx_pipe(dev) \
+	usb_rcvbulkpipe((dev), AR5523_DATA_RX_PIPE)
+
+#define	AR5523_DATA_TIMEOUT	10000
+#define	AR5523_CMD_TIMEOUT	1000
+
+#define AR5523_TX_DATA_COUNT		4
+#define AR5523_TX_DATA_RESTART_COUNT	1
+#define AR5523_RX_DATA_COUNT		16
+#define AR5523_RX_DATA_REFILL_COUNT	8
+
+#define AR5523_CMD_ID	1
+#define AR5523_DATA_ID	2
+
+#define AR5523_TX_WD_TIMEOUT	(HZ * 2)
+#define AR5523_FLUSH_TIMEOUT	(HZ * 3)
+
+enum AR5523_flags {
+	AR5523_HW_UP,
+	AR5523_USB_DISCONNECTED,
+	AR5523_CONNECTED
+};
+
+struct ar5523_tx_cmd {
+	struct ar5523		*ar;
+	struct urb		*urb_tx;
+	void			*buf_tx;
+	void			*odata;
+	int			olen;
+	int			flags;
+	int			res;
+	struct completion	done;
+};
+
+/* This struct is placed in tx_info->driver_data. It must not be larger
+ *  than IEEE80211_TX_INFO_DRIVER_DATA_SIZE.
+ */
+struct ar5523_tx_data {
+	struct list_head	list;
+	struct ar5523		*ar;
+	struct sk_buff		*skb;
+	struct urb		*urb;
+};
+
+struct ar5523_rx_data {
+	struct	list_head	list;
+	struct ar5523		*ar;
+	struct urb		*urb;
+	struct sk_buff		*skb;
+};
+
+struct ar5523 {
+	struct usb_device	*dev;
+	struct ieee80211_hw	*hw;
+
+	unsigned long		flags;
+	struct mutex		mutex;
+	struct workqueue_struct *wq;
+
+	struct ar5523_tx_cmd	tx_cmd;
+
+	struct delayed_work	stat_work;
+
+	struct timer_list	tx_wd_timer;
+	struct work_struct	tx_wd_work;
+	struct work_struct	tx_work;
+	struct list_head	tx_queue_pending;
+	struct list_head	tx_queue_submitted;
+	spinlock_t		tx_data_list_lock;
+	wait_queue_head_t	tx_flush_waitq;
+
+	/* Queued + Submitted TX frames */
+	atomic_t		tx_nr_total;
+
+	/* Submitted TX frames */
+	atomic_t		tx_nr_pending;
+
+	void			*rx_cmd_buf;
+	struct urb		*rx_cmd_urb;
+
+	struct ar5523_rx_data	rx_data[AR5523_RX_DATA_COUNT];
+	spinlock_t		rx_data_list_lock;
+	struct list_head	rx_data_free;
+	struct list_head	rx_data_used;
+	atomic_t		rx_data_free_cnt;
+
+	struct work_struct	rx_refill_work;
+
+	int			rxbufsz;
+	u8			serial[16];
+
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate	rates[12];
+	struct ieee80211_supported_band band;
+	struct ieee80211_vif	*vif;
+};
+
+/* flags for sending firmware commands */
+#define AR5523_CMD_FLAG_READ	(1 << 1)
+#define AR5523_CMD_FLAG_MAGIC	(1 << 2)
+
+#define ar5523_dbg(ar, format, arg...) \
+	dev_dbg(&(ar)->dev->dev, format, ## arg)
+#define ar5523_err(ar, format, arg...) \
+do { \
+	if (!test_bit(AR5523_USB_DISCONNECTED, &ar->flags)) { \
+		dev_err(&(ar)->dev->dev, format, ## arg); \
+	} \
+} while (0)
+#define ar5523_info(ar, format, arg...)	\
+	dev_info(&(ar)->dev->dev, format, ## arg)
+
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 3/4] ar5523: Add Firmware API header file
  2012-10-11 20:01 [PATCH 0/4] Driver for the ar5523 chipset Pontus Fuchs
  2012-10-11 20:01 ` [PATCH 1/4] ar5523: Add main driver file Pontus Fuchs
  2012-10-11 20:01 ` [PATCH 2/4] ar5523: Add driver header file Pontus Fuchs
@ 2012-10-11 20:01 ` Pontus Fuchs
  2012-10-11 20:01 ` [PATCH 4/4] ar5523: Add Kconfig and Makefile Pontus Fuchs
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 27+ messages in thread
From: Pontus Fuchs @ 2012-10-11 20:01 UTC (permalink / raw)
  To: gregkh; +Cc: linux-wireless, hch, s.L-H, Pontus Fuchs

Structures and defines used for communication with the FW.

Signed-off-by: Pontus Fuchs <pontus.fuchs@gmail.com>
---
 drivers/staging/ar5523/ar5523_hw.h |  447 ++++++++++++++++++++++++++++++++++++
 1 file changed, 447 insertions(+)
 create mode 100644 drivers/staging/ar5523/ar5523_hw.h

diff --git a/drivers/staging/ar5523/ar5523_hw.h b/drivers/staging/ar5523/ar5523_hw.h
new file mode 100644
index 0000000..3e37a08
--- /dev/null
+++ b/drivers/staging/ar5523/ar5523_hw.h
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 2012 Pontus Fuchs <pontus.fuchs@gmail.com>
+ *
+ *  This file is free software: you may copy, redistribute and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation, either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ *  This file is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (c) 2006
+ *	Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 Sam Leffler, Errno Consulting
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* all fields are big endian */
+struct ar5523_fwblock {
+	__be32		flags;
+#define AR5523_WRITE_BLOCK	(1 << 4)
+
+	__be32	len;
+#define AR5523_MAX_FWBLOCK_SIZE	2048
+
+	__be32		total;
+	__be32		remain;
+	__be32		rxtotal;
+	__be32		pad[123];
+} __packed;
+
+#define AR5523_MAX_RXCMDSZ	1024
+#define AR5523_MAX_TXCMDSZ	1024
+
+struct ar5523_cmd_hdr {
+	__be32		len;
+	__be32		code;
+/* NB: these are defined for rev 1.5 firmware; rev 1.6 is different */
+/* messages from Host -> Target */
+#define	WDCMSG_HOST_AVAILABLE		0x01
+#define WDCMSG_BIND			0x02
+#define WDCMSG_TARGET_RESET		0x03
+#define WDCMSG_TARGET_GET_CAPABILITY	0x04
+#define WDCMSG_TARGET_SET_CONFIG	0x05
+#define WDCMSG_TARGET_GET_STATUS	0x06
+#define WDCMSG_TARGET_GET_STATS		0x07
+#define WDCMSG_TARGET_START		0x08
+#define WDCMSG_TARGET_STOP		0x09
+#define WDCMSG_TARGET_ENABLE		0x0a
+#define WDCMSG_TARGET_DISABLE		0x0b
+#define	WDCMSG_CREATE_CONNECTION	0x0c
+#define WDCMSG_UPDATE_CONNECT_ATTR	0x0d
+#define	WDCMSG_DELETE_CONNECT		0x0e
+#define	WDCMSG_SEND			0x0f
+#define WDCMSG_FLUSH			0x10
+/* messages from Target -> Host */
+#define	WDCMSG_STATS_UPDATE		0x11
+#define	WDCMSG_BMISS			0x12
+#define	WDCMSG_DEVICE_AVAIL		0x13
+#define	WDCMSG_SEND_COMPLETE		0x14
+#define	WDCMSG_DATA_AVAIL		0x15
+#define	WDCMSG_SET_PWR_MODE		0x16
+#define	WDCMSG_BMISS_ACK		0x17
+#define	WDCMSG_SET_LED_STEADY		0x18
+#define	WDCMSG_SET_LED_BLINK		0x19
+/* more messages */
+#define	WDCMSG_SETUP_BEACON_DESC	0x1a
+#define	WDCMSG_BEACON_INIT		0x1b
+#define	WDCMSG_RESET_KEY_CACHE		0x1c
+#define	WDCMSG_RESET_KEY_CACHE_ENTRY	0x1d
+#define	WDCMSG_SET_KEY_CACHE_ENTRY	0x1e
+#define	WDCMSG_SET_DECOMP_MASK		0x1f
+#define	WDCMSG_SET_REGULATORY_DOMAIN	0x20
+#define	WDCMSG_SET_LED_STATE		0x21
+#define	WDCMSG_WRITE_ASSOCID		0x22
+#define	WDCMSG_SET_STA_BEACON_TIMERS	0x23
+#define	WDCMSG_GET_TSF			0x24
+#define	WDCMSG_RESET_TSF		0x25
+#define	WDCMSG_SET_ADHOC_MODE		0x26
+#define	WDCMSG_SET_BASIC_RATE		0x27
+#define	WDCMSG_MIB_CONTROL		0x28
+#define	WDCMSG_GET_CHANNEL_DATA		0x29
+#define	WDCMSG_GET_CUR_RSSI		0x2a
+#define	WDCMSG_SET_ANTENNA_SWITCH	0x2b
+#define	WDCMSG_USE_SHORT_SLOT_TIME	0x2f
+#define	WDCMSG_SET_POWER_MODE		0x30
+#define	WDCMSG_SETUP_PSPOLL_DESC	0x31
+#define	WDCMSG_SET_RX_MULTICAST_FILTER	0x32
+#define	WDCMSG_RX_FILTER		0x33
+#define	WDCMSG_PER_CALIBRATION		0x34
+#define	WDCMSG_RESET			0x35
+#define	WDCMSG_DISABLE			0x36
+#define	WDCMSG_PHY_DISABLE		0x37
+#define	WDCMSG_SET_TX_POWER_LIMIT	0x38
+#define	WDCMSG_SET_TX_QUEUE_PARAMS	0x39
+#define	WDCMSG_SETUP_TX_QUEUE		0x3a
+#define	WDCMSG_RELEASE_TX_QUEUE		0x3b
+#define	WDCMSG_SET_DEFAULT_KEY		0x43
+
+	__u32		priv;	/* driver private data,
+				   don't care about endianess */
+	__be32		magic;
+	__be32		reserved2[4];
+};
+
+struct ar5523_cmd_host_available {
+	__be32	sw_ver_major;
+	__be32	sw_ver_minor;
+	__be32	sw_ver_patch;
+	__be32	sw_ver_build;
+} __packed;
+
+#define	ATH_SW_VER_MAJOR	1
+#define	ATH_SW_VER_MINOR	5
+#define	ATH_SW_VER_PATCH	0
+#define	ATH_SW_VER_BUILD	9999
+
+struct ar5523_chunk {
+	u8		seqnum;		/* sequence number for ordering */
+	u8		flags;
+#define	UATH_CFLAGS_FINAL	0x01	/* final chunk of a msg */
+#define	UATH_CFLAGS_RXMSG	0x02	/* chunk contains rx completion */
+#define	UATH_CFLAGS_DEBUG	0x04	/* for debugging */
+	__be16		length;		/* chunk size in bytes */
+	/* chunk data follows */
+} __packed;
+
+/*
+ * Message format for a WDCMSG_DATA_AVAIL message from Target to Host.
+ */
+struct ar5523_rx_desc {
+	__be32	len;		/* msg length including header */
+	__be32	code;		/* WDCMSG_DATA_AVAIL */
+	__be32	gennum;		/* generation number */
+	__be32	status;		/* start of RECEIVE_INFO */
+#define	UATH_STATUS_OK			0
+#define	UATH_STATUS_STOP_IN_PROGRESS	1
+#define	UATH_STATUS_CRC_ERR		2
+#define	UATH_STATUS_PHY_ERR		3
+#define	UATH_STATUS_DECRYPT_CRC_ERR	4
+#define	UATH_STATUS_DECRYPT_MIC_ERR	5
+#define	UATH_STATUS_DECOMP_ERR		6
+#define	UATH_STATUS_KEY_ERR		7
+#define	UATH_STATUS_ERR			8
+	__be32	tstamp_low;	/* low-order 32-bits of rx timestamp */
+	__be32	tstamp_high;	/* high-order 32-bits of rx timestamp */
+	__be32	framelen;	/* frame length */
+	__be32	rate;		/* rx rate code */
+	__be32	antenna;
+	__be32	rssi;
+	__be32	channel;
+	__be32	phyerror;
+	__be32	connix;		/* key table ix for bss traffic */
+	__be32	decrypterror;
+	__be32	keycachemiss;
+	__be32	pad;		/* XXX? */
+} __packed;
+
+struct ar5523_tx_desc {
+	__be32	msglen;
+	__be32	msgid;		/* msg id (supplied by host) */
+	__be32	type;		/* opcode: WDMSG_SEND or WDCMSG_FLUSH */
+	__be32	txqid;		/* tx queue id and flags */
+#define	UATH_TXQID_MASK		0x0f
+#define	UATH_TXQID_MINRATE	0x10	/* use min tx rate */
+#define	UATH_TXQID_FF		0x20	/* content is fast frame */
+	__be32	connid;		/* tx connection id */
+#define UATH_ID_INVALID	0xffffffff	/* for sending prior to connection */
+	__be32	flags;		/* non-zero if response desired */
+#define UATH_TX_NOTIFY	(1 << 24)	/* f/w will send a UATH_NOTIF_TX */
+	__be32	buflen;		/* payload length */
+} __packed;
+
+
+#define AR5523_ID_BSS		2
+#define AR5523_ID_BROADCAST	0xffffffff
+
+/* structure for command UATH_CMD_WRITE_MAC */
+struct ar5523_write_mac {
+	__be32	reg;
+	__be32	len;
+	u8		data[32];
+} __packed;
+
+struct ar5523_cmd_rateset {
+	__u8		length;
+#define AR5523_MAX_NRATES	32
+	__u8		set[AR5523_MAX_NRATES];
+};
+
+struct ar5523_cmd_set_associd {		/* AR5523_WRITE_ASSOCID */
+	__be32	defaultrateix;
+	__be32	associd;
+	__be32	timoffset;
+	__be32	turboprime;
+	__u8	bssid[6];
+} __packed;
+
+/* structure for command WDCMSG_RESET */
+struct ar5523_cmd_reset {
+	__be32	flags;		/* channel flags */
+#define	UATH_CHAN_TURBO	0x0100
+#define	UATH_CHAN_CCK	0x0200
+#define	UATH_CHAN_OFDM	0x0400
+#define	UATH_CHAN_2GHZ	0x1000
+#define	UATH_CHAN_5GHZ	0x2000
+	__be32	freq;		/* channel frequency */
+	__be32	maxrdpower;
+	__be32	cfgctl;
+	__be32	twiceantennareduction;
+	__be32	channelchange;
+	__be32	keeprccontent;
+} __packed;
+
+/* structure for command WDCMSG_SET_BASIC_RATE */
+struct ar5523_cmd_rates {
+	__be32	connid;
+	__be32	keeprccontent;
+	__be32	size;
+	struct ar5523_cmd_rateset rateset;
+} __packed;
+
+enum {
+	WLAN_MODE_NONE = 0,
+	WLAN_MODE_11b,
+	WLAN_MODE_11a,
+	WLAN_MODE_11g,
+	WLAN_MODE_11a_TURBO,
+	WLAN_MODE_11g_TURBO,
+	WLAN_MODE_11a_TURBO_PRIME,
+	WLAN_MODE_11g_TURBO_PRIME,
+	WLAN_MODE_11a_XR,
+	WLAN_MODE_11g_XR,
+};
+
+struct ar5523_cmd_connection_attr {
+	__be32	longpreambleonly;
+	struct ar5523_cmd_rateset	rateset;
+	__be32	wlanmode;
+} __packed;
+
+/* structure for command AR5523_CREATE_CONNECTION */
+struct ar5523_cmd_create_connection {
+	__be32	connid;
+	__be32	bssid;
+	__be32	size;
+	struct ar5523_cmd_connection_attr	connattr;
+} __packed;
+
+struct ar5523_cmd_ledsteady {		/* WDCMSG_SET_LED_STEADY */
+	__be32	lednum;
+#define UATH_LED_LINK		0
+#define UATH_LED_ACTIVITY	1
+	__be32	ledmode;
+#define UATH_LED_OFF	0
+#define UATH_LED_ON	1
+} __packed;
+
+struct ar5523_cmd_ledblink {		/* WDCMSG_SET_LED_BLINK */
+	__be32	lednum;
+	__be32	ledmode;
+	__be32	blinkrate;
+	__be32	slowmode;
+} __packed;
+
+struct ar5523_cmd_ledstate {		/* WDCMSG_SET_LED_STATE */
+	__be32	connected;
+} __packed;
+
+struct ar5523_cmd_txq_attr {
+	__be32	priority;
+	__be32	aifs;
+	__be32	logcwmin;
+	__be32	logcwmax;
+	__be32	bursttime;
+	__be32	mode;
+	__be32	qflags;
+} __packed;
+
+struct ar5523_cmd_txq_setup {		/* WDCMSG_SETUP_TX_QUEUE */
+	__be32	qid;
+	__be32	len;
+	struct ar5523_cmd_txq_attr attr;
+} __packed;
+
+struct ar5523_cmd_rx_filter {		/* WDCMSG_RX_FILTER */
+	__be32	bits;
+#define UATH_FILTER_RX_UCAST		0x00000001
+#define UATH_FILTER_RX_MCAST		0x00000002
+#define UATH_FILTER_RX_BCAST		0x00000004
+#define UATH_FILTER_RX_CONTROL		0x00000008
+#define UATH_FILTER_RX_BEACON		0x00000010	/* beacon frames */
+#define UATH_FILTER_RX_PROM		0x00000020	/* promiscuous mode */
+#define UATH_FILTER_RX_PHY_ERR		0x00000040	/* phy errors */
+#define UATH_FILTER_RX_PHY_RADAR	0x00000080	/* radar phy errors */
+#define UATH_FILTER_RX_XR_POOL		0x00000400	/* XR group polls */
+#define UATH_FILTER_RX_PROBE_REQ	0x00000800
+	__be32	op;
+#define UATH_FILTER_OP_INIT		0x0
+#define UATH_FILTER_OP_SET		0x1
+#define UATH_FILTER_OP_CLEAR		0x2
+#define UATH_FILTER_OP_TEMP		0x3
+#define UATH_FILTER_OP_RESTORE		0x4
+} __packed;
+
+enum {
+	CFG_NONE,			/* Sentinal to indicate "no config" */
+	CFG_REG_DOMAIN,			/* Regulatory Domain */
+	CFG_RATE_CONTROL_ENABLE,
+	CFG_DEF_XMIT_DATA_RATE,		/* NB: if rate control is not enabled */
+	CFG_HW_TX_RETRIES,
+	CFG_SW_TX_RETRIES,
+	CFG_SLOW_CLOCK_ENABLE,
+	CFG_COMP_PROC,
+	CFG_USER_RTS_THRESHOLD,
+	CFG_XR2NORM_RATE_THRESHOLD,
+	CFG_XRMODE_SWITCH_COUNT,
+	CFG_PROTECTION_TYPE,
+	CFG_BURST_SEQ_THRESHOLD,
+	CFG_ABOLT,
+	CFG_IQ_LOG_COUNT_MAX,
+	CFG_MODE_CTS,
+	CFG_WME_ENABLED,
+	CFG_GPRS_CBR_PERIOD,
+	CFG_SERVICE_TYPE,
+	/* MAC Address to use.  Overrides EEPROM */
+	CFG_MAC_ADDR,
+	CFG_DEBUG_EAR,
+	CFG_INIT_REGS,
+	/* An ID for use in error & debug messages */
+	CFG_DEBUG_ID,
+	CFG_COMP_WIN_SZ,
+	CFG_DIVERSITY_CTL,
+	CFG_TP_SCALE,
+	CFG_TPC_HALF_DBM5,
+	CFG_TPC_HALF_DBM2,
+	CFG_OVERRD_TX_POWER,
+	CFG_USE_32KHZ_CLOCK,
+	CFG_GMODE_PROTECTION,
+	CFG_GMODE_PROTECT_RATE_INDEX,
+	CFG_GMODE_NON_ERP_PREAMBLE,
+	CFG_WDC_TRANSPORT_CHUNK_SIZE,
+};
+
+enum {
+	/* Sentinal to indicate "no capability" */
+	CAP_NONE,
+	CAP_ALL,			/* ALL capabilities */
+	CAP_TARGET_VERSION,
+	CAP_TARGET_REVISION,
+	CAP_MAC_VERSION,
+	CAP_MAC_REVISION,
+	CAP_PHY_REVISION,
+	CAP_ANALOG_5GHz_REVISION,
+	CAP_ANALOG_2GHz_REVISION,
+	/* Target supports WDC message debug features */
+	CAP_DEBUG_WDCMSG_SUPPORT,
+
+	CAP_REG_DOMAIN,
+	CAP_COUNTRY_CODE,
+	CAP_REG_CAP_BITS,
+
+	CAP_WIRELESS_MODES,
+	CAP_CHAN_SPREAD_SUPPORT,
+	CAP_SLEEP_AFTER_BEACON_BROKEN,
+	CAP_COMPRESS_SUPPORT,
+	CAP_BURST_SUPPORT,
+	CAP_FAST_FRAMES_SUPPORT,
+	CAP_CHAP_TUNING_SUPPORT,
+	CAP_TURBOG_SUPPORT,
+	CAP_TURBO_PRIME_SUPPORT,
+	CAP_DEVICE_TYPE,
+	CAP_XR_SUPPORT,
+	CAP_WME_SUPPORT,
+	CAP_TOTAL_QUEUES,
+	CAP_CONNECTION_ID_MAX,		/* Should absorb CAP_KEY_CACHE_SIZE */
+
+	CAP_LOW_5GHZ_CHAN,
+	CAP_HIGH_5GHZ_CHAN,
+	CAP_LOW_2GHZ_CHAN,
+	CAP_HIGH_2GHZ_CHAN,
+
+	CAP_MIC_AES_CCM,
+	CAP_MIC_CKIP,
+	CAP_MIC_TKIP,
+	CAP_MIC_TKIP_WME,
+	CAP_CIPHER_AES_CCM,
+	CAP_CIPHER_CKIP,
+	CAP_CIPHER_TKIP,
+
+	CAP_TWICE_ANTENNAGAIN_5G,
+	CAP_TWICE_ANTENNAGAIN_2G,
+};
+
+enum {
+	ST_NONE,                    /* Sentinal to indicate "no status" */
+	ST_ALL,
+	ST_SERVICE_TYPE,
+	ST_WLAN_MODE,
+	ST_FREQ,
+	ST_BAND,
+	ST_LAST_RSSI,
+	ST_PS_FRAMES_DROPPED,
+	ST_CACHED_DEF_ANT,
+	ST_COUNT_OTHER_RX_ANT,
+	ST_USE_FAST_DIVERSITY,
+	ST_MAC_ADDR,
+	ST_RX_GENERATION_NUM,
+	ST_TX_QUEUE_DEPTH,
+	ST_SERIAL_NUMBER,
+	ST_WDC_TRANSPORT_CHUNK_SIZE,
+};
+
+enum {
+	TARGET_DEVICE_AWAKE,
+	TARGET_DEVICE_SLEEP,
+	TARGET_DEVICE_PWRDN,
+	TARGET_DEVICE_PWRSAVE,
+	TARGET_DEVICE_SUSPEND,
+	TARGET_DEVICE_RESUME,
+};
+
+/* this is in net/ieee80211.h, but that conflicts with the mac80211 headers */
+#define IEEE80211_2ADDR_LEN	16
+
+#define AR5523_MIN_RXBUFSZ				\
+	(((sizeof(__be32) + IEEE80211_2ADDR_LEN +	\
+	   sizeof(struct ar5523_rx_desc)) + 3) & ~3)
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 4/4] ar5523: Add Kconfig and Makefile
  2012-10-11 20:01 [PATCH 0/4] Driver for the ar5523 chipset Pontus Fuchs
                   ` (2 preceding siblings ...)
  2012-10-11 20:01 ` [PATCH 3/4] ar5523: Add Firmware API " Pontus Fuchs
@ 2012-10-11 20:01 ` Pontus Fuchs
  2012-10-11 20:31 ` [PATCH 0/4] Driver for the ar5523 chipset Greg KH
  2012-10-13 15:39 ` Christoph Hellwig
  5 siblings, 0 replies; 27+ messages in thread
From: Pontus Fuchs @ 2012-10-11 20:01 UTC (permalink / raw)
  To: gregkh; +Cc: linux-wireless, hch, s.L-H, Pontus Fuchs

Hook into the build system so driver can be built.

Signed-off-by: Pontus Fuchs <pontus.fuchs@gmail.com>
---
 drivers/staging/Kconfig         |    2 ++
 drivers/staging/Makefile        |    1 +
 drivers/staging/ar5523/Kconfig  |    7 +++++++
 drivers/staging/ar5523/Makefile |    1 +
 4 files changed, 11 insertions(+)
 create mode 100644 drivers/staging/ar5523/Kconfig
 create mode 100644 drivers/staging/ar5523/Makefile

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index d805eef..f742293 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -144,4 +144,6 @@ source "drivers/staging/imx-drm/Kconfig"
 
 source "drivers/staging/dgrp/Kconfig"
 
+source "drivers/staging/ar5523/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 76e2ebd..e1cb094 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -64,3 +64,4 @@ obj-$(CONFIG_NET_VENDOR_SILICOM)	+= silicom/
 obj-$(CONFIG_CED1401)		+= ced1401/
 obj-$(CONFIG_DRM_IMX)		+= imx-drm/
 obj-$(CONFIG_DGRP)		+= dgrp/
+obj-$(CONFIG_AR5523)		+= ar5523/
diff --git a/drivers/staging/ar5523/Kconfig b/drivers/staging/ar5523/Kconfig
new file mode 100644
index 0000000..962e839
--- /dev/null
+++ b/drivers/staging/ar5523/Kconfig
@@ -0,0 +1,7 @@
+config AR5523
+	tristate "Atheros AR5523 wireless driver support"
+	depends on MAC80211 && USB
+	select FW_LOADER
+	---help---
+	  This module add support for AR5523 based USB dongles such as D-Link
+	  DWL-G132, Netgear WPN111 and many more.
diff --git a/drivers/staging/ar5523/Makefile b/drivers/staging/ar5523/Makefile
new file mode 100644
index 0000000..574d0aa
--- /dev/null
+++ b/drivers/staging/ar5523/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_AR5523)	:= ar5523.o
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-11 20:01 [PATCH 0/4] Driver for the ar5523 chipset Pontus Fuchs
                   ` (3 preceding siblings ...)
  2012-10-11 20:01 ` [PATCH 4/4] ar5523: Add Kconfig and Makefile Pontus Fuchs
@ 2012-10-11 20:31 ` Greg KH
  2012-10-12  6:52   ` Pontus Fuchs
  2012-10-12  7:11   ` Kalle Valo
  2012-10-13 15:39 ` Christoph Hellwig
  5 siblings, 2 replies; 27+ messages in thread
From: Greg KH @ 2012-10-11 20:31 UTC (permalink / raw)
  To: Pontus Fuchs; +Cc: linux-wireless, hch, s.L-H

On Thu, Oct 11, 2012 at 10:01:38PM +0200, Pontus Fuchs wrote:
> Some time ago I found an old USB wireless dongle in my drawer. I was
> surprised it wasn't supported in Linux. Google gave me an old
> unfinished driver created by Christoph Hellwig. Armed with some
> spare time and naivity I picked up where Christoph left in 2007 and
> now it's actually working pretty fine. It does not crash for me and
> throughput is decent.
> 
> It's based on the FreeBSD driver, which in turn is based on the
> reverse engineered Windows driver, hence a very limited feature set.
> Not even HW crypto offload is supported. But at least there is a
> firmware which has a suitable license:
> 
> http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/contrib/dev/uath/ar5523.bin.uu
> 
> Pontus Fuchs (4):
>   ar5523: Add main driver file
>   ar5523: Add driver header file
>   ar5523: Add Firmware API header file
>   ar5523: Add Kconfig and Makefile
> 
>  drivers/staging/Kconfig            |    2 +
>  drivers/staging/Makefile           |    1 +
>  drivers/staging/ar5523/Kconfig     |    7 +
>  drivers/staging/ar5523/Makefile    |    1 +
>  drivers/staging/ar5523/ar5523.c    | 1824 ++++++++++++++++++++++++++++++++++++
>  drivers/staging/ar5523/ar5523.h    |  164 ++++
>  drivers/staging/ar5523/ar5523_hw.h |  447 +++++++++
>  7 files changed, 2446 insertions(+)
>  create mode 100644 drivers/staging/ar5523/Kconfig
>  create mode 100644 drivers/staging/ar5523/Makefile
>  create mode 100644 drivers/staging/ar5523/ar5523.c
>  create mode 100644 drivers/staging/ar5523/ar5523.h
>  create mode 100644 drivers/staging/ar5523/ar5523_hw.h

Very nice.

But what is keeping this driver from going to the "proper" place in the
kernel tree (i.e. drivers/net/...)?  I don't have a problem adding it to
the staging tree, but it would need a TODO file to list what is needed
to get it out of the staging tree.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-11 20:31 ` [PATCH 0/4] Driver for the ar5523 chipset Greg KH
@ 2012-10-12  6:52   ` Pontus Fuchs
  2012-10-12  7:28     ` Mohammed Shafi
  2012-10-12  8:23     ` Julian Calaby
  2012-10-12  7:11   ` Kalle Valo
  1 sibling, 2 replies; 27+ messages in thread
From: Pontus Fuchs @ 2012-10-12  6:52 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-wireless, hch, s.L-H, linville

Hi,

> But what is keeping this driver from going to the "proper" place in the
> kernel tree (i.e. drivers/net/...)?  I don't have a problem adding it to
> the staging tree, but it would need a TODO file to list what is needed
> to get it out of the staging tree.

I couldn't decide myself so I asked John and he suggested staging. 
Perhaps it should be in driver/net/wireless after all. Can you and John 
give some advice?

Here's a list of things to consider:

* The FW API is reverse engineered and no real documentation is available.
* Limited functionality (notably no HW crypto offload).
* I'm committed to maintaining the driver and fix problems, but only as 
a hobbist.
* It has received very limited testing. S L-H is the only one except 
myself that has run it.

Cheers,

Pontus

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-11 20:31 ` [PATCH 0/4] Driver for the ar5523 chipset Greg KH
  2012-10-12  6:52   ` Pontus Fuchs
@ 2012-10-12  7:11   ` Kalle Valo
  2012-10-12 14:33     ` John W. Linville
  1 sibling, 1 reply; 27+ messages in thread
From: Kalle Valo @ 2012-10-12  7:11 UTC (permalink / raw)
  To: Greg KH; +Cc: Pontus Fuchs, linux-wireless, hch, s.L-H, mcgrof

Greg KH <gregkh@linuxfoundation.org> writes:

> Very nice.

Indeed.

> But what is keeping this driver from going to the "proper" place in the
> kernel tree (i.e. drivers/net/...)?  I don't have a problem adding it to
> the staging tree, but it would need a TODO file to list what is needed
> to get it out of the staging tree.

I did a really quick 5 min review of this driver and IMHO this should go
directly to drivers/net/wireless/ (or maybe to drivers/net/wireless/ath,
Luis?), I don't see any reason why this should go to staging. The code
looks clean and is only 2 kLOC. But I'll review this better during the
weekend and also run some build tests.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-12  6:52   ` Pontus Fuchs
@ 2012-10-12  7:28     ` Mohammed Shafi
  2012-10-12  8:36       ` Pontus Fuchs
  2012-10-12  8:23     ` Julian Calaby
  1 sibling, 1 reply; 27+ messages in thread
From: Mohammed Shafi @ 2012-10-12  7:28 UTC (permalink / raw)
  To: Pontus Fuchs; +Cc: Greg KH, linux-wireless, hch, s.L-H, linville

Hi Pontus,

On Fri, Oct 12, 2012 at 12:22 PM, Pontus Fuchs <pontus.fuchs@gmail.com> wrote:
> Hi,
>
>
>> But what is keeping this driver from going to the "proper" place in the
>> kernel tree (i.e. drivers/net/...)?  I don't have a problem adding it to
>> the staging tree, but it would need a TODO file to list what is needed
>> to get it out of the staging tree.
>
>
> I couldn't decide myself so I asked John and he suggested staging. Perhaps
> it should be in driver/net/wireless after all. Can you and John give some
> advice?
>
> Here's a list of things to consider:
>
> * The FW API is reverse engineered and no real documentation is available.
> * Limited functionality (notably no HW crypto offload).
> * I'm committed to maintaining the driver and fix problems, but only as a
> hobbist.
> * It has received very limited testing. S L-H is the only one except myself
> that has run it.

was just checking out..
http://wireless.kernel.org/en/users/Drivers/ar5523
may be you can update everything there :)

>
> Cheers,
>
> Pontus
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
thanks,
shafi

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-12  6:52   ` Pontus Fuchs
  2012-10-12  7:28     ` Mohammed Shafi
@ 2012-10-12  8:23     ` Julian Calaby
  2012-10-12  8:33       ` Pontus Fuchs
  1 sibling, 1 reply; 27+ messages in thread
From: Julian Calaby @ 2012-10-12  8:23 UTC (permalink / raw)
  To: Pontus Fuchs
  Cc: Greg KH, linux-wireless, hch, s.L-H, linville, Luis R. Rodriguez,
	Sujith Manoharan, Mohammed Shafi Shajakhan

Hi Pontus,

On Fri, Oct 12, 2012 at 5:52 PM, Pontus Fuchs <pontus.fuchs@gmail.com> wrote:
> * The FW API is reverse engineered and no real documentation is available.

Have you asked the Atheros guys (mostly CC'd) if there's any
possibility of the documentation / firmware being released?

Thanks,

-- 
Julian Calaby

Email: julian.calaby@gmail.com
Profile: http://www.google.com/profiles/julian.calaby/
.Plan: http://sites.google.com/site/juliancalaby/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-12  8:23     ` Julian Calaby
@ 2012-10-12  8:33       ` Pontus Fuchs
  2012-10-12 11:45         ` Chadd, Adrian
  2012-10-30 17:08         ` Adrian Chadd
  0 siblings, 2 replies; 27+ messages in thread
From: Pontus Fuchs @ 2012-10-12  8:33 UTC (permalink / raw)
  To: Julian Calaby
  Cc: Greg KH, linux-wireless, hch, s.L-H, linville, Luis R. Rodriguez,
	Sujith Manoharan, Mohammed Shafi Shajakhan, achadd

On 2012-10-12 10:23, Julian Calaby wrote:
> Hi Pontus,
>
> On Fri, Oct 12, 2012 at 5:52 PM, Pontus Fuchs <pontus.fuchs@gmail.com> wrote:
>> * The FW API is reverse engineered and no real documentation is available.
>
> Have you asked the Atheros guys (mostly CC'd) if there's any
> possibility of the documentation / firmware being released?
>

Yes I have. Adrian promised to see what could be done regarding docs.

The FW blob is already released under a good license. I guess the best 
thing would be if Adrian or Luis could submit the fw to linux-firmware 
but lets deal with that when/if the driver gets merged.

Cheers,

Pontus


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-12  7:28     ` Mohammed Shafi
@ 2012-10-12  8:36       ` Pontus Fuchs
  0 siblings, 0 replies; 27+ messages in thread
From: Pontus Fuchs @ 2012-10-12  8:36 UTC (permalink / raw)
  To: Mohammed Shafi; +Cc: Greg KH, linux-wireless, hch, s.L-H, linville

On 2012-10-12 09:28, Mohammed Shafi wrote:

> was just checking out..
> http://wireless.kernel.org/en/users/Drivers/ar5523
> may be you can update everything there :)

Thanks. I'll make sure to update it.

Cheers,

Pontus

^ permalink raw reply	[flat|nested] 27+ messages in thread

* RE: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-12  8:33       ` Pontus Fuchs
@ 2012-10-12 11:45         ` Chadd, Adrian
  2012-10-30 17:08         ` Adrian Chadd
  1 sibling, 0 replies; 27+ messages in thread
From: Chadd, Adrian @ 2012-10-12 11:45 UTC (permalink / raw)
  To: Pontus Fuchs, Julian Calaby
  Cc: Greg KH, linux-wireless, hch, s.L-H, linville, Rodriguez, Luis,
	Manoharan, Sujith, Shajakhan, Mohammed Shafi (Mohammed Shafi)

Yeah, leave it with me. I'm still trying to chase down the right person internally to speak to about such old hardware. :-)

I'll let you know if I can find the firmware source and tools.



Adrian

________________________________________
From: Pontus Fuchs [pontus.fuchs@gmail.com]
Sent: Friday, 12 October 2012 1:33 AM
To: Julian Calaby
Cc: Greg KH; linux-wireless@vger.kernel.org; hch@lst.de; s.L-H@gmx.de; linville@tuxdriver.com; Rodriguez, Luis; Manoharan, Sujith; Shajakhan, Mohammed Shafi (Mohammed Shafi); Chadd, Adrian
Subject: Re: [PATCH 0/4] Driver for the ar5523 chipset

On 2012-10-12 10:23, Julian Calaby wrote:
> Hi Pontus,
>
> On Fri, Oct 12, 2012 at 5:52 PM, Pontus Fuchs <pontus.fuchs@gmail.com> wrote:
>> * The FW API is reverse engineered and no real documentation is available.
>
> Have you asked the Atheros guys (mostly CC'd) if there's any
> possibility of the documentation / firmware being released?
>

Yes I have. Adrian promised to see what could be done regarding docs.

The FW blob is already released under a good license. I guess the best
thing would be if Adrian or Luis could submit the fw to linux-firmware
but lets deal with that when/if the driver gets merged.

Cheers,

Pontus


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-12  7:11   ` Kalle Valo
@ 2012-10-12 14:33     ` John W. Linville
  2012-10-13  5:38       ` Kalle Valo
  0 siblings, 1 reply; 27+ messages in thread
From: John W. Linville @ 2012-10-12 14:33 UTC (permalink / raw)
  To: Kalle Valo; +Cc: Greg KH, Pontus Fuchs, linux-wireless, hch, s.L-H, mcgrof

On Fri, Oct 12, 2012 at 10:11:57AM +0300, Kalle Valo wrote:
> Greg KH <gregkh@linuxfoundation.org> writes:
> 
> > Very nice.
> 
> Indeed.
> 
> > But what is keeping this driver from going to the "proper" place in the
> > kernel tree (i.e. drivers/net/...)?  I don't have a problem adding it to
> > the staging tree, but it would need a TODO file to list what is needed
> > to get it out of the staging tree.
> 
> I did a really quick 5 min review of this driver and IMHO this should go
> directly to drivers/net/wireless/ (or maybe to drivers/net/wireless/ath,
> Luis?), I don't see any reason why this should go to staging. The code
> looks clean and is only 2 kLOC. But I'll review this better during the
> weekend and also run some build tests.

I have no particular objection to that.  FWIW, Pontus contacted me
and said something like "I've got this driver that sucks and needs
a lot of work, should it go to staging?"  Perhaps he was being too
hard on himself? :-)

John
-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-12 14:33     ` John W. Linville
@ 2012-10-13  5:38       ` Kalle Valo
  2012-10-13 10:25         ` Pontus Fuchs
  2012-10-13 15:41         ` Christoph Hellwig
  0 siblings, 2 replies; 27+ messages in thread
From: Kalle Valo @ 2012-10-13  5:38 UTC (permalink / raw)
  To: John W. Linville
  Cc: Greg KH, Pontus Fuchs, linux-wireless, hch, s.L-H, mcgrof

"John W. Linville" <linville@tuxdriver.com> writes:

>> I did a really quick 5 min review of this driver and IMHO this should go
>> directly to drivers/net/wireless/ (or maybe to drivers/net/wireless/ath,
>> Luis?), I don't see any reason why this should go to staging. The code
>> looks clean and is only 2 kLOC. But I'll review this better during the
>> weekend and also run some build tests.
>
> I have no particular objection to that.  FWIW, Pontus contacted me
> and said something like "I've got this driver that sucks and needs
> a lot of work, should it go to staging?"  Perhaps he was being too
> hard on himself? :-)

Well, we are not exactly famous about giving positive feedback ;) So no
wonder new people are extra cautious.

But after a more thorough review I still think that this driver should
go to drivers/net/wireless. For example, it has proper endian support,
it's both gcc and sparse warning free and the code is a pleasure to
read(!).

Here are my comments:

* use ALIGN() and IS_ALIGNED() macros instead of doing it manually
* checkpatch complains about DOS line endings (but that might be due to
  mail server, not sure)
* git-am complained about whitespace damage
* defines inside structures is not common, kernel style is to have the
  defines on top of the structure definition

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/4] ar5523: Add driver header file
  2012-10-11 20:01 ` [PATCH 2/4] ar5523: Add driver header file Pontus Fuchs
@ 2012-10-13  5:41   ` Kalle Valo
  2012-10-13 10:14     ` Pontus Fuchs
  0 siblings, 1 reply; 27+ messages in thread
From: Kalle Valo @ 2012-10-13  5:41 UTC (permalink / raw)
  To: Pontus Fuchs; +Cc: gregkh, linux-wireless, hch, s.L-H

Pontus Fuchs <pontus.fuchs@gmail.com> writes:

> +#define ar5523_err(ar, format, arg...) \
> +do { \
> +	if (!test_bit(AR5523_USB_DISCONNECTED, &ar->flags)) { \
> +		dev_err(&(ar)->dev->dev, format, ## arg); \
> +	} \
> +} while (0)

This looks suspicious and might hide something important. It's better to
fix the real cause than to workaround it in the error log printer. Do
you know why this was added?

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/4] ar5523: Add driver header file
  2012-10-13  5:41   ` Kalle Valo
@ 2012-10-13 10:14     ` Pontus Fuchs
  2012-10-13 15:45       ` Christoph Hellwig
  2012-10-21 19:58       ` Kalle Valo
  0 siblings, 2 replies; 27+ messages in thread
From: Pontus Fuchs @ 2012-10-13 10:14 UTC (permalink / raw)
  To: Kalle Valo; +Cc: gregkh, linux-wireless, hch, s.L-H

On 2012-10-13 07:41, Kalle Valo wrote:
> Pontus Fuchs <pontus.fuchs@gmail.com> writes:
>
>> +#define ar5523_err(ar, format, arg...) \
>> +do { \
>> +	if (!test_bit(AR5523_USB_DISCONNECTED, &ar->flags)) { \
>> +		dev_err(&(ar)->dev->dev, format, ## arg); \
>> +	} \
>> +} while (0)
> This looks suspicious and might hide something important. It's better to
> fix the real cause than to workaround it in the error log printer. Do
> you know why this was added?
Yes. The purpose is to hide all the USB errors that happens when you
hot-unplug the dongle. There can be quite a few URBs in flight and
all of them will fail with -ENODEV or -ESHUTDOWN. Instead of checking
for these errors in all the usb submissions and callbacks I added this
flag. Do you still a problem with this approach?

Cheers,

Pontus


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-13  5:38       ` Kalle Valo
@ 2012-10-13 10:25         ` Pontus Fuchs
  2012-10-21 16:35           ` Kalle Valo
  2012-10-13 15:41         ` Christoph Hellwig
  1 sibling, 1 reply; 27+ messages in thread
From: Pontus Fuchs @ 2012-10-13 10:25 UTC (permalink / raw)
  To: Kalle Valo; +Cc: John W. Linville, Greg KH, linux-wireless, hch, s.L-H, mcgrof

On 2012-10-13 07:38, Kalle Valo wrote:
> But after a more thorough review I still think that this driver should
> go to drivers/net/wireless. For example, it has proper endian support,
> it's both gcc and sparse warning free and the code is a pleasure to
> read(!).
Thanks for the feedback. I'm heavily inspired by the wl12xx code so I guess
the style suites you :)

> Here are my comments:
>
> * use ALIGN() and IS_ALIGNED() macros instead of doing it manually

Will fix.

> * checkpatch complains about DOS line endings (but that might be due to
>    mail server, not sure)

No DOS line endings here. Probably chewed somewhere along the way.

> * git-am complained about whitespace damage

Will fix.

> * defines inside structures is not common, kernel style is to have the
>    defines on top of the structure definition
>

Hmmm. That's legacy from the FreeBSD driver. All those structs are
copy-pasted from here:

http://svn.freebsd.org/base/stable/9/sys/dev/usb/wlan/if_uathreg.h

I would prefer to keep as close as possible to that file but if it's
an issue I can certainly change that.

I'll respin the patches asap.

Cheers,

Pontus


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-11 20:01 [PATCH 0/4] Driver for the ar5523 chipset Pontus Fuchs
                   ` (4 preceding siblings ...)
  2012-10-11 20:31 ` [PATCH 0/4] Driver for the ar5523 chipset Greg KH
@ 2012-10-13 15:39 ` Christoph Hellwig
  2012-10-22 22:43   ` Greg KH
  5 siblings, 1 reply; 27+ messages in thread
From: Christoph Hellwig @ 2012-10-13 15:39 UTC (permalink / raw)
  To: Pontus Fuchs; +Cc: gregkh, linux-wireless, hch, s.L-H

Thanks a lot for picking up the work and finshing the driver.

I agree with the other voices here that the driver should go straight in
and not to staging.


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-13  5:38       ` Kalle Valo
  2012-10-13 10:25         ` Pontus Fuchs
@ 2012-10-13 15:41         ` Christoph Hellwig
  1 sibling, 0 replies; 27+ messages in thread
From: Christoph Hellwig @ 2012-10-13 15:41 UTC (permalink / raw)
  To: Kalle Valo
  Cc: John W. Linville, Greg KH, Pontus Fuchs, linux-wireless, hch,
	s.L-H, mcgrof

On Sat, Oct 13, 2012 at 08:38:42AM +0300, Kalle Valo wrote:
> * defines inside structures is not common, kernel style is to have the
>   defines on top of the structure definition

I don't think that's true.  At least for code I touch a lot having the
constants close to the field they are for is very common.  That might be
because it actually makes a lot sense and eases writing.   So a big NAK
from me for changing that part.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/4] ar5523: Add driver header file
  2012-10-13 10:14     ` Pontus Fuchs
@ 2012-10-13 15:45       ` Christoph Hellwig
  2012-10-21 19:58       ` Kalle Valo
  1 sibling, 0 replies; 27+ messages in thread
From: Christoph Hellwig @ 2012-10-13 15:45 UTC (permalink / raw)
  To: Pontus Fuchs; +Cc: Kalle Valo, gregkh, linux-wireless, hch, s.L-H

On Sat, Oct 13, 2012 at 12:14:29PM +0200, Pontus Fuchs wrote:
> On 2012-10-13 07:41, Kalle Valo wrote:
>> Pontus Fuchs <pontus.fuchs@gmail.com> writes:
>>
>>> +#define ar5523_err(ar, format, arg...) \
>>> +do { \
>>> +	if (!test_bit(AR5523_USB_DISCONNECTED, &ar->flags)) { \
>>> +		dev_err(&(ar)->dev->dev, format, ## arg); \
>>> +	} \
>>> +} while (0)
>> This looks suspicious and might hide something important. It's better to
>> fix the real cause than to workaround it in the error log printer. Do
>> you know why this was added?
> Yes. The purpose is to hide all the USB errors that happens when you
> hot-unplug the dongle. There can be quite a few URBs in flight and
> all of them will fail with -ENODEV or -ESHUTDOWN. Instead of checking
> for these errors in all the usb submissions and callbacks I added this
> flag. Do you still a problem with this approach?

I think it's fine, but it needs a comment explaining it with basically
the same information you just wrote above.


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/4] ar5523: Add main driver file
  2012-10-11 20:01 ` [PATCH 1/4] ar5523: Add main driver file Pontus Fuchs
@ 2012-10-15 20:54   ` Luis R. Rodriguez
  0 siblings, 0 replies; 27+ messages in thread
From: Luis R. Rodriguez @ 2012-10-15 20:54 UTC (permalink / raw)
  To: Pontus Fuchs; +Cc: gregkh, linux-wireless, hch, s.L-H

On Thu, Oct 11, 2012 at 1:01 PM, Pontus Fuchs <pontus.fuchs@gmail.com> wrote:
> This driver is for the AR5523 chipset from Atheros. It was created
> in 2007 by Christoph Hellwig but it was never finished. I found it a
> couple if months ago and after some polishing it's working pretty
> fine.
>
> The driver was written with the FreeBSD driver (uath) as reference,
> which was written with the reverse-engineered windows driver as
> reference, hence the feature set is very limited. Station mode
> only, no HW crypto offload.
>
> Signed-off-by: Pontus Fuchs <pontus.fuchs@gmail.com>

If you guys want to help share code with the BSD family then my
recommendation would be to use a permissive license as we have on
ath9k for example. If that is not an objective then the existing
license is fine. I'm fine with this going into
drivers/net/wireless/ath/ but I unfortunately don't have time right
now to review the code.

  Luis

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-13 10:25         ` Pontus Fuchs
@ 2012-10-21 16:35           ` Kalle Valo
  0 siblings, 0 replies; 27+ messages in thread
From: Kalle Valo @ 2012-10-21 16:35 UTC (permalink / raw)
  To: Pontus Fuchs
  Cc: John W. Linville, Greg KH, linux-wireless, hch, s.L-H, mcgrof

Pontus Fuchs <pontus.fuchs@gmail.com> writes:

> On 2012-10-13 07:38, Kalle Valo wrote:
>> But after a more thorough review I still think that this driver should
>> go to drivers/net/wireless. For example, it has proper endian support,
>> it's both gcc and sparse warning free and the code is a pleasure to
>> read(!).
> Thanks for the feedback. I'm heavily inspired by the wl12xx code so I guess
> the style suites you :)

Haha, then it's no wonder I liked it so much ;

>> * defines inside structures is not common, kernel style is to have the
>>    defines on top of the structure definition
>>
>
> Hmmm. That's legacy from the FreeBSD driver. All those structs are
> copy-pasted from here:
>
> http://svn.freebsd.org/base/stable/9/sys/dev/usb/wlan/if_uathreg.h
>
> I would prefer to keep as close as possible to that file but if it's
> an issue I can certainly change that.

This isn't an issue, it's just style I don't like. But you as the
maintainer have the final call.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/4] ar5523: Add driver header file
  2012-10-13 10:14     ` Pontus Fuchs
  2012-10-13 15:45       ` Christoph Hellwig
@ 2012-10-21 19:58       ` Kalle Valo
  1 sibling, 0 replies; 27+ messages in thread
From: Kalle Valo @ 2012-10-21 19:58 UTC (permalink / raw)
  To: Pontus Fuchs; +Cc: gregkh, linux-wireless, hch, s.L-H

Pontus Fuchs <pontus.fuchs@gmail.com> writes:

> On 2012-10-13 07:41, Kalle Valo wrote:
>> Pontus Fuchs <pontus.fuchs@gmail.com> writes:
>>
>>> +#define ar5523_err(ar, format, arg...) \
>>> +do { \
>>> +	if (!test_bit(AR5523_USB_DISCONNECTED, &ar->flags)) { \
>>> +		dev_err(&(ar)->dev->dev, format, ## arg); \
>>> +	} \
>>> +} while (0)
>> This looks suspicious and might hide something important. It's better to
>> fix the real cause than to workaround it in the error log printer. Do
>> you know why this was added?
>
> Yes. The purpose is to hide all the USB errors that happens when you
> hot-unplug the dongle. There can be quite a few URBs in flight and
> all of them will fail with -ENODEV or -ESHUTDOWN. Instead of checking
> for these errors in all the usb submissions and callbacks I added this
> flag. Do you still a problem with this approach?

IMHO it's bad style to hide that inside a logging macro and might hide
some other bugs. It's better to check the state explicitly in the
callback.

But again this isn't anything important, just something I noticed while
reviewing the code. Do what you think is best.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-13 15:39 ` Christoph Hellwig
@ 2012-10-22 22:43   ` Greg KH
  0 siblings, 0 replies; 27+ messages in thread
From: Greg KH @ 2012-10-22 22:43 UTC (permalink / raw)
  To: Pontus Fuchs, linux-wireless, s.L-H; +Cc: Christoph Hellwig

On Sat, Oct 13, 2012 at 05:39:23PM +0200, Christoph Hellwig wrote:
> Thanks a lot for picking up the work and finshing the driver.
> 
> I agree with the other voices here that the driver should go straight in
> and not to staging.

Ok, I've dropped this from my patch queue, it should go to the wireless
driver maintainers instead.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-12  8:33       ` Pontus Fuchs
  2012-10-12 11:45         ` Chadd, Adrian
@ 2012-10-30 17:08         ` Adrian Chadd
  2012-11-01 17:41           ` Pontus Fuchs
  1 sibling, 1 reply; 27+ messages in thread
From: Adrian Chadd @ 2012-10-30 17:08 UTC (permalink / raw)
  To: Pontus Fuchs
  Cc: Julian Calaby, Greg KH, linux-wireless, hch, s.L-H, linville,
	Luis R. Rodriguez, Sujith Manoharan, Mohammed Shafi Shajakhan,
	achadd

On 12 October 2012 01:33, Pontus Fuchs <pontus.fuchs@gmail.com> wrote:

>> Have you asked the Atheros guys (mostly CC'd) if there's any
>> possibility of the documentation / firmware being released?
>>
>
> Yes I have. Adrian promised to see what could be done regarding docs.
>
> The FW blob is already released under a good license. I guess the best thing
> would be if Adrian or Luis could submit the fw to linux-firmware but lets
> deal with that when/if the driver gets merged.

I've found the firmware source. I have no idea how difficult it will
be to build today, but it's a MIPS CPU core so I should be able to do
it with a cross-compiled MIPS GCC.

I've also found the AR5523 datasheet here.

So, poke me if you'd like me to look up what's going on. It honestly
looks like a MIPS core with a USB device controller and an AR5212 MAC
(the datasheet refers to the AR5212 MAC/BB documentation, so it's
quite likely exactly that.)

On the up side, it's a 30MHz MIPS4kp core with 256KiB of SRAM and some
SPI flash for booting. So it's entirely plausible we could write a
replacement firmware for it based on the FreeBSD AR5212 HAL code (and
whatever extra AR5523 specific mac/bb initvals and calibration data
format.)

In _fact_, since it's basically an AR5212 glued on the side, we could
plausibly extend ath5k and the FreeBSD ath driver to do what ath9k_htc
does. :-)

Ok, that's enough carrot-on-a-stick for y'all.



Adrian

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/4] Driver for the ar5523 chipset
  2012-10-30 17:08         ` Adrian Chadd
@ 2012-11-01 17:41           ` Pontus Fuchs
  0 siblings, 0 replies; 27+ messages in thread
From: Pontus Fuchs @ 2012-11-01 17:41 UTC (permalink / raw)
  To: Adrian Chadd
  Cc: Julian Calaby, linux-wireless, hch, s.L-H, linville,
	Luis R. Rodriguez, Sujith Manoharan, Mohammed Shafi Shajakhan,
	achadd

On 2012-10-30 18:08, Adrian Chadd wrote:

>> I've found the firmware source. I have no idea how difficult it will
>> be to build today, but it's a MIPS CPU core so I should be able to do
>> it with a cross-compiled MIPS GCC.
>>
>> I've also found the AR5523 datasheet here.
>>
>> So, poke me if you'd like me to look up what's going on. It honestly
>> looks like a MIPS core with a USB device controller and an AR5212 MAC
>> (the datasheet refers to the AR5212 MAC/BB documentation, so it's
>> quite likely exactly that.)
>>

Thanks for looking into this. Sounds great. Is it possible for qca
to share any of this me, with or without NDA?

Cheers,

Pontus

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2012-11-01 17:41 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-11 20:01 [PATCH 0/4] Driver for the ar5523 chipset Pontus Fuchs
2012-10-11 20:01 ` [PATCH 1/4] ar5523: Add main driver file Pontus Fuchs
2012-10-15 20:54   ` Luis R. Rodriguez
2012-10-11 20:01 ` [PATCH 2/4] ar5523: Add driver header file Pontus Fuchs
2012-10-13  5:41   ` Kalle Valo
2012-10-13 10:14     ` Pontus Fuchs
2012-10-13 15:45       ` Christoph Hellwig
2012-10-21 19:58       ` Kalle Valo
2012-10-11 20:01 ` [PATCH 3/4] ar5523: Add Firmware API " Pontus Fuchs
2012-10-11 20:01 ` [PATCH 4/4] ar5523: Add Kconfig and Makefile Pontus Fuchs
2012-10-11 20:31 ` [PATCH 0/4] Driver for the ar5523 chipset Greg KH
2012-10-12  6:52   ` Pontus Fuchs
2012-10-12  7:28     ` Mohammed Shafi
2012-10-12  8:36       ` Pontus Fuchs
2012-10-12  8:23     ` Julian Calaby
2012-10-12  8:33       ` Pontus Fuchs
2012-10-12 11:45         ` Chadd, Adrian
2012-10-30 17:08         ` Adrian Chadd
2012-11-01 17:41           ` Pontus Fuchs
2012-10-12  7:11   ` Kalle Valo
2012-10-12 14:33     ` John W. Linville
2012-10-13  5:38       ` Kalle Valo
2012-10-13 10:25         ` Pontus Fuchs
2012-10-21 16:35           ` Kalle Valo
2012-10-13 15:41         ` Christoph Hellwig
2012-10-13 15:39 ` Christoph Hellwig
2012-10-22 22:43   ` Greg KH

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.