18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Atheros CARL9170 driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * USB - frontend 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> 78c2ecf20Sopenharmony_ci * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 108c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 118c2ecf20Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or 128c2ecf20Sopenharmony_ci * (at your option) any later version. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 158c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 168c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 178c2ecf20Sopenharmony_ci * GNU General Public License for more details. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License 208c2ecf20Sopenharmony_ci * along with this program; see the file COPYING. If not, see 218c2ecf20Sopenharmony_ci * http://www.gnu.org/licenses/. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * This file incorporates work covered by the following copyright and 248c2ecf20Sopenharmony_ci * permission notice: 258c2ecf20Sopenharmony_ci * Copyright (c) 2007-2008 Atheros Communications, Inc. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 288c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 298c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 328c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 338c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 348c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 358c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 368c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 378c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include <linux/module.h> 418c2ecf20Sopenharmony_ci#include <linux/slab.h> 428c2ecf20Sopenharmony_ci#include <linux/usb.h> 438c2ecf20Sopenharmony_ci#include <linux/firmware.h> 448c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 458c2ecf20Sopenharmony_ci#include <linux/device.h> 468c2ecf20Sopenharmony_ci#include <net/mac80211.h> 478c2ecf20Sopenharmony_ci#include "carl9170.h" 488c2ecf20Sopenharmony_ci#include "cmd.h" 498c2ecf20Sopenharmony_ci#include "hw.h" 508c2ecf20Sopenharmony_ci#include "fwcmd.h" 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); 538c2ecf20Sopenharmony_ciMODULE_AUTHOR("Christian Lamparter <chunkeey@googlemail.com>"); 548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Atheros AR9170 802.11n USB wireless"); 568c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CARL9170FW_NAME); 578c2ecf20Sopenharmony_ciMODULE_ALIAS("ar9170usb"); 588c2ecf20Sopenharmony_ciMODULE_ALIAS("arusb_lnx"); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Note: 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * Always update our wiki's device list (located at: 648c2ecf20Sopenharmony_ci * https://wireless.wiki.kernel.org/en/users/Drivers/ar9170/devices ), 658c2ecf20Sopenharmony_ci * whenever you add a new device. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistatic const struct usb_device_id carl9170_usb_ids[] = { 688c2ecf20Sopenharmony_ci /* Atheros 9170 */ 698c2ecf20Sopenharmony_ci { USB_DEVICE(0x0cf3, 0x9170) }, 708c2ecf20Sopenharmony_ci /* Atheros TG121N */ 718c2ecf20Sopenharmony_ci { USB_DEVICE(0x0cf3, 0x1001) }, 728c2ecf20Sopenharmony_ci /* TP-Link TL-WN821N v2 */ 738c2ecf20Sopenharmony_ci { USB_DEVICE(0x0cf3, 0x1002), .driver_info = CARL9170_WPS_BUTTON | 748c2ecf20Sopenharmony_ci CARL9170_ONE_LED }, 758c2ecf20Sopenharmony_ci /* 3Com Dual Band 802.11n USB Adapter */ 768c2ecf20Sopenharmony_ci { USB_DEVICE(0x0cf3, 0x1010) }, 778c2ecf20Sopenharmony_ci /* H3C Dual Band 802.11n USB Adapter */ 788c2ecf20Sopenharmony_ci { USB_DEVICE(0x0cf3, 0x1011) }, 798c2ecf20Sopenharmony_ci /* Cace Airpcap NX */ 808c2ecf20Sopenharmony_ci { USB_DEVICE(0xcace, 0x0300) }, 818c2ecf20Sopenharmony_ci /* D-Link DWA 160 A1 */ 828c2ecf20Sopenharmony_ci { USB_DEVICE(0x07d1, 0x3c10) }, 838c2ecf20Sopenharmony_ci /* D-Link DWA 160 A2 */ 848c2ecf20Sopenharmony_ci { USB_DEVICE(0x07d1, 0x3a09) }, 858c2ecf20Sopenharmony_ci /* D-Link DWA 130 D */ 868c2ecf20Sopenharmony_ci { USB_DEVICE(0x07d1, 0x3a0f) }, 878c2ecf20Sopenharmony_ci /* Netgear WNA1000 */ 888c2ecf20Sopenharmony_ci { USB_DEVICE(0x0846, 0x9040) }, 898c2ecf20Sopenharmony_ci /* Netgear WNDA3100 (v1) */ 908c2ecf20Sopenharmony_ci { USB_DEVICE(0x0846, 0x9010) }, 918c2ecf20Sopenharmony_ci /* Netgear WN111 v2 */ 928c2ecf20Sopenharmony_ci { USB_DEVICE(0x0846, 0x9001), .driver_info = CARL9170_ONE_LED }, 938c2ecf20Sopenharmony_ci /* Zydas ZD1221 */ 948c2ecf20Sopenharmony_ci { USB_DEVICE(0x0ace, 0x1221) }, 958c2ecf20Sopenharmony_ci /* Proxim ORiNOCO 802.11n USB */ 968c2ecf20Sopenharmony_ci { USB_DEVICE(0x1435, 0x0804) }, 978c2ecf20Sopenharmony_ci /* WNC Generic 11n USB Dongle */ 988c2ecf20Sopenharmony_ci { USB_DEVICE(0x1435, 0x0326) }, 998c2ecf20Sopenharmony_ci /* ZyXEL NWD271N */ 1008c2ecf20Sopenharmony_ci { USB_DEVICE(0x0586, 0x3417) }, 1018c2ecf20Sopenharmony_ci /* Z-Com UB81 BG */ 1028c2ecf20Sopenharmony_ci { USB_DEVICE(0x0cde, 0x0023) }, 1038c2ecf20Sopenharmony_ci /* Z-Com UB82 ABG */ 1048c2ecf20Sopenharmony_ci { USB_DEVICE(0x0cde, 0x0026) }, 1058c2ecf20Sopenharmony_ci /* Sphairon Homelink 1202 */ 1068c2ecf20Sopenharmony_ci { USB_DEVICE(0x0cde, 0x0027) }, 1078c2ecf20Sopenharmony_ci /* Arcadyan WN7512 */ 1088c2ecf20Sopenharmony_ci { USB_DEVICE(0x083a, 0xf522) }, 1098c2ecf20Sopenharmony_ci /* Planex GWUS300 */ 1108c2ecf20Sopenharmony_ci { USB_DEVICE(0x2019, 0x5304) }, 1118c2ecf20Sopenharmony_ci /* IO-Data WNGDNUS2 */ 1128c2ecf20Sopenharmony_ci { USB_DEVICE(0x04bb, 0x093f) }, 1138c2ecf20Sopenharmony_ci /* NEC WL300NU-G */ 1148c2ecf20Sopenharmony_ci { USB_DEVICE(0x0409, 0x0249) }, 1158c2ecf20Sopenharmony_ci /* NEC WL300NU-AG */ 1168c2ecf20Sopenharmony_ci { USB_DEVICE(0x0409, 0x02b4) }, 1178c2ecf20Sopenharmony_ci /* AVM FRITZ!WLAN USB Stick N */ 1188c2ecf20Sopenharmony_ci { USB_DEVICE(0x057c, 0x8401) }, 1198c2ecf20Sopenharmony_ci /* AVM FRITZ!WLAN USB Stick N 2.4 */ 1208c2ecf20Sopenharmony_ci { USB_DEVICE(0x057c, 0x8402) }, 1218c2ecf20Sopenharmony_ci /* Qwest/Actiontec 802AIN Wireless N USB Network Adapter */ 1228c2ecf20Sopenharmony_ci { USB_DEVICE(0x1668, 0x1200) }, 1238c2ecf20Sopenharmony_ci /* Airlive X.USB a/b/g/n */ 1248c2ecf20Sopenharmony_ci { USB_DEVICE(0x1b75, 0x9170) }, 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* terminate */ 1278c2ecf20Sopenharmony_ci {} 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, carl9170_usb_ids); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic struct usb_driver carl9170_driver; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void carl9170_usb_submit_data_urb(struct ar9170 *ar) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct urb *urb; 1368c2ecf20Sopenharmony_ci int err; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (atomic_inc_return(&ar->tx_anch_urbs) > AR9170_NUM_TX_URBS) 1398c2ecf20Sopenharmony_ci goto err_acc; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci urb = usb_get_from_anchor(&ar->tx_wait); 1428c2ecf20Sopenharmony_ci if (!urb) 1438c2ecf20Sopenharmony_ci goto err_acc; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->tx_anch); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci err = usb_submit_urb(urb, GFP_ATOMIC); 1488c2ecf20Sopenharmony_ci if (unlikely(err)) { 1498c2ecf20Sopenharmony_ci if (net_ratelimit()) { 1508c2ecf20Sopenharmony_ci dev_err(&ar->udev->dev, "tx submit failed (%d)\n", 1518c2ecf20Sopenharmony_ci urb->status); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci usb_unanchor_urb(urb); 1558c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->tx_err); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci usb_free_urb(urb); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (likely(err == 0)) 1618c2ecf20Sopenharmony_ci return; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cierr_acc: 1648c2ecf20Sopenharmony_ci atomic_dec(&ar->tx_anch_urbs); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void carl9170_usb_tx_data_complete(struct urb *urb) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct ar9170 *ar = usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!ar)) { 1728c2ecf20Sopenharmony_ci dev_kfree_skb_irq(urb->context); 1738c2ecf20Sopenharmony_ci return; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci atomic_dec(&ar->tx_anch_urbs); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci switch (urb->status) { 1798c2ecf20Sopenharmony_ci /* everything is fine */ 1808c2ecf20Sopenharmony_ci case 0: 1818c2ecf20Sopenharmony_ci carl9170_tx_callback(ar, (void *)urb->context); 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* disconnect */ 1858c2ecf20Sopenharmony_ci case -ENOENT: 1868c2ecf20Sopenharmony_ci case -ECONNRESET: 1878c2ecf20Sopenharmony_ci case -ENODEV: 1888c2ecf20Sopenharmony_ci case -ESHUTDOWN: 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * Defer the frame clean-up to the tasklet worker. 1918c2ecf20Sopenharmony_ci * This is necessary, because carl9170_tx_drop 1928c2ecf20Sopenharmony_ci * does not work in an irqsave context. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->tx_err); 1958c2ecf20Sopenharmony_ci return; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* a random transmission error has occurred? */ 1988c2ecf20Sopenharmony_ci default: 1998c2ecf20Sopenharmony_ci if (net_ratelimit()) { 2008c2ecf20Sopenharmony_ci dev_err(&ar->udev->dev, "tx failed (%d)\n", 2018c2ecf20Sopenharmony_ci urb->status); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->tx_err); 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (likely(IS_STARTED(ar))) 2098c2ecf20Sopenharmony_ci carl9170_usb_submit_data_urb(ar); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int carl9170_usb_submit_cmd_urb(struct ar9170 *ar) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct urb *urb; 2158c2ecf20Sopenharmony_ci int err; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (atomic_inc_return(&ar->tx_cmd_urbs) != 1) { 2188c2ecf20Sopenharmony_ci atomic_dec(&ar->tx_cmd_urbs); 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci urb = usb_get_from_anchor(&ar->tx_cmd); 2238c2ecf20Sopenharmony_ci if (!urb) { 2248c2ecf20Sopenharmony_ci atomic_dec(&ar->tx_cmd_urbs); 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->tx_anch); 2298c2ecf20Sopenharmony_ci err = usb_submit_urb(urb, GFP_ATOMIC); 2308c2ecf20Sopenharmony_ci if (unlikely(err)) { 2318c2ecf20Sopenharmony_ci usb_unanchor_urb(urb); 2328c2ecf20Sopenharmony_ci atomic_dec(&ar->tx_cmd_urbs); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci usb_free_urb(urb); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return err; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic void carl9170_usb_cmd_complete(struct urb *urb) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct ar9170 *ar = urb->context; 2428c2ecf20Sopenharmony_ci int err = 0; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!ar)) 2458c2ecf20Sopenharmony_ci return; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci atomic_dec(&ar->tx_cmd_urbs); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci switch (urb->status) { 2508c2ecf20Sopenharmony_ci /* everything is fine */ 2518c2ecf20Sopenharmony_ci case 0: 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* disconnect */ 2558c2ecf20Sopenharmony_ci case -ENOENT: 2568c2ecf20Sopenharmony_ci case -ECONNRESET: 2578c2ecf20Sopenharmony_ci case -ENODEV: 2588c2ecf20Sopenharmony_ci case -ESHUTDOWN: 2598c2ecf20Sopenharmony_ci return; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci default: 2628c2ecf20Sopenharmony_ci err = urb->status; 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (!IS_INITIALIZED(ar)) 2678c2ecf20Sopenharmony_ci return; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (err) 2708c2ecf20Sopenharmony_ci dev_err(&ar->udev->dev, "submit cmd cb failed (%d).\n", err); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci err = carl9170_usb_submit_cmd_urb(ar); 2738c2ecf20Sopenharmony_ci if (err) 2748c2ecf20Sopenharmony_ci dev_err(&ar->udev->dev, "submit cmd failed (%d).\n", err); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic void carl9170_usb_rx_irq_complete(struct urb *urb) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct ar9170 *ar = urb->context; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!ar)) 2828c2ecf20Sopenharmony_ci return; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci switch (urb->status) { 2858c2ecf20Sopenharmony_ci /* everything is fine */ 2868c2ecf20Sopenharmony_ci case 0: 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* disconnect */ 2908c2ecf20Sopenharmony_ci case -ENOENT: 2918c2ecf20Sopenharmony_ci case -ECONNRESET: 2928c2ecf20Sopenharmony_ci case -ENODEV: 2938c2ecf20Sopenharmony_ci case -ESHUTDOWN: 2948c2ecf20Sopenharmony_ci return; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci default: 2978c2ecf20Sopenharmony_ci goto resubmit; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* 3018c2ecf20Sopenharmony_ci * While the carl9170 firmware does not use this EP, the 3028c2ecf20Sopenharmony_ci * firmware loader in the EEPROM unfortunately does. 3038c2ecf20Sopenharmony_ci * Therefore we need to be ready to handle out-of-band 3048c2ecf20Sopenharmony_ci * responses and traps in case the firmware crashed and 3058c2ecf20Sopenharmony_ci * the loader took over again. 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_ci carl9170_handle_command_response(ar, urb->transfer_buffer, 3088c2ecf20Sopenharmony_ci urb->actual_length); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ciresubmit: 3118c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->rx_anch); 3128c2ecf20Sopenharmony_ci if (unlikely(usb_submit_urb(urb, GFP_ATOMIC))) 3138c2ecf20Sopenharmony_ci usb_unanchor_urb(urb); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int carl9170_usb_submit_rx_urb(struct ar9170 *ar, gfp_t gfp) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct urb *urb; 3198c2ecf20Sopenharmony_ci int err = 0, runs = 0; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci while ((atomic_read(&ar->rx_anch_urbs) < AR9170_NUM_RX_URBS) && 3228c2ecf20Sopenharmony_ci (runs++ < AR9170_NUM_RX_URBS)) { 3238c2ecf20Sopenharmony_ci err = -ENOSPC; 3248c2ecf20Sopenharmony_ci urb = usb_get_from_anchor(&ar->rx_pool); 3258c2ecf20Sopenharmony_ci if (urb) { 3268c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->rx_anch); 3278c2ecf20Sopenharmony_ci err = usb_submit_urb(urb, gfp); 3288c2ecf20Sopenharmony_ci if (unlikely(err)) { 3298c2ecf20Sopenharmony_ci usb_unanchor_urb(urb); 3308c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->rx_pool); 3318c2ecf20Sopenharmony_ci } else { 3328c2ecf20Sopenharmony_ci atomic_dec(&ar->rx_pool_urbs); 3338c2ecf20Sopenharmony_ci atomic_inc(&ar->rx_anch_urbs); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci usb_free_urb(urb); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return err; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic void carl9170_usb_rx_work(struct ar9170 *ar) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct urb *urb; 3458c2ecf20Sopenharmony_ci int i; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci for (i = 0; i < AR9170_NUM_RX_URBS_POOL; i++) { 3488c2ecf20Sopenharmony_ci urb = usb_get_from_anchor(&ar->rx_work); 3498c2ecf20Sopenharmony_ci if (!urb) 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci atomic_dec(&ar->rx_work_urbs); 3538c2ecf20Sopenharmony_ci if (IS_INITIALIZED(ar)) { 3548c2ecf20Sopenharmony_ci carl9170_rx(ar, urb->transfer_buffer, 3558c2ecf20Sopenharmony_ci urb->actual_length); 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->rx_pool); 3598c2ecf20Sopenharmony_ci atomic_inc(&ar->rx_pool_urbs); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci usb_free_urb(urb); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci carl9170_usb_submit_rx_urb(ar, GFP_ATOMIC); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_civoid carl9170_usb_handle_tx_err(struct ar9170 *ar) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct urb *urb; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci while ((urb = usb_get_from_anchor(&ar->tx_err))) { 3728c2ecf20Sopenharmony_ci struct sk_buff *skb = (void *)urb->context; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci carl9170_tx_drop(ar, skb); 3758c2ecf20Sopenharmony_ci carl9170_tx_callback(ar, skb); 3768c2ecf20Sopenharmony_ci usb_free_urb(urb); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic void carl9170_usb_tasklet(struct tasklet_struct *t) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct ar9170 *ar = from_tasklet(ar, t, usb_tasklet); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (!IS_INITIALIZED(ar)) 3858c2ecf20Sopenharmony_ci return; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci carl9170_usb_rx_work(ar); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* 3908c2ecf20Sopenharmony_ci * Strictly speaking: The tx scheduler is not part of the USB system. 3918c2ecf20Sopenharmony_ci * But the rx worker returns frames back to the mac80211-stack and 3928c2ecf20Sopenharmony_ci * this is the _perfect_ place to generate the next transmissions. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci if (IS_STARTED(ar)) 3958c2ecf20Sopenharmony_ci carl9170_tx_scheduler(ar); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic void carl9170_usb_rx_complete(struct urb *urb) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct ar9170 *ar = (struct ar9170 *)urb->context; 4018c2ecf20Sopenharmony_ci int err; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!ar)) 4048c2ecf20Sopenharmony_ci return; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci atomic_dec(&ar->rx_anch_urbs); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci switch (urb->status) { 4098c2ecf20Sopenharmony_ci case 0: 4108c2ecf20Sopenharmony_ci /* rx path */ 4118c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->rx_work); 4128c2ecf20Sopenharmony_ci atomic_inc(&ar->rx_work_urbs); 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci case -ENOENT: 4168c2ecf20Sopenharmony_ci case -ECONNRESET: 4178c2ecf20Sopenharmony_ci case -ENODEV: 4188c2ecf20Sopenharmony_ci case -ESHUTDOWN: 4198c2ecf20Sopenharmony_ci /* handle disconnect events*/ 4208c2ecf20Sopenharmony_ci return; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci default: 4238c2ecf20Sopenharmony_ci /* handle all other errors */ 4248c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->rx_pool); 4258c2ecf20Sopenharmony_ci atomic_inc(&ar->rx_pool_urbs); 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci err = carl9170_usb_submit_rx_urb(ar, GFP_ATOMIC); 4308c2ecf20Sopenharmony_ci if (unlikely(err)) { 4318c2ecf20Sopenharmony_ci /* 4328c2ecf20Sopenharmony_ci * usb_submit_rx_urb reported a problem. 4338c2ecf20Sopenharmony_ci * In case this is due to a rx buffer shortage, 4348c2ecf20Sopenharmony_ci * elevate the tasklet worker priority to 4358c2ecf20Sopenharmony_ci * the highest available level. 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_ci tasklet_hi_schedule(&ar->usb_tasklet); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (atomic_read(&ar->rx_anch_urbs) == 0) { 4408c2ecf20Sopenharmony_ci /* 4418c2ecf20Sopenharmony_ci * The system is too slow to cope with 4428c2ecf20Sopenharmony_ci * the enormous workload. We have simply 4438c2ecf20Sopenharmony_ci * run out of active rx urbs and this 4448c2ecf20Sopenharmony_ci * unfortunately leads to an unpredictable 4458c2ecf20Sopenharmony_ci * device. 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ieee80211_queue_work(ar->hw, &ar->ping_work); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci } else { 4518c2ecf20Sopenharmony_ci /* 4528c2ecf20Sopenharmony_ci * Using anything less than _high_ priority absolutely 4538c2ecf20Sopenharmony_ci * kills the rx performance my UP-System... 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_ci tasklet_hi_schedule(&ar->usb_tasklet); 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic struct urb *carl9170_usb_alloc_rx_urb(struct ar9170 *ar, gfp_t gfp) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct urb *urb; 4628c2ecf20Sopenharmony_ci void *buf; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci buf = kmalloc(ar->fw.rx_size, gfp); 4658c2ecf20Sopenharmony_ci if (!buf) 4668c2ecf20Sopenharmony_ci return NULL; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci urb = usb_alloc_urb(0, gfp); 4698c2ecf20Sopenharmony_ci if (!urb) { 4708c2ecf20Sopenharmony_ci kfree(buf); 4718c2ecf20Sopenharmony_ci return NULL; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, ar->udev, usb_rcvbulkpipe(ar->udev, 4758c2ecf20Sopenharmony_ci AR9170_USB_EP_RX), buf, ar->fw.rx_size, 4768c2ecf20Sopenharmony_ci carl9170_usb_rx_complete, ar); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci urb->transfer_flags |= URB_FREE_BUFFER; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci return urb; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic int carl9170_usb_send_rx_irq_urb(struct ar9170 *ar) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct urb *urb = NULL; 4868c2ecf20Sopenharmony_ci void *ibuf; 4878c2ecf20Sopenharmony_ci int err = -ENOMEM; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci urb = usb_alloc_urb(0, GFP_KERNEL); 4908c2ecf20Sopenharmony_ci if (!urb) 4918c2ecf20Sopenharmony_ci goto out; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci ibuf = kmalloc(AR9170_USB_EP_CTRL_MAX, GFP_KERNEL); 4948c2ecf20Sopenharmony_ci if (!ibuf) 4958c2ecf20Sopenharmony_ci goto out; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci usb_fill_int_urb(urb, ar->udev, usb_rcvintpipe(ar->udev, 4988c2ecf20Sopenharmony_ci AR9170_USB_EP_IRQ), ibuf, AR9170_USB_EP_CTRL_MAX, 4998c2ecf20Sopenharmony_ci carl9170_usb_rx_irq_complete, ar, 1); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci urb->transfer_flags |= URB_FREE_BUFFER; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->rx_anch); 5048c2ecf20Sopenharmony_ci err = usb_submit_urb(urb, GFP_KERNEL); 5058c2ecf20Sopenharmony_ci if (err) 5068c2ecf20Sopenharmony_ci usb_unanchor_urb(urb); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ciout: 5098c2ecf20Sopenharmony_ci usb_free_urb(urb); 5108c2ecf20Sopenharmony_ci return err; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic int carl9170_usb_init_rx_bulk_urbs(struct ar9170 *ar) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct urb *urb; 5168c2ecf20Sopenharmony_ci int i, err = -EINVAL; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* 5198c2ecf20Sopenharmony_ci * The driver actively maintains a second shadow 5208c2ecf20Sopenharmony_ci * pool for inactive, but fully-prepared rx urbs. 5218c2ecf20Sopenharmony_ci * 5228c2ecf20Sopenharmony_ci * The pool should help the driver to master huge 5238c2ecf20Sopenharmony_ci * workload spikes without running the risk of 5248c2ecf20Sopenharmony_ci * undersupplying the hardware or wasting time by 5258c2ecf20Sopenharmony_ci * processing rx data (streams) inside the urb 5268c2ecf20Sopenharmony_ci * completion (hardirq context). 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_ci for (i = 0; i < AR9170_NUM_RX_URBS_POOL; i++) { 5298c2ecf20Sopenharmony_ci urb = carl9170_usb_alloc_rx_urb(ar, GFP_KERNEL); 5308c2ecf20Sopenharmony_ci if (!urb) { 5318c2ecf20Sopenharmony_ci err = -ENOMEM; 5328c2ecf20Sopenharmony_ci goto err_out; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->rx_pool); 5368c2ecf20Sopenharmony_ci atomic_inc(&ar->rx_pool_urbs); 5378c2ecf20Sopenharmony_ci usb_free_urb(urb); 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci err = carl9170_usb_submit_rx_urb(ar, GFP_KERNEL); 5418c2ecf20Sopenharmony_ci if (err) 5428c2ecf20Sopenharmony_ci goto err_out; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* the device now waiting for the firmware. */ 5458c2ecf20Sopenharmony_ci carl9170_set_state_when(ar, CARL9170_STOPPED, CARL9170_IDLE); 5468c2ecf20Sopenharmony_ci return 0; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cierr_out: 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci usb_scuttle_anchored_urbs(&ar->rx_pool); 5518c2ecf20Sopenharmony_ci usb_scuttle_anchored_urbs(&ar->rx_work); 5528c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&ar->rx_anch); 5538c2ecf20Sopenharmony_ci return err; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic int carl9170_usb_flush(struct ar9170 *ar) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci struct urb *urb; 5598c2ecf20Sopenharmony_ci int ret, err = 0; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci while ((urb = usb_get_from_anchor(&ar->tx_wait))) { 5628c2ecf20Sopenharmony_ci struct sk_buff *skb = (void *)urb->context; 5638c2ecf20Sopenharmony_ci carl9170_tx_drop(ar, skb); 5648c2ecf20Sopenharmony_ci carl9170_tx_callback(ar, skb); 5658c2ecf20Sopenharmony_ci usb_free_urb(urb); 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci ret = usb_wait_anchor_empty_timeout(&ar->tx_cmd, 1000); 5698c2ecf20Sopenharmony_ci if (ret == 0) 5708c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* lets wait a while until the tx - queues are dried out */ 5738c2ecf20Sopenharmony_ci ret = usb_wait_anchor_empty_timeout(&ar->tx_anch, 1000); 5748c2ecf20Sopenharmony_ci if (ret == 0) 5758c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&ar->tx_anch); 5788c2ecf20Sopenharmony_ci carl9170_usb_handle_tx_err(ar); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return err; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic void carl9170_usb_cancel_urbs(struct ar9170 *ar) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci int err; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci carl9170_set_state(ar, CARL9170_UNKNOWN_STATE); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci err = carl9170_usb_flush(ar); 5908c2ecf20Sopenharmony_ci if (err) 5918c2ecf20Sopenharmony_ci dev_err(&ar->udev->dev, "stuck tx urbs!\n"); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci usb_poison_anchored_urbs(&ar->tx_anch); 5948c2ecf20Sopenharmony_ci carl9170_usb_handle_tx_err(ar); 5958c2ecf20Sopenharmony_ci usb_poison_anchored_urbs(&ar->rx_anch); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci tasklet_kill(&ar->usb_tasklet); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci usb_scuttle_anchored_urbs(&ar->rx_work); 6008c2ecf20Sopenharmony_ci usb_scuttle_anchored_urbs(&ar->rx_pool); 6018c2ecf20Sopenharmony_ci usb_scuttle_anchored_urbs(&ar->tx_cmd); 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ciint __carl9170_exec_cmd(struct ar9170 *ar, struct carl9170_cmd *cmd, 6058c2ecf20Sopenharmony_ci const bool free_buf) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct urb *urb; 6088c2ecf20Sopenharmony_ci int err = 0; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (!IS_INITIALIZED(ar)) { 6118c2ecf20Sopenharmony_ci err = -EPERM; 6128c2ecf20Sopenharmony_ci goto err_free; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (WARN_ON(cmd->hdr.len > CARL9170_MAX_CMD_LEN - 4)) { 6168c2ecf20Sopenharmony_ci err = -EINVAL; 6178c2ecf20Sopenharmony_ci goto err_free; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci urb = usb_alloc_urb(0, GFP_ATOMIC); 6218c2ecf20Sopenharmony_ci if (!urb) { 6228c2ecf20Sopenharmony_ci err = -ENOMEM; 6238c2ecf20Sopenharmony_ci goto err_free; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (ar->usb_ep_cmd_is_bulk) 6278c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, ar->udev, 6288c2ecf20Sopenharmony_ci usb_sndbulkpipe(ar->udev, AR9170_USB_EP_CMD), 6298c2ecf20Sopenharmony_ci cmd, cmd->hdr.len + 4, 6308c2ecf20Sopenharmony_ci carl9170_usb_cmd_complete, ar); 6318c2ecf20Sopenharmony_ci else 6328c2ecf20Sopenharmony_ci usb_fill_int_urb(urb, ar->udev, 6338c2ecf20Sopenharmony_ci usb_sndintpipe(ar->udev, AR9170_USB_EP_CMD), 6348c2ecf20Sopenharmony_ci cmd, cmd->hdr.len + 4, 6358c2ecf20Sopenharmony_ci carl9170_usb_cmd_complete, ar, 1); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if (free_buf) 6388c2ecf20Sopenharmony_ci urb->transfer_flags |= URB_FREE_BUFFER; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->tx_cmd); 6418c2ecf20Sopenharmony_ci usb_free_urb(urb); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return carl9170_usb_submit_cmd_urb(ar); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cierr_free: 6468c2ecf20Sopenharmony_ci if (free_buf) 6478c2ecf20Sopenharmony_ci kfree(cmd); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci return err; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ciint carl9170_exec_cmd(struct ar9170 *ar, const enum carl9170_cmd_oids cmd, 6538c2ecf20Sopenharmony_ci unsigned int plen, void *payload, unsigned int outlen, void *out) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci int err = -ENOMEM; 6568c2ecf20Sopenharmony_ci unsigned long time_left; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (!IS_ACCEPTING_CMD(ar)) 6598c2ecf20Sopenharmony_ci return -EIO; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (!(cmd & CARL9170_CMD_ASYNC_FLAG)) 6628c2ecf20Sopenharmony_ci might_sleep(); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci ar->cmd.hdr.len = plen; 6658c2ecf20Sopenharmony_ci ar->cmd.hdr.cmd = cmd; 6668c2ecf20Sopenharmony_ci /* writing multiple regs fills this buffer already */ 6678c2ecf20Sopenharmony_ci if (plen && payload != (u8 *)(ar->cmd.data)) 6688c2ecf20Sopenharmony_ci memcpy(ar->cmd.data, payload, plen); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci spin_lock_bh(&ar->cmd_lock); 6718c2ecf20Sopenharmony_ci ar->readbuf = (u8 *)out; 6728c2ecf20Sopenharmony_ci ar->readlen = outlen; 6738c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->cmd_lock); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci reinit_completion(&ar->cmd_wait); 6768c2ecf20Sopenharmony_ci err = __carl9170_exec_cmd(ar, &ar->cmd, false); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (!(cmd & CARL9170_CMD_ASYNC_FLAG)) { 6798c2ecf20Sopenharmony_ci time_left = wait_for_completion_timeout(&ar->cmd_wait, HZ); 6808c2ecf20Sopenharmony_ci if (time_left == 0) { 6818c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 6828c2ecf20Sopenharmony_ci goto err_unbuf; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (ar->readlen != outlen) { 6868c2ecf20Sopenharmony_ci err = -EMSGSIZE; 6878c2ecf20Sopenharmony_ci goto err_unbuf; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci return 0; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cierr_unbuf: 6948c2ecf20Sopenharmony_ci /* Maybe the device was removed in the moment we were waiting? */ 6958c2ecf20Sopenharmony_ci if (IS_STARTED(ar)) { 6968c2ecf20Sopenharmony_ci dev_err(&ar->udev->dev, "no command feedback " 6978c2ecf20Sopenharmony_ci "received (%d).\n", err); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci /* provide some maybe useful debug information */ 7008c2ecf20Sopenharmony_ci print_hex_dump_bytes("carl9170 cmd: ", DUMP_PREFIX_NONE, 7018c2ecf20Sopenharmony_ci &ar->cmd, plen + 4); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci carl9170_restart(ar, CARL9170_RR_COMMAND_TIMEOUT); 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* invalidate to avoid completing the next command prematurely */ 7078c2ecf20Sopenharmony_ci spin_lock_bh(&ar->cmd_lock); 7088c2ecf20Sopenharmony_ci ar->readbuf = NULL; 7098c2ecf20Sopenharmony_ci ar->readlen = 0; 7108c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->cmd_lock); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci return err; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_civoid carl9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci struct urb *urb; 7188c2ecf20Sopenharmony_ci struct ar9170_stream *tx_stream; 7198c2ecf20Sopenharmony_ci void *data; 7208c2ecf20Sopenharmony_ci unsigned int len; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (!IS_STARTED(ar)) 7238c2ecf20Sopenharmony_ci goto err_drop; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci urb = usb_alloc_urb(0, GFP_ATOMIC); 7268c2ecf20Sopenharmony_ci if (!urb) 7278c2ecf20Sopenharmony_ci goto err_drop; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (ar->fw.tx_stream) { 7308c2ecf20Sopenharmony_ci tx_stream = (void *) (skb->data - sizeof(*tx_stream)); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci len = skb->len + sizeof(*tx_stream); 7338c2ecf20Sopenharmony_ci tx_stream->length = cpu_to_le16(len); 7348c2ecf20Sopenharmony_ci tx_stream->tag = cpu_to_le16(AR9170_TX_STREAM_TAG); 7358c2ecf20Sopenharmony_ci data = tx_stream; 7368c2ecf20Sopenharmony_ci } else { 7378c2ecf20Sopenharmony_ci data = skb->data; 7388c2ecf20Sopenharmony_ci len = skb->len; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, ar->udev, usb_sndbulkpipe(ar->udev, 7428c2ecf20Sopenharmony_ci AR9170_USB_EP_TX), data, len, 7438c2ecf20Sopenharmony_ci carl9170_usb_tx_data_complete, skb); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci urb->transfer_flags |= URB_ZERO_PACKET; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &ar->tx_wait); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci usb_free_urb(urb); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci carl9170_usb_submit_data_urb(ar); 7528c2ecf20Sopenharmony_ci return; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cierr_drop: 7558c2ecf20Sopenharmony_ci carl9170_tx_drop(ar, skb); 7568c2ecf20Sopenharmony_ci carl9170_tx_callback(ar, skb); 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic void carl9170_release_firmware(struct ar9170 *ar) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci if (ar->fw.fw) { 7628c2ecf20Sopenharmony_ci release_firmware(ar->fw.fw); 7638c2ecf20Sopenharmony_ci memset(&ar->fw, 0, sizeof(ar->fw)); 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_civoid carl9170_usb_stop(struct ar9170 *ar) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci int ret; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci carl9170_set_state_when(ar, CARL9170_IDLE, CARL9170_STOPPED); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci ret = carl9170_usb_flush(ar); 7748c2ecf20Sopenharmony_ci if (ret) 7758c2ecf20Sopenharmony_ci dev_err(&ar->udev->dev, "kill pending tx urbs.\n"); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci usb_poison_anchored_urbs(&ar->tx_anch); 7788c2ecf20Sopenharmony_ci carl9170_usb_handle_tx_err(ar); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* kill any pending command */ 7818c2ecf20Sopenharmony_ci spin_lock_bh(&ar->cmd_lock); 7828c2ecf20Sopenharmony_ci ar->readlen = 0; 7838c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->cmd_lock); 7848c2ecf20Sopenharmony_ci complete(&ar->cmd_wait); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci /* 7878c2ecf20Sopenharmony_ci * Note: 7888c2ecf20Sopenharmony_ci * So far we freed all tx urbs, but we won't dare to touch any rx urbs. 7898c2ecf20Sopenharmony_ci * Else we would end up with a unresponsive device... 7908c2ecf20Sopenharmony_ci */ 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ciint carl9170_usb_open(struct ar9170 *ar) 7948c2ecf20Sopenharmony_ci{ 7958c2ecf20Sopenharmony_ci usb_unpoison_anchored_urbs(&ar->tx_anch); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci carl9170_set_state_when(ar, CARL9170_STOPPED, CARL9170_IDLE); 7988c2ecf20Sopenharmony_ci return 0; 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic int carl9170_usb_load_firmware(struct ar9170 *ar) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci const u8 *data; 8048c2ecf20Sopenharmony_ci u8 *buf; 8058c2ecf20Sopenharmony_ci unsigned int transfer; 8068c2ecf20Sopenharmony_ci size_t len; 8078c2ecf20Sopenharmony_ci u32 addr; 8088c2ecf20Sopenharmony_ci int err = 0; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci buf = kmalloc(4096, GFP_KERNEL); 8118c2ecf20Sopenharmony_ci if (!buf) { 8128c2ecf20Sopenharmony_ci err = -ENOMEM; 8138c2ecf20Sopenharmony_ci goto err_out; 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci data = ar->fw.fw->data; 8178c2ecf20Sopenharmony_ci len = ar->fw.fw->size; 8188c2ecf20Sopenharmony_ci addr = ar->fw.address; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci /* this removes the miniboot image */ 8218c2ecf20Sopenharmony_ci data += ar->fw.offset; 8228c2ecf20Sopenharmony_ci len -= ar->fw.offset; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci while (len) { 8258c2ecf20Sopenharmony_ci transfer = min_t(unsigned int, len, 4096u); 8268c2ecf20Sopenharmony_ci memcpy(buf, data, transfer); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci err = usb_control_msg(ar->udev, usb_sndctrlpipe(ar->udev, 0), 8298c2ecf20Sopenharmony_ci 0x30 /* FW DL */, 0x40 | USB_DIR_OUT, 8308c2ecf20Sopenharmony_ci addr >> 8, 0, buf, transfer, 100); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (err < 0) { 8338c2ecf20Sopenharmony_ci kfree(buf); 8348c2ecf20Sopenharmony_ci goto err_out; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci len -= transfer; 8388c2ecf20Sopenharmony_ci data += transfer; 8398c2ecf20Sopenharmony_ci addr += transfer; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci kfree(buf); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci err = usb_control_msg(ar->udev, usb_sndctrlpipe(ar->udev, 0), 8448c2ecf20Sopenharmony_ci 0x31 /* FW DL COMPLETE */, 8458c2ecf20Sopenharmony_ci 0x40 | USB_DIR_OUT, 0, 0, NULL, 0, 200); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci if (wait_for_completion_timeout(&ar->fw_boot_wait, HZ) == 0) { 8488c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 8498c2ecf20Sopenharmony_ci goto err_out; 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci err = carl9170_echo_test(ar, 0x4a110123); 8538c2ecf20Sopenharmony_ci if (err) 8548c2ecf20Sopenharmony_ci goto err_out; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci /* now, start the command response counter */ 8578c2ecf20Sopenharmony_ci ar->cmd_seq = -1; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci return 0; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_cierr_out: 8628c2ecf20Sopenharmony_ci dev_err(&ar->udev->dev, "firmware upload failed (%d).\n", err); 8638c2ecf20Sopenharmony_ci return err; 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ciint carl9170_usb_restart(struct ar9170 *ar) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci int err = 0; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (ar->intf->condition != USB_INTERFACE_BOUND) 8718c2ecf20Sopenharmony_ci return 0; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* 8748c2ecf20Sopenharmony_ci * Disable the command response sequence counter check. 8758c2ecf20Sopenharmony_ci * We already know that the device/firmware is in a bad state. 8768c2ecf20Sopenharmony_ci * So, no extra points are awarded to anyone who reminds the 8778c2ecf20Sopenharmony_ci * driver about that. 8788c2ecf20Sopenharmony_ci */ 8798c2ecf20Sopenharmony_ci ar->cmd_seq = -2; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci err = carl9170_reboot(ar); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci carl9170_usb_stop(ar); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci if (err) 8868c2ecf20Sopenharmony_ci goto err_out; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci tasklet_schedule(&ar->usb_tasklet); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci /* The reboot procedure can take quite a while to complete. */ 8918c2ecf20Sopenharmony_ci msleep(1100); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci err = carl9170_usb_open(ar); 8948c2ecf20Sopenharmony_ci if (err) 8958c2ecf20Sopenharmony_ci goto err_out; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci err = carl9170_usb_load_firmware(ar); 8988c2ecf20Sopenharmony_ci if (err) 8998c2ecf20Sopenharmony_ci goto err_out; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci return 0; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cierr_out: 9048c2ecf20Sopenharmony_ci carl9170_usb_cancel_urbs(ar); 9058c2ecf20Sopenharmony_ci return err; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_civoid carl9170_usb_reset(struct ar9170 *ar) 9098c2ecf20Sopenharmony_ci{ 9108c2ecf20Sopenharmony_ci /* 9118c2ecf20Sopenharmony_ci * This is the last resort to get the device going again 9128c2ecf20Sopenharmony_ci * without any *user replugging action*. 9138c2ecf20Sopenharmony_ci * 9148c2ecf20Sopenharmony_ci * But there is a catch: usb_reset really is like a physical 9158c2ecf20Sopenharmony_ci * *reconnect*. The mac80211 state will be lost in the process. 9168c2ecf20Sopenharmony_ci * Therefore a userspace application, which is monitoring 9178c2ecf20Sopenharmony_ci * the link must step in. 9188c2ecf20Sopenharmony_ci */ 9198c2ecf20Sopenharmony_ci carl9170_usb_cancel_urbs(ar); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci carl9170_usb_stop(ar); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci usb_queue_reset_device(ar->intf); 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cistatic int carl9170_usb_init_device(struct ar9170 *ar) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci int err; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci /* 9318c2ecf20Sopenharmony_ci * The carl9170 firmware let's the driver know when it's 9328c2ecf20Sopenharmony_ci * ready for action. But we have to be prepared to gracefully 9338c2ecf20Sopenharmony_ci * handle all spurious [flushed] messages after each (re-)boot. 9348c2ecf20Sopenharmony_ci * Thus the command response counter remains disabled until it 9358c2ecf20Sopenharmony_ci * can be safely synchronized. 9368c2ecf20Sopenharmony_ci */ 9378c2ecf20Sopenharmony_ci ar->cmd_seq = -2; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci err = carl9170_usb_send_rx_irq_urb(ar); 9408c2ecf20Sopenharmony_ci if (err) 9418c2ecf20Sopenharmony_ci goto err_out; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci err = carl9170_usb_init_rx_bulk_urbs(ar); 9448c2ecf20Sopenharmony_ci if (err) 9458c2ecf20Sopenharmony_ci goto err_unrx; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci err = carl9170_usb_open(ar); 9488c2ecf20Sopenharmony_ci if (err) 9498c2ecf20Sopenharmony_ci goto err_unrx; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci mutex_lock(&ar->mutex); 9528c2ecf20Sopenharmony_ci err = carl9170_usb_load_firmware(ar); 9538c2ecf20Sopenharmony_ci mutex_unlock(&ar->mutex); 9548c2ecf20Sopenharmony_ci if (err) 9558c2ecf20Sopenharmony_ci goto err_stop; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci return 0; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cierr_stop: 9608c2ecf20Sopenharmony_ci carl9170_usb_stop(ar); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_cierr_unrx: 9638c2ecf20Sopenharmony_ci carl9170_usb_cancel_urbs(ar); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_cierr_out: 9668c2ecf20Sopenharmony_ci return err; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cistatic void carl9170_usb_firmware_failed(struct ar9170 *ar) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci /* Store a copies of the usb_interface and usb_device pointer locally. 9728c2ecf20Sopenharmony_ci * This is because release_driver initiates carl9170_usb_disconnect, 9738c2ecf20Sopenharmony_ci * which in turn frees our driver context (ar). 9748c2ecf20Sopenharmony_ci */ 9758c2ecf20Sopenharmony_ci struct usb_interface *intf = ar->intf; 9768c2ecf20Sopenharmony_ci struct usb_device *udev = ar->udev; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci complete(&ar->fw_load_wait); 9798c2ecf20Sopenharmony_ci /* at this point 'ar' could be already freed. Don't use it anymore */ 9808c2ecf20Sopenharmony_ci ar = NULL; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci /* unbind anything failed */ 9838c2ecf20Sopenharmony_ci usb_lock_device(udev); 9848c2ecf20Sopenharmony_ci usb_driver_release_interface(&carl9170_driver, intf); 9858c2ecf20Sopenharmony_ci usb_unlock_device(udev); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci usb_put_intf(intf); 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_cistatic void carl9170_usb_firmware_finish(struct ar9170 *ar) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci struct usb_interface *intf = ar->intf; 9938c2ecf20Sopenharmony_ci int err; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci err = carl9170_parse_firmware(ar); 9968c2ecf20Sopenharmony_ci if (err) 9978c2ecf20Sopenharmony_ci goto err_freefw; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci err = carl9170_usb_init_device(ar); 10008c2ecf20Sopenharmony_ci if (err) 10018c2ecf20Sopenharmony_ci goto err_freefw; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci err = carl9170_register(ar); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci carl9170_usb_stop(ar); 10068c2ecf20Sopenharmony_ci if (err) 10078c2ecf20Sopenharmony_ci goto err_unrx; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci complete(&ar->fw_load_wait); 10108c2ecf20Sopenharmony_ci usb_put_intf(intf); 10118c2ecf20Sopenharmony_ci return; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_cierr_unrx: 10148c2ecf20Sopenharmony_ci carl9170_usb_cancel_urbs(ar); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_cierr_freefw: 10178c2ecf20Sopenharmony_ci carl9170_release_firmware(ar); 10188c2ecf20Sopenharmony_ci carl9170_usb_firmware_failed(ar); 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_cistatic void carl9170_usb_firmware_step2(const struct firmware *fw, 10228c2ecf20Sopenharmony_ci void *context) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci struct ar9170 *ar = context; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci if (fw) { 10278c2ecf20Sopenharmony_ci ar->fw.fw = fw; 10288c2ecf20Sopenharmony_ci carl9170_usb_firmware_finish(ar); 10298c2ecf20Sopenharmony_ci return; 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci dev_err(&ar->udev->dev, "firmware not found.\n"); 10338c2ecf20Sopenharmony_ci carl9170_usb_firmware_failed(ar); 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_cistatic int carl9170_usb_probe(struct usb_interface *intf, 10378c2ecf20Sopenharmony_ci const struct usb_device_id *id) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *ep; 10408c2ecf20Sopenharmony_ci struct ar9170 *ar; 10418c2ecf20Sopenharmony_ci struct usb_device *udev; 10428c2ecf20Sopenharmony_ci int i, err; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci err = usb_reset_device(interface_to_usbdev(intf)); 10458c2ecf20Sopenharmony_ci if (err) 10468c2ecf20Sopenharmony_ci return err; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci ar = carl9170_alloc(sizeof(*ar)); 10498c2ecf20Sopenharmony_ci if (IS_ERR(ar)) 10508c2ecf20Sopenharmony_ci return PTR_ERR(ar); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci udev = interface_to_usbdev(intf); 10538c2ecf20Sopenharmony_ci ar->udev = udev; 10548c2ecf20Sopenharmony_ci ar->intf = intf; 10558c2ecf20Sopenharmony_ci ar->features = id->driver_info; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* We need to remember the type of endpoint 4 because it differs 10588c2ecf20Sopenharmony_ci * between high- and full-speed configuration. The high-speed 10598c2ecf20Sopenharmony_ci * configuration specifies it as interrupt and the full-speed 10608c2ecf20Sopenharmony_ci * configuration as bulk endpoint. This information is required 10618c2ecf20Sopenharmony_ci * later when sending urbs to that endpoint. 10628c2ecf20Sopenharmony_ci */ 10638c2ecf20Sopenharmony_ci for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; ++i) { 10648c2ecf20Sopenharmony_ci ep = &intf->cur_altsetting->endpoint[i].desc; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci if (usb_endpoint_num(ep) == AR9170_USB_EP_CMD && 10678c2ecf20Sopenharmony_ci usb_endpoint_dir_out(ep) && 10688c2ecf20Sopenharmony_ci usb_endpoint_type(ep) == USB_ENDPOINT_XFER_BULK) 10698c2ecf20Sopenharmony_ci ar->usb_ep_cmd_is_bulk = true; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci usb_set_intfdata(intf, ar); 10738c2ecf20Sopenharmony_ci SET_IEEE80211_DEV(ar->hw, &intf->dev); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci init_usb_anchor(&ar->rx_anch); 10768c2ecf20Sopenharmony_ci init_usb_anchor(&ar->rx_pool); 10778c2ecf20Sopenharmony_ci init_usb_anchor(&ar->rx_work); 10788c2ecf20Sopenharmony_ci init_usb_anchor(&ar->tx_wait); 10798c2ecf20Sopenharmony_ci init_usb_anchor(&ar->tx_anch); 10808c2ecf20Sopenharmony_ci init_usb_anchor(&ar->tx_cmd); 10818c2ecf20Sopenharmony_ci init_usb_anchor(&ar->tx_err); 10828c2ecf20Sopenharmony_ci init_completion(&ar->cmd_wait); 10838c2ecf20Sopenharmony_ci init_completion(&ar->fw_boot_wait); 10848c2ecf20Sopenharmony_ci init_completion(&ar->fw_load_wait); 10858c2ecf20Sopenharmony_ci tasklet_setup(&ar->usb_tasklet, carl9170_usb_tasklet); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci atomic_set(&ar->tx_cmd_urbs, 0); 10888c2ecf20Sopenharmony_ci atomic_set(&ar->tx_anch_urbs, 0); 10898c2ecf20Sopenharmony_ci atomic_set(&ar->rx_work_urbs, 0); 10908c2ecf20Sopenharmony_ci atomic_set(&ar->rx_anch_urbs, 0); 10918c2ecf20Sopenharmony_ci atomic_set(&ar->rx_pool_urbs, 0); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci usb_get_intf(intf); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci carl9170_set_state(ar, CARL9170_STOPPED); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci err = request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME, 10988c2ecf20Sopenharmony_ci &ar->udev->dev, GFP_KERNEL, ar, carl9170_usb_firmware_step2); 10998c2ecf20Sopenharmony_ci if (err) { 11008c2ecf20Sopenharmony_ci usb_put_intf(intf); 11018c2ecf20Sopenharmony_ci carl9170_free(ar); 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci return err; 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic void carl9170_usb_disconnect(struct usb_interface *intf) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci struct ar9170 *ar = usb_get_intfdata(intf); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci if (WARN_ON(!ar)) 11118c2ecf20Sopenharmony_ci return; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci wait_for_completion(&ar->fw_load_wait); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (IS_INITIALIZED(ar)) { 11168c2ecf20Sopenharmony_ci carl9170_reboot(ar); 11178c2ecf20Sopenharmony_ci carl9170_usb_stop(ar); 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci carl9170_usb_cancel_urbs(ar); 11218c2ecf20Sopenharmony_ci carl9170_unregister(ar); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci carl9170_release_firmware(ar); 11268c2ecf20Sopenharmony_ci carl9170_free(ar); 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 11308c2ecf20Sopenharmony_cistatic int carl9170_usb_suspend(struct usb_interface *intf, 11318c2ecf20Sopenharmony_ci pm_message_t message) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci struct ar9170 *ar = usb_get_intfdata(intf); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (!ar) 11368c2ecf20Sopenharmony_ci return -ENODEV; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci carl9170_usb_cancel_urbs(ar); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci return 0; 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_cistatic int carl9170_usb_resume(struct usb_interface *intf) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci struct ar9170 *ar = usb_get_intfdata(intf); 11468c2ecf20Sopenharmony_ci int err; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (!ar) 11498c2ecf20Sopenharmony_ci return -ENODEV; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci usb_unpoison_anchored_urbs(&ar->rx_anch); 11528c2ecf20Sopenharmony_ci carl9170_set_state(ar, CARL9170_STOPPED); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci /* 11558c2ecf20Sopenharmony_ci * The USB documentation demands that [for suspend] all traffic 11568c2ecf20Sopenharmony_ci * to and from the device has to stop. This would be fine, but 11578c2ecf20Sopenharmony_ci * there's a catch: the device[usb phy] does not come back. 11588c2ecf20Sopenharmony_ci * 11598c2ecf20Sopenharmony_ci * Upon resume the firmware will "kill" itself and the 11608c2ecf20Sopenharmony_ci * boot-code sorts out the magic voodoo. 11618c2ecf20Sopenharmony_ci * Not very nice, but there's not much what could go wrong. 11628c2ecf20Sopenharmony_ci */ 11638c2ecf20Sopenharmony_ci msleep(1100); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci err = carl9170_usb_init_device(ar); 11668c2ecf20Sopenharmony_ci if (err) 11678c2ecf20Sopenharmony_ci goto err_unrx; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci return 0; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_cierr_unrx: 11728c2ecf20Sopenharmony_ci carl9170_usb_cancel_urbs(ar); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci return err; 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_cistatic struct usb_driver carl9170_driver = { 11798c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 11808c2ecf20Sopenharmony_ci .probe = carl9170_usb_probe, 11818c2ecf20Sopenharmony_ci .disconnect = carl9170_usb_disconnect, 11828c2ecf20Sopenharmony_ci .id_table = carl9170_usb_ids, 11838c2ecf20Sopenharmony_ci .soft_unbind = 1, 11848c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 11858c2ecf20Sopenharmony_ci .suspend = carl9170_usb_suspend, 11868c2ecf20Sopenharmony_ci .resume = carl9170_usb_resume, 11878c2ecf20Sopenharmony_ci .reset_resume = carl9170_usb_resume, 11888c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 11898c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 11908c2ecf20Sopenharmony_ci}; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cimodule_usb_driver(carl9170_driver); 1193