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