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