18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Linux device driver for USB based Prism54 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on the islsm (softmac prism54) driver, which is: 98c2ecf20Sopenharmony_ci * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/usb.h> 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/firmware.h> 168c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/crc32.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <net/mac80211.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "p54.h" 238c2ecf20Sopenharmony_ci#include "lmac.h" 248c2ecf20Sopenharmony_ci#include "p54usb.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); 278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Prism54 USB wireless driver"); 288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 298c2ecf20Sopenharmony_ciMODULE_ALIAS("prism54usb"); 308c2ecf20Sopenharmony_ciMODULE_FIRMWARE("isl3886usb"); 318c2ecf20Sopenharmony_ciMODULE_FIRMWARE("isl3887usb"); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct usb_driver p54u_driver; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * Note: 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * Always update our wiki's device list (located at: 398c2ecf20Sopenharmony_ci * http://wireless.wiki.kernel.org/en/users/Drivers/p54/devices ), 408c2ecf20Sopenharmony_ci * whenever you add a new device. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic const struct usb_device_id p54u_table[] = { 448c2ecf20Sopenharmony_ci /* Version 1 devices (pci chip + net2280) */ 458c2ecf20Sopenharmony_ci {USB_DEVICE(0x0411, 0x0050)}, /* Buffalo WLI2-USB2-G54 */ 468c2ecf20Sopenharmony_ci {USB_DEVICE(0x045e, 0x00c2)}, /* Microsoft MN-710 */ 478c2ecf20Sopenharmony_ci {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */ 488c2ecf20Sopenharmony_ci {USB_DEVICE(0x0675, 0x0530)}, /* DrayTek Vigor 530 */ 498c2ecf20Sopenharmony_ci {USB_DEVICE(0x06b9, 0x0120)}, /* Thomson SpeedTouch 120g */ 508c2ecf20Sopenharmony_ci {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */ 518c2ecf20Sopenharmony_ci {USB_DEVICE(0x07aa, 0x001c)}, /* Corega CG-WLUSB2GT */ 528c2ecf20Sopenharmony_ci {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */ 538c2ecf20Sopenharmony_ci {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */ 548c2ecf20Sopenharmony_ci {USB_DEVICE(0x083a, 0x5501)}, /* Phillips CPWUA054 */ 558c2ecf20Sopenharmony_ci {USB_DEVICE(0x0846, 0x4200)}, /* Netgear WG121 */ 568c2ecf20Sopenharmony_ci {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */ 578c2ecf20Sopenharmony_ci {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */ 588c2ecf20Sopenharmony_ci {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */ 598c2ecf20Sopenharmony_ci {USB_DEVICE(0x0bf8, 0x1007)}, /* Fujitsu E-5400 USB */ 608c2ecf20Sopenharmony_ci {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */ 618c2ecf20Sopenharmony_ci {USB_DEVICE(0x0db0, 0x6826)}, /* MSI UB54G (MS-6826) */ 628c2ecf20Sopenharmony_ci {USB_DEVICE(0x107b, 0x55f2)}, /* Gateway WGU-210 (Gemtek) */ 638c2ecf20Sopenharmony_ci {USB_DEVICE(0x124a, 0x4023)}, /* Shuttle PN15, Airvast WM168g, IOGear GWU513 */ 648c2ecf20Sopenharmony_ci {USB_DEVICE(0x124a, 0x4026)}, /* AirVasT USB wireless device */ 658c2ecf20Sopenharmony_ci {USB_DEVICE(0x1435, 0x0210)}, /* Inventel UR054G */ 668c2ecf20Sopenharmony_ci {USB_DEVICE(0x15a9, 0x0002)}, /* Gemtek WUBI-100GW 802.11g */ 678c2ecf20Sopenharmony_ci {USB_DEVICE(0x1630, 0x0005)}, /* 2Wire 802.11g USB (v1) / Z-Com */ 688c2ecf20Sopenharmony_ci {USB_DEVICE(0x182d, 0x096b)}, /* Sitecom WL-107 */ 698c2ecf20Sopenharmony_ci {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */ 708c2ecf20Sopenharmony_ci {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */ 718c2ecf20Sopenharmony_ci {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */ 728c2ecf20Sopenharmony_ci {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */ 738c2ecf20Sopenharmony_ci {USB_DEVICE(0x2001, 0x3762)}, /* Conceptronic C54U */ 748c2ecf20Sopenharmony_ci {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */ 758c2ecf20Sopenharmony_ci {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* Version 2 devices (3887) */ 788c2ecf20Sopenharmony_ci {USB_DEVICE(0x0471, 0x1230)}, /* Philips CPWUA054/00 */ 798c2ecf20Sopenharmony_ci {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */ 808c2ecf20Sopenharmony_ci {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */ 818c2ecf20Sopenharmony_ci {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */ 828c2ecf20Sopenharmony_ci {USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */ 838c2ecf20Sopenharmony_ci {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */ 848c2ecf20Sopenharmony_ci {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */ 858c2ecf20Sopenharmony_ci {USB_DEVICE(0x07aa, 0x0020)}, /* Corega WLUSB2GTST USB */ 868c2ecf20Sopenharmony_ci {USB_DEVICE(0x0803, 0x4310)}, /* Zoom 4410a */ 878c2ecf20Sopenharmony_ci {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */ 888c2ecf20Sopenharmony_ci {USB_DEVICE(0x083a, 0x4531)}, /* T-Com Sinus 154 data II */ 898c2ecf20Sopenharmony_ci {USB_DEVICE(0x083a, 0xc501)}, /* Zoom Wireless-G 4410 */ 908c2ecf20Sopenharmony_ci {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */ 918c2ecf20Sopenharmony_ci {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */ 928c2ecf20Sopenharmony_ci {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */ 938c2ecf20Sopenharmony_ci {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */ 948c2ecf20Sopenharmony_ci {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/ 958c2ecf20Sopenharmony_ci {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/ 968c2ecf20Sopenharmony_ci /* {USB_DEVICE(0x0cde, 0x0006)}, * Medion MD40900 already listed above, 978c2ecf20Sopenharmony_ci * just noting it here for clarity */ 988c2ecf20Sopenharmony_ci {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */ 998c2ecf20Sopenharmony_ci {USB_DEVICE(0x0cde, 0x0015)}, /* Zcomax XG-705A */ 1008c2ecf20Sopenharmony_ci {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */ 1018c2ecf20Sopenharmony_ci {USB_DEVICE(0x124a, 0x4025)}, /* IOGear GWU513 (GW3887IK chip) */ 1028c2ecf20Sopenharmony_ci {USB_DEVICE(0x1260, 0xee22)}, /* SMC 2862W-G version 2 */ 1038c2ecf20Sopenharmony_ci {USB_DEVICE(0x13b1, 0x000a)}, /* Linksys WUSB54G ver 2 */ 1048c2ecf20Sopenharmony_ci {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */ 1058c2ecf20Sopenharmony_ci {USB_DEVICE(0x1413, 0x5400)}, /* Telsey 802.11g USB2.0 Adapter */ 1068c2ecf20Sopenharmony_ci {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */ 1078c2ecf20Sopenharmony_ci /* {USB_DEVICE(0x15a9, 0x0002)}, * Also SparkLAN WL-682 with 3887 */ 1088c2ecf20Sopenharmony_ci {USB_DEVICE(0x1668, 0x1050)}, /* Actiontec 802UIG-1 */ 1098c2ecf20Sopenharmony_ci {USB_DEVICE(0x1740, 0x1000)}, /* Senao NUB-350 */ 1108c2ecf20Sopenharmony_ci {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */ 1118c2ecf20Sopenharmony_ci {USB_DEVICE(0x2001, 0x3705)}, /* D-Link DWL-G120 rev C1 */ 1128c2ecf20Sopenharmony_ci {USB_DEVICE(0x413c, 0x5513)}, /* Dell WLA3310 USB Wireless Adapter */ 1138c2ecf20Sopenharmony_ci {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */ 1148c2ecf20Sopenharmony_ci {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */ 1158c2ecf20Sopenharmony_ci {} 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, p54u_table); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic const struct { 1218c2ecf20Sopenharmony_ci u32 intf; 1228c2ecf20Sopenharmony_ci enum p54u_hw_type type; 1238c2ecf20Sopenharmony_ci const char *fw; 1248c2ecf20Sopenharmony_ci char hw[20]; 1258c2ecf20Sopenharmony_ci} p54u_fwlist[__NUM_P54U_HWTYPES] = { 1268c2ecf20Sopenharmony_ci { 1278c2ecf20Sopenharmony_ci .type = P54U_NET2280, 1288c2ecf20Sopenharmony_ci .intf = FW_LM86, 1298c2ecf20Sopenharmony_ci .fw = "isl3886usb", 1308c2ecf20Sopenharmony_ci .hw = "ISL3886 + net2280", 1318c2ecf20Sopenharmony_ci }, 1328c2ecf20Sopenharmony_ci { 1338c2ecf20Sopenharmony_ci .type = P54U_3887, 1348c2ecf20Sopenharmony_ci .intf = FW_LM87, 1358c2ecf20Sopenharmony_ci .fw = "isl3887usb", 1368c2ecf20Sopenharmony_ci .hw = "ISL3887", 1378c2ecf20Sopenharmony_ci }, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void p54u_rx_cb(struct urb *urb) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct sk_buff *skb = (struct sk_buff *) urb->context; 1438c2ecf20Sopenharmony_ci struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb; 1448c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = info->dev; 1458c2ecf20Sopenharmony_ci struct p54u_priv *priv = dev->priv; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci skb_unlink(skb, &priv->rx_queue); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (unlikely(urb->status)) { 1508c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 1518c2ecf20Sopenharmony_ci return; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci skb_put(skb, urb->actual_length); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (priv->hw_type == P54U_NET2280) 1578c2ecf20Sopenharmony_ci skb_pull(skb, priv->common.tx_hdr_len); 1588c2ecf20Sopenharmony_ci if (priv->common.fw_interface == FW_LM87) { 1598c2ecf20Sopenharmony_ci skb_pull(skb, 4); 1608c2ecf20Sopenharmony_ci skb_put(skb, 4); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (p54_rx(dev, skb)) { 1648c2ecf20Sopenharmony_ci skb = dev_alloc_skb(priv->common.rx_mtu + 32); 1658c2ecf20Sopenharmony_ci if (unlikely(!skb)) { 1668c2ecf20Sopenharmony_ci /* TODO check rx queue length and refill *somewhere* */ 1678c2ecf20Sopenharmony_ci return; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci info = (struct p54u_rx_info *) skb->cb; 1718c2ecf20Sopenharmony_ci info->urb = urb; 1728c2ecf20Sopenharmony_ci info->dev = dev; 1738c2ecf20Sopenharmony_ci urb->transfer_buffer = skb_tail_pointer(skb); 1748c2ecf20Sopenharmony_ci urb->context = skb; 1758c2ecf20Sopenharmony_ci } else { 1768c2ecf20Sopenharmony_ci if (priv->hw_type == P54U_NET2280) 1778c2ecf20Sopenharmony_ci skb_push(skb, priv->common.tx_hdr_len); 1788c2ecf20Sopenharmony_ci if (priv->common.fw_interface == FW_LM87) { 1798c2ecf20Sopenharmony_ci skb_push(skb, 4); 1808c2ecf20Sopenharmony_ci skb_put(skb, 4); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci skb_reset_tail_pointer(skb); 1838c2ecf20Sopenharmony_ci skb_trim(skb, 0); 1848c2ecf20Sopenharmony_ci urb->transfer_buffer = skb_tail_pointer(skb); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci skb_queue_tail(&priv->rx_queue, skb); 1878c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &priv->submitted); 1888c2ecf20Sopenharmony_ci if (usb_submit_urb(urb, GFP_ATOMIC)) { 1898c2ecf20Sopenharmony_ci skb_unlink(skb, &priv->rx_queue); 1908c2ecf20Sopenharmony_ci usb_unanchor_urb(urb); 1918c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void p54u_tx_cb(struct urb *urb) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct sk_buff *skb = urb->context; 1988c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = 1998c2ecf20Sopenharmony_ci usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci p54_free_skb(dev, skb); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void p54u_tx_dummy_cb(struct urb *urb) { } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void p54u_free_urbs(struct ieee80211_hw *dev) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct p54u_priv *priv = dev->priv; 2098c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&priv->submitted); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void p54u_stop(struct ieee80211_hw *dev) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * TODO: figure out how to reliably stop the 3887 and net2280 so 2168c2ecf20Sopenharmony_ci * the hardware is still usable next time we want to start it. 2178c2ecf20Sopenharmony_ci * until then, we just stop listening to the hardware.. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci p54u_free_urbs(dev); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int p54u_init_urbs(struct ieee80211_hw *dev) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct p54u_priv *priv = dev->priv; 2258c2ecf20Sopenharmony_ci struct urb *entry = NULL; 2268c2ecf20Sopenharmony_ci struct sk_buff *skb; 2278c2ecf20Sopenharmony_ci struct p54u_rx_info *info; 2288c2ecf20Sopenharmony_ci int ret = 0; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci while (skb_queue_len(&priv->rx_queue) < 32) { 2318c2ecf20Sopenharmony_ci skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL); 2328c2ecf20Sopenharmony_ci if (!skb) { 2338c2ecf20Sopenharmony_ci ret = -ENOMEM; 2348c2ecf20Sopenharmony_ci goto err; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci entry = usb_alloc_urb(0, GFP_KERNEL); 2378c2ecf20Sopenharmony_ci if (!entry) { 2388c2ecf20Sopenharmony_ci ret = -ENOMEM; 2398c2ecf20Sopenharmony_ci goto err; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci usb_fill_bulk_urb(entry, priv->udev, 2438c2ecf20Sopenharmony_ci usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), 2448c2ecf20Sopenharmony_ci skb_tail_pointer(skb), 2458c2ecf20Sopenharmony_ci priv->common.rx_mtu + 32, p54u_rx_cb, skb); 2468c2ecf20Sopenharmony_ci info = (struct p54u_rx_info *) skb->cb; 2478c2ecf20Sopenharmony_ci info->urb = entry; 2488c2ecf20Sopenharmony_ci info->dev = dev; 2498c2ecf20Sopenharmony_ci skb_queue_tail(&priv->rx_queue, skb); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci usb_anchor_urb(entry, &priv->submitted); 2528c2ecf20Sopenharmony_ci ret = usb_submit_urb(entry, GFP_KERNEL); 2538c2ecf20Sopenharmony_ci if (ret) { 2548c2ecf20Sopenharmony_ci skb_unlink(skb, &priv->rx_queue); 2558c2ecf20Sopenharmony_ci usb_unanchor_urb(entry); 2568c2ecf20Sopenharmony_ci goto err; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci usb_free_urb(entry); 2598c2ecf20Sopenharmony_ci entry = NULL; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci err: 2658c2ecf20Sopenharmony_ci usb_free_urb(entry); 2668c2ecf20Sopenharmony_ci kfree_skb(skb); 2678c2ecf20Sopenharmony_ci p54u_free_urbs(dev); 2688c2ecf20Sopenharmony_ci return ret; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int p54u_open(struct ieee80211_hw *dev) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci /* 2748c2ecf20Sopenharmony_ci * TODO: Because we don't know how to reliably stop the 3887 and 2758c2ecf20Sopenharmony_ci * the isl3886+net2280, other than brutally cut off all 2768c2ecf20Sopenharmony_ci * communications. We have to reinitialize the urbs on every start. 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ci return p54u_init_urbs(dev); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic __le32 p54u_lm87_chksum(const __le32 *data, size_t length) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci u32 chk = 0; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci length >>= 2; 2868c2ecf20Sopenharmony_ci while (length--) { 2878c2ecf20Sopenharmony_ci chk ^= le32_to_cpu(*data++); 2888c2ecf20Sopenharmony_ci chk = (chk >> 5) ^ (chk << 3); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return cpu_to_le32(chk); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct p54u_priv *priv = dev->priv; 2978c2ecf20Sopenharmony_ci struct urb *data_urb; 2988c2ecf20Sopenharmony_ci struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci data_urb = usb_alloc_urb(0, GFP_ATOMIC); 3018c2ecf20Sopenharmony_ci if (!data_urb) { 3028c2ecf20Sopenharmony_ci p54_free_skb(dev, skb); 3038c2ecf20Sopenharmony_ci return; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len); 3078c2ecf20Sopenharmony_ci hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci usb_fill_bulk_urb(data_urb, priv->udev, 3108c2ecf20Sopenharmony_ci usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), 3118c2ecf20Sopenharmony_ci hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? 3128c2ecf20Sopenharmony_ci p54u_tx_cb : p54u_tx_dummy_cb, skb); 3138c2ecf20Sopenharmony_ci data_urb->transfer_flags |= URB_ZERO_PACKET; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci usb_anchor_urb(data_urb, &priv->submitted); 3168c2ecf20Sopenharmony_ci if (usb_submit_urb(data_urb, GFP_ATOMIC)) { 3178c2ecf20Sopenharmony_ci usb_unanchor_urb(data_urb); 3188c2ecf20Sopenharmony_ci p54_free_skb(dev, skb); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci usb_free_urb(data_urb); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct p54u_priv *priv = dev->priv; 3268c2ecf20Sopenharmony_ci struct urb *int_urb = NULL, *data_urb = NULL; 3278c2ecf20Sopenharmony_ci struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); 3288c2ecf20Sopenharmony_ci struct net2280_reg_write *reg = NULL; 3298c2ecf20Sopenharmony_ci int err = -ENOMEM; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci reg = kmalloc(sizeof(*reg), GFP_ATOMIC); 3328c2ecf20Sopenharmony_ci if (!reg) 3338c2ecf20Sopenharmony_ci goto out; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci int_urb = usb_alloc_urb(0, GFP_ATOMIC); 3368c2ecf20Sopenharmony_ci if (!int_urb) 3378c2ecf20Sopenharmony_ci goto out; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci data_urb = usb_alloc_urb(0, GFP_ATOMIC); 3408c2ecf20Sopenharmony_ci if (!data_urb) 3418c2ecf20Sopenharmony_ci goto out; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci reg->port = cpu_to_le16(NET2280_DEV_U32); 3448c2ecf20Sopenharmony_ci reg->addr = cpu_to_le32(P54U_DEV_BASE); 3458c2ecf20Sopenharmony_ci reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 3488c2ecf20Sopenharmony_ci hdr->len = cpu_to_le16(skb->len); 3498c2ecf20Sopenharmony_ci hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci usb_fill_bulk_urb(int_urb, priv->udev, 3528c2ecf20Sopenharmony_ci usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), 3538c2ecf20Sopenharmony_ci p54u_tx_dummy_cb, dev); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* 3568c2ecf20Sopenharmony_ci * URB_FREE_BUFFER triggers a code path in the USB subsystem that will 3578c2ecf20Sopenharmony_ci * free what is inside the transfer_buffer after the last reference to 3588c2ecf20Sopenharmony_ci * the int_urb is dropped. 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_ci int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET; 3618c2ecf20Sopenharmony_ci reg = NULL; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci usb_fill_bulk_urb(data_urb, priv->udev, 3648c2ecf20Sopenharmony_ci usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), 3658c2ecf20Sopenharmony_ci hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? 3668c2ecf20Sopenharmony_ci p54u_tx_cb : p54u_tx_dummy_cb, skb); 3678c2ecf20Sopenharmony_ci data_urb->transfer_flags |= URB_ZERO_PACKET; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci usb_anchor_urb(int_urb, &priv->submitted); 3708c2ecf20Sopenharmony_ci err = usb_submit_urb(int_urb, GFP_ATOMIC); 3718c2ecf20Sopenharmony_ci if (err) { 3728c2ecf20Sopenharmony_ci usb_unanchor_urb(int_urb); 3738c2ecf20Sopenharmony_ci goto out; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci usb_anchor_urb(data_urb, &priv->submitted); 3778c2ecf20Sopenharmony_ci err = usb_submit_urb(data_urb, GFP_ATOMIC); 3788c2ecf20Sopenharmony_ci if (err) { 3798c2ecf20Sopenharmony_ci usb_unanchor_urb(data_urb); 3808c2ecf20Sopenharmony_ci goto out; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ciout: 3838c2ecf20Sopenharmony_ci usb_free_urb(int_urb); 3848c2ecf20Sopenharmony_ci usb_free_urb(data_urb); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (err) { 3878c2ecf20Sopenharmony_ci kfree(reg); 3888c2ecf20Sopenharmony_ci p54_free_skb(dev, skb); 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int p54u_write(struct p54u_priv *priv, 3938c2ecf20Sopenharmony_ci struct net2280_reg_write *buf, 3948c2ecf20Sopenharmony_ci enum net2280_op_type type, 3958c2ecf20Sopenharmony_ci __le32 addr, __le32 val) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci unsigned int ep; 3988c2ecf20Sopenharmony_ci int alen; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (type & 0x0800) 4018c2ecf20Sopenharmony_ci ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV); 4028c2ecf20Sopenharmony_ci else 4038c2ecf20Sopenharmony_ci ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci buf->port = cpu_to_le16(type); 4068c2ecf20Sopenharmony_ci buf->addr = addr; 4078c2ecf20Sopenharmony_ci buf->val = val; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic int p54u_read(struct p54u_priv *priv, void *buf, 4138c2ecf20Sopenharmony_ci enum net2280_op_type type, 4148c2ecf20Sopenharmony_ci __le32 addr, __le32 *val) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct net2280_reg_read *read = buf; 4178c2ecf20Sopenharmony_ci __le32 *reg = buf; 4188c2ecf20Sopenharmony_ci unsigned int ep; 4198c2ecf20Sopenharmony_ci int alen, err; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (type & 0x0800) 4228c2ecf20Sopenharmony_ci ep = P54U_PIPE_DEV; 4238c2ecf20Sopenharmony_ci else 4248c2ecf20Sopenharmony_ci ep = P54U_PIPE_BRG; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci read->port = cpu_to_le16(type); 4278c2ecf20Sopenharmony_ci read->addr = addr; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), 4308c2ecf20Sopenharmony_ci read, sizeof(*read), &alen, 1000); 4318c2ecf20Sopenharmony_ci if (err) 4328c2ecf20Sopenharmony_ci return err; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep), 4358c2ecf20Sopenharmony_ci reg, sizeof(*reg), &alen, 1000); 4368c2ecf20Sopenharmony_ci if (err) 4378c2ecf20Sopenharmony_ci return err; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci *val = *reg; 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep, 4448c2ecf20Sopenharmony_ci void *data, size_t len) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci int alen; 4478c2ecf20Sopenharmony_ci return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), 4488c2ecf20Sopenharmony_ci data, len, &alen, 2000); 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic int p54u_device_reset(struct ieee80211_hw *dev) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct p54u_priv *priv = dev->priv; 4548c2ecf20Sopenharmony_ci int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (lock) { 4578c2ecf20Sopenharmony_ci ret = usb_lock_device_for_reset(priv->udev, priv->intf); 4588c2ecf20Sopenharmony_ci if (ret < 0) { 4598c2ecf20Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) unable to lock " 4608c2ecf20Sopenharmony_ci "device for reset (%d)!\n", ret); 4618c2ecf20Sopenharmony_ci return ret; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci ret = usb_reset_device(priv->udev); 4668c2ecf20Sopenharmony_ci if (lock) 4678c2ecf20Sopenharmony_ci usb_unlock_device(priv->udev); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (ret) 4708c2ecf20Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) unable to reset " 4718c2ecf20Sopenharmony_ci "device (%d)!\n", ret); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci return ret; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic const char p54u_romboot_3887[] = "~~~~"; 4778c2ecf20Sopenharmony_cistatic int p54u_firmware_reset_3887(struct ieee80211_hw *dev) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct p54u_priv *priv = dev->priv; 4808c2ecf20Sopenharmony_ci u8 *buf; 4818c2ecf20Sopenharmony_ci int ret; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci buf = kmemdup(p54u_romboot_3887, 4, GFP_KERNEL); 4848c2ecf20Sopenharmony_ci if (!buf) 4858c2ecf20Sopenharmony_ci return -ENOMEM; 4868c2ecf20Sopenharmony_ci ret = p54u_bulk_msg(priv, P54U_PIPE_DATA, 4878c2ecf20Sopenharmony_ci buf, 4); 4888c2ecf20Sopenharmony_ci kfree(buf); 4898c2ecf20Sopenharmony_ci if (ret) 4908c2ecf20Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) unable to jump to " 4918c2ecf20Sopenharmony_ci "boot ROM (%d)!\n", ret); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return ret; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic const char p54u_firmware_upload_3887[] = "<\r"; 4978c2ecf20Sopenharmony_cistatic int p54u_upload_firmware_3887(struct ieee80211_hw *dev) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct p54u_priv *priv = dev->priv; 5008c2ecf20Sopenharmony_ci int err, alen; 5018c2ecf20Sopenharmony_ci u8 carry = 0; 5028c2ecf20Sopenharmony_ci u8 *buf, *tmp; 5038c2ecf20Sopenharmony_ci const u8 *data; 5048c2ecf20Sopenharmony_ci unsigned int left, remains, block_size; 5058c2ecf20Sopenharmony_ci struct x2_header *hdr; 5068c2ecf20Sopenharmony_ci unsigned long timeout; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci err = p54u_firmware_reset_3887(dev); 5098c2ecf20Sopenharmony_ci if (err) 5108c2ecf20Sopenharmony_ci return err; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL); 5138c2ecf20Sopenharmony_ci if (!buf) 5148c2ecf20Sopenharmony_ci return -ENOMEM; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci left = block_size = min_t(size_t, P54U_FW_BLOCK, priv->fw->size); 5178c2ecf20Sopenharmony_ci strcpy(buf, p54u_firmware_upload_3887); 5188c2ecf20Sopenharmony_ci left -= strlen(p54u_firmware_upload_3887); 5198c2ecf20Sopenharmony_ci tmp += strlen(p54u_firmware_upload_3887); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci data = priv->fw->data; 5228c2ecf20Sopenharmony_ci remains = priv->fw->size; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887)); 5258c2ecf20Sopenharmony_ci memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE); 5268c2ecf20Sopenharmony_ci hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR); 5278c2ecf20Sopenharmony_ci hdr->fw_length = cpu_to_le32(priv->fw->size); 5288c2ecf20Sopenharmony_ci hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr, 5298c2ecf20Sopenharmony_ci sizeof(u32)*2)); 5308c2ecf20Sopenharmony_ci left -= sizeof(*hdr); 5318c2ecf20Sopenharmony_ci tmp += sizeof(*hdr); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci while (remains) { 5348c2ecf20Sopenharmony_ci while (left--) { 5358c2ecf20Sopenharmony_ci if (carry) { 5368c2ecf20Sopenharmony_ci *tmp++ = carry; 5378c2ecf20Sopenharmony_ci carry = 0; 5388c2ecf20Sopenharmony_ci remains--; 5398c2ecf20Sopenharmony_ci continue; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci switch (*data) { 5428c2ecf20Sopenharmony_ci case '~': 5438c2ecf20Sopenharmony_ci *tmp++ = '}'; 5448c2ecf20Sopenharmony_ci carry = '^'; 5458c2ecf20Sopenharmony_ci break; 5468c2ecf20Sopenharmony_ci case '}': 5478c2ecf20Sopenharmony_ci *tmp++ = '}'; 5488c2ecf20Sopenharmony_ci carry = ']'; 5498c2ecf20Sopenharmony_ci break; 5508c2ecf20Sopenharmony_ci default: 5518c2ecf20Sopenharmony_ci *tmp++ = *data; 5528c2ecf20Sopenharmony_ci remains--; 5538c2ecf20Sopenharmony_ci break; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci data++; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size); 5598c2ecf20Sopenharmony_ci if (err) { 5608c2ecf20Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware " 5618c2ecf20Sopenharmony_ci "upload failed!\n"); 5628c2ecf20Sopenharmony_ci goto err_upload_failed; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci tmp = buf; 5668c2ecf20Sopenharmony_ci left = block_size = min((unsigned int)P54U_FW_BLOCK, remains); 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data, 5708c2ecf20Sopenharmony_ci priv->fw->size)); 5718c2ecf20Sopenharmony_ci err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32)); 5728c2ecf20Sopenharmony_ci if (err) { 5738c2ecf20Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n"); 5748c2ecf20Sopenharmony_ci goto err_upload_failed; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(1000); 5778c2ecf20Sopenharmony_ci while (!(err = usb_bulk_msg(priv->udev, 5788c2ecf20Sopenharmony_ci usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { 5798c2ecf20Sopenharmony_ci if (alen > 2 && !memcmp(buf, "OK", 2)) 5808c2ecf20Sopenharmony_ci break; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (alen > 5 && !memcmp(buf, "ERROR", 5)) { 5838c2ecf20Sopenharmony_ci err = -EINVAL; 5848c2ecf20Sopenharmony_ci break; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 5888c2ecf20Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware boot " 5898c2ecf20Sopenharmony_ci "timed out!\n"); 5908c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 5918c2ecf20Sopenharmony_ci break; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci if (err) { 5958c2ecf20Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n"); 5968c2ecf20Sopenharmony_ci goto err_upload_failed; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci buf[0] = 'g'; 6008c2ecf20Sopenharmony_ci buf[1] = '\r'; 6018c2ecf20Sopenharmony_ci err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2); 6028c2ecf20Sopenharmony_ci if (err) { 6038c2ecf20Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware boot failed!\n"); 6048c2ecf20Sopenharmony_ci goto err_upload_failed; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(1000); 6088c2ecf20Sopenharmony_ci while (!(err = usb_bulk_msg(priv->udev, 6098c2ecf20Sopenharmony_ci usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { 6108c2ecf20Sopenharmony_ci if (alen > 0 && buf[0] == 'g') 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 6148c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 6158c2ecf20Sopenharmony_ci break; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci if (err) 6198c2ecf20Sopenharmony_ci goto err_upload_failed; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cierr_upload_failed: 6228c2ecf20Sopenharmony_ci kfree(buf); 6238c2ecf20Sopenharmony_ci return err; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci struct p54u_priv *priv = dev->priv; 6298c2ecf20Sopenharmony_ci const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE; 6308c2ecf20Sopenharmony_ci int err, alen; 6318c2ecf20Sopenharmony_ci void *buf; 6328c2ecf20Sopenharmony_ci __le32 reg; 6338c2ecf20Sopenharmony_ci unsigned int remains, offset; 6348c2ecf20Sopenharmony_ci const u8 *data; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci buf = kmalloc(512, GFP_KERNEL); 6378c2ecf20Sopenharmony_ci if (!buf) 6388c2ecf20Sopenharmony_ci return -ENOMEM; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci#define P54U_WRITE(type, addr, data) \ 6418c2ecf20Sopenharmony_ci do {\ 6428c2ecf20Sopenharmony_ci err = p54u_write(priv, buf, type,\ 6438c2ecf20Sopenharmony_ci cpu_to_le32((u32)(unsigned long)addr), data);\ 6448c2ecf20Sopenharmony_ci if (err) \ 6458c2ecf20Sopenharmony_ci goto fail;\ 6468c2ecf20Sopenharmony_ci } while (0) 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci#define P54U_READ(type, addr) \ 6498c2ecf20Sopenharmony_ci do {\ 6508c2ecf20Sopenharmony_ci err = p54u_read(priv, buf, type,\ 6518c2ecf20Sopenharmony_ci cpu_to_le32((u32)(unsigned long)addr), ®);\ 6528c2ecf20Sopenharmony_ci if (err)\ 6538c2ecf20Sopenharmony_ci goto fail;\ 6548c2ecf20Sopenharmony_ci } while (0) 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* power down net2280 bridge */ 6578c2ecf20Sopenharmony_ci P54U_READ(NET2280_BRG_U32, NET2280_GPIOCTL); 6588c2ecf20Sopenharmony_ci reg |= cpu_to_le32(P54U_BRG_POWER_DOWN); 6598c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~P54U_BRG_POWER_UP); 6608c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci mdelay(100); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci /* power up bridge */ 6658c2ecf20Sopenharmony_ci reg |= cpu_to_le32(P54U_BRG_POWER_UP); 6668c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~P54U_BRG_POWER_DOWN); 6678c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci mdelay(100); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_DEVINIT, 6728c2ecf20Sopenharmony_ci cpu_to_le32(NET2280_CLK_30Mhz | 6738c2ecf20Sopenharmony_ci NET2280_PCI_ENABLE | 6748c2ecf20Sopenharmony_ci NET2280_PCI_SOFT_RESET)); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci mdelay(20); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_CFG_U16, PCI_COMMAND, 6798c2ecf20Sopenharmony_ci cpu_to_le32(PCI_COMMAND_MEMORY | 6808c2ecf20Sopenharmony_ci PCI_COMMAND_MASTER)); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_0, 6838c2ecf20Sopenharmony_ci cpu_to_le32(NET2280_BASE)); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci P54U_READ(NET2280_BRG_CFG_U16, PCI_STATUS); 6868c2ecf20Sopenharmony_ci reg |= cpu_to_le32(PCI_STATUS_REC_MASTER_ABORT); 6878c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_CFG_U16, PCI_STATUS, reg); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci // TODO: we really need this? 6908c2ecf20Sopenharmony_ci P54U_READ(NET2280_BRG_U32, NET2280_RELNUM); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_RSP, 6938c2ecf20Sopenharmony_ci cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); 6948c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_EPC_RSP, 6958c2ecf20Sopenharmony_ci cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_2, 6988c2ecf20Sopenharmony_ci cpu_to_le32(NET2280_BASE2)); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* finally done setting up the bridge */ 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | PCI_COMMAND, 7038c2ecf20Sopenharmony_ci cpu_to_le32(PCI_COMMAND_MEMORY | 7048c2ecf20Sopenharmony_ci PCI_COMMAND_MASTER)); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | 0x40 /* TRDY timeout */, 0); 7078c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_CFG_U32, 0x10000 | PCI_BASE_ADDRESS_0, 7088c2ecf20Sopenharmony_ci cpu_to_le32(P54U_DEV_BASE)); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); 7118c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, 7128c2ecf20Sopenharmony_ci cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* do romboot */ 7158c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 0); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); 7188c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); 7198c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT); 7208c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); 7218c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci mdelay(20); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); 7268c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci mdelay(20); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); 7318c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci mdelay(100); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci P54U_READ(NET2280_DEV_U32, &devreg->int_ident); 7368c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* finally, we can upload firmware now! */ 7398c2ecf20Sopenharmony_ci remains = priv->fw->size; 7408c2ecf20Sopenharmony_ci data = priv->fw->data; 7418c2ecf20Sopenharmony_ci offset = ISL38XX_DEV_FIRMWARE_ADDR; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci while (remains) { 7448c2ecf20Sopenharmony_ci unsigned int block_len = min(remains, (unsigned int)512); 7458c2ecf20Sopenharmony_ci memcpy(buf, data, block_len); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len); 7488c2ecf20Sopenharmony_ci if (err) { 7498c2ecf20Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware block " 7508c2ecf20Sopenharmony_ci "upload failed\n"); 7518c2ecf20Sopenharmony_ci goto fail; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->direct_mem_base, 7558c2ecf20Sopenharmony_ci cpu_to_le32(0xc0000f00)); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, 7588c2ecf20Sopenharmony_ci 0x0020 | (unsigned long)&devreg->direct_mem_win, 0); 7598c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, 7608c2ecf20Sopenharmony_ci 0x0020 | (unsigned long)&devreg->direct_mem_win, 7618c2ecf20Sopenharmony_ci cpu_to_le32(1)); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, 7648c2ecf20Sopenharmony_ci 0x0024 | (unsigned long)&devreg->direct_mem_win, 7658c2ecf20Sopenharmony_ci cpu_to_le32(block_len)); 7668c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, 7678c2ecf20Sopenharmony_ci 0x0028 | (unsigned long)&devreg->direct_mem_win, 7688c2ecf20Sopenharmony_ci cpu_to_le32(offset)); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->dma_addr, 7718c2ecf20Sopenharmony_ci cpu_to_le32(NET2280_EPA_FIFO_PCI_ADDR)); 7728c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->dma_len, 7738c2ecf20Sopenharmony_ci cpu_to_le32(block_len >> 2)); 7748c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->dma_ctrl, 7758c2ecf20Sopenharmony_ci cpu_to_le32(ISL38XX_DMA_MASTER_CONTROL_TRIGGER)); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci mdelay(10); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci P54U_READ(NET2280_DEV_U32, 7808c2ecf20Sopenharmony_ci 0x002C | (unsigned long)&devreg->direct_mem_win); 7818c2ecf20Sopenharmony_ci if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) || 7828c2ecf20Sopenharmony_ci !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) { 7838c2ecf20Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware DMA " 7848c2ecf20Sopenharmony_ci "transfer failed\n"); 7858c2ecf20Sopenharmony_ci goto fail; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_STAT, 7898c2ecf20Sopenharmony_ci cpu_to_le32(NET2280_FIFO_FLUSH)); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci remains -= block_len; 7928c2ecf20Sopenharmony_ci data += block_len; 7938c2ecf20Sopenharmony_ci offset += block_len; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* do ramboot */ 7978c2ecf20Sopenharmony_ci P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); 7988c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); 7998c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); 8008c2ecf20Sopenharmony_ci reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT); 8018c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci mdelay(20); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); 8068c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); 8098c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci mdelay(100); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci P54U_READ(NET2280_DEV_U32, &devreg->int_ident); 8148c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci /* start up the firmware */ 8178c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 8188c2ecf20Sopenharmony_ci cpu_to_le32(ISL38XX_INT_IDENT_INIT)); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, 8218c2ecf20Sopenharmony_ci cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 8248c2ecf20Sopenharmony_ci cpu_to_le32(NET2280_PCI_INTA_INTERRUPT_ENABLE | 8258c2ecf20Sopenharmony_ci NET2280_USB_INTERRUPT_ENABLE)); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->dev_int, 8288c2ecf20Sopenharmony_ci cpu_to_le32(ISL38XX_DEV_INT_RESET)); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci err = usb_interrupt_msg(priv->udev, 8318c2ecf20Sopenharmony_ci usb_rcvbulkpipe(priv->udev, P54U_PIPE_INT), 8328c2ecf20Sopenharmony_ci buf, sizeof(__le32), &alen, 1000); 8338c2ecf20Sopenharmony_ci if (err || alen != sizeof(__le32)) 8348c2ecf20Sopenharmony_ci goto fail; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci P54U_READ(NET2280_DEV_U32, &devreg->int_ident); 8378c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (!(reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT))) 8408c2ecf20Sopenharmony_ci err = -EINVAL; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); 8438c2ecf20Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, 8448c2ecf20Sopenharmony_ci cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci#undef P54U_WRITE 8478c2ecf20Sopenharmony_ci#undef P54U_READ 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cifail: 8508c2ecf20Sopenharmony_ci kfree(buf); 8518c2ecf20Sopenharmony_ci return err; 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic int p54_find_type(struct p54u_priv *priv) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci int i; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci for (i = 0; i < __NUM_P54U_HWTYPES; i++) 8598c2ecf20Sopenharmony_ci if (p54u_fwlist[i].type == priv->hw_type) 8608c2ecf20Sopenharmony_ci break; 8618c2ecf20Sopenharmony_ci if (i == __NUM_P54U_HWTYPES) 8628c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci return i; 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_cistatic int p54u_start_ops(struct p54u_priv *priv) 8688c2ecf20Sopenharmony_ci{ 8698c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = priv->common.hw; 8708c2ecf20Sopenharmony_ci int ret; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci ret = p54_parse_firmware(dev, priv->fw); 8738c2ecf20Sopenharmony_ci if (ret) 8748c2ecf20Sopenharmony_ci goto err_out; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci ret = p54_find_type(priv); 8778c2ecf20Sopenharmony_ci if (ret < 0) 8788c2ecf20Sopenharmony_ci goto err_out; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (priv->common.fw_interface != p54u_fwlist[ret].intf) { 8818c2ecf20Sopenharmony_ci dev_err(&priv->udev->dev, "wrong firmware, please get " 8828c2ecf20Sopenharmony_ci "a firmware for \"%s\" and try again.\n", 8838c2ecf20Sopenharmony_ci p54u_fwlist[ret].hw); 8848c2ecf20Sopenharmony_ci ret = -ENODEV; 8858c2ecf20Sopenharmony_ci goto err_out; 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci ret = priv->upload_fw(dev); 8898c2ecf20Sopenharmony_ci if (ret) 8908c2ecf20Sopenharmony_ci goto err_out; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci ret = p54u_open(dev); 8938c2ecf20Sopenharmony_ci if (ret) 8948c2ecf20Sopenharmony_ci goto err_out; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci ret = p54_read_eeprom(dev); 8978c2ecf20Sopenharmony_ci if (ret) 8988c2ecf20Sopenharmony_ci goto err_stop; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci p54u_stop(dev); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci ret = p54_register_common(dev, &priv->udev->dev); 9038c2ecf20Sopenharmony_ci if (ret) 9048c2ecf20Sopenharmony_ci goto err_stop; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci return 0; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cierr_stop: 9098c2ecf20Sopenharmony_ci p54u_stop(dev); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cierr_out: 9128c2ecf20Sopenharmony_ci /* 9138c2ecf20Sopenharmony_ci * p54u_disconnect will do the rest of the 9148c2ecf20Sopenharmony_ci * cleanup 9158c2ecf20Sopenharmony_ci */ 9168c2ecf20Sopenharmony_ci return ret; 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic void p54u_load_firmware_cb(const struct firmware *firmware, 9208c2ecf20Sopenharmony_ci void *context) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci struct p54u_priv *priv = context; 9238c2ecf20Sopenharmony_ci struct usb_device *udev = priv->udev; 9248c2ecf20Sopenharmony_ci struct usb_interface *intf = priv->intf; 9258c2ecf20Sopenharmony_ci int err; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (firmware) { 9288c2ecf20Sopenharmony_ci priv->fw = firmware; 9298c2ecf20Sopenharmony_ci err = p54u_start_ops(priv); 9308c2ecf20Sopenharmony_ci } else { 9318c2ecf20Sopenharmony_ci err = -ENOENT; 9328c2ecf20Sopenharmony_ci dev_err(&udev->dev, "Firmware not found.\n"); 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci complete(&priv->fw_wait_load); 9368c2ecf20Sopenharmony_ci /* 9378c2ecf20Sopenharmony_ci * At this point p54u_disconnect may have already freed 9388c2ecf20Sopenharmony_ci * the "priv" context. Do not use it anymore! 9398c2ecf20Sopenharmony_ci */ 9408c2ecf20Sopenharmony_ci priv = NULL; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (err) { 9438c2ecf20Sopenharmony_ci dev_err(&intf->dev, "failed to initialize device (%d)\n", err); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci usb_lock_device(udev); 9468c2ecf20Sopenharmony_ci usb_driver_release_interface(&p54u_driver, intf); 9478c2ecf20Sopenharmony_ci usb_unlock_device(udev); 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci usb_put_intf(intf); 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_cistatic int p54u_load_firmware(struct ieee80211_hw *dev, 9548c2ecf20Sopenharmony_ci struct usb_interface *intf) 9558c2ecf20Sopenharmony_ci{ 9568c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 9578c2ecf20Sopenharmony_ci struct p54u_priv *priv = dev->priv; 9588c2ecf20Sopenharmony_ci struct device *device = &udev->dev; 9598c2ecf20Sopenharmony_ci int err, i; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci init_completion(&priv->fw_wait_load); 9648c2ecf20Sopenharmony_ci i = p54_find_type(priv); 9658c2ecf20Sopenharmony_ci if (i < 0) 9668c2ecf20Sopenharmony_ci return i; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci dev_info(&priv->udev->dev, "Loading firmware file %s\n", 9698c2ecf20Sopenharmony_ci p54u_fwlist[i].fw); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci usb_get_intf(intf); 9728c2ecf20Sopenharmony_ci err = request_firmware_nowait(THIS_MODULE, 1, p54u_fwlist[i].fw, 9738c2ecf20Sopenharmony_ci device, GFP_KERNEL, priv, 9748c2ecf20Sopenharmony_ci p54u_load_firmware_cb); 9758c2ecf20Sopenharmony_ci if (err) { 9768c2ecf20Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s " 9778c2ecf20Sopenharmony_ci "(%d)!\n", p54u_fwlist[i].fw, err); 9788c2ecf20Sopenharmony_ci usb_put_intf(intf); 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci return err; 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cistatic int p54u_probe(struct usb_interface *intf, 9858c2ecf20Sopenharmony_ci const struct usb_device_id *id) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 9888c2ecf20Sopenharmony_ci struct ieee80211_hw *dev; 9898c2ecf20Sopenharmony_ci struct p54u_priv *priv; 9908c2ecf20Sopenharmony_ci int err; 9918c2ecf20Sopenharmony_ci unsigned int i, recognized_pipes; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci dev = p54_init_common(sizeof(*priv)); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if (!dev) { 9968c2ecf20Sopenharmony_ci dev_err(&udev->dev, "(p54usb) ieee80211 alloc failed\n"); 9978c2ecf20Sopenharmony_ci return -ENOMEM; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci priv = dev->priv; 10018c2ecf20Sopenharmony_ci priv->hw_type = P54U_INVALID_HW; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci SET_IEEE80211_DEV(dev, &intf->dev); 10048c2ecf20Sopenharmony_ci usb_set_intfdata(intf, dev); 10058c2ecf20Sopenharmony_ci priv->udev = udev; 10068c2ecf20Sopenharmony_ci priv->intf = intf; 10078c2ecf20Sopenharmony_ci skb_queue_head_init(&priv->rx_queue); 10088c2ecf20Sopenharmony_ci init_usb_anchor(&priv->submitted); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci /* really lazy and simple way of figuring out if we're a 3887 */ 10118c2ecf20Sopenharmony_ci /* TODO: should just stick the identification in the device table */ 10128c2ecf20Sopenharmony_ci i = intf->altsetting->desc.bNumEndpoints; 10138c2ecf20Sopenharmony_ci recognized_pipes = 0; 10148c2ecf20Sopenharmony_ci while (i--) { 10158c2ecf20Sopenharmony_ci switch (intf->altsetting->endpoint[i].desc.bEndpointAddress) { 10168c2ecf20Sopenharmony_ci case P54U_PIPE_DATA: 10178c2ecf20Sopenharmony_ci case P54U_PIPE_MGMT: 10188c2ecf20Sopenharmony_ci case P54U_PIPE_BRG: 10198c2ecf20Sopenharmony_ci case P54U_PIPE_DEV: 10208c2ecf20Sopenharmony_ci case P54U_PIPE_DATA | USB_DIR_IN: 10218c2ecf20Sopenharmony_ci case P54U_PIPE_MGMT | USB_DIR_IN: 10228c2ecf20Sopenharmony_ci case P54U_PIPE_BRG | USB_DIR_IN: 10238c2ecf20Sopenharmony_ci case P54U_PIPE_DEV | USB_DIR_IN: 10248c2ecf20Sopenharmony_ci case P54U_PIPE_INT | USB_DIR_IN: 10258c2ecf20Sopenharmony_ci recognized_pipes++; 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci priv->common.open = p54u_open; 10298c2ecf20Sopenharmony_ci priv->common.stop = p54u_stop; 10308c2ecf20Sopenharmony_ci if (recognized_pipes < P54U_PIPE_NUMBER) { 10318c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 10328c2ecf20Sopenharmony_ci /* ISL3887 needs a full reset on resume */ 10338c2ecf20Sopenharmony_ci udev->reset_resume = 1; 10348c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 10358c2ecf20Sopenharmony_ci err = p54u_device_reset(dev); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci priv->hw_type = P54U_3887; 10388c2ecf20Sopenharmony_ci dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr); 10398c2ecf20Sopenharmony_ci priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr); 10408c2ecf20Sopenharmony_ci priv->common.tx = p54u_tx_lm87; 10418c2ecf20Sopenharmony_ci priv->upload_fw = p54u_upload_firmware_3887; 10428c2ecf20Sopenharmony_ci } else { 10438c2ecf20Sopenharmony_ci priv->hw_type = P54U_NET2280; 10448c2ecf20Sopenharmony_ci dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr); 10458c2ecf20Sopenharmony_ci priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr); 10468c2ecf20Sopenharmony_ci priv->common.tx = p54u_tx_net2280; 10478c2ecf20Sopenharmony_ci priv->upload_fw = p54u_upload_firmware_net2280; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci err = p54u_load_firmware(dev, intf); 10508c2ecf20Sopenharmony_ci if (err) 10518c2ecf20Sopenharmony_ci p54_free_common(dev); 10528c2ecf20Sopenharmony_ci return err; 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_cistatic void p54u_disconnect(struct usb_interface *intf) 10568c2ecf20Sopenharmony_ci{ 10578c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = usb_get_intfdata(intf); 10588c2ecf20Sopenharmony_ci struct p54u_priv *priv; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci if (!dev) 10618c2ecf20Sopenharmony_ci return; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci priv = dev->priv; 10648c2ecf20Sopenharmony_ci wait_for_completion(&priv->fw_wait_load); 10658c2ecf20Sopenharmony_ci p54_unregister_common(dev); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci release_firmware(priv->fw); 10688c2ecf20Sopenharmony_ci p54_free_common(dev); 10698c2ecf20Sopenharmony_ci} 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_cistatic int p54u_pre_reset(struct usb_interface *intf) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = usb_get_intfdata(intf); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci if (!dev) 10768c2ecf20Sopenharmony_ci return -ENODEV; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci p54u_stop(dev); 10798c2ecf20Sopenharmony_ci return 0; 10808c2ecf20Sopenharmony_ci} 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_cistatic int p54u_resume(struct usb_interface *intf) 10838c2ecf20Sopenharmony_ci{ 10848c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = usb_get_intfdata(intf); 10858c2ecf20Sopenharmony_ci struct p54u_priv *priv; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci if (!dev) 10888c2ecf20Sopenharmony_ci return -ENODEV; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci priv = dev->priv; 10918c2ecf20Sopenharmony_ci if (unlikely(!(priv->upload_fw && priv->fw))) 10928c2ecf20Sopenharmony_ci return 0; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci return priv->upload_fw(dev); 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_cistatic int p54u_post_reset(struct usb_interface *intf) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = usb_get_intfdata(intf); 11008c2ecf20Sopenharmony_ci struct p54u_priv *priv; 11018c2ecf20Sopenharmony_ci int err; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci err = p54u_resume(intf); 11048c2ecf20Sopenharmony_ci if (err) 11058c2ecf20Sopenharmony_ci return err; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci /* reinitialize old device state */ 11088c2ecf20Sopenharmony_ci priv = dev->priv; 11098c2ecf20Sopenharmony_ci if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED) 11108c2ecf20Sopenharmony_ci ieee80211_restart_hw(dev); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci return 0; 11138c2ecf20Sopenharmony_ci} 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_cistatic int p54u_suspend(struct usb_interface *intf, pm_message_t message) 11188c2ecf20Sopenharmony_ci{ 11198c2ecf20Sopenharmony_ci return p54u_pre_reset(intf); 11208c2ecf20Sopenharmony_ci} 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_cistatic struct usb_driver p54u_driver = { 11258c2ecf20Sopenharmony_ci .name = "p54usb", 11268c2ecf20Sopenharmony_ci .id_table = p54u_table, 11278c2ecf20Sopenharmony_ci .probe = p54u_probe, 11288c2ecf20Sopenharmony_ci .disconnect = p54u_disconnect, 11298c2ecf20Sopenharmony_ci .pre_reset = p54u_pre_reset, 11308c2ecf20Sopenharmony_ci .post_reset = p54u_post_reset, 11318c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 11328c2ecf20Sopenharmony_ci .suspend = p54u_suspend, 11338c2ecf20Sopenharmony_ci .resume = p54u_resume, 11348c2ecf20Sopenharmony_ci .reset_resume = p54u_resume, 11358c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 11368c2ecf20Sopenharmony_ci .soft_unbind = 1, 11378c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 11388c2ecf20Sopenharmony_ci}; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cimodule_usb_driver(p54u_driver); 1141