18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2006 Damien Bergamini <damien.bergamini@free.fr>
38c2ecf20Sopenharmony_ci * Copyright (c) 2006 Sam Leffler, Errno Consulting
48c2ecf20Sopenharmony_ci * Copyright (c) 2007 Christoph Hellwig <hch@lst.de>
58c2ecf20Sopenharmony_ci * Copyright (c) 2008-2009 Weongyo Jeong <weongyo@freebsd.org>
68c2ecf20Sopenharmony_ci * Copyright (c) 2012 Pontus Fuchs <pontus.fuchs@gmail.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
98c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
108c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
138c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
148c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
158c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
168c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
178c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
188c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * This driver is based on the uath driver written by Damien Bergamini for
238c2ecf20Sopenharmony_ci * OpenBSD, who did black-box analysis of the Windows binary driver to find
248c2ecf20Sopenharmony_ci * out how the hardware works.  It contains a lot magic numbers because of
258c2ecf20Sopenharmony_ci * that and only has minimal functionality.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci#include <linux/compiler.h>
288c2ecf20Sopenharmony_ci#include <linux/kernel.h>
298c2ecf20Sopenharmony_ci#include <linux/module.h>
308c2ecf20Sopenharmony_ci#include <linux/list.h>
318c2ecf20Sopenharmony_ci#include <linux/completion.h>
328c2ecf20Sopenharmony_ci#include <linux/firmware.h>
338c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
348c2ecf20Sopenharmony_ci#include <linux/usb.h>
358c2ecf20Sopenharmony_ci#include <net/mac80211.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include "ar5523.h"
388c2ecf20Sopenharmony_ci#include "ar5523_hw.h"
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci * Various supported device vendors/products.
428c2ecf20Sopenharmony_ci * UB51: AR5005UG 802.11b/g, UB52: AR5005UX 802.11a/b/g
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int ar5523_submit_rx_cmd(struct ar5523 *ar);
468c2ecf20Sopenharmony_cistatic void ar5523_data_tx_pkt_put(struct ar5523 *ar);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic void ar5523_read_reply(struct ar5523 *ar, struct ar5523_cmd_hdr *hdr,
498c2ecf20Sopenharmony_ci			      struct ar5523_tx_cmd *cmd)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	int dlen, olen;
528c2ecf20Sopenharmony_ci	__be32 *rp;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	dlen = be32_to_cpu(hdr->len) - sizeof(*hdr);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (dlen < 0) {
578c2ecf20Sopenharmony_ci		WARN_ON(1);
588c2ecf20Sopenharmony_ci		goto out;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "Code = %d len = %d\n", be32_to_cpu(hdr->code) & 0xff,
628c2ecf20Sopenharmony_ci		   dlen);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	rp = (__be32 *)(hdr + 1);
658c2ecf20Sopenharmony_ci	if (dlen >= sizeof(u32)) {
668c2ecf20Sopenharmony_ci		olen = be32_to_cpu(rp[0]);
678c2ecf20Sopenharmony_ci		dlen -= sizeof(u32);
688c2ecf20Sopenharmony_ci		if (olen == 0) {
698c2ecf20Sopenharmony_ci			/* convention is 0 =>'s one word */
708c2ecf20Sopenharmony_ci			olen = sizeof(u32);
718c2ecf20Sopenharmony_ci		}
728c2ecf20Sopenharmony_ci	} else
738c2ecf20Sopenharmony_ci		olen = 0;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (cmd->odata) {
768c2ecf20Sopenharmony_ci		if (cmd->olen < olen) {
778c2ecf20Sopenharmony_ci			ar5523_err(ar, "olen too small %d < %d\n",
788c2ecf20Sopenharmony_ci				   cmd->olen, olen);
798c2ecf20Sopenharmony_ci			cmd->olen = 0;
808c2ecf20Sopenharmony_ci			cmd->res = -EOVERFLOW;
818c2ecf20Sopenharmony_ci		} else {
828c2ecf20Sopenharmony_ci			cmd->olen = olen;
838c2ecf20Sopenharmony_ci			memcpy(cmd->odata, &rp[1], olen);
848c2ecf20Sopenharmony_ci			cmd->res = 0;
858c2ecf20Sopenharmony_ci		}
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ciout:
898c2ecf20Sopenharmony_ci	complete(&cmd->done);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void ar5523_cmd_rx_cb(struct urb *urb)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct ar5523 *ar = urb->context;
958c2ecf20Sopenharmony_ci	struct ar5523_tx_cmd *cmd = &ar->tx_cmd;
968c2ecf20Sopenharmony_ci	struct ar5523_cmd_hdr *hdr = ar->rx_cmd_buf;
978c2ecf20Sopenharmony_ci	int dlen;
988c2ecf20Sopenharmony_ci	u32 code, hdrlen;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (urb->status) {
1018c2ecf20Sopenharmony_ci		if (urb->status != -ESHUTDOWN)
1028c2ecf20Sopenharmony_ci			ar5523_err(ar, "RX USB error %d.\n", urb->status);
1038c2ecf20Sopenharmony_ci		goto skip;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (urb->actual_length < sizeof(struct ar5523_cmd_hdr)) {
1078c2ecf20Sopenharmony_ci		ar5523_err(ar, "RX USB to short.\n");
1088c2ecf20Sopenharmony_ci		goto skip;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "%s code %02x priv %d\n", __func__,
1128c2ecf20Sopenharmony_ci		   be32_to_cpu(hdr->code) & 0xff, hdr->priv);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	code = be32_to_cpu(hdr->code);
1158c2ecf20Sopenharmony_ci	hdrlen = be32_to_cpu(hdr->len);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	switch (code & 0xff) {
1188c2ecf20Sopenharmony_ci	default:
1198c2ecf20Sopenharmony_ci		/* reply to a read command */
1208c2ecf20Sopenharmony_ci		if (hdr->priv != AR5523_CMD_ID) {
1218c2ecf20Sopenharmony_ci			ar5523_err(ar, "Unexpected command id: %02x\n",
1228c2ecf20Sopenharmony_ci				   code & 0xff);
1238c2ecf20Sopenharmony_ci			goto skip;
1248c2ecf20Sopenharmony_ci		}
1258c2ecf20Sopenharmony_ci		ar5523_read_reply(ar, hdr, cmd);
1268c2ecf20Sopenharmony_ci		break;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	case WDCMSG_DEVICE_AVAIL:
1298c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "WDCMSG_DEVICE_AVAIL\n");
1308c2ecf20Sopenharmony_ci		cmd->res = 0;
1318c2ecf20Sopenharmony_ci		cmd->olen = 0;
1328c2ecf20Sopenharmony_ci		complete(&cmd->done);
1338c2ecf20Sopenharmony_ci		break;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	case WDCMSG_SEND_COMPLETE:
1368c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "WDCMSG_SEND_COMPLETE: %d pending\n",
1378c2ecf20Sopenharmony_ci			atomic_read(&ar->tx_nr_pending));
1388c2ecf20Sopenharmony_ci		if (!test_bit(AR5523_HW_UP, &ar->flags))
1398c2ecf20Sopenharmony_ci			ar5523_dbg(ar, "Unexpected WDCMSG_SEND_COMPLETE\n");
1408c2ecf20Sopenharmony_ci		else {
1418c2ecf20Sopenharmony_ci			mod_timer(&ar->tx_wd_timer,
1428c2ecf20Sopenharmony_ci				  jiffies + AR5523_TX_WD_TIMEOUT);
1438c2ecf20Sopenharmony_ci			ar5523_data_tx_pkt_put(ar);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		}
1468c2ecf20Sopenharmony_ci		break;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	case WDCMSG_TARGET_START:
1498c2ecf20Sopenharmony_ci		/* This command returns a bogus id so it needs special
1508c2ecf20Sopenharmony_ci		   handling */
1518c2ecf20Sopenharmony_ci		dlen = hdrlen - sizeof(*hdr);
1528c2ecf20Sopenharmony_ci		if (dlen != (int)sizeof(u32)) {
1538c2ecf20Sopenharmony_ci			ar5523_err(ar, "Invalid reply to WDCMSG_TARGET_START");
1548c2ecf20Sopenharmony_ci			return;
1558c2ecf20Sopenharmony_ci		}
1568c2ecf20Sopenharmony_ci		if (!cmd->odata) {
1578c2ecf20Sopenharmony_ci			ar5523_err(ar, "Unexpected WDCMSG_TARGET_START reply");
1588c2ecf20Sopenharmony_ci			return;
1598c2ecf20Sopenharmony_ci		}
1608c2ecf20Sopenharmony_ci		memcpy(cmd->odata, hdr + 1, sizeof(u32));
1618c2ecf20Sopenharmony_ci		cmd->olen = sizeof(u32);
1628c2ecf20Sopenharmony_ci		cmd->res = 0;
1638c2ecf20Sopenharmony_ci		complete(&cmd->done);
1648c2ecf20Sopenharmony_ci		break;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	case WDCMSG_STATS_UPDATE:
1678c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "WDCMSG_STATS_UPDATE\n");
1688c2ecf20Sopenharmony_ci		break;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ciskip:
1728c2ecf20Sopenharmony_ci	ar5523_submit_rx_cmd(ar);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic int ar5523_alloc_rx_cmd(struct ar5523 *ar)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	ar->rx_cmd_urb = usb_alloc_urb(0, GFP_KERNEL);
1788c2ecf20Sopenharmony_ci	if (!ar->rx_cmd_urb)
1798c2ecf20Sopenharmony_ci		return -ENOMEM;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	ar->rx_cmd_buf = usb_alloc_coherent(ar->dev, AR5523_MAX_RXCMDSZ,
1828c2ecf20Sopenharmony_ci					    GFP_KERNEL,
1838c2ecf20Sopenharmony_ci					    &ar->rx_cmd_urb->transfer_dma);
1848c2ecf20Sopenharmony_ci	if (!ar->rx_cmd_buf) {
1858c2ecf20Sopenharmony_ci		usb_free_urb(ar->rx_cmd_urb);
1868c2ecf20Sopenharmony_ci		return -ENOMEM;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci	return 0;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic void ar5523_cancel_rx_cmd(struct ar5523 *ar)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	usb_kill_urb(ar->rx_cmd_urb);
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic void ar5523_free_rx_cmd(struct ar5523 *ar)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	usb_free_coherent(ar->dev, AR5523_MAX_RXCMDSZ,
1998c2ecf20Sopenharmony_ci			  ar->rx_cmd_buf, ar->rx_cmd_urb->transfer_dma);
2008c2ecf20Sopenharmony_ci	usb_free_urb(ar->rx_cmd_urb);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic int ar5523_submit_rx_cmd(struct ar5523 *ar)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	int error;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(ar->rx_cmd_urb, ar->dev,
2088c2ecf20Sopenharmony_ci			  ar5523_cmd_rx_pipe(ar->dev), ar->rx_cmd_buf,
2098c2ecf20Sopenharmony_ci			  AR5523_MAX_RXCMDSZ, ar5523_cmd_rx_cb, ar);
2108c2ecf20Sopenharmony_ci	ar->rx_cmd_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	error = usb_submit_urb(ar->rx_cmd_urb, GFP_ATOMIC);
2138c2ecf20Sopenharmony_ci	if (error) {
2148c2ecf20Sopenharmony_ci		if (error != -ENODEV)
2158c2ecf20Sopenharmony_ci			ar5523_err(ar, "error %d when submitting rx urb\n",
2168c2ecf20Sopenharmony_ci				   error);
2178c2ecf20Sopenharmony_ci		return error;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci	return 0;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci/*
2238c2ecf20Sopenharmony_ci * Command submitted cb
2248c2ecf20Sopenharmony_ci */
2258c2ecf20Sopenharmony_cistatic void ar5523_cmd_tx_cb(struct urb *urb)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	struct ar5523_tx_cmd *cmd = urb->context;
2288c2ecf20Sopenharmony_ci	struct ar5523 *ar = cmd->ar;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (urb->status) {
2318c2ecf20Sopenharmony_ci		ar5523_err(ar, "Failed to TX command. Status = %d\n",
2328c2ecf20Sopenharmony_ci			   urb->status);
2338c2ecf20Sopenharmony_ci		cmd->res = urb->status;
2348c2ecf20Sopenharmony_ci		complete(&cmd->done);
2358c2ecf20Sopenharmony_ci		return;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (!(cmd->flags & AR5523_CMD_FLAG_READ)) {
2398c2ecf20Sopenharmony_ci		cmd->res = 0;
2408c2ecf20Sopenharmony_ci		complete(&cmd->done);
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic void ar5523_cancel_tx_cmd(struct ar5523 *ar)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	usb_kill_urb(ar->tx_cmd.urb_tx);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int ar5523_cmd(struct ar5523 *ar, u32 code, const void *idata,
2508c2ecf20Sopenharmony_ci		      int ilen, void *odata, int olen, int flags)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct ar5523_cmd_hdr *hdr;
2538c2ecf20Sopenharmony_ci	struct ar5523_tx_cmd *cmd = &ar->tx_cmd;
2548c2ecf20Sopenharmony_ci	int xferlen, error;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	/* always bulk-out a multiple of 4 bytes */
2578c2ecf20Sopenharmony_ci	xferlen = (sizeof(struct ar5523_cmd_hdr) + ilen + 3) & ~3;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	hdr = (struct ar5523_cmd_hdr *)cmd->buf_tx;
2608c2ecf20Sopenharmony_ci	memset(hdr, 0, sizeof(struct ar5523_cmd_hdr));
2618c2ecf20Sopenharmony_ci	hdr->len  = cpu_to_be32(xferlen);
2628c2ecf20Sopenharmony_ci	hdr->code = cpu_to_be32(code);
2638c2ecf20Sopenharmony_ci	hdr->priv = AR5523_CMD_ID;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (flags & AR5523_CMD_FLAG_MAGIC)
2668c2ecf20Sopenharmony_ci		hdr->magic = cpu_to_be32(1 << 24);
2678c2ecf20Sopenharmony_ci	if (ilen)
2688c2ecf20Sopenharmony_ci		memcpy(hdr + 1, idata, ilen);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	cmd->odata = odata;
2718c2ecf20Sopenharmony_ci	cmd->olen = olen;
2728c2ecf20Sopenharmony_ci	cmd->flags = flags;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "do cmd %02x\n", code);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(cmd->urb_tx, ar->dev, ar5523_cmd_tx_pipe(ar->dev),
2778c2ecf20Sopenharmony_ci			  cmd->buf_tx, xferlen, ar5523_cmd_tx_cb, cmd);
2788c2ecf20Sopenharmony_ci	cmd->urb_tx->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	error = usb_submit_urb(cmd->urb_tx, GFP_KERNEL);
2818c2ecf20Sopenharmony_ci	if (error) {
2828c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not send command 0x%x, error=%d\n",
2838c2ecf20Sopenharmony_ci			   code, error);
2848c2ecf20Sopenharmony_ci		return error;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout(&cmd->done, 2 * HZ)) {
2888c2ecf20Sopenharmony_ci		ar5523_cancel_tx_cmd(ar);
2898c2ecf20Sopenharmony_ci		cmd->odata = NULL;
2908c2ecf20Sopenharmony_ci		ar5523_err(ar, "timeout waiting for command %02x reply\n",
2918c2ecf20Sopenharmony_ci			   code);
2928c2ecf20Sopenharmony_ci		cmd->res = -ETIMEDOUT;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci	return cmd->res;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int ar5523_cmd_write(struct ar5523 *ar, u32 code, const void *data,
2988c2ecf20Sopenharmony_ci			    int len, int flags)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	flags &= ~AR5523_CMD_FLAG_READ;
3018c2ecf20Sopenharmony_ci	return ar5523_cmd(ar, code, data, len, NULL, 0, flags);
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic int ar5523_cmd_read(struct ar5523 *ar, u32 code, const void *idata,
3058c2ecf20Sopenharmony_ci			   int ilen, void *odata, int olen, int flags)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	flags |= AR5523_CMD_FLAG_READ;
3088c2ecf20Sopenharmony_ci	return ar5523_cmd(ar, code, idata, ilen, odata, olen, flags);
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int ar5523_config(struct ar5523 *ar, u32 reg, u32 val)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct ar5523_write_mac write;
3148c2ecf20Sopenharmony_ci	int error;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	write.reg = cpu_to_be32(reg);
3178c2ecf20Sopenharmony_ci	write.len = cpu_to_be32(0);	/* 0 = single write */
3188c2ecf20Sopenharmony_ci	*(__be32 *)write.data = cpu_to_be32(val);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	error = ar5523_cmd_write(ar, WDCMSG_TARGET_SET_CONFIG, &write,
3218c2ecf20Sopenharmony_ci				 3 * sizeof(u32), 0);
3228c2ecf20Sopenharmony_ci	if (error != 0)
3238c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not write register 0x%02x\n", reg);
3248c2ecf20Sopenharmony_ci	return error;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic int ar5523_config_multi(struct ar5523 *ar, u32 reg, const void *data,
3288c2ecf20Sopenharmony_ci			       int len)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct ar5523_write_mac write;
3318c2ecf20Sopenharmony_ci	int error;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	write.reg = cpu_to_be32(reg);
3348c2ecf20Sopenharmony_ci	write.len = cpu_to_be32(len);
3358c2ecf20Sopenharmony_ci	memcpy(write.data, data, len);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* properly handle the case where len is zero (reset) */
3388c2ecf20Sopenharmony_ci	error = ar5523_cmd_write(ar, WDCMSG_TARGET_SET_CONFIG, &write,
3398c2ecf20Sopenharmony_ci	    (len == 0) ? sizeof(u32) : 2 * sizeof(u32) + len, 0);
3408c2ecf20Sopenharmony_ci	if (error != 0)
3418c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not write %d bytes to register 0x%02x\n",
3428c2ecf20Sopenharmony_ci			   len, reg);
3438c2ecf20Sopenharmony_ci	return error;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic int ar5523_get_status(struct ar5523 *ar, u32 which, void *odata,
3478c2ecf20Sopenharmony_ci			     int olen)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	int error;
3508c2ecf20Sopenharmony_ci	__be32 which_be;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	which_be = cpu_to_be32(which);
3538c2ecf20Sopenharmony_ci	error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_STATUS,
3548c2ecf20Sopenharmony_ci	    &which_be, sizeof(which_be), odata, olen, AR5523_CMD_FLAG_MAGIC);
3558c2ecf20Sopenharmony_ci	if (error != 0)
3568c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not read EEPROM offset 0x%02x\n", which);
3578c2ecf20Sopenharmony_ci	return error;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int ar5523_get_capability(struct ar5523 *ar, u32 cap, u32 *val)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	int error;
3638c2ecf20Sopenharmony_ci	__be32 cap_be, val_be;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	cap_be = cpu_to_be32(cap);
3668c2ecf20Sopenharmony_ci	error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_CAPABILITY, &cap_be,
3678c2ecf20Sopenharmony_ci				sizeof(cap_be), &val_be, sizeof(__be32),
3688c2ecf20Sopenharmony_ci				AR5523_CMD_FLAG_MAGIC);
3698c2ecf20Sopenharmony_ci	if (error != 0) {
3708c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not read capability %u\n", cap);
3718c2ecf20Sopenharmony_ci		return error;
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci	*val = be32_to_cpu(val_be);
3748c2ecf20Sopenharmony_ci	return error;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic int ar5523_get_devcap(struct ar5523 *ar)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci#define	GETCAP(x) do {				\
3808c2ecf20Sopenharmony_ci	error = ar5523_get_capability(ar, x, &cap);		\
3818c2ecf20Sopenharmony_ci	if (error != 0)					\
3828c2ecf20Sopenharmony_ci		return error;				\
3838c2ecf20Sopenharmony_ci	ar5523_info(ar, "Cap: "			\
3848c2ecf20Sopenharmony_ci	    "%s=0x%08x\n", #x, cap);	\
3858c2ecf20Sopenharmony_ci} while (0)
3868c2ecf20Sopenharmony_ci	int error;
3878c2ecf20Sopenharmony_ci	u32 cap;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* collect device capabilities */
3908c2ecf20Sopenharmony_ci	GETCAP(CAP_TARGET_VERSION);
3918c2ecf20Sopenharmony_ci	GETCAP(CAP_TARGET_REVISION);
3928c2ecf20Sopenharmony_ci	GETCAP(CAP_MAC_VERSION);
3938c2ecf20Sopenharmony_ci	GETCAP(CAP_MAC_REVISION);
3948c2ecf20Sopenharmony_ci	GETCAP(CAP_PHY_REVISION);
3958c2ecf20Sopenharmony_ci	GETCAP(CAP_ANALOG_5GHz_REVISION);
3968c2ecf20Sopenharmony_ci	GETCAP(CAP_ANALOG_2GHz_REVISION);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	GETCAP(CAP_REG_DOMAIN);
3998c2ecf20Sopenharmony_ci	GETCAP(CAP_REG_CAP_BITS);
4008c2ecf20Sopenharmony_ci	GETCAP(CAP_WIRELESS_MODES);
4018c2ecf20Sopenharmony_ci	GETCAP(CAP_CHAN_SPREAD_SUPPORT);
4028c2ecf20Sopenharmony_ci	GETCAP(CAP_COMPRESS_SUPPORT);
4038c2ecf20Sopenharmony_ci	GETCAP(CAP_BURST_SUPPORT);
4048c2ecf20Sopenharmony_ci	GETCAP(CAP_FAST_FRAMES_SUPPORT);
4058c2ecf20Sopenharmony_ci	GETCAP(CAP_CHAP_TUNING_SUPPORT);
4068c2ecf20Sopenharmony_ci	GETCAP(CAP_TURBOG_SUPPORT);
4078c2ecf20Sopenharmony_ci	GETCAP(CAP_TURBO_PRIME_SUPPORT);
4088c2ecf20Sopenharmony_ci	GETCAP(CAP_DEVICE_TYPE);
4098c2ecf20Sopenharmony_ci	GETCAP(CAP_WME_SUPPORT);
4108c2ecf20Sopenharmony_ci	GETCAP(CAP_TOTAL_QUEUES);
4118c2ecf20Sopenharmony_ci	GETCAP(CAP_CONNECTION_ID_MAX);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	GETCAP(CAP_LOW_5GHZ_CHAN);
4148c2ecf20Sopenharmony_ci	GETCAP(CAP_HIGH_5GHZ_CHAN);
4158c2ecf20Sopenharmony_ci	GETCAP(CAP_LOW_2GHZ_CHAN);
4168c2ecf20Sopenharmony_ci	GETCAP(CAP_HIGH_2GHZ_CHAN);
4178c2ecf20Sopenharmony_ci	GETCAP(CAP_TWICE_ANTENNAGAIN_5G);
4188c2ecf20Sopenharmony_ci	GETCAP(CAP_TWICE_ANTENNAGAIN_2G);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	GETCAP(CAP_CIPHER_AES_CCM);
4218c2ecf20Sopenharmony_ci	GETCAP(CAP_CIPHER_TKIP);
4228c2ecf20Sopenharmony_ci	GETCAP(CAP_MIC_TKIP);
4238c2ecf20Sopenharmony_ci	return 0;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic int ar5523_set_ledsteady(struct ar5523 *ar, int lednum, int ledmode)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct ar5523_cmd_ledsteady led;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	led.lednum = cpu_to_be32(lednum);
4318c2ecf20Sopenharmony_ci	led.ledmode = cpu_to_be32(ledmode);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "set %s led %s (steady)\n",
4348c2ecf20Sopenharmony_ci		   (lednum == UATH_LED_LINK) ? "link" : "activity",
4358c2ecf20Sopenharmony_ci		   ledmode ? "on" : "off");
4368c2ecf20Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_SET_LED_STEADY, &led, sizeof(led),
4378c2ecf20Sopenharmony_ci				 0);
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic int ar5523_set_rxfilter(struct ar5523 *ar, u32 bits, u32 op)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct ar5523_cmd_rx_filter rxfilter;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	rxfilter.bits = cpu_to_be32(bits);
4458c2ecf20Sopenharmony_ci	rxfilter.op = cpu_to_be32(op);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "setting Rx filter=0x%x flags=0x%x\n", bits, op);
4488c2ecf20Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_RX_FILTER, &rxfilter,
4498c2ecf20Sopenharmony_ci				 sizeof(rxfilter), 0);
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic int ar5523_reset_tx_queues(struct ar5523 *ar)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	__be32 qid = cpu_to_be32(0);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "resetting Tx queue\n");
4578c2ecf20Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_RELEASE_TX_QUEUE,
4588c2ecf20Sopenharmony_ci				 &qid, sizeof(qid), 0);
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic int ar5523_set_chan(struct ar5523 *ar)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	struct ieee80211_conf *conf = &ar->hw->conf;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	struct ar5523_cmd_reset reset;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	memset(&reset, 0, sizeof(reset));
4688c2ecf20Sopenharmony_ci	reset.flags |= cpu_to_be32(UATH_CHAN_2GHZ);
4698c2ecf20Sopenharmony_ci	reset.flags |= cpu_to_be32(UATH_CHAN_OFDM);
4708c2ecf20Sopenharmony_ci	reset.freq = cpu_to_be32(conf->chandef.chan->center_freq);
4718c2ecf20Sopenharmony_ci	reset.maxrdpower = cpu_to_be32(50);	/* XXX */
4728c2ecf20Sopenharmony_ci	reset.channelchange = cpu_to_be32(1);
4738c2ecf20Sopenharmony_ci	reset.keeprccontent = cpu_to_be32(0);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "set chan flags 0x%x freq %d\n",
4768c2ecf20Sopenharmony_ci		   be32_to_cpu(reset.flags),
4778c2ecf20Sopenharmony_ci		   conf->chandef.chan->center_freq);
4788c2ecf20Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_RESET, &reset, sizeof(reset), 0);
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic int ar5523_queue_init(struct ar5523 *ar)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	struct ar5523_cmd_txq_setup qinfo;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "setting up Tx queue\n");
4868c2ecf20Sopenharmony_ci	qinfo.qid	     = cpu_to_be32(0);
4878c2ecf20Sopenharmony_ci	qinfo.len	     = cpu_to_be32(sizeof(qinfo.attr));
4888c2ecf20Sopenharmony_ci	qinfo.attr.priority  = cpu_to_be32(0);	/* XXX */
4898c2ecf20Sopenharmony_ci	qinfo.attr.aifs	     = cpu_to_be32(3);
4908c2ecf20Sopenharmony_ci	qinfo.attr.logcwmin  = cpu_to_be32(4);
4918c2ecf20Sopenharmony_ci	qinfo.attr.logcwmax  = cpu_to_be32(10);
4928c2ecf20Sopenharmony_ci	qinfo.attr.bursttime = cpu_to_be32(0);
4938c2ecf20Sopenharmony_ci	qinfo.attr.mode	     = cpu_to_be32(0);
4948c2ecf20Sopenharmony_ci	qinfo.attr.qflags    = cpu_to_be32(1);	/* XXX? */
4958c2ecf20Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_SETUP_TX_QUEUE, &qinfo,
4968c2ecf20Sopenharmony_ci				 sizeof(qinfo), 0);
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic int ar5523_switch_chan(struct ar5523 *ar)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	int error;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	error = ar5523_set_chan(ar);
5048c2ecf20Sopenharmony_ci	if (error) {
5058c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not set chan, error %d\n", error);
5068c2ecf20Sopenharmony_ci		goto out_err;
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	/* reset Tx rings */
5108c2ecf20Sopenharmony_ci	error = ar5523_reset_tx_queues(ar);
5118c2ecf20Sopenharmony_ci	if (error) {
5128c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not reset Tx queues, error %d\n",
5138c2ecf20Sopenharmony_ci			   error);
5148c2ecf20Sopenharmony_ci		goto out_err;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci	/* set Tx rings WME properties */
5178c2ecf20Sopenharmony_ci	error = ar5523_queue_init(ar);
5188c2ecf20Sopenharmony_ci	if (error)
5198c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not init wme, error %d\n", error);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ciout_err:
5228c2ecf20Sopenharmony_ci	return error;
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic void ar5523_rx_data_put(struct ar5523 *ar,
5268c2ecf20Sopenharmony_ci				struct ar5523_rx_data *data)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	unsigned long flags;
5298c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ar->rx_data_list_lock, flags);
5308c2ecf20Sopenharmony_ci	list_move(&data->list, &ar->rx_data_free);
5318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ar->rx_data_list_lock, flags);
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic void ar5523_data_rx_cb(struct urb *urb)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	struct ar5523_rx_data *data = urb->context;
5378c2ecf20Sopenharmony_ci	struct ar5523 *ar = data->ar;
5388c2ecf20Sopenharmony_ci	struct ar5523_rx_desc *desc;
5398c2ecf20Sopenharmony_ci	struct ar5523_chunk *chunk;
5408c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = ar->hw;
5418c2ecf20Sopenharmony_ci	struct ieee80211_rx_status *rx_status;
5428c2ecf20Sopenharmony_ci	u32 rxlen;
5438c2ecf20Sopenharmony_ci	int usblen = urb->actual_length;
5448c2ecf20Sopenharmony_ci	int hdrlen, pad;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "%s\n", __func__);
5478c2ecf20Sopenharmony_ci	/* sync/async unlink faults aren't errors */
5488c2ecf20Sopenharmony_ci	if (urb->status) {
5498c2ecf20Sopenharmony_ci		if (urb->status != -ESHUTDOWN)
5508c2ecf20Sopenharmony_ci			ar5523_err(ar, "%s: USB err: %d\n", __func__,
5518c2ecf20Sopenharmony_ci				   urb->status);
5528c2ecf20Sopenharmony_ci		goto skip;
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	if (usblen < AR5523_MIN_RXBUFSZ) {
5568c2ecf20Sopenharmony_ci		ar5523_err(ar, "RX: wrong xfer size (usblen=%d)\n", usblen);
5578c2ecf20Sopenharmony_ci		goto skip;
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	chunk = (struct ar5523_chunk *) data->skb->data;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (((chunk->flags & UATH_CFLAGS_FINAL) == 0) ||
5638c2ecf20Sopenharmony_ci		chunk->seqnum != 0) {
5648c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "RX: No final flag. s: %d f: %02x l: %d\n",
5658c2ecf20Sopenharmony_ci			   chunk->seqnum, chunk->flags,
5668c2ecf20Sopenharmony_ci			   be16_to_cpu(chunk->length));
5678c2ecf20Sopenharmony_ci		goto skip;
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	/* Rx descriptor is located at the end, 32-bit aligned */
5718c2ecf20Sopenharmony_ci	desc = (struct ar5523_rx_desc *)
5728c2ecf20Sopenharmony_ci		(data->skb->data + usblen - sizeof(struct ar5523_rx_desc));
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	rxlen = be32_to_cpu(desc->len);
5758c2ecf20Sopenharmony_ci	if (rxlen > ar->rxbufsz) {
5768c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "RX: Bad descriptor (len=%d)\n",
5778c2ecf20Sopenharmony_ci			   be32_to_cpu(desc->len));
5788c2ecf20Sopenharmony_ci		goto skip;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	if (!rxlen) {
5828c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "RX: rxlen is 0\n");
5838c2ecf20Sopenharmony_ci		goto skip;
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	if (be32_to_cpu(desc->status) != 0) {
5878c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "Bad RX status (0x%x len = %d). Skip\n",
5888c2ecf20Sopenharmony_ci			   be32_to_cpu(desc->status), be32_to_cpu(desc->len));
5898c2ecf20Sopenharmony_ci		goto skip;
5908c2ecf20Sopenharmony_ci	}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	skb_reserve(data->skb, sizeof(*chunk));
5938c2ecf20Sopenharmony_ci	skb_put(data->skb, rxlen - sizeof(struct ar5523_rx_desc));
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	hdrlen = ieee80211_get_hdrlen_from_skb(data->skb);
5968c2ecf20Sopenharmony_ci	if (!IS_ALIGNED(hdrlen, 4)) {
5978c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "eek, alignment workaround activated\n");
5988c2ecf20Sopenharmony_ci		pad = ALIGN(hdrlen, 4) - hdrlen;
5998c2ecf20Sopenharmony_ci		memmove(data->skb->data + pad, data->skb->data, hdrlen);
6008c2ecf20Sopenharmony_ci		skb_pull(data->skb, pad);
6018c2ecf20Sopenharmony_ci		skb_put(data->skb, pad);
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	rx_status = IEEE80211_SKB_RXCB(data->skb);
6058c2ecf20Sopenharmony_ci	memset(rx_status, 0, sizeof(*rx_status));
6068c2ecf20Sopenharmony_ci	rx_status->freq = be32_to_cpu(desc->channel);
6078c2ecf20Sopenharmony_ci	rx_status->band = hw->conf.chandef.chan->band;
6088c2ecf20Sopenharmony_ci	rx_status->signal = -95 + be32_to_cpu(desc->rssi);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	ieee80211_rx_irqsafe(hw, data->skb);
6118c2ecf20Sopenharmony_ci	data->skb = NULL;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ciskip:
6148c2ecf20Sopenharmony_ci	if (data->skb) {
6158c2ecf20Sopenharmony_ci		dev_kfree_skb_irq(data->skb);
6168c2ecf20Sopenharmony_ci		data->skb = NULL;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	ar5523_rx_data_put(ar, data);
6208c2ecf20Sopenharmony_ci	if (atomic_inc_return(&ar->rx_data_free_cnt) >=
6218c2ecf20Sopenharmony_ci	    AR5523_RX_DATA_REFILL_COUNT &&
6228c2ecf20Sopenharmony_ci	    test_bit(AR5523_HW_UP, &ar->flags))
6238c2ecf20Sopenharmony_ci		queue_work(ar->wq, &ar->rx_refill_work);
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic void ar5523_rx_refill_work(struct work_struct *work)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	struct ar5523 *ar = container_of(work, struct ar5523, rx_refill_work);
6298c2ecf20Sopenharmony_ci	struct ar5523_rx_data *data;
6308c2ecf20Sopenharmony_ci	unsigned long flags;
6318c2ecf20Sopenharmony_ci	int error;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "%s\n", __func__);
6348c2ecf20Sopenharmony_ci	do {
6358c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ar->rx_data_list_lock, flags);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci		if (!list_empty(&ar->rx_data_free))
6388c2ecf20Sopenharmony_ci			data = (struct ar5523_rx_data *) ar->rx_data_free.next;
6398c2ecf20Sopenharmony_ci		else
6408c2ecf20Sopenharmony_ci			data = NULL;
6418c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ar->rx_data_list_lock, flags);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci		if (!data)
6448c2ecf20Sopenharmony_ci			goto done;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci		data->skb = alloc_skb(ar->rxbufsz, GFP_KERNEL);
6478c2ecf20Sopenharmony_ci		if (!data->skb) {
6488c2ecf20Sopenharmony_ci			ar5523_err(ar, "could not allocate rx skbuff\n");
6498c2ecf20Sopenharmony_ci			return;
6508c2ecf20Sopenharmony_ci		}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(data->urb, ar->dev,
6538c2ecf20Sopenharmony_ci				  ar5523_data_rx_pipe(ar->dev), data->skb->data,
6548c2ecf20Sopenharmony_ci				  ar->rxbufsz, ar5523_data_rx_cb, data);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ar->rx_data_list_lock, flags);
6578c2ecf20Sopenharmony_ci		list_move(&data->list, &ar->rx_data_used);
6588c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ar->rx_data_list_lock, flags);
6598c2ecf20Sopenharmony_ci		atomic_dec(&ar->rx_data_free_cnt);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci		error = usb_submit_urb(data->urb, GFP_KERNEL);
6628c2ecf20Sopenharmony_ci		if (error) {
6638c2ecf20Sopenharmony_ci			kfree_skb(data->skb);
6648c2ecf20Sopenharmony_ci			if (error != -ENODEV)
6658c2ecf20Sopenharmony_ci				ar5523_err(ar, "Err sending rx data urb %d\n",
6668c2ecf20Sopenharmony_ci					   error);
6678c2ecf20Sopenharmony_ci			ar5523_rx_data_put(ar, data);
6688c2ecf20Sopenharmony_ci			atomic_inc(&ar->rx_data_free_cnt);
6698c2ecf20Sopenharmony_ci			return;
6708c2ecf20Sopenharmony_ci		}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	} while (true);
6738c2ecf20Sopenharmony_cidone:
6748c2ecf20Sopenharmony_ci	return;
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic void ar5523_cancel_rx_bufs(struct ar5523 *ar)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	struct ar5523_rx_data *data;
6808c2ecf20Sopenharmony_ci	unsigned long flags;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	do {
6838c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ar->rx_data_list_lock, flags);
6848c2ecf20Sopenharmony_ci		if (!list_empty(&ar->rx_data_used))
6858c2ecf20Sopenharmony_ci			data = (struct ar5523_rx_data *) ar->rx_data_used.next;
6868c2ecf20Sopenharmony_ci		else
6878c2ecf20Sopenharmony_ci			data = NULL;
6888c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ar->rx_data_list_lock, flags);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci		if (!data)
6918c2ecf20Sopenharmony_ci			break;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci		usb_kill_urb(data->urb);
6948c2ecf20Sopenharmony_ci		list_move(&data->list, &ar->rx_data_free);
6958c2ecf20Sopenharmony_ci		atomic_inc(&ar->rx_data_free_cnt);
6968c2ecf20Sopenharmony_ci	} while (data);
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic void ar5523_free_rx_bufs(struct ar5523 *ar)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	struct ar5523_rx_data *data;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	ar5523_cancel_rx_bufs(ar);
7048c2ecf20Sopenharmony_ci	while (!list_empty(&ar->rx_data_free)) {
7058c2ecf20Sopenharmony_ci		data = (struct ar5523_rx_data *) ar->rx_data_free.next;
7068c2ecf20Sopenharmony_ci		list_del(&data->list);
7078c2ecf20Sopenharmony_ci		usb_free_urb(data->urb);
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cistatic int ar5523_alloc_rx_bufs(struct ar5523 *ar)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	int i;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	for (i = 0; i < AR5523_RX_DATA_COUNT; i++) {
7168c2ecf20Sopenharmony_ci		struct ar5523_rx_data *data = &ar->rx_data[i];
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci		data->ar = ar;
7198c2ecf20Sopenharmony_ci		data->urb = usb_alloc_urb(0, GFP_KERNEL);
7208c2ecf20Sopenharmony_ci		if (!data->urb)
7218c2ecf20Sopenharmony_ci			goto err;
7228c2ecf20Sopenharmony_ci		list_add_tail(&data->list, &ar->rx_data_free);
7238c2ecf20Sopenharmony_ci		atomic_inc(&ar->rx_data_free_cnt);
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci	return 0;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_cierr:
7288c2ecf20Sopenharmony_ci	ar5523_free_rx_bufs(ar);
7298c2ecf20Sopenharmony_ci	return -ENOMEM;
7308c2ecf20Sopenharmony_ci}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_cistatic void ar5523_data_tx_pkt_put(struct ar5523 *ar)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	atomic_dec(&ar->tx_nr_total);
7358c2ecf20Sopenharmony_ci	if (!atomic_dec_return(&ar->tx_nr_pending)) {
7368c2ecf20Sopenharmony_ci		del_timer(&ar->tx_wd_timer);
7378c2ecf20Sopenharmony_ci		wake_up(&ar->tx_flush_waitq);
7388c2ecf20Sopenharmony_ci	}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	if (atomic_read(&ar->tx_nr_total) < AR5523_TX_DATA_RESTART_COUNT) {
7418c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "restart tx queue\n");
7428c2ecf20Sopenharmony_ci		ieee80211_wake_queues(ar->hw);
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_cistatic void ar5523_data_tx_cb(struct urb *urb)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	struct sk_buff *skb = urb->context;
7498c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
7508c2ecf20Sopenharmony_ci	struct ar5523_tx_data *data = (struct ar5523_tx_data *)
7518c2ecf20Sopenharmony_ci				       txi->driver_data;
7528c2ecf20Sopenharmony_ci	struct ar5523 *ar = data->ar;
7538c2ecf20Sopenharmony_ci	unsigned long flags;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "data tx urb completed: %d\n", urb->status);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ar->tx_data_list_lock, flags);
7588c2ecf20Sopenharmony_ci	list_del(&data->list);
7598c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	if (urb->status) {
7628c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "%s: urb status: %d\n", __func__, urb->status);
7638c2ecf20Sopenharmony_ci		ar5523_data_tx_pkt_put(ar);
7648c2ecf20Sopenharmony_ci		ieee80211_free_txskb(ar->hw, skb);
7658c2ecf20Sopenharmony_ci	} else {
7668c2ecf20Sopenharmony_ci		skb_pull(skb, sizeof(struct ar5523_tx_desc) + sizeof(__be32));
7678c2ecf20Sopenharmony_ci		ieee80211_tx_status_irqsafe(ar->hw, skb);
7688c2ecf20Sopenharmony_ci	}
7698c2ecf20Sopenharmony_ci	usb_free_urb(urb);
7708c2ecf20Sopenharmony_ci}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_cistatic void ar5523_tx(struct ieee80211_hw *hw,
7738c2ecf20Sopenharmony_ci		       struct ieee80211_tx_control *control,
7748c2ecf20Sopenharmony_ci		       struct sk_buff *skb)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
7778c2ecf20Sopenharmony_ci	struct ar5523_tx_data *data = (struct ar5523_tx_data *)
7788c2ecf20Sopenharmony_ci					txi->driver_data;
7798c2ecf20Sopenharmony_ci	struct ar5523 *ar = hw->priv;
7808c2ecf20Sopenharmony_ci	unsigned long flags;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "tx called\n");
7838c2ecf20Sopenharmony_ci	if (atomic_inc_return(&ar->tx_nr_total) >= AR5523_TX_DATA_COUNT) {
7848c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "tx queue full\n");
7858c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "stop queues (tot %d pend %d)\n",
7868c2ecf20Sopenharmony_ci			   atomic_read(&ar->tx_nr_total),
7878c2ecf20Sopenharmony_ci			   atomic_read(&ar->tx_nr_pending));
7888c2ecf20Sopenharmony_ci		ieee80211_stop_queues(hw);
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ar->tx_data_list_lock, flags);
7928c2ecf20Sopenharmony_ci	list_add_tail(&data->list, &ar->tx_queue_pending);
7938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	ieee80211_queue_work(ar->hw, &ar->tx_work);
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_cistatic void ar5523_tx_work_locked(struct ar5523 *ar)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	struct ar5523_tx_data *data;
8018c2ecf20Sopenharmony_ci	struct ar5523_tx_desc *desc;
8028c2ecf20Sopenharmony_ci	struct ar5523_chunk *chunk;
8038c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *txi;
8048c2ecf20Sopenharmony_ci	struct urb *urb;
8058c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8068c2ecf20Sopenharmony_ci	int error = 0, paylen;
8078c2ecf20Sopenharmony_ci	u32 txqid;
8088c2ecf20Sopenharmony_ci	unsigned long flags;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct ar5523_tx_data) >
8118c2ecf20Sopenharmony_ci		     IEEE80211_TX_INFO_DRIVER_DATA_SIZE);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "%s\n", __func__);
8148c2ecf20Sopenharmony_ci	do {
8158c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ar->tx_data_list_lock, flags);
8168c2ecf20Sopenharmony_ci		if (!list_empty(&ar->tx_queue_pending)) {
8178c2ecf20Sopenharmony_ci			data = (struct ar5523_tx_data *)
8188c2ecf20Sopenharmony_ci				ar->tx_queue_pending.next;
8198c2ecf20Sopenharmony_ci			list_del(&data->list);
8208c2ecf20Sopenharmony_ci		} else
8218c2ecf20Sopenharmony_ci			data = NULL;
8228c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci		if (!data)
8258c2ecf20Sopenharmony_ci			break;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci		txi = container_of((void *)data, struct ieee80211_tx_info,
8288c2ecf20Sopenharmony_ci				   driver_data);
8298c2ecf20Sopenharmony_ci		txqid = 0;
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci		skb = container_of((void *)txi, struct sk_buff, cb);
8328c2ecf20Sopenharmony_ci		paylen = skb->len;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci		urb = usb_alloc_urb(0, GFP_KERNEL);
8358c2ecf20Sopenharmony_ci		if (!urb) {
8368c2ecf20Sopenharmony_ci			ieee80211_free_txskb(ar->hw, skb);
8378c2ecf20Sopenharmony_ci			continue;
8388c2ecf20Sopenharmony_ci		}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci		data->ar = ar;
8418c2ecf20Sopenharmony_ci		data->urb = urb;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci		desc = skb_push(skb, sizeof(*desc));
8448c2ecf20Sopenharmony_ci		chunk = skb_push(skb, sizeof(*chunk));
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci		chunk->seqnum = 0;
8478c2ecf20Sopenharmony_ci		chunk->flags = UATH_CFLAGS_FINAL;
8488c2ecf20Sopenharmony_ci		chunk->length = cpu_to_be16(skb->len);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci		desc->msglen = cpu_to_be32(skb->len);
8518c2ecf20Sopenharmony_ci		desc->msgid  = AR5523_DATA_ID;
8528c2ecf20Sopenharmony_ci		desc->buflen = cpu_to_be32(paylen);
8538c2ecf20Sopenharmony_ci		desc->type   = cpu_to_be32(WDCMSG_SEND);
8548c2ecf20Sopenharmony_ci		desc->flags  = cpu_to_be32(UATH_TX_NOTIFY);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci		if (test_bit(AR5523_CONNECTED, &ar->flags))
8578c2ecf20Sopenharmony_ci			desc->connid = cpu_to_be32(AR5523_ID_BSS);
8588c2ecf20Sopenharmony_ci		else
8598c2ecf20Sopenharmony_ci			desc->connid = cpu_to_be32(AR5523_ID_BROADCAST);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci		if (txi->flags & IEEE80211_TX_CTL_USE_MINRATE)
8628c2ecf20Sopenharmony_ci			txqid |= UATH_TXQID_MINRATE;
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci		desc->txqid = cpu_to_be32(txqid);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci		urb->transfer_flags = URB_ZERO_PACKET;
8678c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(urb, ar->dev, ar5523_data_tx_pipe(ar->dev),
8688c2ecf20Sopenharmony_ci				  skb->data, skb->len, ar5523_data_tx_cb, skb);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ar->tx_data_list_lock, flags);
8718c2ecf20Sopenharmony_ci		list_add_tail(&data->list, &ar->tx_queue_submitted);
8728c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
8738c2ecf20Sopenharmony_ci		mod_timer(&ar->tx_wd_timer, jiffies + AR5523_TX_WD_TIMEOUT);
8748c2ecf20Sopenharmony_ci		atomic_inc(&ar->tx_nr_pending);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "TX Frame (%d pending)\n",
8778c2ecf20Sopenharmony_ci			   atomic_read(&ar->tx_nr_pending));
8788c2ecf20Sopenharmony_ci		error = usb_submit_urb(urb, GFP_KERNEL);
8798c2ecf20Sopenharmony_ci		if (error) {
8808c2ecf20Sopenharmony_ci			ar5523_err(ar, "error %d when submitting tx urb\n",
8818c2ecf20Sopenharmony_ci				   error);
8828c2ecf20Sopenharmony_ci			spin_lock_irqsave(&ar->tx_data_list_lock, flags);
8838c2ecf20Sopenharmony_ci			list_del(&data->list);
8848c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
8858c2ecf20Sopenharmony_ci			atomic_dec(&ar->tx_nr_pending);
8868c2ecf20Sopenharmony_ci			ar5523_data_tx_pkt_put(ar);
8878c2ecf20Sopenharmony_ci			usb_free_urb(urb);
8888c2ecf20Sopenharmony_ci			ieee80211_free_txskb(ar->hw, skb);
8898c2ecf20Sopenharmony_ci		}
8908c2ecf20Sopenharmony_ci	} while (true);
8918c2ecf20Sopenharmony_ci}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_cistatic void ar5523_tx_work(struct work_struct *work)
8948c2ecf20Sopenharmony_ci{
8958c2ecf20Sopenharmony_ci	struct ar5523 *ar = container_of(work, struct ar5523, tx_work);
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "%s\n", __func__);
8988c2ecf20Sopenharmony_ci	mutex_lock(&ar->mutex);
8998c2ecf20Sopenharmony_ci	ar5523_tx_work_locked(ar);
9008c2ecf20Sopenharmony_ci	mutex_unlock(&ar->mutex);
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_cistatic void ar5523_tx_wd_timer(struct timer_list *t)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	struct ar5523 *ar = from_timer(ar, t, tx_wd_timer);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "TX watchdog timer triggered\n");
9088c2ecf20Sopenharmony_ci	ieee80211_queue_work(ar->hw, &ar->tx_wd_work);
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic void ar5523_tx_wd_work(struct work_struct *work)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	struct ar5523 *ar = container_of(work, struct ar5523, tx_wd_work);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	/* Occasionally the TX queues stop responding. The only way to
9168c2ecf20Sopenharmony_ci	 * recover seems to be to reset the dongle.
9178c2ecf20Sopenharmony_ci	 */
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	mutex_lock(&ar->mutex);
9208c2ecf20Sopenharmony_ci	ar5523_err(ar, "TX queue stuck (tot %d pend %d)\n",
9218c2ecf20Sopenharmony_ci		   atomic_read(&ar->tx_nr_total),
9228c2ecf20Sopenharmony_ci		   atomic_read(&ar->tx_nr_pending));
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	ar5523_err(ar, "Will restart dongle.\n");
9258c2ecf20Sopenharmony_ci	ar5523_cmd_write(ar, WDCMSG_TARGET_RESET, NULL, 0, 0);
9268c2ecf20Sopenharmony_ci	mutex_unlock(&ar->mutex);
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_cistatic void ar5523_flush_tx(struct ar5523 *ar)
9308c2ecf20Sopenharmony_ci{
9318c2ecf20Sopenharmony_ci	ar5523_tx_work_locked(ar);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	/* Don't waste time trying to flush if USB is disconnected */
9348c2ecf20Sopenharmony_ci	if (test_bit(AR5523_USB_DISCONNECTED, &ar->flags))
9358c2ecf20Sopenharmony_ci		return;
9368c2ecf20Sopenharmony_ci	if (!wait_event_timeout(ar->tx_flush_waitq,
9378c2ecf20Sopenharmony_ci	    !atomic_read(&ar->tx_nr_pending), AR5523_FLUSH_TIMEOUT))
9388c2ecf20Sopenharmony_ci		ar5523_err(ar, "flush timeout (tot %d pend %d)\n",
9398c2ecf20Sopenharmony_ci			   atomic_read(&ar->tx_nr_total),
9408c2ecf20Sopenharmony_ci			   atomic_read(&ar->tx_nr_pending));
9418c2ecf20Sopenharmony_ci}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_cistatic void ar5523_free_tx_cmd(struct ar5523 *ar)
9448c2ecf20Sopenharmony_ci{
9458c2ecf20Sopenharmony_ci	struct ar5523_tx_cmd *cmd = &ar->tx_cmd;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	usb_free_coherent(ar->dev, AR5523_MAX_RXCMDSZ, cmd->buf_tx,
9488c2ecf20Sopenharmony_ci			  cmd->urb_tx->transfer_dma);
9498c2ecf20Sopenharmony_ci	usb_free_urb(cmd->urb_tx);
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_cistatic int ar5523_alloc_tx_cmd(struct ar5523 *ar)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	struct ar5523_tx_cmd *cmd = &ar->tx_cmd;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	cmd->ar = ar;
9578c2ecf20Sopenharmony_ci	init_completion(&cmd->done);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	cmd->urb_tx = usb_alloc_urb(0, GFP_KERNEL);
9608c2ecf20Sopenharmony_ci	if (!cmd->urb_tx)
9618c2ecf20Sopenharmony_ci		return -ENOMEM;
9628c2ecf20Sopenharmony_ci	cmd->buf_tx = usb_alloc_coherent(ar->dev, AR5523_MAX_TXCMDSZ,
9638c2ecf20Sopenharmony_ci					 GFP_KERNEL,
9648c2ecf20Sopenharmony_ci					 &cmd->urb_tx->transfer_dma);
9658c2ecf20Sopenharmony_ci	if (!cmd->buf_tx) {
9668c2ecf20Sopenharmony_ci		usb_free_urb(cmd->urb_tx);
9678c2ecf20Sopenharmony_ci		return -ENOMEM;
9688c2ecf20Sopenharmony_ci	}
9698c2ecf20Sopenharmony_ci	return 0;
9708c2ecf20Sopenharmony_ci}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci/*
9738c2ecf20Sopenharmony_ci * This function is called periodically (every second) when associated to
9748c2ecf20Sopenharmony_ci * query device statistics.
9758c2ecf20Sopenharmony_ci */
9768c2ecf20Sopenharmony_cistatic void ar5523_stat_work(struct work_struct *work)
9778c2ecf20Sopenharmony_ci{
9788c2ecf20Sopenharmony_ci	struct ar5523 *ar = container_of(work, struct ar5523, stat_work.work);
9798c2ecf20Sopenharmony_ci	int error;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "%s\n", __func__);
9828c2ecf20Sopenharmony_ci	mutex_lock(&ar->mutex);
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	/*
9858c2ecf20Sopenharmony_ci	 * Send request for statistics asynchronously once a second. This
9868c2ecf20Sopenharmony_ci	 * seems to be important. Throughput is a lot better if this is done.
9878c2ecf20Sopenharmony_ci	 */
9888c2ecf20Sopenharmony_ci	error = ar5523_cmd_write(ar, WDCMSG_TARGET_GET_STATS, NULL, 0, 0);
9898c2ecf20Sopenharmony_ci	if (error)
9908c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not query stats, error %d\n", error);
9918c2ecf20Sopenharmony_ci	mutex_unlock(&ar->mutex);
9928c2ecf20Sopenharmony_ci	ieee80211_queue_delayed_work(ar->hw, &ar->stat_work, HZ);
9938c2ecf20Sopenharmony_ci}
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci/*
9968c2ecf20Sopenharmony_ci * Interface routines to the mac80211 stack.
9978c2ecf20Sopenharmony_ci */
9988c2ecf20Sopenharmony_cistatic int ar5523_start(struct ieee80211_hw *hw)
9998c2ecf20Sopenharmony_ci{
10008c2ecf20Sopenharmony_ci	struct ar5523 *ar = hw->priv;
10018c2ecf20Sopenharmony_ci	int error;
10028c2ecf20Sopenharmony_ci	__be32 val;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "start called\n");
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	mutex_lock(&ar->mutex);
10078c2ecf20Sopenharmony_ci	val = cpu_to_be32(0);
10088c2ecf20Sopenharmony_ci	ar5523_cmd_write(ar, WDCMSG_BIND, &val, sizeof(val), 0);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	/* set MAC address */
10118c2ecf20Sopenharmony_ci	ar5523_config_multi(ar, CFG_MAC_ADDR, &ar->hw->wiphy->perm_addr,
10128c2ecf20Sopenharmony_ci			    ETH_ALEN);
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	/* XXX honor net80211 state */
10158c2ecf20Sopenharmony_ci	ar5523_config(ar, CFG_RATE_CONTROL_ENABLE, 0x00000001);
10168c2ecf20Sopenharmony_ci	ar5523_config(ar, CFG_DIVERSITY_CTL, 0x00000001);
10178c2ecf20Sopenharmony_ci	ar5523_config(ar, CFG_ABOLT, 0x0000003f);
10188c2ecf20Sopenharmony_ci	ar5523_config(ar, CFG_WME_ENABLED, 0x00000000);
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	ar5523_config(ar, CFG_SERVICE_TYPE, 1);
10218c2ecf20Sopenharmony_ci	ar5523_config(ar, CFG_TP_SCALE, 0x00000000);
10228c2ecf20Sopenharmony_ci	ar5523_config(ar, CFG_TPC_HALF_DBM5, 0x0000003c);
10238c2ecf20Sopenharmony_ci	ar5523_config(ar, CFG_TPC_HALF_DBM2, 0x0000003c);
10248c2ecf20Sopenharmony_ci	ar5523_config(ar, CFG_OVERRD_TX_POWER, 0x00000000);
10258c2ecf20Sopenharmony_ci	ar5523_config(ar, CFG_GMODE_PROTECTION, 0x00000000);
10268c2ecf20Sopenharmony_ci	ar5523_config(ar, CFG_GMODE_PROTECT_RATE_INDEX, 0x00000003);
10278c2ecf20Sopenharmony_ci	ar5523_config(ar, CFG_PROTECTION_TYPE, 0x00000000);
10288c2ecf20Sopenharmony_ci	ar5523_config(ar, CFG_MODE_CTS, 0x00000002);
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	error = ar5523_cmd_read(ar, WDCMSG_TARGET_START, NULL, 0,
10318c2ecf20Sopenharmony_ci	    &val, sizeof(val), AR5523_CMD_FLAG_MAGIC);
10328c2ecf20Sopenharmony_ci	if (error) {
10338c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "could not start target, error %d\n", error);
10348c2ecf20Sopenharmony_ci		goto err;
10358c2ecf20Sopenharmony_ci	}
10368c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "WDCMSG_TARGET_START returns handle: 0x%x\n",
10378c2ecf20Sopenharmony_ci		   be32_to_cpu(val));
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	ar5523_switch_chan(ar);
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	val = cpu_to_be32(TARGET_DEVICE_AWAKE);
10428c2ecf20Sopenharmony_ci	ar5523_cmd_write(ar, WDCMSG_SET_PWR_MODE, &val, sizeof(val), 0);
10438c2ecf20Sopenharmony_ci	/* XXX? check */
10448c2ecf20Sopenharmony_ci	ar5523_cmd_write(ar, WDCMSG_RESET_KEY_CACHE, NULL, 0, 0);
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	set_bit(AR5523_HW_UP, &ar->flags);
10478c2ecf20Sopenharmony_ci	queue_work(ar->wq, &ar->rx_refill_work);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	/* enable Rx */
10508c2ecf20Sopenharmony_ci	ar5523_set_rxfilter(ar, 0, UATH_FILTER_OP_INIT);
10518c2ecf20Sopenharmony_ci	ar5523_set_rxfilter(ar,
10528c2ecf20Sopenharmony_ci			    UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST |
10538c2ecf20Sopenharmony_ci			    UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON,
10548c2ecf20Sopenharmony_ci			    UATH_FILTER_OP_SET);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	ar5523_set_ledsteady(ar, UATH_LED_ACTIVITY, UATH_LED_ON);
10578c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "start OK\n");
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_cierr:
10608c2ecf20Sopenharmony_ci	mutex_unlock(&ar->mutex);
10618c2ecf20Sopenharmony_ci	return error;
10628c2ecf20Sopenharmony_ci}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_cistatic void ar5523_stop(struct ieee80211_hw *hw)
10658c2ecf20Sopenharmony_ci{
10668c2ecf20Sopenharmony_ci	struct ar5523 *ar = hw->priv;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "stop called\n");
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&ar->stat_work);
10718c2ecf20Sopenharmony_ci	mutex_lock(&ar->mutex);
10728c2ecf20Sopenharmony_ci	clear_bit(AR5523_HW_UP, &ar->flags);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	ar5523_set_ledsteady(ar, UATH_LED_LINK, UATH_LED_OFF);
10758c2ecf20Sopenharmony_ci	ar5523_set_ledsteady(ar, UATH_LED_ACTIVITY, UATH_LED_OFF);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	ar5523_cmd_write(ar, WDCMSG_TARGET_STOP, NULL, 0, 0);
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	del_timer_sync(&ar->tx_wd_timer);
10808c2ecf20Sopenharmony_ci	cancel_work_sync(&ar->tx_wd_work);
10818c2ecf20Sopenharmony_ci	cancel_work_sync(&ar->rx_refill_work);
10828c2ecf20Sopenharmony_ci	ar5523_cancel_rx_bufs(ar);
10838c2ecf20Sopenharmony_ci	mutex_unlock(&ar->mutex);
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_cistatic int ar5523_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
10878c2ecf20Sopenharmony_ci{
10888c2ecf20Sopenharmony_ci	struct ar5523 *ar = hw->priv;
10898c2ecf20Sopenharmony_ci	int ret;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "set_rts_threshold called\n");
10928c2ecf20Sopenharmony_ci	mutex_lock(&ar->mutex);
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	ret = ar5523_config(ar, CFG_USER_RTS_THRESHOLD, value);
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	mutex_unlock(&ar->mutex);
10978c2ecf20Sopenharmony_ci	return ret;
10988c2ecf20Sopenharmony_ci}
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_cistatic void ar5523_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
11018c2ecf20Sopenharmony_ci			 u32 queues, bool drop)
11028c2ecf20Sopenharmony_ci{
11038c2ecf20Sopenharmony_ci	struct ar5523 *ar = hw->priv;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "flush called\n");
11068c2ecf20Sopenharmony_ci	ar5523_flush_tx(ar);
11078c2ecf20Sopenharmony_ci}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_cistatic int ar5523_add_interface(struct ieee80211_hw *hw,
11108c2ecf20Sopenharmony_ci				struct ieee80211_vif *vif)
11118c2ecf20Sopenharmony_ci{
11128c2ecf20Sopenharmony_ci	struct ar5523 *ar = hw->priv;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "add interface called\n");
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	if (ar->vif) {
11178c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "invalid add_interface\n");
11188c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
11198c2ecf20Sopenharmony_ci	}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	switch (vif->type) {
11228c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
11238c2ecf20Sopenharmony_ci		ar->vif = vif;
11248c2ecf20Sopenharmony_ci		break;
11258c2ecf20Sopenharmony_ci	default:
11268c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
11278c2ecf20Sopenharmony_ci	}
11288c2ecf20Sopenharmony_ci	return 0;
11298c2ecf20Sopenharmony_ci}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_cistatic void ar5523_remove_interface(struct ieee80211_hw *hw,
11328c2ecf20Sopenharmony_ci				    struct ieee80211_vif *vif)
11338c2ecf20Sopenharmony_ci{
11348c2ecf20Sopenharmony_ci	struct ar5523 *ar = hw->priv;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "remove interface called\n");
11378c2ecf20Sopenharmony_ci	ar->vif = NULL;
11388c2ecf20Sopenharmony_ci}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_cistatic int ar5523_hwconfig(struct ieee80211_hw *hw, u32 changed)
11418c2ecf20Sopenharmony_ci{
11428c2ecf20Sopenharmony_ci	struct ar5523 *ar = hw->priv;
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "config called\n");
11458c2ecf20Sopenharmony_ci	mutex_lock(&ar->mutex);
11468c2ecf20Sopenharmony_ci	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
11478c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "Do channel switch\n");
11488c2ecf20Sopenharmony_ci		ar5523_flush_tx(ar);
11498c2ecf20Sopenharmony_ci		ar5523_switch_chan(ar);
11508c2ecf20Sopenharmony_ci	}
11518c2ecf20Sopenharmony_ci	mutex_unlock(&ar->mutex);
11528c2ecf20Sopenharmony_ci	return 0;
11538c2ecf20Sopenharmony_ci}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_cistatic int ar5523_get_wlan_mode(struct ar5523 *ar,
11568c2ecf20Sopenharmony_ci				struct ieee80211_bss_conf *bss_conf)
11578c2ecf20Sopenharmony_ci{
11588c2ecf20Sopenharmony_ci	struct ieee80211_supported_band *band;
11598c2ecf20Sopenharmony_ci	int bit;
11608c2ecf20Sopenharmony_ci	struct ieee80211_sta *sta;
11618c2ecf20Sopenharmony_ci	u32 sta_rate_set;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	band = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
11648c2ecf20Sopenharmony_ci	sta = ieee80211_find_sta(ar->vif, bss_conf->bssid);
11658c2ecf20Sopenharmony_ci	if (!sta) {
11668c2ecf20Sopenharmony_ci		ar5523_info(ar, "STA not found!\n");
11678c2ecf20Sopenharmony_ci		return WLAN_MODE_11b;
11688c2ecf20Sopenharmony_ci	}
11698c2ecf20Sopenharmony_ci	sta_rate_set = sta->supp_rates[ar->hw->conf.chandef.chan->band];
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	for (bit = 0; bit < band->n_bitrates; bit++) {
11728c2ecf20Sopenharmony_ci		if (sta_rate_set & 1) {
11738c2ecf20Sopenharmony_ci			int rate = band->bitrates[bit].bitrate;
11748c2ecf20Sopenharmony_ci			switch (rate) {
11758c2ecf20Sopenharmony_ci			case 60:
11768c2ecf20Sopenharmony_ci			case 90:
11778c2ecf20Sopenharmony_ci			case 120:
11788c2ecf20Sopenharmony_ci			case 180:
11798c2ecf20Sopenharmony_ci			case 240:
11808c2ecf20Sopenharmony_ci			case 360:
11818c2ecf20Sopenharmony_ci			case 480:
11828c2ecf20Sopenharmony_ci			case 540:
11838c2ecf20Sopenharmony_ci				return WLAN_MODE_11g;
11848c2ecf20Sopenharmony_ci			}
11858c2ecf20Sopenharmony_ci		}
11868c2ecf20Sopenharmony_ci		sta_rate_set >>= 1;
11878c2ecf20Sopenharmony_ci	}
11888c2ecf20Sopenharmony_ci	return WLAN_MODE_11b;
11898c2ecf20Sopenharmony_ci}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_cistatic void ar5523_create_rateset(struct ar5523 *ar,
11928c2ecf20Sopenharmony_ci				  struct ieee80211_bss_conf *bss_conf,
11938c2ecf20Sopenharmony_ci				  struct ar5523_cmd_rateset *rs,
11948c2ecf20Sopenharmony_ci				  bool basic)
11958c2ecf20Sopenharmony_ci{
11968c2ecf20Sopenharmony_ci	struct ieee80211_supported_band *band;
11978c2ecf20Sopenharmony_ci	struct ieee80211_sta *sta;
11988c2ecf20Sopenharmony_ci	int bit, i = 0;
11998c2ecf20Sopenharmony_ci	u32 sta_rate_set, basic_rate_set;
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	sta = ieee80211_find_sta(ar->vif, bss_conf->bssid);
12028c2ecf20Sopenharmony_ci	basic_rate_set = bss_conf->basic_rates;
12038c2ecf20Sopenharmony_ci	if (!sta) {
12048c2ecf20Sopenharmony_ci		ar5523_info(ar, "STA not found. Cannot set rates\n");
12058c2ecf20Sopenharmony_ci		sta_rate_set = bss_conf->basic_rates;
12068c2ecf20Sopenharmony_ci	} else
12078c2ecf20Sopenharmony_ci		sta_rate_set = sta->supp_rates[ar->hw->conf.chandef.chan->band];
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "sta rate_set = %08x\n", sta_rate_set);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	band = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
12128c2ecf20Sopenharmony_ci	for (bit = 0; bit < band->n_bitrates; bit++) {
12138c2ecf20Sopenharmony_ci		BUG_ON(i >= AR5523_MAX_NRATES);
12148c2ecf20Sopenharmony_ci		ar5523_dbg(ar, "Considering rate %d : %d\n",
12158c2ecf20Sopenharmony_ci			   band->bitrates[bit].hw_value, sta_rate_set & 1);
12168c2ecf20Sopenharmony_ci		if (sta_rate_set & 1) {
12178c2ecf20Sopenharmony_ci			rs->set[i] = band->bitrates[bit].hw_value;
12188c2ecf20Sopenharmony_ci			if (basic_rate_set & 1 && basic)
12198c2ecf20Sopenharmony_ci				rs->set[i] |= 0x80;
12208c2ecf20Sopenharmony_ci			i++;
12218c2ecf20Sopenharmony_ci		}
12228c2ecf20Sopenharmony_ci		sta_rate_set >>= 1;
12238c2ecf20Sopenharmony_ci		basic_rate_set >>= 1;
12248c2ecf20Sopenharmony_ci	}
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	rs->length = i;
12278c2ecf20Sopenharmony_ci}
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_cistatic int ar5523_set_basic_rates(struct ar5523 *ar,
12308c2ecf20Sopenharmony_ci				  struct ieee80211_bss_conf *bss)
12318c2ecf20Sopenharmony_ci{
12328c2ecf20Sopenharmony_ci	struct ar5523_cmd_rates rates;
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	memset(&rates, 0, sizeof(rates));
12358c2ecf20Sopenharmony_ci	rates.connid = cpu_to_be32(2);		/* XXX */
12368c2ecf20Sopenharmony_ci	rates.size   = cpu_to_be32(sizeof(struct ar5523_cmd_rateset));
12378c2ecf20Sopenharmony_ci	ar5523_create_rateset(ar, bss, &rates.rateset, true);
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_SET_BASIC_RATE, &rates,
12408c2ecf20Sopenharmony_ci				sizeof(rates), 0);
12418c2ecf20Sopenharmony_ci}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_cistatic int ar5523_create_connection(struct ar5523 *ar,
12448c2ecf20Sopenharmony_ci				    struct ieee80211_vif *vif,
12458c2ecf20Sopenharmony_ci				    struct ieee80211_bss_conf *bss)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	struct ar5523_cmd_create_connection create;
12488c2ecf20Sopenharmony_ci	int wlan_mode;
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	memset(&create, 0, sizeof(create));
12518c2ecf20Sopenharmony_ci	create.connid = cpu_to_be32(2);
12528c2ecf20Sopenharmony_ci	create.bssid = cpu_to_be32(0);
12538c2ecf20Sopenharmony_ci	/* XXX packed or not?  */
12548c2ecf20Sopenharmony_ci	create.size = cpu_to_be32(sizeof(struct ar5523_cmd_rateset));
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	ar5523_create_rateset(ar, bss, &create.connattr.rateset, false);
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	wlan_mode = ar5523_get_wlan_mode(ar, bss);
12598c2ecf20Sopenharmony_ci	create.connattr.wlanmode = cpu_to_be32(wlan_mode);
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_CREATE_CONNECTION, &create,
12628c2ecf20Sopenharmony_ci				sizeof(create), 0);
12638c2ecf20Sopenharmony_ci}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_cistatic int ar5523_write_associd(struct ar5523 *ar,
12668c2ecf20Sopenharmony_ci				struct ieee80211_bss_conf *bss)
12678c2ecf20Sopenharmony_ci{
12688c2ecf20Sopenharmony_ci	struct ar5523_cmd_set_associd associd;
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	memset(&associd, 0, sizeof(associd));
12718c2ecf20Sopenharmony_ci	associd.defaultrateix = cpu_to_be32(0);	/* XXX */
12728c2ecf20Sopenharmony_ci	associd.associd = cpu_to_be32(bss->aid);
12738c2ecf20Sopenharmony_ci	associd.timoffset = cpu_to_be32(0x3b);	/* XXX */
12748c2ecf20Sopenharmony_ci	memcpy(associd.bssid, bss->bssid, ETH_ALEN);
12758c2ecf20Sopenharmony_ci	return ar5523_cmd_write(ar, WDCMSG_WRITE_ASSOCID, &associd,
12768c2ecf20Sopenharmony_ci				sizeof(associd), 0);
12778c2ecf20Sopenharmony_ci}
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_cistatic void ar5523_bss_info_changed(struct ieee80211_hw *hw,
12808c2ecf20Sopenharmony_ci				    struct ieee80211_vif *vif,
12818c2ecf20Sopenharmony_ci				    struct ieee80211_bss_conf *bss,
12828c2ecf20Sopenharmony_ci				    u32 changed)
12838c2ecf20Sopenharmony_ci{
12848c2ecf20Sopenharmony_ci	struct ar5523 *ar = hw->priv;
12858c2ecf20Sopenharmony_ci	int error;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "bss_info_changed called\n");
12888c2ecf20Sopenharmony_ci	mutex_lock(&ar->mutex);
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	if (!(changed & BSS_CHANGED_ASSOC))
12918c2ecf20Sopenharmony_ci		goto out_unlock;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	if (bss->assoc) {
12948c2ecf20Sopenharmony_ci		error = ar5523_create_connection(ar, vif, bss);
12958c2ecf20Sopenharmony_ci		if (error) {
12968c2ecf20Sopenharmony_ci			ar5523_err(ar, "could not create connection\n");
12978c2ecf20Sopenharmony_ci			goto out_unlock;
12988c2ecf20Sopenharmony_ci		}
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci		error = ar5523_set_basic_rates(ar, bss);
13018c2ecf20Sopenharmony_ci		if (error) {
13028c2ecf20Sopenharmony_ci			ar5523_err(ar, "could not set negotiated rate set\n");
13038c2ecf20Sopenharmony_ci			goto out_unlock;
13048c2ecf20Sopenharmony_ci		}
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci		error = ar5523_write_associd(ar, bss);
13078c2ecf20Sopenharmony_ci		if (error) {
13088c2ecf20Sopenharmony_ci			ar5523_err(ar, "could not set association\n");
13098c2ecf20Sopenharmony_ci			goto out_unlock;
13108c2ecf20Sopenharmony_ci		}
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci		/* turn link LED on */
13138c2ecf20Sopenharmony_ci		ar5523_set_ledsteady(ar, UATH_LED_LINK, UATH_LED_ON);
13148c2ecf20Sopenharmony_ci		set_bit(AR5523_CONNECTED, &ar->flags);
13158c2ecf20Sopenharmony_ci		ieee80211_queue_delayed_work(hw, &ar->stat_work, HZ);
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	} else {
13188c2ecf20Sopenharmony_ci		cancel_delayed_work(&ar->stat_work);
13198c2ecf20Sopenharmony_ci		clear_bit(AR5523_CONNECTED, &ar->flags);
13208c2ecf20Sopenharmony_ci		ar5523_set_ledsteady(ar, UATH_LED_LINK, UATH_LED_OFF);
13218c2ecf20Sopenharmony_ci	}
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ciout_unlock:
13248c2ecf20Sopenharmony_ci	mutex_unlock(&ar->mutex);
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci}
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci#define AR5523_SUPPORTED_FILTERS (FIF_ALLMULTI | \
13298c2ecf20Sopenharmony_ci				  FIF_FCSFAIL | \
13308c2ecf20Sopenharmony_ci				  FIF_OTHER_BSS)
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_cistatic void ar5523_configure_filter(struct ieee80211_hw *hw,
13338c2ecf20Sopenharmony_ci				    unsigned int changed_flags,
13348c2ecf20Sopenharmony_ci				    unsigned int *total_flags,
13358c2ecf20Sopenharmony_ci				    u64 multicast)
13368c2ecf20Sopenharmony_ci{
13378c2ecf20Sopenharmony_ci	struct ar5523 *ar = hw->priv;
13388c2ecf20Sopenharmony_ci	u32 filter = 0;
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "configure_filter called\n");
13418c2ecf20Sopenharmony_ci	mutex_lock(&ar->mutex);
13428c2ecf20Sopenharmony_ci	ar5523_flush_tx(ar);
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	*total_flags &= AR5523_SUPPORTED_FILTERS;
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	/* The filters seems strange. UATH_FILTER_RX_BCAST and
13478c2ecf20Sopenharmony_ci	 * UATH_FILTER_RX_MCAST does not result in those frames being RXed.
13488c2ecf20Sopenharmony_ci	 * The only way I have found to get [mb]cast frames seems to be
13498c2ecf20Sopenharmony_ci	 * to set UATH_FILTER_RX_PROM. */
13508c2ecf20Sopenharmony_ci	filter |= UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST |
13518c2ecf20Sopenharmony_ci		  UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON |
13528c2ecf20Sopenharmony_ci		  UATH_FILTER_RX_PROM;
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	ar5523_set_rxfilter(ar, 0, UATH_FILTER_OP_INIT);
13558c2ecf20Sopenharmony_ci	ar5523_set_rxfilter(ar, filter, UATH_FILTER_OP_SET);
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	mutex_unlock(&ar->mutex);
13588c2ecf20Sopenharmony_ci}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_cistatic const struct ieee80211_ops ar5523_ops = {
13618c2ecf20Sopenharmony_ci	.start			= ar5523_start,
13628c2ecf20Sopenharmony_ci	.stop			= ar5523_stop,
13638c2ecf20Sopenharmony_ci	.tx			= ar5523_tx,
13648c2ecf20Sopenharmony_ci	.set_rts_threshold	= ar5523_set_rts_threshold,
13658c2ecf20Sopenharmony_ci	.add_interface		= ar5523_add_interface,
13668c2ecf20Sopenharmony_ci	.remove_interface	= ar5523_remove_interface,
13678c2ecf20Sopenharmony_ci	.config			= ar5523_hwconfig,
13688c2ecf20Sopenharmony_ci	.bss_info_changed	= ar5523_bss_info_changed,
13698c2ecf20Sopenharmony_ci	.configure_filter	= ar5523_configure_filter,
13708c2ecf20Sopenharmony_ci	.flush			= ar5523_flush,
13718c2ecf20Sopenharmony_ci};
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_cistatic int ar5523_host_available(struct ar5523 *ar)
13748c2ecf20Sopenharmony_ci{
13758c2ecf20Sopenharmony_ci	struct ar5523_cmd_host_available setup;
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	/* inform target the host is available */
13788c2ecf20Sopenharmony_ci	setup.sw_ver_major = cpu_to_be32(ATH_SW_VER_MAJOR);
13798c2ecf20Sopenharmony_ci	setup.sw_ver_minor = cpu_to_be32(ATH_SW_VER_MINOR);
13808c2ecf20Sopenharmony_ci	setup.sw_ver_patch = cpu_to_be32(ATH_SW_VER_PATCH);
13818c2ecf20Sopenharmony_ci	setup.sw_ver_build = cpu_to_be32(ATH_SW_VER_BUILD);
13828c2ecf20Sopenharmony_ci	return ar5523_cmd_read(ar, WDCMSG_HOST_AVAILABLE,
13838c2ecf20Sopenharmony_ci			       &setup, sizeof(setup), NULL, 0, 0);
13848c2ecf20Sopenharmony_ci}
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_cistatic int ar5523_get_devstatus(struct ar5523 *ar)
13878c2ecf20Sopenharmony_ci{
13888c2ecf20Sopenharmony_ci	u8 macaddr[ETH_ALEN];
13898c2ecf20Sopenharmony_ci	int error;
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	/* retrieve MAC address */
13928c2ecf20Sopenharmony_ci	error = ar5523_get_status(ar, ST_MAC_ADDR, macaddr, ETH_ALEN);
13938c2ecf20Sopenharmony_ci	if (error) {
13948c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not read MAC address\n");
13958c2ecf20Sopenharmony_ci		return error;
13968c2ecf20Sopenharmony_ci	}
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	SET_IEEE80211_PERM_ADDR(ar->hw, macaddr);
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	error = ar5523_get_status(ar, ST_SERIAL_NUMBER,
14018c2ecf20Sopenharmony_ci	    &ar->serial[0], sizeof(ar->serial));
14028c2ecf20Sopenharmony_ci	if (error) {
14038c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not read device serial number\n");
14048c2ecf20Sopenharmony_ci		return error;
14058c2ecf20Sopenharmony_ci	}
14068c2ecf20Sopenharmony_ci	return 0;
14078c2ecf20Sopenharmony_ci}
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci#define AR5523_SANE_RXBUFSZ 2000
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_cistatic int ar5523_get_max_rxsz(struct ar5523 *ar)
14128c2ecf20Sopenharmony_ci{
14138c2ecf20Sopenharmony_ci	int error;
14148c2ecf20Sopenharmony_ci	__be32 rxsize;
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	/* Get max rx size */
14178c2ecf20Sopenharmony_ci	error = ar5523_get_status(ar, ST_WDC_TRANSPORT_CHUNK_SIZE, &rxsize,
14188c2ecf20Sopenharmony_ci				  sizeof(rxsize));
14198c2ecf20Sopenharmony_ci	if (error != 0) {
14208c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not read max RX size\n");
14218c2ecf20Sopenharmony_ci		return error;
14228c2ecf20Sopenharmony_ci	}
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	ar->rxbufsz = be32_to_cpu(rxsize);
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	if (!ar->rxbufsz || ar->rxbufsz > AR5523_SANE_RXBUFSZ) {
14278c2ecf20Sopenharmony_ci		ar5523_err(ar, "Bad rxbufsz from device. Using %d instead\n",
14288c2ecf20Sopenharmony_ci			   AR5523_SANE_RXBUFSZ);
14298c2ecf20Sopenharmony_ci		ar->rxbufsz = AR5523_SANE_RXBUFSZ;
14308c2ecf20Sopenharmony_ci	}
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "Max RX buf size: %d\n", ar->rxbufsz);
14338c2ecf20Sopenharmony_ci	return 0;
14348c2ecf20Sopenharmony_ci}
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci/*
14378c2ecf20Sopenharmony_ci * This is copied from rtl818x, but we should probably move this
14388c2ecf20Sopenharmony_ci * to common code as in OpenBSD.
14398c2ecf20Sopenharmony_ci */
14408c2ecf20Sopenharmony_cistatic const struct ieee80211_rate ar5523_rates[] = {
14418c2ecf20Sopenharmony_ci	{ .bitrate = 10, .hw_value = 2, },
14428c2ecf20Sopenharmony_ci	{ .bitrate = 20, .hw_value = 4 },
14438c2ecf20Sopenharmony_ci	{ .bitrate = 55, .hw_value = 11, },
14448c2ecf20Sopenharmony_ci	{ .bitrate = 110, .hw_value = 22, },
14458c2ecf20Sopenharmony_ci	{ .bitrate = 60, .hw_value = 12, },
14468c2ecf20Sopenharmony_ci	{ .bitrate = 90, .hw_value = 18, },
14478c2ecf20Sopenharmony_ci	{ .bitrate = 120, .hw_value = 24, },
14488c2ecf20Sopenharmony_ci	{ .bitrate = 180, .hw_value = 36, },
14498c2ecf20Sopenharmony_ci	{ .bitrate = 240, .hw_value = 48, },
14508c2ecf20Sopenharmony_ci	{ .bitrate = 360, .hw_value = 72, },
14518c2ecf20Sopenharmony_ci	{ .bitrate = 480, .hw_value = 96, },
14528c2ecf20Sopenharmony_ci	{ .bitrate = 540, .hw_value = 108, },
14538c2ecf20Sopenharmony_ci};
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_cistatic const struct ieee80211_channel ar5523_channels[] = {
14568c2ecf20Sopenharmony_ci	{ .center_freq = 2412 },
14578c2ecf20Sopenharmony_ci	{ .center_freq = 2417 },
14588c2ecf20Sopenharmony_ci	{ .center_freq = 2422 },
14598c2ecf20Sopenharmony_ci	{ .center_freq = 2427 },
14608c2ecf20Sopenharmony_ci	{ .center_freq = 2432 },
14618c2ecf20Sopenharmony_ci	{ .center_freq = 2437 },
14628c2ecf20Sopenharmony_ci	{ .center_freq = 2442 },
14638c2ecf20Sopenharmony_ci	{ .center_freq = 2447 },
14648c2ecf20Sopenharmony_ci	{ .center_freq = 2452 },
14658c2ecf20Sopenharmony_ci	{ .center_freq = 2457 },
14668c2ecf20Sopenharmony_ci	{ .center_freq = 2462 },
14678c2ecf20Sopenharmony_ci	{ .center_freq = 2467 },
14688c2ecf20Sopenharmony_ci	{ .center_freq = 2472 },
14698c2ecf20Sopenharmony_ci	{ .center_freq = 2484 },
14708c2ecf20Sopenharmony_ci};
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_cistatic int ar5523_init_modes(struct ar5523 *ar)
14738c2ecf20Sopenharmony_ci{
14748c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(ar->channels) != sizeof(ar5523_channels));
14758c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(ar->rates) != sizeof(ar5523_rates));
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	memcpy(ar->channels, ar5523_channels, sizeof(ar5523_channels));
14788c2ecf20Sopenharmony_ci	memcpy(ar->rates, ar5523_rates, sizeof(ar5523_rates));
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	ar->band.band = NL80211_BAND_2GHZ;
14818c2ecf20Sopenharmony_ci	ar->band.channels = ar->channels;
14828c2ecf20Sopenharmony_ci	ar->band.n_channels = ARRAY_SIZE(ar5523_channels);
14838c2ecf20Sopenharmony_ci	ar->band.bitrates = ar->rates;
14848c2ecf20Sopenharmony_ci	ar->band.n_bitrates = ARRAY_SIZE(ar5523_rates);
14858c2ecf20Sopenharmony_ci	ar->hw->wiphy->bands[NL80211_BAND_2GHZ] = &ar->band;
14868c2ecf20Sopenharmony_ci	return 0;
14878c2ecf20Sopenharmony_ci}
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci/*
14908c2ecf20Sopenharmony_ci * Load the MIPS R4000 microcode into the device.  Once the image is loaded,
14918c2ecf20Sopenharmony_ci * the device will detach itself from the bus and reattach later with a new
14928c2ecf20Sopenharmony_ci * product Id (a la ezusb).
14938c2ecf20Sopenharmony_ci */
14948c2ecf20Sopenharmony_cistatic int ar5523_load_firmware(struct usb_device *dev)
14958c2ecf20Sopenharmony_ci{
14968c2ecf20Sopenharmony_ci	struct ar5523_fwblock *txblock, *rxblock;
14978c2ecf20Sopenharmony_ci	const struct firmware *fw;
14988c2ecf20Sopenharmony_ci	void *fwbuf;
14998c2ecf20Sopenharmony_ci	int len, offset;
15008c2ecf20Sopenharmony_ci	int foolen; /* XXX(hch): handle short transfers */
15018c2ecf20Sopenharmony_ci	int error = -ENXIO;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	if (request_firmware(&fw, AR5523_FIRMWARE_FILE, &dev->dev)) {
15048c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "no firmware found: %s\n",
15058c2ecf20Sopenharmony_ci			AR5523_FIRMWARE_FILE);
15068c2ecf20Sopenharmony_ci		return -ENOENT;
15078c2ecf20Sopenharmony_ci	}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	txblock = kmalloc(sizeof(*txblock), GFP_KERNEL);
15108c2ecf20Sopenharmony_ci	if (!txblock)
15118c2ecf20Sopenharmony_ci		goto out;
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	rxblock = kmalloc(sizeof(*rxblock), GFP_KERNEL);
15148c2ecf20Sopenharmony_ci	if (!rxblock)
15158c2ecf20Sopenharmony_ci		goto out_free_txblock;
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	fwbuf = kmalloc(AR5523_MAX_FWBLOCK_SIZE, GFP_KERNEL);
15188c2ecf20Sopenharmony_ci	if (!fwbuf)
15198c2ecf20Sopenharmony_ci		goto out_free_rxblock;
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci	memset(txblock, 0, sizeof(struct ar5523_fwblock));
15228c2ecf20Sopenharmony_ci	txblock->flags = cpu_to_be32(AR5523_WRITE_BLOCK);
15238c2ecf20Sopenharmony_ci	txblock->total = cpu_to_be32(fw->size);
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	offset = 0;
15268c2ecf20Sopenharmony_ci	len = fw->size;
15278c2ecf20Sopenharmony_ci	while (len > 0) {
15288c2ecf20Sopenharmony_ci		int mlen = min(len, AR5523_MAX_FWBLOCK_SIZE);
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci		txblock->remain = cpu_to_be32(len - mlen);
15318c2ecf20Sopenharmony_ci		txblock->len = cpu_to_be32(mlen);
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci		/* send firmware block meta-data */
15348c2ecf20Sopenharmony_ci		error = usb_bulk_msg(dev, ar5523_cmd_tx_pipe(dev),
15358c2ecf20Sopenharmony_ci				     txblock, sizeof(*txblock), &foolen,
15368c2ecf20Sopenharmony_ci				     AR5523_CMD_TIMEOUT);
15378c2ecf20Sopenharmony_ci		if (error) {
15388c2ecf20Sopenharmony_ci			dev_err(&dev->dev,
15398c2ecf20Sopenharmony_ci				"could not send firmware block info\n");
15408c2ecf20Sopenharmony_ci			goto out_free_fwbuf;
15418c2ecf20Sopenharmony_ci		}
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci		/* send firmware block data */
15448c2ecf20Sopenharmony_ci		memcpy(fwbuf, fw->data + offset, mlen);
15458c2ecf20Sopenharmony_ci		error = usb_bulk_msg(dev, ar5523_data_tx_pipe(dev),
15468c2ecf20Sopenharmony_ci				     fwbuf, mlen, &foolen,
15478c2ecf20Sopenharmony_ci				     AR5523_DATA_TIMEOUT);
15488c2ecf20Sopenharmony_ci		if (error) {
15498c2ecf20Sopenharmony_ci			dev_err(&dev->dev,
15508c2ecf20Sopenharmony_ci				"could not send firmware block data\n");
15518c2ecf20Sopenharmony_ci			goto out_free_fwbuf;
15528c2ecf20Sopenharmony_ci		}
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci		/* wait for ack from firmware */
15558c2ecf20Sopenharmony_ci		error = usb_bulk_msg(dev, ar5523_cmd_rx_pipe(dev),
15568c2ecf20Sopenharmony_ci				     rxblock, sizeof(*rxblock), &foolen,
15578c2ecf20Sopenharmony_ci				     AR5523_CMD_TIMEOUT);
15588c2ecf20Sopenharmony_ci		if (error) {
15598c2ecf20Sopenharmony_ci			dev_err(&dev->dev,
15608c2ecf20Sopenharmony_ci				"could not read firmware answer\n");
15618c2ecf20Sopenharmony_ci			goto out_free_fwbuf;
15628c2ecf20Sopenharmony_ci		}
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci		len -= mlen;
15658c2ecf20Sopenharmony_ci		offset += mlen;
15668c2ecf20Sopenharmony_ci	}
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci	/*
15698c2ecf20Sopenharmony_ci	 * Set the error to -ENXIO to make sure we continue probing for
15708c2ecf20Sopenharmony_ci	 * a driver.
15718c2ecf20Sopenharmony_ci	 */
15728c2ecf20Sopenharmony_ci	error = -ENXIO;
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci out_free_fwbuf:
15758c2ecf20Sopenharmony_ci	kfree(fwbuf);
15768c2ecf20Sopenharmony_ci out_free_rxblock:
15778c2ecf20Sopenharmony_ci	kfree(rxblock);
15788c2ecf20Sopenharmony_ci out_free_txblock:
15798c2ecf20Sopenharmony_ci	kfree(txblock);
15808c2ecf20Sopenharmony_ci out:
15818c2ecf20Sopenharmony_ci	release_firmware(fw);
15828c2ecf20Sopenharmony_ci	return error;
15838c2ecf20Sopenharmony_ci}
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_cistatic int ar5523_probe(struct usb_interface *intf,
15868c2ecf20Sopenharmony_ci			const struct usb_device_id *id)
15878c2ecf20Sopenharmony_ci{
15888c2ecf20Sopenharmony_ci	struct usb_device *dev = interface_to_usbdev(intf);
15898c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw;
15908c2ecf20Sopenharmony_ci	struct ar5523 *ar;
15918c2ecf20Sopenharmony_ci	int error = -ENOMEM;
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	/*
15948c2ecf20Sopenharmony_ci	 * Load firmware if the device requires it.  This will return
15958c2ecf20Sopenharmony_ci	 * -ENXIO on success and we'll get called back afer the usb
15968c2ecf20Sopenharmony_ci	 * id changes to indicate that the firmware is present.
15978c2ecf20Sopenharmony_ci	 */
15988c2ecf20Sopenharmony_ci	if (id->driver_info & AR5523_FLAG_PRE_FIRMWARE)
15998c2ecf20Sopenharmony_ci		return ar5523_load_firmware(dev);
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci	hw = ieee80211_alloc_hw(sizeof(*ar), &ar5523_ops);
16038c2ecf20Sopenharmony_ci	if (!hw)
16048c2ecf20Sopenharmony_ci		goto out;
16058c2ecf20Sopenharmony_ci	SET_IEEE80211_DEV(hw, &intf->dev);
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	ar = hw->priv;
16088c2ecf20Sopenharmony_ci	ar->hw = hw;
16098c2ecf20Sopenharmony_ci	ar->dev = dev;
16108c2ecf20Sopenharmony_ci	mutex_init(&ar->mutex);
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&ar->stat_work, ar5523_stat_work);
16138c2ecf20Sopenharmony_ci	timer_setup(&ar->tx_wd_timer, ar5523_tx_wd_timer, 0);
16148c2ecf20Sopenharmony_ci	INIT_WORK(&ar->tx_wd_work, ar5523_tx_wd_work);
16158c2ecf20Sopenharmony_ci	INIT_WORK(&ar->tx_work, ar5523_tx_work);
16168c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ar->tx_queue_pending);
16178c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ar->tx_queue_submitted);
16188c2ecf20Sopenharmony_ci	spin_lock_init(&ar->tx_data_list_lock);
16198c2ecf20Sopenharmony_ci	atomic_set(&ar->tx_nr_total, 0);
16208c2ecf20Sopenharmony_ci	atomic_set(&ar->tx_nr_pending, 0);
16218c2ecf20Sopenharmony_ci	init_waitqueue_head(&ar->tx_flush_waitq);
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	atomic_set(&ar->rx_data_free_cnt, 0);
16248c2ecf20Sopenharmony_ci	INIT_WORK(&ar->rx_refill_work, ar5523_rx_refill_work);
16258c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ar->rx_data_free);
16268c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ar->rx_data_used);
16278c2ecf20Sopenharmony_ci	spin_lock_init(&ar->rx_data_list_lock);
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	ar->wq = create_singlethread_workqueue("ar5523");
16308c2ecf20Sopenharmony_ci	if (!ar->wq) {
16318c2ecf20Sopenharmony_ci		ar5523_err(ar, "Could not create wq\n");
16328c2ecf20Sopenharmony_ci		goto out_free_ar;
16338c2ecf20Sopenharmony_ci	}
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	error = ar5523_alloc_rx_bufs(ar);
16368c2ecf20Sopenharmony_ci	if (error) {
16378c2ecf20Sopenharmony_ci		ar5523_err(ar, "Could not allocate rx buffers\n");
16388c2ecf20Sopenharmony_ci		goto out_free_wq;
16398c2ecf20Sopenharmony_ci	}
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	error = ar5523_alloc_rx_cmd(ar);
16428c2ecf20Sopenharmony_ci	if (error) {
16438c2ecf20Sopenharmony_ci		ar5523_err(ar, "Could not allocate rx command buffers\n");
16448c2ecf20Sopenharmony_ci		goto out_free_rx_bufs;
16458c2ecf20Sopenharmony_ci	}
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	error = ar5523_alloc_tx_cmd(ar);
16488c2ecf20Sopenharmony_ci	if (error) {
16498c2ecf20Sopenharmony_ci		ar5523_err(ar, "Could not allocate tx command buffers\n");
16508c2ecf20Sopenharmony_ci		goto out_free_rx_cmd;
16518c2ecf20Sopenharmony_ci	}
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci	error = ar5523_submit_rx_cmd(ar);
16548c2ecf20Sopenharmony_ci	if (error) {
16558c2ecf20Sopenharmony_ci		ar5523_err(ar, "Failed to submit rx cmd\n");
16568c2ecf20Sopenharmony_ci		goto out_free_tx_cmd;
16578c2ecf20Sopenharmony_ci	}
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci	/*
16608c2ecf20Sopenharmony_ci	 * We're now ready to send/receive firmware commands.
16618c2ecf20Sopenharmony_ci	 */
16628c2ecf20Sopenharmony_ci	error = ar5523_host_available(ar);
16638c2ecf20Sopenharmony_ci	if (error) {
16648c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not initialize adapter\n");
16658c2ecf20Sopenharmony_ci		goto out_cancel_rx_cmd;
16668c2ecf20Sopenharmony_ci	}
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	error = ar5523_get_max_rxsz(ar);
16698c2ecf20Sopenharmony_ci	if (error) {
16708c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not get caps from adapter\n");
16718c2ecf20Sopenharmony_ci		goto out_cancel_rx_cmd;
16728c2ecf20Sopenharmony_ci	}
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	error = ar5523_get_devcap(ar);
16758c2ecf20Sopenharmony_ci	if (error) {
16768c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not get caps from adapter\n");
16778c2ecf20Sopenharmony_ci		goto out_cancel_rx_cmd;
16788c2ecf20Sopenharmony_ci	}
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	error = ar5523_get_devstatus(ar);
16818c2ecf20Sopenharmony_ci	if (error != 0) {
16828c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not get device status\n");
16838c2ecf20Sopenharmony_ci		goto out_cancel_rx_cmd;
16848c2ecf20Sopenharmony_ci	}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	ar5523_info(ar, "MAC/BBP AR5523, RF AR%c112\n",
16878c2ecf20Sopenharmony_ci			(id->driver_info & AR5523_FLAG_ABG) ? '5' : '2');
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	ar->vif = NULL;
16908c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, HAS_RATE_CONTROL);
16918c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, RX_INCLUDES_FCS);
16928c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, SIGNAL_DBM);
16938c2ecf20Sopenharmony_ci	hw->extra_tx_headroom = sizeof(struct ar5523_tx_desc) +
16948c2ecf20Sopenharmony_ci				sizeof(struct ar5523_chunk);
16958c2ecf20Sopenharmony_ci	hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
16968c2ecf20Sopenharmony_ci	hw->queues = 1;
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	error = ar5523_init_modes(ar);
16998c2ecf20Sopenharmony_ci	if (error)
17008c2ecf20Sopenharmony_ci		goto out_cancel_rx_cmd;
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, hw);
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci	error = ieee80211_register_hw(hw);
17078c2ecf20Sopenharmony_ci	if (error) {
17088c2ecf20Sopenharmony_ci		ar5523_err(ar, "could not register device\n");
17098c2ecf20Sopenharmony_ci		goto out_cancel_rx_cmd;
17108c2ecf20Sopenharmony_ci	}
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	ar5523_info(ar, "Found and initialized AR5523 device\n");
17138c2ecf20Sopenharmony_ci	return 0;
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ciout_cancel_rx_cmd:
17168c2ecf20Sopenharmony_ci	ar5523_cancel_rx_cmd(ar);
17178c2ecf20Sopenharmony_ciout_free_tx_cmd:
17188c2ecf20Sopenharmony_ci	ar5523_free_tx_cmd(ar);
17198c2ecf20Sopenharmony_ciout_free_rx_cmd:
17208c2ecf20Sopenharmony_ci	ar5523_free_rx_cmd(ar);
17218c2ecf20Sopenharmony_ciout_free_rx_bufs:
17228c2ecf20Sopenharmony_ci	ar5523_free_rx_bufs(ar);
17238c2ecf20Sopenharmony_ciout_free_wq:
17248c2ecf20Sopenharmony_ci	destroy_workqueue(ar->wq);
17258c2ecf20Sopenharmony_ciout_free_ar:
17268c2ecf20Sopenharmony_ci	ieee80211_free_hw(hw);
17278c2ecf20Sopenharmony_ciout:
17288c2ecf20Sopenharmony_ci	return error;
17298c2ecf20Sopenharmony_ci}
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_cistatic void ar5523_disconnect(struct usb_interface *intf)
17328c2ecf20Sopenharmony_ci{
17338c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = usb_get_intfdata(intf);
17348c2ecf20Sopenharmony_ci	struct ar5523 *ar = hw->priv;
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci	ar5523_dbg(ar, "detaching\n");
17378c2ecf20Sopenharmony_ci	set_bit(AR5523_USB_DISCONNECTED, &ar->flags);
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci	ieee80211_unregister_hw(hw);
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci	ar5523_cancel_rx_cmd(ar);
17428c2ecf20Sopenharmony_ci	ar5523_free_tx_cmd(ar);
17438c2ecf20Sopenharmony_ci	ar5523_free_rx_cmd(ar);
17448c2ecf20Sopenharmony_ci	ar5523_free_rx_bufs(ar);
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_ci	destroy_workqueue(ar->wq);
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_ci	ieee80211_free_hw(hw);
17498c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, NULL);
17508c2ecf20Sopenharmony_ci}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci#define AR5523_DEVICE_UG(vendor, device) \
17538c2ecf20Sopenharmony_ci	{ USB_DEVICE((vendor), (device)) }, \
17548c2ecf20Sopenharmony_ci	{ USB_DEVICE((vendor), (device) + 1), \
17558c2ecf20Sopenharmony_ci		.driver_info = AR5523_FLAG_PRE_FIRMWARE }
17568c2ecf20Sopenharmony_ci#define AR5523_DEVICE_UX(vendor, device) \
17578c2ecf20Sopenharmony_ci	{ USB_DEVICE((vendor), (device)), \
17588c2ecf20Sopenharmony_ci		.driver_info = AR5523_FLAG_ABG }, \
17598c2ecf20Sopenharmony_ci	{ USB_DEVICE((vendor), (device) + 1), \
17608c2ecf20Sopenharmony_ci		.driver_info = AR5523_FLAG_ABG|AR5523_FLAG_PRE_FIRMWARE }
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_cistatic const struct usb_device_id ar5523_id_table[] = {
17638c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x168c, 0x0001),	/* Atheros / AR5523 */
17648c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x0cf3, 0x0001),	/* Atheros2 / AR5523_1 */
17658c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x0cf3, 0x0003),	/* Atheros2 / AR5523_2 */
17668c2ecf20Sopenharmony_ci	AR5523_DEVICE_UX(0x0cf3, 0x0005),	/* Atheros2 / AR5523_3 */
17678c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x0d8e, 0x7801),	/* Conceptronic / AR5523_1 */
17688c2ecf20Sopenharmony_ci	AR5523_DEVICE_UX(0x0d8e, 0x7811),	/* Conceptronic / AR5523_2 */
17698c2ecf20Sopenharmony_ci	AR5523_DEVICE_UX(0x2001, 0x3a00),	/* Dlink / DWLAG132 */
17708c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x2001, 0x3a02),	/* Dlink / DWLG132 */
17718c2ecf20Sopenharmony_ci	AR5523_DEVICE_UX(0x2001, 0x3a04),	/* Dlink / DWLAG122 */
17728c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x07d1, 0x3a07),	/* D-Link / WUA-2340 rev A1 */
17738c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x1690, 0x0712),	/* Gigaset / AR5523 */
17748c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x1690, 0x0710),	/* Gigaset / SMCWUSBTG */
17758c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x129b, 0x160b),	/* Gigaset / USB stick 108
17768c2ecf20Sopenharmony_ci						   (CyberTAN Technology) */
17778c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x16ab, 0x7801),	/* Globalsun / AR5523_1 */
17788c2ecf20Sopenharmony_ci	AR5523_DEVICE_UX(0x16ab, 0x7811),	/* Globalsun / AR5523_2 */
17798c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x0d8e, 0x7802),	/* Globalsun / AR5523_3 */
17808c2ecf20Sopenharmony_ci	AR5523_DEVICE_UX(0x0846, 0x4300),	/* Netgear / WG111U */
17818c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x0846, 0x4250),	/* Netgear / WG111T */
17828c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x0846, 0x5f00),	/* Netgear / WPN111 */
17838c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x083a, 0x4506),	/* SMC / EZ Connect
17848c2ecf20Sopenharmony_ci						   SMCWUSBT-G2 */
17858c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x157e, 0x3006),	/* Umedia / AR5523_1 */
17868c2ecf20Sopenharmony_ci	AR5523_DEVICE_UX(0x157e, 0x3205),	/* Umedia / AR5523_2 */
17878c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x157e, 0x3006),	/* Umedia / TEW444UBEU */
17888c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x1435, 0x0826),	/* Wistronneweb / AR5523_1 */
17898c2ecf20Sopenharmony_ci	AR5523_DEVICE_UX(0x1435, 0x0828),	/* Wistronneweb / AR5523_2 */
17908c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x0cde, 0x0012),	/* Zcom / AR5523 */
17918c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x1385, 0x4250),	/* Netgear3 / WG111T (2) */
17928c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x1385, 0x5f00),	/* Netgear / WPN111 */
17938c2ecf20Sopenharmony_ci	AR5523_DEVICE_UG(0x1385, 0x5f02),	/* Netgear / WPN111 */
17948c2ecf20Sopenharmony_ci	{ }
17958c2ecf20Sopenharmony_ci};
17968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, ar5523_id_table);
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_cistatic struct usb_driver ar5523_driver = {
17998c2ecf20Sopenharmony_ci	.name		= "ar5523",
18008c2ecf20Sopenharmony_ci	.id_table	= ar5523_id_table,
18018c2ecf20Sopenharmony_ci	.probe		= ar5523_probe,
18028c2ecf20Sopenharmony_ci	.disconnect	= ar5523_disconnect,
18038c2ecf20Sopenharmony_ci};
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_cimodule_usb_driver(ar5523_driver);
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
18088c2ecf20Sopenharmony_ciMODULE_FIRMWARE(AR5523_FIRMWARE_FILE);
1809