162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2006 Damien Bergamini <damien.bergamini@free.fr>
362306a36Sopenharmony_ci * Copyright (c) 2006 Sam Leffler, Errno Consulting
462306a36Sopenharmony_ci * Copyright (c) 2007 Christoph Hellwig <hch@lst.de>
562306a36Sopenharmony_ci * Copyright (c) 2008-2009 Weongyo Jeong <weongyo@freebsd.org>
662306a36Sopenharmony_ci * Copyright (c) 2012 Pontus Fuchs <pontus.fuchs@gmail.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
962306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
1062306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1362306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1462306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1562306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1662306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1762306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1862306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * This driver is based on the uath driver written by Damien Bergamini for
2362306a36Sopenharmony_ci * OpenBSD, who did black-box analysis of the Windows binary driver to find
2462306a36Sopenharmony_ci * out how the hardware works.  It contains a lot magic numbers because of
2562306a36Sopenharmony_ci * that and only has minimal functionality.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci#include <linux/compiler.h>
2862306a36Sopenharmony_ci#include <linux/kernel.h>
2962306a36Sopenharmony_ci#include <linux/module.h>
3062306a36Sopenharmony_ci#include <linux/list.h>
3162306a36Sopenharmony_ci#include <linux/completion.h>
3262306a36Sopenharmony_ci#include <linux/firmware.h>
3362306a36Sopenharmony_ci#include <linux/skbuff.h>
3462306a36Sopenharmony_ci#include <linux/usb.h>
3562306a36Sopenharmony_ci#include <net/mac80211.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "ar5523.h"
3862306a36Sopenharmony_ci#include "ar5523_hw.h"
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/*
4162306a36Sopenharmony_ci * Various supported device vendors/products.
4262306a36Sopenharmony_ci * UB51: AR5005UG 802.11b/g, UB52: AR5005UX 802.11a/b/g
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int ar5523_submit_rx_cmd(struct ar5523 *ar);
4662306a36Sopenharmony_cistatic void ar5523_data_tx_pkt_put(struct ar5523 *ar);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void ar5523_read_reply(struct ar5523 *ar, struct ar5523_cmd_hdr *hdr,
4962306a36Sopenharmony_ci			      struct ar5523_tx_cmd *cmd)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	int dlen, olen;
5262306a36Sopenharmony_ci	__be32 *rp;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	dlen = be32_to_cpu(hdr->len) - sizeof(*hdr);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (dlen < 0) {
5762306a36Sopenharmony_ci		WARN_ON(1);
5862306a36Sopenharmony_ci		goto out;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	ar5523_dbg(ar, "Code = %d len = %d\n", be32_to_cpu(hdr->code) & 0xff,
6262306a36Sopenharmony_ci		   dlen);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	rp = (__be32 *)(hdr + 1);
6562306a36Sopenharmony_ci	if (dlen >= sizeof(u32)) {
6662306a36Sopenharmony_ci		olen = be32_to_cpu(rp[0]);
6762306a36Sopenharmony_ci		dlen -= sizeof(u32);
6862306a36Sopenharmony_ci		if (olen == 0) {
6962306a36Sopenharmony_ci			/* convention is 0 =>'s one word */
7062306a36Sopenharmony_ci			olen = sizeof(u32);
7162306a36Sopenharmony_ci		}
7262306a36Sopenharmony_ci	} else
7362306a36Sopenharmony_ci		olen = 0;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (cmd->odata) {
7662306a36Sopenharmony_ci		if (cmd->olen < olen) {
7762306a36Sopenharmony_ci			ar5523_err(ar, "olen too small %d < %d\n",
7862306a36Sopenharmony_ci				   cmd->olen, olen);
7962306a36Sopenharmony_ci			cmd->olen = 0;
8062306a36Sopenharmony_ci			cmd->res = -EOVERFLOW;
8162306a36Sopenharmony_ci		} else {
8262306a36Sopenharmony_ci			cmd->olen = olen;
8362306a36Sopenharmony_ci			memcpy(cmd->odata, &rp[1], olen);
8462306a36Sopenharmony_ci			cmd->res = 0;
8562306a36Sopenharmony_ci		}
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ciout:
8962306a36Sopenharmony_ci	complete(&cmd->done);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void ar5523_cmd_rx_cb(struct urb *urb)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct ar5523 *ar = urb->context;
9562306a36Sopenharmony_ci	struct ar5523_tx_cmd *cmd = &ar->tx_cmd;
9662306a36Sopenharmony_ci	struct ar5523_cmd_hdr *hdr = ar->rx_cmd_buf;
9762306a36Sopenharmony_ci	int dlen;
9862306a36Sopenharmony_ci	u32 code, hdrlen;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (urb->status) {
10162306a36Sopenharmony_ci		if (urb->status != -ESHUTDOWN)
10262306a36Sopenharmony_ci			ar5523_err(ar, "RX USB error %d.\n", urb->status);
10362306a36Sopenharmony_ci		goto skip;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (urb->actual_length < sizeof(struct ar5523_cmd_hdr)) {
10762306a36Sopenharmony_ci		ar5523_err(ar, "RX USB too short.\n");
10862306a36Sopenharmony_ci		goto skip;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	ar5523_dbg(ar, "%s code %02x priv %d\n", __func__,
11262306a36Sopenharmony_ci		   be32_to_cpu(hdr->code) & 0xff, hdr->priv);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	code = be32_to_cpu(hdr->code);
11562306a36Sopenharmony_ci	hdrlen = be32_to_cpu(hdr->len);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	switch (code & 0xff) {
11862306a36Sopenharmony_ci	default:
11962306a36Sopenharmony_ci		/* reply to a read command */
12062306a36Sopenharmony_ci		if (hdr->priv != AR5523_CMD_ID) {
12162306a36Sopenharmony_ci			ar5523_err(ar, "Unexpected command id: %02x\n",
12262306a36Sopenharmony_ci				   code & 0xff);
12362306a36Sopenharmony_ci			goto skip;
12462306a36Sopenharmony_ci		}
12562306a36Sopenharmony_ci		ar5523_read_reply(ar, hdr, cmd);
12662306a36Sopenharmony_ci		break;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	case WDCMSG_DEVICE_AVAIL:
12962306a36Sopenharmony_ci		ar5523_dbg(ar, "WDCMSG_DEVICE_AVAIL\n");
13062306a36Sopenharmony_ci		cmd->res = 0;
13162306a36Sopenharmony_ci		cmd->olen = 0;
13262306a36Sopenharmony_ci		complete(&cmd->done);
13362306a36Sopenharmony_ci		break;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	case WDCMSG_SEND_COMPLETE:
13662306a36Sopenharmony_ci		ar5523_dbg(ar, "WDCMSG_SEND_COMPLETE: %d pending\n",
13762306a36Sopenharmony_ci			atomic_read(&ar->tx_nr_pending));
13862306a36Sopenharmony_ci		if (!test_bit(AR5523_HW_UP, &ar->flags))
13962306a36Sopenharmony_ci			ar5523_dbg(ar, "Unexpected WDCMSG_SEND_COMPLETE\n");
14062306a36Sopenharmony_ci		else {
14162306a36Sopenharmony_ci			mod_timer(&ar->tx_wd_timer,
14262306a36Sopenharmony_ci				  jiffies + AR5523_TX_WD_TIMEOUT);
14362306a36Sopenharmony_ci			ar5523_data_tx_pkt_put(ar);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		}
14662306a36Sopenharmony_ci		break;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	case WDCMSG_TARGET_START:
14962306a36Sopenharmony_ci		/* This command returns a bogus id so it needs special
15062306a36Sopenharmony_ci		   handling */
15162306a36Sopenharmony_ci		dlen = hdrlen - sizeof(*hdr);
15262306a36Sopenharmony_ci		if (dlen != (int)sizeof(u32)) {
15362306a36Sopenharmony_ci			ar5523_err(ar, "Invalid reply to WDCMSG_TARGET_START");
15462306a36Sopenharmony_ci			return;
15562306a36Sopenharmony_ci		}
15662306a36Sopenharmony_ci		if (!cmd->odata) {
15762306a36Sopenharmony_ci			ar5523_err(ar, "Unexpected WDCMSG_TARGET_START reply");
15862306a36Sopenharmony_ci			return;
15962306a36Sopenharmony_ci		}
16062306a36Sopenharmony_ci		memcpy(cmd->odata, hdr + 1, sizeof(u32));
16162306a36Sopenharmony_ci		cmd->olen = sizeof(u32);
16262306a36Sopenharmony_ci		cmd->res = 0;
16362306a36Sopenharmony_ci		complete(&cmd->done);
16462306a36Sopenharmony_ci		break;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	case WDCMSG_STATS_UPDATE:
16762306a36Sopenharmony_ci		ar5523_dbg(ar, "WDCMSG_STATS_UPDATE\n");
16862306a36Sopenharmony_ci		break;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ciskip:
17262306a36Sopenharmony_ci	ar5523_submit_rx_cmd(ar);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int ar5523_alloc_rx_cmd(struct ar5523 *ar)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	ar->rx_cmd_urb = usb_alloc_urb(0, GFP_KERNEL);
17862306a36Sopenharmony_ci	if (!ar->rx_cmd_urb)
17962306a36Sopenharmony_ci		return -ENOMEM;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	ar->rx_cmd_buf = usb_alloc_coherent(ar->dev, AR5523_MAX_RXCMDSZ,
18262306a36Sopenharmony_ci					    GFP_KERNEL,
18362306a36Sopenharmony_ci					    &ar->rx_cmd_urb->transfer_dma);
18462306a36Sopenharmony_ci	if (!ar->rx_cmd_buf) {
18562306a36Sopenharmony_ci		usb_free_urb(ar->rx_cmd_urb);
18662306a36Sopenharmony_ci		return -ENOMEM;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci	return 0;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic void ar5523_cancel_rx_cmd(struct ar5523 *ar)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	usb_kill_urb(ar->rx_cmd_urb);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void ar5523_free_rx_cmd(struct ar5523 *ar)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	usb_free_coherent(ar->dev, AR5523_MAX_RXCMDSZ,
19962306a36Sopenharmony_ci			  ar->rx_cmd_buf, ar->rx_cmd_urb->transfer_dma);
20062306a36Sopenharmony_ci	usb_free_urb(ar->rx_cmd_urb);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int ar5523_submit_rx_cmd(struct ar5523 *ar)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	int error;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	usb_fill_bulk_urb(ar->rx_cmd_urb, ar->dev,
20862306a36Sopenharmony_ci			  ar5523_cmd_rx_pipe(ar->dev), ar->rx_cmd_buf,
20962306a36Sopenharmony_ci			  AR5523_MAX_RXCMDSZ, ar5523_cmd_rx_cb, ar);
21062306a36Sopenharmony_ci	ar->rx_cmd_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	error = usb_submit_urb(ar->rx_cmd_urb, GFP_ATOMIC);
21362306a36Sopenharmony_ci	if (error) {
21462306a36Sopenharmony_ci		if (error != -ENODEV)
21562306a36Sopenharmony_ci			ar5523_err(ar, "error %d when submitting rx urb\n",
21662306a36Sopenharmony_ci				   error);
21762306a36Sopenharmony_ci		return error;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci	return 0;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci/*
22362306a36Sopenharmony_ci * Command submitted cb
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_cistatic void ar5523_cmd_tx_cb(struct urb *urb)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct ar5523_tx_cmd *cmd = urb->context;
22862306a36Sopenharmony_ci	struct ar5523 *ar = cmd->ar;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (urb->status) {
23162306a36Sopenharmony_ci		ar5523_err(ar, "Failed to TX command. Status = %d\n",
23262306a36Sopenharmony_ci			   urb->status);
23362306a36Sopenharmony_ci		cmd->res = urb->status;
23462306a36Sopenharmony_ci		complete(&cmd->done);
23562306a36Sopenharmony_ci		return;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (!(cmd->flags & AR5523_CMD_FLAG_READ)) {
23962306a36Sopenharmony_ci		cmd->res = 0;
24062306a36Sopenharmony_ci		complete(&cmd->done);
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic void ar5523_cancel_tx_cmd(struct ar5523 *ar)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	usb_kill_urb(ar->tx_cmd.urb_tx);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic int ar5523_cmd(struct ar5523 *ar, u32 code, const void *idata,
25062306a36Sopenharmony_ci		      int ilen, void *odata, int olen, int flags)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct ar5523_cmd_hdr *hdr;
25362306a36Sopenharmony_ci	struct ar5523_tx_cmd *cmd = &ar->tx_cmd;
25462306a36Sopenharmony_ci	int xferlen, error;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* always bulk-out a multiple of 4 bytes */
25762306a36Sopenharmony_ci	xferlen = (sizeof(struct ar5523_cmd_hdr) + ilen + 3) & ~3;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	hdr = (struct ar5523_cmd_hdr *)cmd->buf_tx;
26062306a36Sopenharmony_ci	memset(hdr, 0, sizeof(struct ar5523_cmd_hdr));
26162306a36Sopenharmony_ci	hdr->len  = cpu_to_be32(xferlen);
26262306a36Sopenharmony_ci	hdr->code = cpu_to_be32(code);
26362306a36Sopenharmony_ci	hdr->priv = AR5523_CMD_ID;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (flags & AR5523_CMD_FLAG_MAGIC)
26662306a36Sopenharmony_ci		hdr->magic = cpu_to_be32(1 << 24);
26762306a36Sopenharmony_ci	if (ilen)
26862306a36Sopenharmony_ci		memcpy(hdr + 1, idata, ilen);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	cmd->odata = odata;
27162306a36Sopenharmony_ci	cmd->olen = olen;
27262306a36Sopenharmony_ci	cmd->flags = flags;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	ar5523_dbg(ar, "do cmd %02x\n", code);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	usb_fill_bulk_urb(cmd->urb_tx, ar->dev, ar5523_cmd_tx_pipe(ar->dev),
27762306a36Sopenharmony_ci			  cmd->buf_tx, xferlen, ar5523_cmd_tx_cb, cmd);
27862306a36Sopenharmony_ci	cmd->urb_tx->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	error = usb_submit_urb(cmd->urb_tx, GFP_KERNEL);
28162306a36Sopenharmony_ci	if (error) {
28262306a36Sopenharmony_ci		ar5523_err(ar, "could not send command 0x%x, error=%d\n",
28362306a36Sopenharmony_ci			   code, error);
28462306a36Sopenharmony_ci		return error;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&cmd->done, 2 * HZ)) {
28862306a36Sopenharmony_ci		ar5523_cancel_tx_cmd(ar);
28962306a36Sopenharmony_ci		cmd->odata = NULL;
29062306a36Sopenharmony_ci		ar5523_err(ar, "timeout waiting for command %02x reply\n",
29162306a36Sopenharmony_ci			   code);
29262306a36Sopenharmony_ci		cmd->res = -ETIMEDOUT;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci	return cmd->res;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic int ar5523_cmd_write(struct ar5523 *ar, u32 code, const void *data,
29862306a36Sopenharmony_ci			    int len, int flags)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	flags &= ~AR5523_CMD_FLAG_READ;
30162306a36Sopenharmony_ci	return ar5523_cmd(ar, code, data, len, NULL, 0, flags);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int ar5523_cmd_read(struct ar5523 *ar, u32 code, const void *idata,
30562306a36Sopenharmony_ci			   int ilen, void *odata, int olen, int flags)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	flags |= AR5523_CMD_FLAG_READ;
30862306a36Sopenharmony_ci	return ar5523_cmd(ar, code, idata, ilen, odata, olen, flags);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic int ar5523_config(struct ar5523 *ar, u32 reg, u32 val)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct ar5523_write_mac write;
31462306a36Sopenharmony_ci	int error;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	write.reg = cpu_to_be32(reg);
31762306a36Sopenharmony_ci	write.len = cpu_to_be32(0);	/* 0 = single write */
31862306a36Sopenharmony_ci	*(__be32 *)write.data = cpu_to_be32(val);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	error = ar5523_cmd_write(ar, WDCMSG_TARGET_SET_CONFIG, &write,
32162306a36Sopenharmony_ci				 3 * sizeof(u32), 0);
32262306a36Sopenharmony_ci	if (error != 0)
32362306a36Sopenharmony_ci		ar5523_err(ar, "could not write register 0x%02x\n", reg);
32462306a36Sopenharmony_ci	return error;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic int ar5523_config_multi(struct ar5523 *ar, u32 reg, const void *data,
32862306a36Sopenharmony_ci			       int len)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	struct ar5523_write_mac write;
33162306a36Sopenharmony_ci	int error;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	write.reg = cpu_to_be32(reg);
33462306a36Sopenharmony_ci	write.len = cpu_to_be32(len);
33562306a36Sopenharmony_ci	memcpy(write.data, data, len);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* properly handle the case where len is zero (reset) */
33862306a36Sopenharmony_ci	error = ar5523_cmd_write(ar, WDCMSG_TARGET_SET_CONFIG, &write,
33962306a36Sopenharmony_ci	    (len == 0) ? sizeof(u32) : 2 * sizeof(u32) + len, 0);
34062306a36Sopenharmony_ci	if (error != 0)
34162306a36Sopenharmony_ci		ar5523_err(ar, "could not write %d bytes to register 0x%02x\n",
34262306a36Sopenharmony_ci			   len, reg);
34362306a36Sopenharmony_ci	return error;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic int ar5523_get_status(struct ar5523 *ar, u32 which, void *odata,
34762306a36Sopenharmony_ci			     int olen)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	int error;
35062306a36Sopenharmony_ci	__be32 which_be;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	which_be = cpu_to_be32(which);
35362306a36Sopenharmony_ci	error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_STATUS,
35462306a36Sopenharmony_ci	    &which_be, sizeof(which_be), odata, olen, AR5523_CMD_FLAG_MAGIC);
35562306a36Sopenharmony_ci	if (error != 0)
35662306a36Sopenharmony_ci		ar5523_err(ar, "could not read EEPROM offset 0x%02x\n", which);
35762306a36Sopenharmony_ci	return error;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int ar5523_get_capability(struct ar5523 *ar, u32 cap, u32 *val)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	int error;
36362306a36Sopenharmony_ci	__be32 cap_be, val_be;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	cap_be = cpu_to_be32(cap);
36662306a36Sopenharmony_ci	error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_CAPABILITY, &cap_be,
36762306a36Sopenharmony_ci				sizeof(cap_be), &val_be, sizeof(__be32),
36862306a36Sopenharmony_ci				AR5523_CMD_FLAG_MAGIC);
36962306a36Sopenharmony_ci	if (error != 0) {
37062306a36Sopenharmony_ci		ar5523_err(ar, "could not read capability %u\n", cap);
37162306a36Sopenharmony_ci		return error;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci	*val = be32_to_cpu(val_be);
37462306a36Sopenharmony_ci	return error;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic int ar5523_get_devcap(struct ar5523 *ar)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci#define	GETCAP(x) do {				\
38062306a36Sopenharmony_ci	error = ar5523_get_capability(ar, x, &cap);		\
38162306a36Sopenharmony_ci	if (error != 0)					\
38262306a36Sopenharmony_ci		return error;				\
38362306a36Sopenharmony_ci	ar5523_info(ar, "Cap: "			\
38462306a36Sopenharmony_ci	    "%s=0x%08x\n", #x, cap);	\
38562306a36Sopenharmony_ci} while (0)
38662306a36Sopenharmony_ci	int error;
38762306a36Sopenharmony_ci	u32 cap;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* collect device capabilities */
39062306a36Sopenharmony_ci	GETCAP(CAP_TARGET_VERSION);
39162306a36Sopenharmony_ci	GETCAP(CAP_TARGET_REVISION);
39262306a36Sopenharmony_ci	GETCAP(CAP_MAC_VERSION);
39362306a36Sopenharmony_ci	GETCAP(CAP_MAC_REVISION);
39462306a36Sopenharmony_ci	GETCAP(CAP_PHY_REVISION);
39562306a36Sopenharmony_ci	GETCAP(CAP_ANALOG_5GHz_REVISION);
39662306a36Sopenharmony_ci	GETCAP(CAP_ANALOG_2GHz_REVISION);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	GETCAP(CAP_REG_DOMAIN);
39962306a36Sopenharmony_ci	GETCAP(CAP_REG_CAP_BITS);
40062306a36Sopenharmony_ci	GETCAP(CAP_WIRELESS_MODES);
40162306a36Sopenharmony_ci	GETCAP(CAP_CHAN_SPREAD_SUPPORT);
40262306a36Sopenharmony_ci	GETCAP(CAP_COMPRESS_SUPPORT);
40362306a36Sopenharmony_ci	GETCAP(CAP_BURST_SUPPORT);
40462306a36Sopenharmony_ci	GETCAP(CAP_FAST_FRAMES_SUPPORT);
40562306a36Sopenharmony_ci	GETCAP(CAP_CHAP_TUNING_SUPPORT);
40662306a36Sopenharmony_ci	GETCAP(CAP_TURBOG_SUPPORT);
40762306a36Sopenharmony_ci	GETCAP(CAP_TURBO_PRIME_SUPPORT);
40862306a36Sopenharmony_ci	GETCAP(CAP_DEVICE_TYPE);
40962306a36Sopenharmony_ci	GETCAP(CAP_WME_SUPPORT);
41062306a36Sopenharmony_ci	GETCAP(CAP_TOTAL_QUEUES);
41162306a36Sopenharmony_ci	GETCAP(CAP_CONNECTION_ID_MAX);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	GETCAP(CAP_LOW_5GHZ_CHAN);
41462306a36Sopenharmony_ci	GETCAP(CAP_HIGH_5GHZ_CHAN);
41562306a36Sopenharmony_ci	GETCAP(CAP_LOW_2GHZ_CHAN);
41662306a36Sopenharmony_ci	GETCAP(CAP_HIGH_2GHZ_CHAN);
41762306a36Sopenharmony_ci	GETCAP(CAP_TWICE_ANTENNAGAIN_5G);
41862306a36Sopenharmony_ci	GETCAP(CAP_TWICE_ANTENNAGAIN_2G);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	GETCAP(CAP_CIPHER_AES_CCM);
42162306a36Sopenharmony_ci	GETCAP(CAP_CIPHER_TKIP);
42262306a36Sopenharmony_ci	GETCAP(CAP_MIC_TKIP);
42362306a36Sopenharmony_ci	return 0;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic int ar5523_set_ledsteady(struct ar5523 *ar, int lednum, int ledmode)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	struct ar5523_cmd_ledsteady led;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	led.lednum = cpu_to_be32(lednum);
43162306a36Sopenharmony_ci	led.ledmode = cpu_to_be32(ledmode);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	ar5523_dbg(ar, "set %s led %s (steady)\n",
43462306a36Sopenharmony_ci		   (lednum == UATH_LED_LINK) ? "link" : "activity",
43562306a36Sopenharmony_ci		   ledmode ? "on" : "off");
43662306a36Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_SET_LED_STEADY, &led, sizeof(led),
43762306a36Sopenharmony_ci				 0);
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic int ar5523_set_rxfilter(struct ar5523 *ar, u32 bits, u32 op)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct ar5523_cmd_rx_filter rxfilter;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	rxfilter.bits = cpu_to_be32(bits);
44562306a36Sopenharmony_ci	rxfilter.op = cpu_to_be32(op);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	ar5523_dbg(ar, "setting Rx filter=0x%x flags=0x%x\n", bits, op);
44862306a36Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_RX_FILTER, &rxfilter,
44962306a36Sopenharmony_ci				 sizeof(rxfilter), 0);
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int ar5523_reset_tx_queues(struct ar5523 *ar)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	__be32 qid = cpu_to_be32(0);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	ar5523_dbg(ar, "resetting Tx queue\n");
45762306a36Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_RELEASE_TX_QUEUE,
45862306a36Sopenharmony_ci				 &qid, sizeof(qid), 0);
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic int ar5523_set_chan(struct ar5523 *ar)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	struct ieee80211_conf *conf = &ar->hw->conf;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	struct ar5523_cmd_reset reset;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	memset(&reset, 0, sizeof(reset));
46862306a36Sopenharmony_ci	reset.flags |= cpu_to_be32(UATH_CHAN_2GHZ);
46962306a36Sopenharmony_ci	reset.flags |= cpu_to_be32(UATH_CHAN_OFDM);
47062306a36Sopenharmony_ci	reset.freq = cpu_to_be32(conf->chandef.chan->center_freq);
47162306a36Sopenharmony_ci	reset.maxrdpower = cpu_to_be32(50);	/* XXX */
47262306a36Sopenharmony_ci	reset.channelchange = cpu_to_be32(1);
47362306a36Sopenharmony_ci	reset.keeprccontent = cpu_to_be32(0);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	ar5523_dbg(ar, "set chan flags 0x%x freq %d\n",
47662306a36Sopenharmony_ci		   be32_to_cpu(reset.flags),
47762306a36Sopenharmony_ci		   conf->chandef.chan->center_freq);
47862306a36Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_RESET, &reset, sizeof(reset), 0);
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic int ar5523_queue_init(struct ar5523 *ar)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	struct ar5523_cmd_txq_setup qinfo;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	ar5523_dbg(ar, "setting up Tx queue\n");
48662306a36Sopenharmony_ci	qinfo.qid	     = cpu_to_be32(0);
48762306a36Sopenharmony_ci	qinfo.len	     = cpu_to_be32(sizeof(qinfo.attr));
48862306a36Sopenharmony_ci	qinfo.attr.priority  = cpu_to_be32(0);	/* XXX */
48962306a36Sopenharmony_ci	qinfo.attr.aifs	     = cpu_to_be32(3);
49062306a36Sopenharmony_ci	qinfo.attr.logcwmin  = cpu_to_be32(4);
49162306a36Sopenharmony_ci	qinfo.attr.logcwmax  = cpu_to_be32(10);
49262306a36Sopenharmony_ci	qinfo.attr.bursttime = cpu_to_be32(0);
49362306a36Sopenharmony_ci	qinfo.attr.mode	     = cpu_to_be32(0);
49462306a36Sopenharmony_ci	qinfo.attr.qflags    = cpu_to_be32(1);	/* XXX? */
49562306a36Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_SETUP_TX_QUEUE, &qinfo,
49662306a36Sopenharmony_ci				 sizeof(qinfo), 0);
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic int ar5523_switch_chan(struct ar5523 *ar)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	int error;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	error = ar5523_set_chan(ar);
50462306a36Sopenharmony_ci	if (error) {
50562306a36Sopenharmony_ci		ar5523_err(ar, "could not set chan, error %d\n", error);
50662306a36Sopenharmony_ci		goto out_err;
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* reset Tx rings */
51062306a36Sopenharmony_ci	error = ar5523_reset_tx_queues(ar);
51162306a36Sopenharmony_ci	if (error) {
51262306a36Sopenharmony_ci		ar5523_err(ar, "could not reset Tx queues, error %d\n",
51362306a36Sopenharmony_ci			   error);
51462306a36Sopenharmony_ci		goto out_err;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci	/* set Tx rings WME properties */
51762306a36Sopenharmony_ci	error = ar5523_queue_init(ar);
51862306a36Sopenharmony_ci	if (error)
51962306a36Sopenharmony_ci		ar5523_err(ar, "could not init wme, error %d\n", error);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ciout_err:
52262306a36Sopenharmony_ci	return error;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic void ar5523_rx_data_put(struct ar5523 *ar,
52662306a36Sopenharmony_ci				struct ar5523_rx_data *data)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	unsigned long flags;
52962306a36Sopenharmony_ci	spin_lock_irqsave(&ar->rx_data_list_lock, flags);
53062306a36Sopenharmony_ci	list_move(&data->list, &ar->rx_data_free);
53162306a36Sopenharmony_ci	spin_unlock_irqrestore(&ar->rx_data_list_lock, flags);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic void ar5523_data_rx_cb(struct urb *urb)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct ar5523_rx_data *data = urb->context;
53762306a36Sopenharmony_ci	struct ar5523 *ar = data->ar;
53862306a36Sopenharmony_ci	struct ar5523_rx_desc *desc;
53962306a36Sopenharmony_ci	struct ar5523_chunk *chunk;
54062306a36Sopenharmony_ci	struct ieee80211_hw *hw = ar->hw;
54162306a36Sopenharmony_ci	struct ieee80211_rx_status *rx_status;
54262306a36Sopenharmony_ci	u32 rxlen;
54362306a36Sopenharmony_ci	int usblen = urb->actual_length;
54462306a36Sopenharmony_ci	int hdrlen, pad;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	ar5523_dbg(ar, "%s\n", __func__);
54762306a36Sopenharmony_ci	/* sync/async unlink faults aren't errors */
54862306a36Sopenharmony_ci	if (urb->status) {
54962306a36Sopenharmony_ci		if (urb->status != -ESHUTDOWN)
55062306a36Sopenharmony_ci			ar5523_err(ar, "%s: USB err: %d\n", __func__,
55162306a36Sopenharmony_ci				   urb->status);
55262306a36Sopenharmony_ci		goto skip;
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (usblen < AR5523_MIN_RXBUFSZ) {
55662306a36Sopenharmony_ci		ar5523_err(ar, "RX: wrong xfer size (usblen=%d)\n", usblen);
55762306a36Sopenharmony_ci		goto skip;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	chunk = (struct ar5523_chunk *) data->skb->data;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (((chunk->flags & UATH_CFLAGS_FINAL) == 0) ||
56362306a36Sopenharmony_ci		chunk->seqnum != 0) {
56462306a36Sopenharmony_ci		ar5523_dbg(ar, "RX: No final flag. s: %d f: %02x l: %d\n",
56562306a36Sopenharmony_ci			   chunk->seqnum, chunk->flags,
56662306a36Sopenharmony_ci			   be16_to_cpu(chunk->length));
56762306a36Sopenharmony_ci		goto skip;
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/* Rx descriptor is located at the end, 32-bit aligned */
57162306a36Sopenharmony_ci	desc = (struct ar5523_rx_desc *)
57262306a36Sopenharmony_ci		(data->skb->data + usblen - sizeof(struct ar5523_rx_desc));
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	rxlen = be32_to_cpu(desc->len);
57562306a36Sopenharmony_ci	if (rxlen > ar->rxbufsz) {
57662306a36Sopenharmony_ci		ar5523_dbg(ar, "RX: Bad descriptor (len=%d)\n",
57762306a36Sopenharmony_ci			   be32_to_cpu(desc->len));
57862306a36Sopenharmony_ci		goto skip;
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (!rxlen) {
58262306a36Sopenharmony_ci		ar5523_dbg(ar, "RX: rxlen is 0\n");
58362306a36Sopenharmony_ci		goto skip;
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	if (be32_to_cpu(desc->status) != 0) {
58762306a36Sopenharmony_ci		ar5523_dbg(ar, "Bad RX status (0x%x len = %d). Skip\n",
58862306a36Sopenharmony_ci			   be32_to_cpu(desc->status), be32_to_cpu(desc->len));
58962306a36Sopenharmony_ci		goto skip;
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	skb_reserve(data->skb, sizeof(*chunk));
59362306a36Sopenharmony_ci	skb_put(data->skb, rxlen - sizeof(struct ar5523_rx_desc));
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	hdrlen = ieee80211_get_hdrlen_from_skb(data->skb);
59662306a36Sopenharmony_ci	if (!IS_ALIGNED(hdrlen, 4)) {
59762306a36Sopenharmony_ci		ar5523_dbg(ar, "eek, alignment workaround activated\n");
59862306a36Sopenharmony_ci		pad = ALIGN(hdrlen, 4) - hdrlen;
59962306a36Sopenharmony_ci		memmove(data->skb->data + pad, data->skb->data, hdrlen);
60062306a36Sopenharmony_ci		skb_pull(data->skb, pad);
60162306a36Sopenharmony_ci		skb_put(data->skb, pad);
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	rx_status = IEEE80211_SKB_RXCB(data->skb);
60562306a36Sopenharmony_ci	memset(rx_status, 0, sizeof(*rx_status));
60662306a36Sopenharmony_ci	rx_status->freq = be32_to_cpu(desc->channel);
60762306a36Sopenharmony_ci	rx_status->band = hw->conf.chandef.chan->band;
60862306a36Sopenharmony_ci	rx_status->signal = -95 + be32_to_cpu(desc->rssi);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	ieee80211_rx_irqsafe(hw, data->skb);
61162306a36Sopenharmony_ci	data->skb = NULL;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ciskip:
61462306a36Sopenharmony_ci	if (data->skb) {
61562306a36Sopenharmony_ci		dev_kfree_skb_irq(data->skb);
61662306a36Sopenharmony_ci		data->skb = NULL;
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	ar5523_rx_data_put(ar, data);
62062306a36Sopenharmony_ci	if (atomic_inc_return(&ar->rx_data_free_cnt) >=
62162306a36Sopenharmony_ci	    AR5523_RX_DATA_REFILL_COUNT &&
62262306a36Sopenharmony_ci	    test_bit(AR5523_HW_UP, &ar->flags))
62362306a36Sopenharmony_ci		queue_work(ar->wq, &ar->rx_refill_work);
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic void ar5523_rx_refill_work(struct work_struct *work)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	struct ar5523 *ar = container_of(work, struct ar5523, rx_refill_work);
62962306a36Sopenharmony_ci	struct ar5523_rx_data *data;
63062306a36Sopenharmony_ci	unsigned long flags;
63162306a36Sopenharmony_ci	int error;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	ar5523_dbg(ar, "%s\n", __func__);
63462306a36Sopenharmony_ci	do {
63562306a36Sopenharmony_ci		spin_lock_irqsave(&ar->rx_data_list_lock, flags);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci		if (!list_empty(&ar->rx_data_free))
63862306a36Sopenharmony_ci			data = (struct ar5523_rx_data *) ar->rx_data_free.next;
63962306a36Sopenharmony_ci		else
64062306a36Sopenharmony_ci			data = NULL;
64162306a36Sopenharmony_ci		spin_unlock_irqrestore(&ar->rx_data_list_lock, flags);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		if (!data)
64462306a36Sopenharmony_ci			goto done;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci		data->skb = alloc_skb(ar->rxbufsz, GFP_KERNEL);
64762306a36Sopenharmony_ci		if (!data->skb) {
64862306a36Sopenharmony_ci			ar5523_err(ar, "could not allocate rx skbuff\n");
64962306a36Sopenharmony_ci			return;
65062306a36Sopenharmony_ci		}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci		usb_fill_bulk_urb(data->urb, ar->dev,
65362306a36Sopenharmony_ci				  ar5523_data_rx_pipe(ar->dev), data->skb->data,
65462306a36Sopenharmony_ci				  ar->rxbufsz, ar5523_data_rx_cb, data);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		spin_lock_irqsave(&ar->rx_data_list_lock, flags);
65762306a36Sopenharmony_ci		list_move(&data->list, &ar->rx_data_used);
65862306a36Sopenharmony_ci		spin_unlock_irqrestore(&ar->rx_data_list_lock, flags);
65962306a36Sopenharmony_ci		atomic_dec(&ar->rx_data_free_cnt);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci		error = usb_submit_urb(data->urb, GFP_KERNEL);
66262306a36Sopenharmony_ci		if (error) {
66362306a36Sopenharmony_ci			kfree_skb(data->skb);
66462306a36Sopenharmony_ci			if (error != -ENODEV)
66562306a36Sopenharmony_ci				ar5523_err(ar, "Err sending rx data urb %d\n",
66662306a36Sopenharmony_ci					   error);
66762306a36Sopenharmony_ci			ar5523_rx_data_put(ar, data);
66862306a36Sopenharmony_ci			atomic_inc(&ar->rx_data_free_cnt);
66962306a36Sopenharmony_ci			return;
67062306a36Sopenharmony_ci		}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	} while (true);
67362306a36Sopenharmony_cidone:
67462306a36Sopenharmony_ci	return;
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cistatic void ar5523_cancel_rx_bufs(struct ar5523 *ar)
67862306a36Sopenharmony_ci{
67962306a36Sopenharmony_ci	struct ar5523_rx_data *data;
68062306a36Sopenharmony_ci	unsigned long flags;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	do {
68362306a36Sopenharmony_ci		spin_lock_irqsave(&ar->rx_data_list_lock, flags);
68462306a36Sopenharmony_ci		if (!list_empty(&ar->rx_data_used))
68562306a36Sopenharmony_ci			data = (struct ar5523_rx_data *) ar->rx_data_used.next;
68662306a36Sopenharmony_ci		else
68762306a36Sopenharmony_ci			data = NULL;
68862306a36Sopenharmony_ci		spin_unlock_irqrestore(&ar->rx_data_list_lock, flags);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci		if (!data)
69162306a36Sopenharmony_ci			break;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci		usb_kill_urb(data->urb);
69462306a36Sopenharmony_ci		list_move(&data->list, &ar->rx_data_free);
69562306a36Sopenharmony_ci		atomic_inc(&ar->rx_data_free_cnt);
69662306a36Sopenharmony_ci	} while (data);
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic void ar5523_free_rx_bufs(struct ar5523 *ar)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	struct ar5523_rx_data *data;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	ar5523_cancel_rx_bufs(ar);
70462306a36Sopenharmony_ci	while (!list_empty(&ar->rx_data_free)) {
70562306a36Sopenharmony_ci		data = (struct ar5523_rx_data *) ar->rx_data_free.next;
70662306a36Sopenharmony_ci		list_del(&data->list);
70762306a36Sopenharmony_ci		usb_free_urb(data->urb);
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic int ar5523_alloc_rx_bufs(struct ar5523 *ar)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	int i;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	for (i = 0; i < AR5523_RX_DATA_COUNT; i++) {
71662306a36Sopenharmony_ci		struct ar5523_rx_data *data = &ar->rx_data[i];
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		data->ar = ar;
71962306a36Sopenharmony_ci		data->urb = usb_alloc_urb(0, GFP_KERNEL);
72062306a36Sopenharmony_ci		if (!data->urb)
72162306a36Sopenharmony_ci			goto err;
72262306a36Sopenharmony_ci		list_add_tail(&data->list, &ar->rx_data_free);
72362306a36Sopenharmony_ci		atomic_inc(&ar->rx_data_free_cnt);
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci	return 0;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cierr:
72862306a36Sopenharmony_ci	ar5523_free_rx_bufs(ar);
72962306a36Sopenharmony_ci	return -ENOMEM;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic void ar5523_data_tx_pkt_put(struct ar5523 *ar)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	atomic_dec(&ar->tx_nr_total);
73562306a36Sopenharmony_ci	if (!atomic_dec_return(&ar->tx_nr_pending)) {
73662306a36Sopenharmony_ci		del_timer(&ar->tx_wd_timer);
73762306a36Sopenharmony_ci		wake_up(&ar->tx_flush_waitq);
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	if (atomic_read(&ar->tx_nr_total) < AR5523_TX_DATA_RESTART_COUNT) {
74162306a36Sopenharmony_ci		ar5523_dbg(ar, "restart tx queue\n");
74262306a36Sopenharmony_ci		ieee80211_wake_queues(ar->hw);
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_cistatic void ar5523_data_tx_cb(struct urb *urb)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	struct sk_buff *skb = urb->context;
74962306a36Sopenharmony_ci	struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
75062306a36Sopenharmony_ci	struct ar5523_tx_data *data = (struct ar5523_tx_data *)
75162306a36Sopenharmony_ci				       txi->driver_data;
75262306a36Sopenharmony_ci	struct ar5523 *ar = data->ar;
75362306a36Sopenharmony_ci	unsigned long flags;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	ar5523_dbg(ar, "data tx urb completed: %d\n", urb->status);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	spin_lock_irqsave(&ar->tx_data_list_lock, flags);
75862306a36Sopenharmony_ci	list_del(&data->list);
75962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	if (urb->status) {
76262306a36Sopenharmony_ci		ar5523_dbg(ar, "%s: urb status: %d\n", __func__, urb->status);
76362306a36Sopenharmony_ci		ar5523_data_tx_pkt_put(ar);
76462306a36Sopenharmony_ci		ieee80211_free_txskb(ar->hw, skb);
76562306a36Sopenharmony_ci	} else {
76662306a36Sopenharmony_ci		skb_pull(skb, sizeof(struct ar5523_tx_desc) + sizeof(__be32));
76762306a36Sopenharmony_ci		ieee80211_tx_status_irqsafe(ar->hw, skb);
76862306a36Sopenharmony_ci	}
76962306a36Sopenharmony_ci	usb_free_urb(urb);
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cistatic void ar5523_tx(struct ieee80211_hw *hw,
77362306a36Sopenharmony_ci		       struct ieee80211_tx_control *control,
77462306a36Sopenharmony_ci		       struct sk_buff *skb)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
77762306a36Sopenharmony_ci	struct ar5523_tx_data *data = (struct ar5523_tx_data *)
77862306a36Sopenharmony_ci					txi->driver_data;
77962306a36Sopenharmony_ci	struct ar5523 *ar = hw->priv;
78062306a36Sopenharmony_ci	unsigned long flags;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	ar5523_dbg(ar, "tx called\n");
78362306a36Sopenharmony_ci	if (atomic_inc_return(&ar->tx_nr_total) >= AR5523_TX_DATA_COUNT) {
78462306a36Sopenharmony_ci		ar5523_dbg(ar, "tx queue full\n");
78562306a36Sopenharmony_ci		ar5523_dbg(ar, "stop queues (tot %d pend %d)\n",
78662306a36Sopenharmony_ci			   atomic_read(&ar->tx_nr_total),
78762306a36Sopenharmony_ci			   atomic_read(&ar->tx_nr_pending));
78862306a36Sopenharmony_ci		ieee80211_stop_queues(hw);
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	spin_lock_irqsave(&ar->tx_data_list_lock, flags);
79262306a36Sopenharmony_ci	list_add_tail(&data->list, &ar->tx_queue_pending);
79362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	ieee80211_queue_work(ar->hw, &ar->tx_work);
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic void ar5523_tx_work_locked(struct ar5523 *ar)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	struct ar5523_tx_data *data;
80162306a36Sopenharmony_ci	struct ar5523_tx_desc *desc;
80262306a36Sopenharmony_ci	struct ar5523_chunk *chunk;
80362306a36Sopenharmony_ci	struct ieee80211_tx_info *txi;
80462306a36Sopenharmony_ci	struct urb *urb;
80562306a36Sopenharmony_ci	struct sk_buff *skb;
80662306a36Sopenharmony_ci	int error = 0, paylen;
80762306a36Sopenharmony_ci	u32 txqid;
80862306a36Sopenharmony_ci	unsigned long flags;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct ar5523_tx_data) >
81162306a36Sopenharmony_ci		     IEEE80211_TX_INFO_DRIVER_DATA_SIZE);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	ar5523_dbg(ar, "%s\n", __func__);
81462306a36Sopenharmony_ci	do {
81562306a36Sopenharmony_ci		spin_lock_irqsave(&ar->tx_data_list_lock, flags);
81662306a36Sopenharmony_ci		if (!list_empty(&ar->tx_queue_pending)) {
81762306a36Sopenharmony_ci			data = (struct ar5523_tx_data *)
81862306a36Sopenharmony_ci				ar->tx_queue_pending.next;
81962306a36Sopenharmony_ci			list_del(&data->list);
82062306a36Sopenharmony_ci		} else
82162306a36Sopenharmony_ci			data = NULL;
82262306a36Sopenharmony_ci		spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci		if (!data)
82562306a36Sopenharmony_ci			break;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci		txi = container_of((void *)data, struct ieee80211_tx_info,
82862306a36Sopenharmony_ci				   driver_data);
82962306a36Sopenharmony_ci		txqid = 0;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci		skb = container_of((void *)txi, struct sk_buff, cb);
83262306a36Sopenharmony_ci		paylen = skb->len;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci		urb = usb_alloc_urb(0, GFP_KERNEL);
83562306a36Sopenharmony_ci		if (!urb) {
83662306a36Sopenharmony_ci			ieee80211_free_txskb(ar->hw, skb);
83762306a36Sopenharmony_ci			continue;
83862306a36Sopenharmony_ci		}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci		data->ar = ar;
84162306a36Sopenharmony_ci		data->urb = urb;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci		desc = skb_push(skb, sizeof(*desc));
84462306a36Sopenharmony_ci		chunk = skb_push(skb, sizeof(*chunk));
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci		chunk->seqnum = 0;
84762306a36Sopenharmony_ci		chunk->flags = UATH_CFLAGS_FINAL;
84862306a36Sopenharmony_ci		chunk->length = cpu_to_be16(skb->len);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci		desc->msglen = cpu_to_be32(skb->len);
85162306a36Sopenharmony_ci		desc->msgid  = AR5523_DATA_ID;
85262306a36Sopenharmony_ci		desc->buflen = cpu_to_be32(paylen);
85362306a36Sopenharmony_ci		desc->type   = cpu_to_be32(WDCMSG_SEND);
85462306a36Sopenharmony_ci		desc->flags  = cpu_to_be32(UATH_TX_NOTIFY);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		if (test_bit(AR5523_CONNECTED, &ar->flags))
85762306a36Sopenharmony_ci			desc->connid = cpu_to_be32(AR5523_ID_BSS);
85862306a36Sopenharmony_ci		else
85962306a36Sopenharmony_ci			desc->connid = cpu_to_be32(AR5523_ID_BROADCAST);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci		if (txi->flags & IEEE80211_TX_CTL_USE_MINRATE)
86262306a36Sopenharmony_ci			txqid |= UATH_TXQID_MINRATE;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci		desc->txqid = cpu_to_be32(txqid);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci		urb->transfer_flags = URB_ZERO_PACKET;
86762306a36Sopenharmony_ci		usb_fill_bulk_urb(urb, ar->dev, ar5523_data_tx_pipe(ar->dev),
86862306a36Sopenharmony_ci				  skb->data, skb->len, ar5523_data_tx_cb, skb);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci		spin_lock_irqsave(&ar->tx_data_list_lock, flags);
87162306a36Sopenharmony_ci		list_add_tail(&data->list, &ar->tx_queue_submitted);
87262306a36Sopenharmony_ci		spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
87362306a36Sopenharmony_ci		mod_timer(&ar->tx_wd_timer, jiffies + AR5523_TX_WD_TIMEOUT);
87462306a36Sopenharmony_ci		atomic_inc(&ar->tx_nr_pending);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci		ar5523_dbg(ar, "TX Frame (%d pending)\n",
87762306a36Sopenharmony_ci			   atomic_read(&ar->tx_nr_pending));
87862306a36Sopenharmony_ci		error = usb_submit_urb(urb, GFP_KERNEL);
87962306a36Sopenharmony_ci		if (error) {
88062306a36Sopenharmony_ci			ar5523_err(ar, "error %d when submitting tx urb\n",
88162306a36Sopenharmony_ci				   error);
88262306a36Sopenharmony_ci			spin_lock_irqsave(&ar->tx_data_list_lock, flags);
88362306a36Sopenharmony_ci			list_del(&data->list);
88462306a36Sopenharmony_ci			spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
88562306a36Sopenharmony_ci			atomic_dec(&ar->tx_nr_pending);
88662306a36Sopenharmony_ci			ar5523_data_tx_pkt_put(ar);
88762306a36Sopenharmony_ci			usb_free_urb(urb);
88862306a36Sopenharmony_ci			ieee80211_free_txskb(ar->hw, skb);
88962306a36Sopenharmony_ci		}
89062306a36Sopenharmony_ci	} while (true);
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_cistatic void ar5523_tx_work(struct work_struct *work)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	struct ar5523 *ar = container_of(work, struct ar5523, tx_work);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	ar5523_dbg(ar, "%s\n", __func__);
89862306a36Sopenharmony_ci	mutex_lock(&ar->mutex);
89962306a36Sopenharmony_ci	ar5523_tx_work_locked(ar);
90062306a36Sopenharmony_ci	mutex_unlock(&ar->mutex);
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic void ar5523_tx_wd_timer(struct timer_list *t)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct ar5523 *ar = from_timer(ar, t, tx_wd_timer);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	ar5523_dbg(ar, "TX watchdog timer triggered\n");
90862306a36Sopenharmony_ci	ieee80211_queue_work(ar->hw, &ar->tx_wd_work);
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic void ar5523_tx_wd_work(struct work_struct *work)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	struct ar5523 *ar = container_of(work, struct ar5523, tx_wd_work);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	/* Occasionally the TX queues stop responding. The only way to
91662306a36Sopenharmony_ci	 * recover seems to be to reset the dongle.
91762306a36Sopenharmony_ci	 */
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	mutex_lock(&ar->mutex);
92062306a36Sopenharmony_ci	ar5523_err(ar, "TX queue stuck (tot %d pend %d)\n",
92162306a36Sopenharmony_ci		   atomic_read(&ar->tx_nr_total),
92262306a36Sopenharmony_ci		   atomic_read(&ar->tx_nr_pending));
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	ar5523_err(ar, "Will restart dongle.\n");
92562306a36Sopenharmony_ci	ar5523_cmd_write(ar, WDCMSG_TARGET_RESET, NULL, 0, 0);
92662306a36Sopenharmony_ci	mutex_unlock(&ar->mutex);
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_cistatic void ar5523_flush_tx(struct ar5523 *ar)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	ar5523_tx_work_locked(ar);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	/* Don't waste time trying to flush if USB is disconnected */
93462306a36Sopenharmony_ci	if (test_bit(AR5523_USB_DISCONNECTED, &ar->flags))
93562306a36Sopenharmony_ci		return;
93662306a36Sopenharmony_ci	if (!wait_event_timeout(ar->tx_flush_waitq,
93762306a36Sopenharmony_ci	    !atomic_read(&ar->tx_nr_pending), AR5523_FLUSH_TIMEOUT))
93862306a36Sopenharmony_ci		ar5523_err(ar, "flush timeout (tot %d pend %d)\n",
93962306a36Sopenharmony_ci			   atomic_read(&ar->tx_nr_total),
94062306a36Sopenharmony_ci			   atomic_read(&ar->tx_nr_pending));
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic void ar5523_free_tx_cmd(struct ar5523 *ar)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	struct ar5523_tx_cmd *cmd = &ar->tx_cmd;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	usb_free_coherent(ar->dev, AR5523_MAX_RXCMDSZ, cmd->buf_tx,
94862306a36Sopenharmony_ci			  cmd->urb_tx->transfer_dma);
94962306a36Sopenharmony_ci	usb_free_urb(cmd->urb_tx);
95062306a36Sopenharmony_ci}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_cistatic int ar5523_alloc_tx_cmd(struct ar5523 *ar)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	struct ar5523_tx_cmd *cmd = &ar->tx_cmd;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	cmd->ar = ar;
95762306a36Sopenharmony_ci	init_completion(&cmd->done);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	cmd->urb_tx = usb_alloc_urb(0, GFP_KERNEL);
96062306a36Sopenharmony_ci	if (!cmd->urb_tx)
96162306a36Sopenharmony_ci		return -ENOMEM;
96262306a36Sopenharmony_ci	cmd->buf_tx = usb_alloc_coherent(ar->dev, AR5523_MAX_TXCMDSZ,
96362306a36Sopenharmony_ci					 GFP_KERNEL,
96462306a36Sopenharmony_ci					 &cmd->urb_tx->transfer_dma);
96562306a36Sopenharmony_ci	if (!cmd->buf_tx) {
96662306a36Sopenharmony_ci		usb_free_urb(cmd->urb_tx);
96762306a36Sopenharmony_ci		return -ENOMEM;
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci	return 0;
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci/*
97362306a36Sopenharmony_ci * This function is called periodically (every second) when associated to
97462306a36Sopenharmony_ci * query device statistics.
97562306a36Sopenharmony_ci */
97662306a36Sopenharmony_cistatic void ar5523_stat_work(struct work_struct *work)
97762306a36Sopenharmony_ci{
97862306a36Sopenharmony_ci	struct ar5523 *ar = container_of(work, struct ar5523, stat_work.work);
97962306a36Sopenharmony_ci	int error;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	ar5523_dbg(ar, "%s\n", __func__);
98262306a36Sopenharmony_ci	mutex_lock(&ar->mutex);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	/*
98562306a36Sopenharmony_ci	 * Send request for statistics asynchronously once a second. This
98662306a36Sopenharmony_ci	 * seems to be important. Throughput is a lot better if this is done.
98762306a36Sopenharmony_ci	 */
98862306a36Sopenharmony_ci	error = ar5523_cmd_write(ar, WDCMSG_TARGET_GET_STATS, NULL, 0, 0);
98962306a36Sopenharmony_ci	if (error)
99062306a36Sopenharmony_ci		ar5523_err(ar, "could not query stats, error %d\n", error);
99162306a36Sopenharmony_ci	mutex_unlock(&ar->mutex);
99262306a36Sopenharmony_ci	ieee80211_queue_delayed_work(ar->hw, &ar->stat_work, HZ);
99362306a36Sopenharmony_ci}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci/*
99662306a36Sopenharmony_ci * Interface routines to the mac80211 stack.
99762306a36Sopenharmony_ci */
99862306a36Sopenharmony_cistatic int ar5523_start(struct ieee80211_hw *hw)
99962306a36Sopenharmony_ci{
100062306a36Sopenharmony_ci	struct ar5523 *ar = hw->priv;
100162306a36Sopenharmony_ci	int error;
100262306a36Sopenharmony_ci	__be32 val;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	ar5523_dbg(ar, "start called\n");
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	mutex_lock(&ar->mutex);
100762306a36Sopenharmony_ci	val = cpu_to_be32(0);
100862306a36Sopenharmony_ci	ar5523_cmd_write(ar, WDCMSG_BIND, &val, sizeof(val), 0);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	/* set MAC address */
101162306a36Sopenharmony_ci	ar5523_config_multi(ar, CFG_MAC_ADDR, &ar->hw->wiphy->perm_addr,
101262306a36Sopenharmony_ci			    ETH_ALEN);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	/* XXX honor net80211 state */
101562306a36Sopenharmony_ci	ar5523_config(ar, CFG_RATE_CONTROL_ENABLE, 0x00000001);
101662306a36Sopenharmony_ci	ar5523_config(ar, CFG_DIVERSITY_CTL, 0x00000001);
101762306a36Sopenharmony_ci	ar5523_config(ar, CFG_ABOLT, 0x0000003f);
101862306a36Sopenharmony_ci	ar5523_config(ar, CFG_WME_ENABLED, 0x00000000);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	ar5523_config(ar, CFG_SERVICE_TYPE, 1);
102162306a36Sopenharmony_ci	ar5523_config(ar, CFG_TP_SCALE, 0x00000000);
102262306a36Sopenharmony_ci	ar5523_config(ar, CFG_TPC_HALF_DBM5, 0x0000003c);
102362306a36Sopenharmony_ci	ar5523_config(ar, CFG_TPC_HALF_DBM2, 0x0000003c);
102462306a36Sopenharmony_ci	ar5523_config(ar, CFG_OVERRD_TX_POWER, 0x00000000);
102562306a36Sopenharmony_ci	ar5523_config(ar, CFG_GMODE_PROTECTION, 0x00000000);
102662306a36Sopenharmony_ci	ar5523_config(ar, CFG_GMODE_PROTECT_RATE_INDEX, 0x00000003);
102762306a36Sopenharmony_ci	ar5523_config(ar, CFG_PROTECTION_TYPE, 0x00000000);
102862306a36Sopenharmony_ci	ar5523_config(ar, CFG_MODE_CTS, 0x00000002);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	error = ar5523_cmd_read(ar, WDCMSG_TARGET_START, NULL, 0,
103162306a36Sopenharmony_ci	    &val, sizeof(val), AR5523_CMD_FLAG_MAGIC);
103262306a36Sopenharmony_ci	if (error) {
103362306a36Sopenharmony_ci		ar5523_dbg(ar, "could not start target, error %d\n", error);
103462306a36Sopenharmony_ci		goto err;
103562306a36Sopenharmony_ci	}
103662306a36Sopenharmony_ci	ar5523_dbg(ar, "WDCMSG_TARGET_START returns handle: 0x%x\n",
103762306a36Sopenharmony_ci		   be32_to_cpu(val));
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	ar5523_switch_chan(ar);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	val = cpu_to_be32(TARGET_DEVICE_AWAKE);
104262306a36Sopenharmony_ci	ar5523_cmd_write(ar, WDCMSG_SET_PWR_MODE, &val, sizeof(val), 0);
104362306a36Sopenharmony_ci	/* XXX? check */
104462306a36Sopenharmony_ci	ar5523_cmd_write(ar, WDCMSG_RESET_KEY_CACHE, NULL, 0, 0);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	set_bit(AR5523_HW_UP, &ar->flags);
104762306a36Sopenharmony_ci	queue_work(ar->wq, &ar->rx_refill_work);
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	/* enable Rx */
105062306a36Sopenharmony_ci	ar5523_set_rxfilter(ar, 0, UATH_FILTER_OP_INIT);
105162306a36Sopenharmony_ci	ar5523_set_rxfilter(ar,
105262306a36Sopenharmony_ci			    UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST |
105362306a36Sopenharmony_ci			    UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON,
105462306a36Sopenharmony_ci			    UATH_FILTER_OP_SET);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	ar5523_set_ledsteady(ar, UATH_LED_ACTIVITY, UATH_LED_ON);
105762306a36Sopenharmony_ci	ar5523_dbg(ar, "start OK\n");
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_cierr:
106062306a36Sopenharmony_ci	mutex_unlock(&ar->mutex);
106162306a36Sopenharmony_ci	return error;
106262306a36Sopenharmony_ci}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_cistatic void ar5523_stop(struct ieee80211_hw *hw)
106562306a36Sopenharmony_ci{
106662306a36Sopenharmony_ci	struct ar5523 *ar = hw->priv;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	ar5523_dbg(ar, "stop called\n");
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	cancel_delayed_work_sync(&ar->stat_work);
107162306a36Sopenharmony_ci	mutex_lock(&ar->mutex);
107262306a36Sopenharmony_ci	clear_bit(AR5523_HW_UP, &ar->flags);
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	ar5523_set_ledsteady(ar, UATH_LED_LINK, UATH_LED_OFF);
107562306a36Sopenharmony_ci	ar5523_set_ledsteady(ar, UATH_LED_ACTIVITY, UATH_LED_OFF);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	ar5523_cmd_write(ar, WDCMSG_TARGET_STOP, NULL, 0, 0);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	del_timer_sync(&ar->tx_wd_timer);
108062306a36Sopenharmony_ci	cancel_work_sync(&ar->tx_wd_work);
108162306a36Sopenharmony_ci	cancel_work_sync(&ar->rx_refill_work);
108262306a36Sopenharmony_ci	ar5523_cancel_rx_bufs(ar);
108362306a36Sopenharmony_ci	mutex_unlock(&ar->mutex);
108462306a36Sopenharmony_ci}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_cistatic int ar5523_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
108762306a36Sopenharmony_ci{
108862306a36Sopenharmony_ci	struct ar5523 *ar = hw->priv;
108962306a36Sopenharmony_ci	int ret;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	ar5523_dbg(ar, "set_rts_threshold called\n");
109262306a36Sopenharmony_ci	mutex_lock(&ar->mutex);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	ret = ar5523_config(ar, CFG_USER_RTS_THRESHOLD, value);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	mutex_unlock(&ar->mutex);
109762306a36Sopenharmony_ci	return ret;
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_cistatic void ar5523_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
110162306a36Sopenharmony_ci			 u32 queues, bool drop)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	struct ar5523 *ar = hw->priv;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	ar5523_dbg(ar, "flush called\n");
110662306a36Sopenharmony_ci	ar5523_flush_tx(ar);
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic int ar5523_add_interface(struct ieee80211_hw *hw,
111062306a36Sopenharmony_ci				struct ieee80211_vif *vif)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	struct ar5523 *ar = hw->priv;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	ar5523_dbg(ar, "add interface called\n");
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	if (ar->vif) {
111762306a36Sopenharmony_ci		ar5523_dbg(ar, "invalid add_interface\n");
111862306a36Sopenharmony_ci		return -EOPNOTSUPP;
111962306a36Sopenharmony_ci	}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	switch (vif->type) {
112262306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
112362306a36Sopenharmony_ci		ar->vif = vif;
112462306a36Sopenharmony_ci		break;
112562306a36Sopenharmony_ci	default:
112662306a36Sopenharmony_ci		return -EOPNOTSUPP;
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci	return 0;
112962306a36Sopenharmony_ci}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_cistatic void ar5523_remove_interface(struct ieee80211_hw *hw,
113262306a36Sopenharmony_ci				    struct ieee80211_vif *vif)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	struct ar5523 *ar = hw->priv;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	ar5523_dbg(ar, "remove interface called\n");
113762306a36Sopenharmony_ci	ar->vif = NULL;
113862306a36Sopenharmony_ci}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_cistatic int ar5523_hwconfig(struct ieee80211_hw *hw, u32 changed)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	struct ar5523 *ar = hw->priv;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	ar5523_dbg(ar, "config called\n");
114562306a36Sopenharmony_ci	mutex_lock(&ar->mutex);
114662306a36Sopenharmony_ci	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
114762306a36Sopenharmony_ci		ar5523_dbg(ar, "Do channel switch\n");
114862306a36Sopenharmony_ci		ar5523_flush_tx(ar);
114962306a36Sopenharmony_ci		ar5523_switch_chan(ar);
115062306a36Sopenharmony_ci	}
115162306a36Sopenharmony_ci	mutex_unlock(&ar->mutex);
115262306a36Sopenharmony_ci	return 0;
115362306a36Sopenharmony_ci}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_cistatic int ar5523_get_wlan_mode(struct ar5523 *ar,
115662306a36Sopenharmony_ci				struct ieee80211_bss_conf *bss_conf)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	struct ieee80211_supported_band *band;
115962306a36Sopenharmony_ci	int bit;
116062306a36Sopenharmony_ci	struct ieee80211_sta *sta;
116162306a36Sopenharmony_ci	u32 sta_rate_set;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	band = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
116462306a36Sopenharmony_ci	sta = ieee80211_find_sta(ar->vif, bss_conf->bssid);
116562306a36Sopenharmony_ci	if (!sta) {
116662306a36Sopenharmony_ci		ar5523_info(ar, "STA not found!\n");
116762306a36Sopenharmony_ci		return WLAN_MODE_11b;
116862306a36Sopenharmony_ci	}
116962306a36Sopenharmony_ci	sta_rate_set = sta->deflink.supp_rates[ar->hw->conf.chandef.chan->band];
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	for (bit = 0; bit < band->n_bitrates; bit++) {
117262306a36Sopenharmony_ci		if (sta_rate_set & 1) {
117362306a36Sopenharmony_ci			int rate = band->bitrates[bit].bitrate;
117462306a36Sopenharmony_ci			switch (rate) {
117562306a36Sopenharmony_ci			case 60:
117662306a36Sopenharmony_ci			case 90:
117762306a36Sopenharmony_ci			case 120:
117862306a36Sopenharmony_ci			case 180:
117962306a36Sopenharmony_ci			case 240:
118062306a36Sopenharmony_ci			case 360:
118162306a36Sopenharmony_ci			case 480:
118262306a36Sopenharmony_ci			case 540:
118362306a36Sopenharmony_ci				return WLAN_MODE_11g;
118462306a36Sopenharmony_ci			}
118562306a36Sopenharmony_ci		}
118662306a36Sopenharmony_ci		sta_rate_set >>= 1;
118762306a36Sopenharmony_ci	}
118862306a36Sopenharmony_ci	return WLAN_MODE_11b;
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_cistatic void ar5523_create_rateset(struct ar5523 *ar,
119262306a36Sopenharmony_ci				  struct ieee80211_bss_conf *bss_conf,
119362306a36Sopenharmony_ci				  struct ar5523_cmd_rateset *rs,
119462306a36Sopenharmony_ci				  bool basic)
119562306a36Sopenharmony_ci{
119662306a36Sopenharmony_ci	struct ieee80211_supported_band *band;
119762306a36Sopenharmony_ci	struct ieee80211_sta *sta;
119862306a36Sopenharmony_ci	int bit, i = 0;
119962306a36Sopenharmony_ci	u32 sta_rate_set, basic_rate_set;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	sta = ieee80211_find_sta(ar->vif, bss_conf->bssid);
120262306a36Sopenharmony_ci	basic_rate_set = bss_conf->basic_rates;
120362306a36Sopenharmony_ci	if (!sta) {
120462306a36Sopenharmony_ci		ar5523_info(ar, "STA not found. Cannot set rates\n");
120562306a36Sopenharmony_ci		sta_rate_set = bss_conf->basic_rates;
120662306a36Sopenharmony_ci	} else
120762306a36Sopenharmony_ci		sta_rate_set = sta->deflink.supp_rates[ar->hw->conf.chandef.chan->band];
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	ar5523_dbg(ar, "sta rate_set = %08x\n", sta_rate_set);
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	band = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
121262306a36Sopenharmony_ci	for (bit = 0; bit < band->n_bitrates; bit++) {
121362306a36Sopenharmony_ci		BUG_ON(i >= AR5523_MAX_NRATES);
121462306a36Sopenharmony_ci		ar5523_dbg(ar, "Considering rate %d : %d\n",
121562306a36Sopenharmony_ci			   band->bitrates[bit].hw_value, sta_rate_set & 1);
121662306a36Sopenharmony_ci		if (sta_rate_set & 1) {
121762306a36Sopenharmony_ci			rs->set[i] = band->bitrates[bit].hw_value;
121862306a36Sopenharmony_ci			if (basic_rate_set & 1 && basic)
121962306a36Sopenharmony_ci				rs->set[i] |= 0x80;
122062306a36Sopenharmony_ci			i++;
122162306a36Sopenharmony_ci		}
122262306a36Sopenharmony_ci		sta_rate_set >>= 1;
122362306a36Sopenharmony_ci		basic_rate_set >>= 1;
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	rs->length = i;
122762306a36Sopenharmony_ci}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_cistatic int ar5523_set_basic_rates(struct ar5523 *ar,
123062306a36Sopenharmony_ci				  struct ieee80211_bss_conf *bss)
123162306a36Sopenharmony_ci{
123262306a36Sopenharmony_ci	struct ar5523_cmd_rates rates;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	memset(&rates, 0, sizeof(rates));
123562306a36Sopenharmony_ci	rates.connid = cpu_to_be32(2);		/* XXX */
123662306a36Sopenharmony_ci	rates.size   = cpu_to_be32(sizeof(struct ar5523_cmd_rateset));
123762306a36Sopenharmony_ci	ar5523_create_rateset(ar, bss, &rates.rateset, true);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_SET_BASIC_RATE, &rates,
124062306a36Sopenharmony_ci				sizeof(rates), 0);
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_cistatic int ar5523_create_connection(struct ar5523 *ar,
124462306a36Sopenharmony_ci				    struct ieee80211_vif *vif,
124562306a36Sopenharmony_ci				    struct ieee80211_bss_conf *bss)
124662306a36Sopenharmony_ci{
124762306a36Sopenharmony_ci	struct ar5523_cmd_create_connection create;
124862306a36Sopenharmony_ci	int wlan_mode;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	memset(&create, 0, sizeof(create));
125162306a36Sopenharmony_ci	create.connid = cpu_to_be32(2);
125262306a36Sopenharmony_ci	create.bssid = cpu_to_be32(0);
125362306a36Sopenharmony_ci	/* XXX packed or not?  */
125462306a36Sopenharmony_ci	create.size = cpu_to_be32(sizeof(struct ar5523_cmd_rateset));
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	ar5523_create_rateset(ar, bss, &create.connattr.rateset, false);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	wlan_mode = ar5523_get_wlan_mode(ar, bss);
125962306a36Sopenharmony_ci	create.connattr.wlanmode = cpu_to_be32(wlan_mode);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_CREATE_CONNECTION, &create,
126262306a36Sopenharmony_ci				sizeof(create), 0);
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_cistatic int ar5523_write_associd(struct ar5523 *ar, struct ieee80211_vif *vif)
126662306a36Sopenharmony_ci{
126762306a36Sopenharmony_ci	struct ieee80211_bss_conf *bss = &vif->bss_conf;
126862306a36Sopenharmony_ci	struct ar5523_cmd_set_associd associd;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	memset(&associd, 0, sizeof(associd));
127162306a36Sopenharmony_ci	associd.defaultrateix = cpu_to_be32(0);	/* XXX */
127262306a36Sopenharmony_ci	associd.associd = cpu_to_be32(vif->cfg.aid);
127362306a36Sopenharmony_ci	associd.timoffset = cpu_to_be32(0x3b);	/* XXX */
127462306a36Sopenharmony_ci	memcpy(associd.bssid, bss->bssid, ETH_ALEN);
127562306a36Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_WRITE_ASSOCID, &associd,
127662306a36Sopenharmony_ci				sizeof(associd), 0);
127762306a36Sopenharmony_ci}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_cistatic void ar5523_bss_info_changed(struct ieee80211_hw *hw,
128062306a36Sopenharmony_ci				    struct ieee80211_vif *vif,
128162306a36Sopenharmony_ci				    struct ieee80211_bss_conf *bss,
128262306a36Sopenharmony_ci				    u64 changed)
128362306a36Sopenharmony_ci{
128462306a36Sopenharmony_ci	struct ar5523 *ar = hw->priv;
128562306a36Sopenharmony_ci	int error;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	ar5523_dbg(ar, "bss_info_changed called\n");
128862306a36Sopenharmony_ci	mutex_lock(&ar->mutex);
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	if (!(changed & BSS_CHANGED_ASSOC))
129162306a36Sopenharmony_ci		goto out_unlock;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	if (vif->cfg.assoc) {
129462306a36Sopenharmony_ci		error = ar5523_create_connection(ar, vif, bss);
129562306a36Sopenharmony_ci		if (error) {
129662306a36Sopenharmony_ci			ar5523_err(ar, "could not create connection\n");
129762306a36Sopenharmony_ci			goto out_unlock;
129862306a36Sopenharmony_ci		}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci		error = ar5523_set_basic_rates(ar, bss);
130162306a36Sopenharmony_ci		if (error) {
130262306a36Sopenharmony_ci			ar5523_err(ar, "could not set negotiated rate set\n");
130362306a36Sopenharmony_ci			goto out_unlock;
130462306a36Sopenharmony_ci		}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci		error = ar5523_write_associd(ar, vif);
130762306a36Sopenharmony_ci		if (error) {
130862306a36Sopenharmony_ci			ar5523_err(ar, "could not set association\n");
130962306a36Sopenharmony_ci			goto out_unlock;
131062306a36Sopenharmony_ci		}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci		/* turn link LED on */
131362306a36Sopenharmony_ci		ar5523_set_ledsteady(ar, UATH_LED_LINK, UATH_LED_ON);
131462306a36Sopenharmony_ci		set_bit(AR5523_CONNECTED, &ar->flags);
131562306a36Sopenharmony_ci		ieee80211_queue_delayed_work(hw, &ar->stat_work, HZ);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	} else {
131862306a36Sopenharmony_ci		cancel_delayed_work(&ar->stat_work);
131962306a36Sopenharmony_ci		clear_bit(AR5523_CONNECTED, &ar->flags);
132062306a36Sopenharmony_ci		ar5523_set_ledsteady(ar, UATH_LED_LINK, UATH_LED_OFF);
132162306a36Sopenharmony_ci	}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ciout_unlock:
132462306a36Sopenharmony_ci	mutex_unlock(&ar->mutex);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci#define AR5523_SUPPORTED_FILTERS (FIF_ALLMULTI | \
132962306a36Sopenharmony_ci				  FIF_FCSFAIL | \
133062306a36Sopenharmony_ci				  FIF_OTHER_BSS)
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_cistatic void ar5523_configure_filter(struct ieee80211_hw *hw,
133362306a36Sopenharmony_ci				    unsigned int changed_flags,
133462306a36Sopenharmony_ci				    unsigned int *total_flags,
133562306a36Sopenharmony_ci				    u64 multicast)
133662306a36Sopenharmony_ci{
133762306a36Sopenharmony_ci	struct ar5523 *ar = hw->priv;
133862306a36Sopenharmony_ci	u32 filter = 0;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	ar5523_dbg(ar, "configure_filter called\n");
134162306a36Sopenharmony_ci	mutex_lock(&ar->mutex);
134262306a36Sopenharmony_ci	ar5523_flush_tx(ar);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	*total_flags &= AR5523_SUPPORTED_FILTERS;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	/* The filters seems strange. UATH_FILTER_RX_BCAST and
134762306a36Sopenharmony_ci	 * UATH_FILTER_RX_MCAST does not result in those frames being RXed.
134862306a36Sopenharmony_ci	 * The only way I have found to get [mb]cast frames seems to be
134962306a36Sopenharmony_ci	 * to set UATH_FILTER_RX_PROM. */
135062306a36Sopenharmony_ci	filter |= UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST |
135162306a36Sopenharmony_ci		  UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON |
135262306a36Sopenharmony_ci		  UATH_FILTER_RX_PROM;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	ar5523_set_rxfilter(ar, 0, UATH_FILTER_OP_INIT);
135562306a36Sopenharmony_ci	ar5523_set_rxfilter(ar, filter, UATH_FILTER_OP_SET);
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	mutex_unlock(&ar->mutex);
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_cistatic const struct ieee80211_ops ar5523_ops = {
136162306a36Sopenharmony_ci	.start			= ar5523_start,
136262306a36Sopenharmony_ci	.stop			= ar5523_stop,
136362306a36Sopenharmony_ci	.tx			= ar5523_tx,
136462306a36Sopenharmony_ci	.wake_tx_queue		= ieee80211_handle_wake_tx_queue,
136562306a36Sopenharmony_ci	.set_rts_threshold	= ar5523_set_rts_threshold,
136662306a36Sopenharmony_ci	.add_interface		= ar5523_add_interface,
136762306a36Sopenharmony_ci	.remove_interface	= ar5523_remove_interface,
136862306a36Sopenharmony_ci	.config			= ar5523_hwconfig,
136962306a36Sopenharmony_ci	.bss_info_changed	= ar5523_bss_info_changed,
137062306a36Sopenharmony_ci	.configure_filter	= ar5523_configure_filter,
137162306a36Sopenharmony_ci	.flush			= ar5523_flush,
137262306a36Sopenharmony_ci};
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_cistatic int ar5523_host_available(struct ar5523 *ar)
137562306a36Sopenharmony_ci{
137662306a36Sopenharmony_ci	struct ar5523_cmd_host_available setup;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	/* inform target the host is available */
137962306a36Sopenharmony_ci	setup.sw_ver_major = cpu_to_be32(ATH_SW_VER_MAJOR);
138062306a36Sopenharmony_ci	setup.sw_ver_minor = cpu_to_be32(ATH_SW_VER_MINOR);
138162306a36Sopenharmony_ci	setup.sw_ver_patch = cpu_to_be32(ATH_SW_VER_PATCH);
138262306a36Sopenharmony_ci	setup.sw_ver_build = cpu_to_be32(ATH_SW_VER_BUILD);
138362306a36Sopenharmony_ci	return ar5523_cmd_read(ar, WDCMSG_HOST_AVAILABLE,
138462306a36Sopenharmony_ci			       &setup, sizeof(setup), NULL, 0, 0);
138562306a36Sopenharmony_ci}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_cistatic int ar5523_get_devstatus(struct ar5523 *ar)
138862306a36Sopenharmony_ci{
138962306a36Sopenharmony_ci	u8 macaddr[ETH_ALEN];
139062306a36Sopenharmony_ci	int error;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	/* retrieve MAC address */
139362306a36Sopenharmony_ci	error = ar5523_get_status(ar, ST_MAC_ADDR, macaddr, ETH_ALEN);
139462306a36Sopenharmony_ci	if (error) {
139562306a36Sopenharmony_ci		ar5523_err(ar, "could not read MAC address\n");
139662306a36Sopenharmony_ci		return error;
139762306a36Sopenharmony_ci	}
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	SET_IEEE80211_PERM_ADDR(ar->hw, macaddr);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	error = ar5523_get_status(ar, ST_SERIAL_NUMBER,
140262306a36Sopenharmony_ci	    &ar->serial[0], sizeof(ar->serial));
140362306a36Sopenharmony_ci	if (error) {
140462306a36Sopenharmony_ci		ar5523_err(ar, "could not read device serial number\n");
140562306a36Sopenharmony_ci		return error;
140662306a36Sopenharmony_ci	}
140762306a36Sopenharmony_ci	return 0;
140862306a36Sopenharmony_ci}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci#define AR5523_SANE_RXBUFSZ 2000
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_cistatic int ar5523_get_max_rxsz(struct ar5523 *ar)
141362306a36Sopenharmony_ci{
141462306a36Sopenharmony_ci	int error;
141562306a36Sopenharmony_ci	__be32 rxsize;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	/* Get max rx size */
141862306a36Sopenharmony_ci	error = ar5523_get_status(ar, ST_WDC_TRANSPORT_CHUNK_SIZE, &rxsize,
141962306a36Sopenharmony_ci				  sizeof(rxsize));
142062306a36Sopenharmony_ci	if (error != 0) {
142162306a36Sopenharmony_ci		ar5523_err(ar, "could not read max RX size\n");
142262306a36Sopenharmony_ci		return error;
142362306a36Sopenharmony_ci	}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	ar->rxbufsz = be32_to_cpu(rxsize);
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	if (!ar->rxbufsz || ar->rxbufsz > AR5523_SANE_RXBUFSZ) {
142862306a36Sopenharmony_ci		ar5523_err(ar, "Bad rxbufsz from device. Using %d instead\n",
142962306a36Sopenharmony_ci			   AR5523_SANE_RXBUFSZ);
143062306a36Sopenharmony_ci		ar->rxbufsz = AR5523_SANE_RXBUFSZ;
143162306a36Sopenharmony_ci	}
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	ar5523_dbg(ar, "Max RX buf size: %d\n", ar->rxbufsz);
143462306a36Sopenharmony_ci	return 0;
143562306a36Sopenharmony_ci}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci/*
143862306a36Sopenharmony_ci * This is copied from rtl818x, but we should probably move this
143962306a36Sopenharmony_ci * to common code as in OpenBSD.
144062306a36Sopenharmony_ci */
144162306a36Sopenharmony_cistatic const struct ieee80211_rate ar5523_rates[] = {
144262306a36Sopenharmony_ci	{ .bitrate = 10, .hw_value = 2, },
144362306a36Sopenharmony_ci	{ .bitrate = 20, .hw_value = 4 },
144462306a36Sopenharmony_ci	{ .bitrate = 55, .hw_value = 11, },
144562306a36Sopenharmony_ci	{ .bitrate = 110, .hw_value = 22, },
144662306a36Sopenharmony_ci	{ .bitrate = 60, .hw_value = 12, },
144762306a36Sopenharmony_ci	{ .bitrate = 90, .hw_value = 18, },
144862306a36Sopenharmony_ci	{ .bitrate = 120, .hw_value = 24, },
144962306a36Sopenharmony_ci	{ .bitrate = 180, .hw_value = 36, },
145062306a36Sopenharmony_ci	{ .bitrate = 240, .hw_value = 48, },
145162306a36Sopenharmony_ci	{ .bitrate = 360, .hw_value = 72, },
145262306a36Sopenharmony_ci	{ .bitrate = 480, .hw_value = 96, },
145362306a36Sopenharmony_ci	{ .bitrate = 540, .hw_value = 108, },
145462306a36Sopenharmony_ci};
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_cistatic const struct ieee80211_channel ar5523_channels[] = {
145762306a36Sopenharmony_ci	{ .center_freq = 2412 },
145862306a36Sopenharmony_ci	{ .center_freq = 2417 },
145962306a36Sopenharmony_ci	{ .center_freq = 2422 },
146062306a36Sopenharmony_ci	{ .center_freq = 2427 },
146162306a36Sopenharmony_ci	{ .center_freq = 2432 },
146262306a36Sopenharmony_ci	{ .center_freq = 2437 },
146362306a36Sopenharmony_ci	{ .center_freq = 2442 },
146462306a36Sopenharmony_ci	{ .center_freq = 2447 },
146562306a36Sopenharmony_ci	{ .center_freq = 2452 },
146662306a36Sopenharmony_ci	{ .center_freq = 2457 },
146762306a36Sopenharmony_ci	{ .center_freq = 2462 },
146862306a36Sopenharmony_ci	{ .center_freq = 2467 },
146962306a36Sopenharmony_ci	{ .center_freq = 2472 },
147062306a36Sopenharmony_ci	{ .center_freq = 2484 },
147162306a36Sopenharmony_ci};
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_cistatic int ar5523_init_modes(struct ar5523 *ar)
147462306a36Sopenharmony_ci{
147562306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(ar->channels) != sizeof(ar5523_channels));
147662306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(ar->rates) != sizeof(ar5523_rates));
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	memcpy(ar->channels, ar5523_channels, sizeof(ar5523_channels));
147962306a36Sopenharmony_ci	memcpy(ar->rates, ar5523_rates, sizeof(ar5523_rates));
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	ar->band.band = NL80211_BAND_2GHZ;
148262306a36Sopenharmony_ci	ar->band.channels = ar->channels;
148362306a36Sopenharmony_ci	ar->band.n_channels = ARRAY_SIZE(ar5523_channels);
148462306a36Sopenharmony_ci	ar->band.bitrates = ar->rates;
148562306a36Sopenharmony_ci	ar->band.n_bitrates = ARRAY_SIZE(ar5523_rates);
148662306a36Sopenharmony_ci	ar->hw->wiphy->bands[NL80211_BAND_2GHZ] = &ar->band;
148762306a36Sopenharmony_ci	return 0;
148862306a36Sopenharmony_ci}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci/*
149162306a36Sopenharmony_ci * Load the MIPS R4000 microcode into the device.  Once the image is loaded,
149262306a36Sopenharmony_ci * the device will detach itself from the bus and reattach later with a new
149362306a36Sopenharmony_ci * product Id (a la ezusb).
149462306a36Sopenharmony_ci */
149562306a36Sopenharmony_cistatic int ar5523_load_firmware(struct usb_device *dev)
149662306a36Sopenharmony_ci{
149762306a36Sopenharmony_ci	struct ar5523_fwblock *txblock, *rxblock;
149862306a36Sopenharmony_ci	const struct firmware *fw;
149962306a36Sopenharmony_ci	void *fwbuf;
150062306a36Sopenharmony_ci	int len, offset;
150162306a36Sopenharmony_ci	int foolen; /* XXX(hch): handle short transfers */
150262306a36Sopenharmony_ci	int error = -ENXIO;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	if (request_firmware(&fw, AR5523_FIRMWARE_FILE, &dev->dev)) {
150562306a36Sopenharmony_ci		dev_err(&dev->dev, "no firmware found: %s\n",
150662306a36Sopenharmony_ci			AR5523_FIRMWARE_FILE);
150762306a36Sopenharmony_ci		return -ENOENT;
150862306a36Sopenharmony_ci	}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	txblock = kzalloc(sizeof(*txblock), GFP_KERNEL);
151162306a36Sopenharmony_ci	if (!txblock)
151262306a36Sopenharmony_ci		goto out;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	rxblock = kmalloc(sizeof(*rxblock), GFP_KERNEL);
151562306a36Sopenharmony_ci	if (!rxblock)
151662306a36Sopenharmony_ci		goto out_free_txblock;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	fwbuf = kmalloc(AR5523_MAX_FWBLOCK_SIZE, GFP_KERNEL);
151962306a36Sopenharmony_ci	if (!fwbuf)
152062306a36Sopenharmony_ci		goto out_free_rxblock;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	txblock->flags = cpu_to_be32(AR5523_WRITE_BLOCK);
152362306a36Sopenharmony_ci	txblock->total = cpu_to_be32(fw->size);
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	offset = 0;
152662306a36Sopenharmony_ci	len = fw->size;
152762306a36Sopenharmony_ci	while (len > 0) {
152862306a36Sopenharmony_ci		int mlen = min(len, AR5523_MAX_FWBLOCK_SIZE);
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci		txblock->remain = cpu_to_be32(len - mlen);
153162306a36Sopenharmony_ci		txblock->len = cpu_to_be32(mlen);
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci		/* send firmware block meta-data */
153462306a36Sopenharmony_ci		error = usb_bulk_msg(dev, ar5523_cmd_tx_pipe(dev),
153562306a36Sopenharmony_ci				     txblock, sizeof(*txblock), &foolen,
153662306a36Sopenharmony_ci				     AR5523_CMD_TIMEOUT);
153762306a36Sopenharmony_ci		if (error) {
153862306a36Sopenharmony_ci			dev_err(&dev->dev,
153962306a36Sopenharmony_ci				"could not send firmware block info\n");
154062306a36Sopenharmony_ci			goto out_free_fwbuf;
154162306a36Sopenharmony_ci		}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci		/* send firmware block data */
154462306a36Sopenharmony_ci		memcpy(fwbuf, fw->data + offset, mlen);
154562306a36Sopenharmony_ci		error = usb_bulk_msg(dev, ar5523_data_tx_pipe(dev),
154662306a36Sopenharmony_ci				     fwbuf, mlen, &foolen,
154762306a36Sopenharmony_ci				     AR5523_DATA_TIMEOUT);
154862306a36Sopenharmony_ci		if (error) {
154962306a36Sopenharmony_ci			dev_err(&dev->dev,
155062306a36Sopenharmony_ci				"could not send firmware block data\n");
155162306a36Sopenharmony_ci			goto out_free_fwbuf;
155262306a36Sopenharmony_ci		}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci		/* wait for ack from firmware */
155562306a36Sopenharmony_ci		error = usb_bulk_msg(dev, ar5523_cmd_rx_pipe(dev),
155662306a36Sopenharmony_ci				     rxblock, sizeof(*rxblock), &foolen,
155762306a36Sopenharmony_ci				     AR5523_CMD_TIMEOUT);
155862306a36Sopenharmony_ci		if (error) {
155962306a36Sopenharmony_ci			dev_err(&dev->dev,
156062306a36Sopenharmony_ci				"could not read firmware answer\n");
156162306a36Sopenharmony_ci			goto out_free_fwbuf;
156262306a36Sopenharmony_ci		}
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci		len -= mlen;
156562306a36Sopenharmony_ci		offset += mlen;
156662306a36Sopenharmony_ci	}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	/*
156962306a36Sopenharmony_ci	 * Set the error to -ENXIO to make sure we continue probing for
157062306a36Sopenharmony_ci	 * a driver.
157162306a36Sopenharmony_ci	 */
157262306a36Sopenharmony_ci	error = -ENXIO;
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci out_free_fwbuf:
157562306a36Sopenharmony_ci	kfree(fwbuf);
157662306a36Sopenharmony_ci out_free_rxblock:
157762306a36Sopenharmony_ci	kfree(rxblock);
157862306a36Sopenharmony_ci out_free_txblock:
157962306a36Sopenharmony_ci	kfree(txblock);
158062306a36Sopenharmony_ci out:
158162306a36Sopenharmony_ci	release_firmware(fw);
158262306a36Sopenharmony_ci	return error;
158362306a36Sopenharmony_ci}
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_cistatic int ar5523_probe(struct usb_interface *intf,
158662306a36Sopenharmony_ci			const struct usb_device_id *id)
158762306a36Sopenharmony_ci{
158862306a36Sopenharmony_ci	struct usb_device *dev = interface_to_usbdev(intf);
158962306a36Sopenharmony_ci	struct ieee80211_hw *hw;
159062306a36Sopenharmony_ci	struct ar5523 *ar;
159162306a36Sopenharmony_ci	int error = -ENOMEM;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	/*
159462306a36Sopenharmony_ci	 * Load firmware if the device requires it.  This will return
159562306a36Sopenharmony_ci	 * -ENXIO on success and we'll get called back afer the usb
159662306a36Sopenharmony_ci	 * id changes to indicate that the firmware is present.
159762306a36Sopenharmony_ci	 */
159862306a36Sopenharmony_ci	if (id->driver_info & AR5523_FLAG_PRE_FIRMWARE)
159962306a36Sopenharmony_ci		return ar5523_load_firmware(dev);
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	hw = ieee80211_alloc_hw(sizeof(*ar), &ar5523_ops);
160362306a36Sopenharmony_ci	if (!hw)
160462306a36Sopenharmony_ci		goto out;
160562306a36Sopenharmony_ci	SET_IEEE80211_DEV(hw, &intf->dev);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	ar = hw->priv;
160862306a36Sopenharmony_ci	ar->hw = hw;
160962306a36Sopenharmony_ci	ar->dev = dev;
161062306a36Sopenharmony_ci	mutex_init(&ar->mutex);
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	INIT_DELAYED_WORK(&ar->stat_work, ar5523_stat_work);
161362306a36Sopenharmony_ci	timer_setup(&ar->tx_wd_timer, ar5523_tx_wd_timer, 0);
161462306a36Sopenharmony_ci	INIT_WORK(&ar->tx_wd_work, ar5523_tx_wd_work);
161562306a36Sopenharmony_ci	INIT_WORK(&ar->tx_work, ar5523_tx_work);
161662306a36Sopenharmony_ci	INIT_LIST_HEAD(&ar->tx_queue_pending);
161762306a36Sopenharmony_ci	INIT_LIST_HEAD(&ar->tx_queue_submitted);
161862306a36Sopenharmony_ci	spin_lock_init(&ar->tx_data_list_lock);
161962306a36Sopenharmony_ci	atomic_set(&ar->tx_nr_total, 0);
162062306a36Sopenharmony_ci	atomic_set(&ar->tx_nr_pending, 0);
162162306a36Sopenharmony_ci	init_waitqueue_head(&ar->tx_flush_waitq);
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	atomic_set(&ar->rx_data_free_cnt, 0);
162462306a36Sopenharmony_ci	INIT_WORK(&ar->rx_refill_work, ar5523_rx_refill_work);
162562306a36Sopenharmony_ci	INIT_LIST_HEAD(&ar->rx_data_free);
162662306a36Sopenharmony_ci	INIT_LIST_HEAD(&ar->rx_data_used);
162762306a36Sopenharmony_ci	spin_lock_init(&ar->rx_data_list_lock);
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	ar->wq = create_singlethread_workqueue("ar5523");
163062306a36Sopenharmony_ci	if (!ar->wq) {
163162306a36Sopenharmony_ci		ar5523_err(ar, "Could not create wq\n");
163262306a36Sopenharmony_ci		goto out_free_ar;
163362306a36Sopenharmony_ci	}
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	error = ar5523_alloc_rx_bufs(ar);
163662306a36Sopenharmony_ci	if (error) {
163762306a36Sopenharmony_ci		ar5523_err(ar, "Could not allocate rx buffers\n");
163862306a36Sopenharmony_ci		goto out_free_wq;
163962306a36Sopenharmony_ci	}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	error = ar5523_alloc_rx_cmd(ar);
164262306a36Sopenharmony_ci	if (error) {
164362306a36Sopenharmony_ci		ar5523_err(ar, "Could not allocate rx command buffers\n");
164462306a36Sopenharmony_ci		goto out_free_rx_bufs;
164562306a36Sopenharmony_ci	}
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	error = ar5523_alloc_tx_cmd(ar);
164862306a36Sopenharmony_ci	if (error) {
164962306a36Sopenharmony_ci		ar5523_err(ar, "Could not allocate tx command buffers\n");
165062306a36Sopenharmony_ci		goto out_free_rx_cmd;
165162306a36Sopenharmony_ci	}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	error = ar5523_submit_rx_cmd(ar);
165462306a36Sopenharmony_ci	if (error) {
165562306a36Sopenharmony_ci		ar5523_err(ar, "Failed to submit rx cmd\n");
165662306a36Sopenharmony_ci		goto out_free_tx_cmd;
165762306a36Sopenharmony_ci	}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	/*
166062306a36Sopenharmony_ci	 * We're now ready to send/receive firmware commands.
166162306a36Sopenharmony_ci	 */
166262306a36Sopenharmony_ci	error = ar5523_host_available(ar);
166362306a36Sopenharmony_ci	if (error) {
166462306a36Sopenharmony_ci		ar5523_err(ar, "could not initialize adapter\n");
166562306a36Sopenharmony_ci		goto out_cancel_rx_cmd;
166662306a36Sopenharmony_ci	}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	error = ar5523_get_max_rxsz(ar);
166962306a36Sopenharmony_ci	if (error) {
167062306a36Sopenharmony_ci		ar5523_err(ar, "could not get caps from adapter\n");
167162306a36Sopenharmony_ci		goto out_cancel_rx_cmd;
167262306a36Sopenharmony_ci	}
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	error = ar5523_get_devcap(ar);
167562306a36Sopenharmony_ci	if (error) {
167662306a36Sopenharmony_ci		ar5523_err(ar, "could not get caps from adapter\n");
167762306a36Sopenharmony_ci		goto out_cancel_rx_cmd;
167862306a36Sopenharmony_ci	}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	error = ar5523_get_devstatus(ar);
168162306a36Sopenharmony_ci	if (error != 0) {
168262306a36Sopenharmony_ci		ar5523_err(ar, "could not get device status\n");
168362306a36Sopenharmony_ci		goto out_cancel_rx_cmd;
168462306a36Sopenharmony_ci	}
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	ar5523_info(ar, "MAC/BBP AR5523, RF AR%c112\n",
168762306a36Sopenharmony_ci			(id->driver_info & AR5523_FLAG_ABG) ? '5' : '2');
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	ar->vif = NULL;
169062306a36Sopenharmony_ci	ieee80211_hw_set(hw, HAS_RATE_CONTROL);
169162306a36Sopenharmony_ci	ieee80211_hw_set(hw, RX_INCLUDES_FCS);
169262306a36Sopenharmony_ci	ieee80211_hw_set(hw, SIGNAL_DBM);
169362306a36Sopenharmony_ci	hw->extra_tx_headroom = sizeof(struct ar5523_tx_desc) +
169462306a36Sopenharmony_ci				sizeof(struct ar5523_chunk);
169562306a36Sopenharmony_ci	hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
169662306a36Sopenharmony_ci	hw->queues = 1;
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	error = ar5523_init_modes(ar);
169962306a36Sopenharmony_ci	if (error)
170062306a36Sopenharmony_ci		goto out_cancel_rx_cmd;
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	usb_set_intfdata(intf, hw);
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	error = ieee80211_register_hw(hw);
170762306a36Sopenharmony_ci	if (error) {
170862306a36Sopenharmony_ci		ar5523_err(ar, "could not register device\n");
170962306a36Sopenharmony_ci		goto out_cancel_rx_cmd;
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	ar5523_info(ar, "Found and initialized AR5523 device\n");
171362306a36Sopenharmony_ci	return 0;
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ciout_cancel_rx_cmd:
171662306a36Sopenharmony_ci	ar5523_cancel_rx_cmd(ar);
171762306a36Sopenharmony_ciout_free_tx_cmd:
171862306a36Sopenharmony_ci	ar5523_free_tx_cmd(ar);
171962306a36Sopenharmony_ciout_free_rx_cmd:
172062306a36Sopenharmony_ci	ar5523_free_rx_cmd(ar);
172162306a36Sopenharmony_ciout_free_rx_bufs:
172262306a36Sopenharmony_ci	ar5523_free_rx_bufs(ar);
172362306a36Sopenharmony_ciout_free_wq:
172462306a36Sopenharmony_ci	destroy_workqueue(ar->wq);
172562306a36Sopenharmony_ciout_free_ar:
172662306a36Sopenharmony_ci	ieee80211_free_hw(hw);
172762306a36Sopenharmony_ciout:
172862306a36Sopenharmony_ci	return error;
172962306a36Sopenharmony_ci}
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_cistatic void ar5523_disconnect(struct usb_interface *intf)
173262306a36Sopenharmony_ci{
173362306a36Sopenharmony_ci	struct ieee80211_hw *hw = usb_get_intfdata(intf);
173462306a36Sopenharmony_ci	struct ar5523 *ar = hw->priv;
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	ar5523_dbg(ar, "detaching\n");
173762306a36Sopenharmony_ci	set_bit(AR5523_USB_DISCONNECTED, &ar->flags);
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	ieee80211_unregister_hw(hw);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	ar5523_cancel_rx_cmd(ar);
174262306a36Sopenharmony_ci	ar5523_free_tx_cmd(ar);
174362306a36Sopenharmony_ci	ar5523_free_rx_cmd(ar);
174462306a36Sopenharmony_ci	ar5523_free_rx_bufs(ar);
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	destroy_workqueue(ar->wq);
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	ieee80211_free_hw(hw);
174962306a36Sopenharmony_ci	usb_set_intfdata(intf, NULL);
175062306a36Sopenharmony_ci}
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci#define AR5523_DEVICE_UG(vendor, device) \
175362306a36Sopenharmony_ci	{ USB_DEVICE((vendor), (device)) }, \
175462306a36Sopenharmony_ci	{ USB_DEVICE((vendor), (device) + 1), \
175562306a36Sopenharmony_ci		.driver_info = AR5523_FLAG_PRE_FIRMWARE }
175662306a36Sopenharmony_ci#define AR5523_DEVICE_UX(vendor, device) \
175762306a36Sopenharmony_ci	{ USB_DEVICE((vendor), (device)), \
175862306a36Sopenharmony_ci		.driver_info = AR5523_FLAG_ABG }, \
175962306a36Sopenharmony_ci	{ USB_DEVICE((vendor), (device) + 1), \
176062306a36Sopenharmony_ci		.driver_info = AR5523_FLAG_ABG|AR5523_FLAG_PRE_FIRMWARE }
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_cistatic const struct usb_device_id ar5523_id_table[] = {
176362306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x168c, 0x0001),	/* Atheros / AR5523 */
176462306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x0cf3, 0x0001),	/* Atheros2 / AR5523_1 */
176562306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x0cf3, 0x0003),	/* Atheros2 / AR5523_2 */
176662306a36Sopenharmony_ci	AR5523_DEVICE_UX(0x0cf3, 0x0005),	/* Atheros2 / AR5523_3 */
176762306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x0d8e, 0x7801),	/* Conceptronic / AR5523_1 */
176862306a36Sopenharmony_ci	AR5523_DEVICE_UX(0x0d8e, 0x7811),	/* Conceptronic / AR5523_2 */
176962306a36Sopenharmony_ci	AR5523_DEVICE_UX(0x2001, 0x3a00),	/* Dlink / DWLAG132 */
177062306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x2001, 0x3a02),	/* Dlink / DWLG132 */
177162306a36Sopenharmony_ci	AR5523_DEVICE_UX(0x2001, 0x3a04),	/* Dlink / DWLAG122 */
177262306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x07d1, 0x3a07),	/* D-Link / WUA-2340 rev A1 */
177362306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x1690, 0x0712),	/* Gigaset / AR5523 */
177462306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x1690, 0x0710),	/* Gigaset / SMCWUSBTG */
177562306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x129b, 0x160b),	/* Gigaset / USB stick 108
177662306a36Sopenharmony_ci						   (CyberTAN Technology) */
177762306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x16ab, 0x7801),	/* Globalsun / AR5523_1 */
177862306a36Sopenharmony_ci	AR5523_DEVICE_UX(0x16ab, 0x7811),	/* Globalsun / AR5523_2 */
177962306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x0d8e, 0x7802),	/* Globalsun / AR5523_3 */
178062306a36Sopenharmony_ci	AR5523_DEVICE_UX(0x0846, 0x4300),	/* Netgear / WG111U */
178162306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x0846, 0x4250),	/* Netgear / WG111T */
178262306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x0846, 0x5f00),	/* Netgear / WPN111 */
178362306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x083a, 0x4506),	/* SMC / EZ Connect
178462306a36Sopenharmony_ci						   SMCWUSBT-G2 */
178562306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x157e, 0x3006),	/* Umedia / AR5523_1, TEW444UBEU*/
178662306a36Sopenharmony_ci	AR5523_DEVICE_UX(0x157e, 0x3205),	/* Umedia / AR5523_2 */
178762306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x1435, 0x0826),	/* Wistronneweb / AR5523_1 */
178862306a36Sopenharmony_ci	AR5523_DEVICE_UX(0x1435, 0x0828),	/* Wistronneweb / AR5523_2 */
178962306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x0cde, 0x0012),	/* Zcom / AR5523 */
179062306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x1385, 0x4250),	/* Netgear3 / WG111T (2) */
179162306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x1385, 0x5f00),	/* Netgear / WPN111 */
179262306a36Sopenharmony_ci	AR5523_DEVICE_UG(0x1385, 0x5f02),	/* Netgear / WPN111 */
179362306a36Sopenharmony_ci	{ }
179462306a36Sopenharmony_ci};
179562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, ar5523_id_table);
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_cistatic struct usb_driver ar5523_driver = {
179862306a36Sopenharmony_ci	.name		= "ar5523",
179962306a36Sopenharmony_ci	.id_table	= ar5523_id_table,
180062306a36Sopenharmony_ci	.probe		= ar5523_probe,
180162306a36Sopenharmony_ci	.disconnect	= ar5523_disconnect,
180262306a36Sopenharmony_ci};
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_cimodule_usb_driver(ar5523_driver);
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
180762306a36Sopenharmony_ciMODULE_FIRMWARE(AR5523_FIRMWARE_FILE);
1808