162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * Linux device driver for USB based Prism54 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on the islsm (softmac prism54) driver, which is: 962306a36Sopenharmony_ci * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/usb.h> 1362306a36Sopenharmony_ci#include <linux/pci.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/firmware.h> 1662306a36Sopenharmony_ci#include <linux/etherdevice.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/crc32.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <net/mac80211.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "p54.h" 2362306a36Sopenharmony_ci#include "lmac.h" 2462306a36Sopenharmony_ci#include "p54usb.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciMODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); 2762306a36Sopenharmony_ciMODULE_DESCRIPTION("Prism54 USB wireless driver"); 2862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2962306a36Sopenharmony_ciMODULE_ALIAS("prism54usb"); 3062306a36Sopenharmony_ciMODULE_FIRMWARE("isl3886usb"); 3162306a36Sopenharmony_ciMODULE_FIRMWARE("isl3887usb"); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct usb_driver p54u_driver; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * Note: 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * Always update our wiki's device list (located at: 3962306a36Sopenharmony_ci * http://wireless.wiki.kernel.org/en/users/Drivers/p54/devices ), 4062306a36Sopenharmony_ci * whenever you add a new device. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic const struct usb_device_id p54u_table[] = { 4462306a36Sopenharmony_ci /* Version 1 devices (pci chip + net2280) */ 4562306a36Sopenharmony_ci {USB_DEVICE(0x0411, 0x0050)}, /* Buffalo WLI2-USB2-G54 */ 4662306a36Sopenharmony_ci {USB_DEVICE(0x045e, 0x00c2)}, /* Microsoft MN-710 */ 4762306a36Sopenharmony_ci {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */ 4862306a36Sopenharmony_ci {USB_DEVICE(0x0675, 0x0530)}, /* DrayTek Vigor 530 */ 4962306a36Sopenharmony_ci {USB_DEVICE(0x06b9, 0x0120)}, /* Thomson SpeedTouch 120g */ 5062306a36Sopenharmony_ci {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */ 5162306a36Sopenharmony_ci {USB_DEVICE(0x07aa, 0x001c)}, /* Corega CG-WLUSB2GT */ 5262306a36Sopenharmony_ci {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */ 5362306a36Sopenharmony_ci {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */ 5462306a36Sopenharmony_ci {USB_DEVICE(0x083a, 0x5501)}, /* Phillips CPWUA054 */ 5562306a36Sopenharmony_ci {USB_DEVICE(0x0846, 0x4200)}, /* Netgear WG121 */ 5662306a36Sopenharmony_ci {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */ 5762306a36Sopenharmony_ci {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */ 5862306a36Sopenharmony_ci {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */ 5962306a36Sopenharmony_ci {USB_DEVICE(0x0bf8, 0x1007)}, /* Fujitsu E-5400 USB */ 6062306a36Sopenharmony_ci {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */ 6162306a36Sopenharmony_ci {USB_DEVICE(0x0db0, 0x6826)}, /* MSI UB54G (MS-6826) */ 6262306a36Sopenharmony_ci {USB_DEVICE(0x107b, 0x55f2)}, /* Gateway WGU-210 (Gemtek) */ 6362306a36Sopenharmony_ci {USB_DEVICE(0x124a, 0x4023)}, /* Shuttle PN15, Airvast WM168g, IOGear GWU513 */ 6462306a36Sopenharmony_ci {USB_DEVICE(0x124a, 0x4026)}, /* AirVasT USB wireless device */ 6562306a36Sopenharmony_ci {USB_DEVICE(0x1435, 0x0210)}, /* Inventel UR054G */ 6662306a36Sopenharmony_ci {USB_DEVICE(0x15a9, 0x0002)}, /* Gemtek WUBI-100GW 802.11g */ 6762306a36Sopenharmony_ci {USB_DEVICE(0x1630, 0x0005)}, /* 2Wire 802.11g USB (v1) / Z-Com */ 6862306a36Sopenharmony_ci {USB_DEVICE(0x182d, 0x096b)}, /* Sitecom WL-107 */ 6962306a36Sopenharmony_ci {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */ 7062306a36Sopenharmony_ci {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */ 7162306a36Sopenharmony_ci {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */ 7262306a36Sopenharmony_ci {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */ 7362306a36Sopenharmony_ci {USB_DEVICE(0x2001, 0x3762)}, /* Conceptronic C54U */ 7462306a36Sopenharmony_ci {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */ 7562306a36Sopenharmony_ci {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Version 2 devices (3887) */ 7862306a36Sopenharmony_ci {USB_DEVICE(0x0471, 0x1230)}, /* Philips CPWUA054/00 */ 7962306a36Sopenharmony_ci {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */ 8062306a36Sopenharmony_ci {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */ 8162306a36Sopenharmony_ci {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */ 8262306a36Sopenharmony_ci {USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */ 8362306a36Sopenharmony_ci {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */ 8462306a36Sopenharmony_ci {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */ 8562306a36Sopenharmony_ci {USB_DEVICE(0x07aa, 0x0020)}, /* Corega WLUSB2GTST USB */ 8662306a36Sopenharmony_ci {USB_DEVICE(0x0803, 0x4310)}, /* Zoom 4410a */ 8762306a36Sopenharmony_ci {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */ 8862306a36Sopenharmony_ci {USB_DEVICE(0x083a, 0x4531)}, /* T-Com Sinus 154 data II */ 8962306a36Sopenharmony_ci {USB_DEVICE(0x083a, 0xc501)}, /* Zoom Wireless-G 4410 */ 9062306a36Sopenharmony_ci {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */ 9162306a36Sopenharmony_ci {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */ 9262306a36Sopenharmony_ci {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */ 9362306a36Sopenharmony_ci {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */ 9462306a36Sopenharmony_ci {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/ 9562306a36Sopenharmony_ci {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/ 9662306a36Sopenharmony_ci /* {USB_DEVICE(0x0cde, 0x0006)}, * Medion MD40900 already listed above, 9762306a36Sopenharmony_ci * just noting it here for clarity */ 9862306a36Sopenharmony_ci {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */ 9962306a36Sopenharmony_ci {USB_DEVICE(0x0cde, 0x0015)}, /* Zcomax XG-705A */ 10062306a36Sopenharmony_ci {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */ 10162306a36Sopenharmony_ci {USB_DEVICE(0x124a, 0x4025)}, /* IOGear GWU513 (GW3887IK chip) */ 10262306a36Sopenharmony_ci {USB_DEVICE(0x1260, 0xee22)}, /* SMC 2862W-G version 2 */ 10362306a36Sopenharmony_ci {USB_DEVICE(0x13b1, 0x000a)}, /* Linksys WUSB54G ver 2 */ 10462306a36Sopenharmony_ci {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */ 10562306a36Sopenharmony_ci {USB_DEVICE(0x1413, 0x5400)}, /* Telsey 802.11g USB2.0 Adapter */ 10662306a36Sopenharmony_ci {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */ 10762306a36Sopenharmony_ci /* {USB_DEVICE(0x15a9, 0x0002)}, * Also SparkLAN WL-682 with 3887 */ 10862306a36Sopenharmony_ci {USB_DEVICE(0x1668, 0x1050)}, /* Actiontec 802UIG-1 */ 10962306a36Sopenharmony_ci {USB_DEVICE(0x1740, 0x1000)}, /* Senao NUB-350 */ 11062306a36Sopenharmony_ci {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */ 11162306a36Sopenharmony_ci {USB_DEVICE(0x2001, 0x3705)}, /* D-Link DWL-G120 rev C1 */ 11262306a36Sopenharmony_ci {USB_DEVICE(0x413c, 0x5513)}, /* Dell WLA3310 USB Wireless Adapter */ 11362306a36Sopenharmony_ci {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */ 11462306a36Sopenharmony_ci {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */ 11562306a36Sopenharmony_ci {} 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, p54u_table); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic const struct { 12162306a36Sopenharmony_ci u32 intf; 12262306a36Sopenharmony_ci enum p54u_hw_type type; 12362306a36Sopenharmony_ci const char *fw; 12462306a36Sopenharmony_ci char hw[20]; 12562306a36Sopenharmony_ci} p54u_fwlist[__NUM_P54U_HWTYPES] = { 12662306a36Sopenharmony_ci { 12762306a36Sopenharmony_ci .type = P54U_NET2280, 12862306a36Sopenharmony_ci .intf = FW_LM86, 12962306a36Sopenharmony_ci .fw = "isl3886usb", 13062306a36Sopenharmony_ci .hw = "ISL3886 + net2280", 13162306a36Sopenharmony_ci }, 13262306a36Sopenharmony_ci { 13362306a36Sopenharmony_ci .type = P54U_3887, 13462306a36Sopenharmony_ci .intf = FW_LM87, 13562306a36Sopenharmony_ci .fw = "isl3887usb", 13662306a36Sopenharmony_ci .hw = "ISL3887", 13762306a36Sopenharmony_ci }, 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void p54u_rx_cb(struct urb *urb) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct sk_buff *skb = (struct sk_buff *) urb->context; 14362306a36Sopenharmony_ci struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb; 14462306a36Sopenharmony_ci struct ieee80211_hw *dev = info->dev; 14562306a36Sopenharmony_ci struct p54u_priv *priv = dev->priv; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci skb_unlink(skb, &priv->rx_queue); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (unlikely(urb->status)) { 15062306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 15162306a36Sopenharmony_ci return; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci skb_put(skb, urb->actual_length); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (priv->hw_type == P54U_NET2280) 15762306a36Sopenharmony_ci skb_pull(skb, priv->common.tx_hdr_len); 15862306a36Sopenharmony_ci if (priv->common.fw_interface == FW_LM87) { 15962306a36Sopenharmony_ci skb_pull(skb, 4); 16062306a36Sopenharmony_ci skb_put(skb, 4); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (p54_rx(dev, skb)) { 16462306a36Sopenharmony_ci skb = dev_alloc_skb(priv->common.rx_mtu + 32); 16562306a36Sopenharmony_ci if (unlikely(!skb)) { 16662306a36Sopenharmony_ci /* TODO check rx queue length and refill *somewhere* */ 16762306a36Sopenharmony_ci return; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci info = (struct p54u_rx_info *) skb->cb; 17162306a36Sopenharmony_ci info->urb = urb; 17262306a36Sopenharmony_ci info->dev = dev; 17362306a36Sopenharmony_ci urb->transfer_buffer = skb_tail_pointer(skb); 17462306a36Sopenharmony_ci urb->context = skb; 17562306a36Sopenharmony_ci } else { 17662306a36Sopenharmony_ci if (priv->hw_type == P54U_NET2280) 17762306a36Sopenharmony_ci skb_push(skb, priv->common.tx_hdr_len); 17862306a36Sopenharmony_ci if (priv->common.fw_interface == FW_LM87) { 17962306a36Sopenharmony_ci skb_push(skb, 4); 18062306a36Sopenharmony_ci skb_put(skb, 4); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci skb_reset_tail_pointer(skb); 18362306a36Sopenharmony_ci skb_trim(skb, 0); 18462306a36Sopenharmony_ci urb->transfer_buffer = skb_tail_pointer(skb); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci skb_queue_tail(&priv->rx_queue, skb); 18762306a36Sopenharmony_ci usb_anchor_urb(urb, &priv->submitted); 18862306a36Sopenharmony_ci if (usb_submit_urb(urb, GFP_ATOMIC)) { 18962306a36Sopenharmony_ci skb_unlink(skb, &priv->rx_queue); 19062306a36Sopenharmony_ci usb_unanchor_urb(urb); 19162306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void p54u_tx_cb(struct urb *urb) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct sk_buff *skb = urb->context; 19862306a36Sopenharmony_ci struct ieee80211_hw *dev = 19962306a36Sopenharmony_ci usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci p54_free_skb(dev, skb); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void p54u_tx_dummy_cb(struct urb *urb) { } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void p54u_free_urbs(struct ieee80211_hw *dev) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct p54u_priv *priv = dev->priv; 20962306a36Sopenharmony_ci usb_kill_anchored_urbs(&priv->submitted); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic void p54u_stop(struct ieee80211_hw *dev) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci /* 21562306a36Sopenharmony_ci * TODO: figure out how to reliably stop the 3887 and net2280 so 21662306a36Sopenharmony_ci * the hardware is still usable next time we want to start it. 21762306a36Sopenharmony_ci * until then, we just stop listening to the hardware.. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci p54u_free_urbs(dev); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic int p54u_init_urbs(struct ieee80211_hw *dev) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct p54u_priv *priv = dev->priv; 22562306a36Sopenharmony_ci struct urb *entry = NULL; 22662306a36Sopenharmony_ci struct sk_buff *skb; 22762306a36Sopenharmony_ci struct p54u_rx_info *info; 22862306a36Sopenharmony_ci int ret = 0; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci while (skb_queue_len(&priv->rx_queue) < 32) { 23162306a36Sopenharmony_ci skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL); 23262306a36Sopenharmony_ci if (!skb) { 23362306a36Sopenharmony_ci ret = -ENOMEM; 23462306a36Sopenharmony_ci goto err; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci entry = usb_alloc_urb(0, GFP_KERNEL); 23762306a36Sopenharmony_ci if (!entry) { 23862306a36Sopenharmony_ci ret = -ENOMEM; 23962306a36Sopenharmony_ci goto err; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci usb_fill_bulk_urb(entry, priv->udev, 24362306a36Sopenharmony_ci usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), 24462306a36Sopenharmony_ci skb_tail_pointer(skb), 24562306a36Sopenharmony_ci priv->common.rx_mtu + 32, p54u_rx_cb, skb); 24662306a36Sopenharmony_ci info = (struct p54u_rx_info *) skb->cb; 24762306a36Sopenharmony_ci info->urb = entry; 24862306a36Sopenharmony_ci info->dev = dev; 24962306a36Sopenharmony_ci skb_queue_tail(&priv->rx_queue, skb); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci usb_anchor_urb(entry, &priv->submitted); 25262306a36Sopenharmony_ci ret = usb_submit_urb(entry, GFP_KERNEL); 25362306a36Sopenharmony_ci if (ret) { 25462306a36Sopenharmony_ci skb_unlink(skb, &priv->rx_queue); 25562306a36Sopenharmony_ci usb_unanchor_urb(entry); 25662306a36Sopenharmony_ci goto err; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci usb_free_urb(entry); 25962306a36Sopenharmony_ci entry = NULL; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci err: 26562306a36Sopenharmony_ci usb_free_urb(entry); 26662306a36Sopenharmony_ci kfree_skb(skb); 26762306a36Sopenharmony_ci p54u_free_urbs(dev); 26862306a36Sopenharmony_ci return ret; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int p54u_open(struct ieee80211_hw *dev) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci /* 27462306a36Sopenharmony_ci * TODO: Because we don't know how to reliably stop the 3887 and 27562306a36Sopenharmony_ci * the isl3886+net2280, other than brutally cut off all 27662306a36Sopenharmony_ci * communications. We have to reinitialize the urbs on every start. 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci return p54u_init_urbs(dev); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic __le32 p54u_lm87_chksum(const __le32 *data, size_t length) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci u32 chk = 0; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci length >>= 2; 28662306a36Sopenharmony_ci while (length--) { 28762306a36Sopenharmony_ci chk ^= le32_to_cpu(*data++); 28862306a36Sopenharmony_ci chk = (chk >> 5) ^ (chk << 3); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return cpu_to_le32(chk); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct p54u_priv *priv = dev->priv; 29762306a36Sopenharmony_ci struct urb *data_urb; 29862306a36Sopenharmony_ci struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci data_urb = usb_alloc_urb(0, GFP_ATOMIC); 30162306a36Sopenharmony_ci if (!data_urb) { 30262306a36Sopenharmony_ci p54_free_skb(dev, skb); 30362306a36Sopenharmony_ci return; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len); 30762306a36Sopenharmony_ci hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci usb_fill_bulk_urb(data_urb, priv->udev, 31062306a36Sopenharmony_ci usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), 31162306a36Sopenharmony_ci hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? 31262306a36Sopenharmony_ci p54u_tx_cb : p54u_tx_dummy_cb, skb); 31362306a36Sopenharmony_ci data_urb->transfer_flags |= URB_ZERO_PACKET; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci usb_anchor_urb(data_urb, &priv->submitted); 31662306a36Sopenharmony_ci if (usb_submit_urb(data_urb, GFP_ATOMIC)) { 31762306a36Sopenharmony_ci usb_unanchor_urb(data_urb); 31862306a36Sopenharmony_ci p54_free_skb(dev, skb); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci usb_free_urb(data_urb); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct p54u_priv *priv = dev->priv; 32662306a36Sopenharmony_ci struct urb *int_urb = NULL, *data_urb = NULL; 32762306a36Sopenharmony_ci struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); 32862306a36Sopenharmony_ci struct net2280_reg_write *reg = NULL; 32962306a36Sopenharmony_ci int err = -ENOMEM; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci reg = kmalloc(sizeof(*reg), GFP_ATOMIC); 33262306a36Sopenharmony_ci if (!reg) 33362306a36Sopenharmony_ci goto out; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci int_urb = usb_alloc_urb(0, GFP_ATOMIC); 33662306a36Sopenharmony_ci if (!int_urb) 33762306a36Sopenharmony_ci goto out; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci data_urb = usb_alloc_urb(0, GFP_ATOMIC); 34062306a36Sopenharmony_ci if (!data_urb) 34162306a36Sopenharmony_ci goto out; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci reg->port = cpu_to_le16(NET2280_DEV_U32); 34462306a36Sopenharmony_ci reg->addr = cpu_to_le32(P54U_DEV_BASE); 34562306a36Sopenharmony_ci reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 34862306a36Sopenharmony_ci hdr->len = cpu_to_le16(skb->len); 34962306a36Sopenharmony_ci hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci usb_fill_bulk_urb(int_urb, priv->udev, 35262306a36Sopenharmony_ci usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), 35362306a36Sopenharmony_ci p54u_tx_dummy_cb, dev); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* 35662306a36Sopenharmony_ci * URB_FREE_BUFFER triggers a code path in the USB subsystem that will 35762306a36Sopenharmony_ci * free what is inside the transfer_buffer after the last reference to 35862306a36Sopenharmony_ci * the int_urb is dropped. 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_ci int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET; 36162306a36Sopenharmony_ci reg = NULL; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci usb_fill_bulk_urb(data_urb, priv->udev, 36462306a36Sopenharmony_ci usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), 36562306a36Sopenharmony_ci hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? 36662306a36Sopenharmony_ci p54u_tx_cb : p54u_tx_dummy_cb, skb); 36762306a36Sopenharmony_ci data_urb->transfer_flags |= URB_ZERO_PACKET; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci usb_anchor_urb(int_urb, &priv->submitted); 37062306a36Sopenharmony_ci err = usb_submit_urb(int_urb, GFP_ATOMIC); 37162306a36Sopenharmony_ci if (err) { 37262306a36Sopenharmony_ci usb_unanchor_urb(int_urb); 37362306a36Sopenharmony_ci goto out; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci usb_anchor_urb(data_urb, &priv->submitted); 37762306a36Sopenharmony_ci err = usb_submit_urb(data_urb, GFP_ATOMIC); 37862306a36Sopenharmony_ci if (err) { 37962306a36Sopenharmony_ci usb_unanchor_urb(data_urb); 38062306a36Sopenharmony_ci goto out; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ciout: 38362306a36Sopenharmony_ci usb_free_urb(int_urb); 38462306a36Sopenharmony_ci usb_free_urb(data_urb); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (err) { 38762306a36Sopenharmony_ci kfree(reg); 38862306a36Sopenharmony_ci p54_free_skb(dev, skb); 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic int p54u_write(struct p54u_priv *priv, 39362306a36Sopenharmony_ci struct net2280_reg_write *buf, 39462306a36Sopenharmony_ci enum net2280_op_type type, 39562306a36Sopenharmony_ci __le32 addr, __le32 val) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci unsigned int ep; 39862306a36Sopenharmony_ci int alen; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (type & 0x0800) 40162306a36Sopenharmony_ci ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV); 40262306a36Sopenharmony_ci else 40362306a36Sopenharmony_ci ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci buf->port = cpu_to_le16(type); 40662306a36Sopenharmony_ci buf->addr = addr; 40762306a36Sopenharmony_ci buf->val = val; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int p54u_read(struct p54u_priv *priv, void *buf, 41362306a36Sopenharmony_ci enum net2280_op_type type, 41462306a36Sopenharmony_ci __le32 addr, __le32 *val) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct net2280_reg_read *read = buf; 41762306a36Sopenharmony_ci __le32 *reg = buf; 41862306a36Sopenharmony_ci unsigned int ep; 41962306a36Sopenharmony_ci int alen, err; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (type & 0x0800) 42262306a36Sopenharmony_ci ep = P54U_PIPE_DEV; 42362306a36Sopenharmony_ci else 42462306a36Sopenharmony_ci ep = P54U_PIPE_BRG; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci read->port = cpu_to_le16(type); 42762306a36Sopenharmony_ci read->addr = addr; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), 43062306a36Sopenharmony_ci read, sizeof(*read), &alen, 1000); 43162306a36Sopenharmony_ci if (err) 43262306a36Sopenharmony_ci return err; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep), 43562306a36Sopenharmony_ci reg, sizeof(*reg), &alen, 1000); 43662306a36Sopenharmony_ci if (err) 43762306a36Sopenharmony_ci return err; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci *val = *reg; 44062306a36Sopenharmony_ci return 0; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep, 44462306a36Sopenharmony_ci void *data, size_t len) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci int alen; 44762306a36Sopenharmony_ci return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), 44862306a36Sopenharmony_ci data, len, &alen, 2000); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic int p54u_device_reset(struct ieee80211_hw *dev) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct p54u_priv *priv = dev->priv; 45462306a36Sopenharmony_ci int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (lock) { 45762306a36Sopenharmony_ci ret = usb_lock_device_for_reset(priv->udev, priv->intf); 45862306a36Sopenharmony_ci if (ret < 0) { 45962306a36Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) unable to lock " 46062306a36Sopenharmony_ci "device for reset (%d)!\n", ret); 46162306a36Sopenharmony_ci return ret; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci ret = usb_reset_device(priv->udev); 46662306a36Sopenharmony_ci if (lock) 46762306a36Sopenharmony_ci usb_unlock_device(priv->udev); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (ret) 47062306a36Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) unable to reset " 47162306a36Sopenharmony_ci "device (%d)!\n", ret); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return ret; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic const char p54u_romboot_3887[] = "~~~~"; 47762306a36Sopenharmony_cistatic int p54u_firmware_reset_3887(struct ieee80211_hw *dev) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct p54u_priv *priv = dev->priv; 48062306a36Sopenharmony_ci u8 *buf; 48162306a36Sopenharmony_ci int ret; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci buf = kmemdup(p54u_romboot_3887, 4, GFP_KERNEL); 48462306a36Sopenharmony_ci if (!buf) 48562306a36Sopenharmony_ci return -ENOMEM; 48662306a36Sopenharmony_ci ret = p54u_bulk_msg(priv, P54U_PIPE_DATA, 48762306a36Sopenharmony_ci buf, 4); 48862306a36Sopenharmony_ci kfree(buf); 48962306a36Sopenharmony_ci if (ret) 49062306a36Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) unable to jump to " 49162306a36Sopenharmony_ci "boot ROM (%d)!\n", ret); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return ret; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic const char p54u_firmware_upload_3887[] = "<\r"; 49762306a36Sopenharmony_cistatic int p54u_upload_firmware_3887(struct ieee80211_hw *dev) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct p54u_priv *priv = dev->priv; 50062306a36Sopenharmony_ci int err, alen; 50162306a36Sopenharmony_ci u8 carry = 0; 50262306a36Sopenharmony_ci u8 *buf, *tmp; 50362306a36Sopenharmony_ci const u8 *data; 50462306a36Sopenharmony_ci unsigned int left, remains, block_size; 50562306a36Sopenharmony_ci struct x2_header *hdr; 50662306a36Sopenharmony_ci unsigned long timeout; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci err = p54u_firmware_reset_3887(dev); 50962306a36Sopenharmony_ci if (err) 51062306a36Sopenharmony_ci return err; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL); 51362306a36Sopenharmony_ci if (!buf) 51462306a36Sopenharmony_ci return -ENOMEM; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci left = block_size = min_t(size_t, P54U_FW_BLOCK, priv->fw->size); 51762306a36Sopenharmony_ci strcpy(buf, p54u_firmware_upload_3887); 51862306a36Sopenharmony_ci left -= strlen(p54u_firmware_upload_3887); 51962306a36Sopenharmony_ci tmp += strlen(p54u_firmware_upload_3887); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci data = priv->fw->data; 52262306a36Sopenharmony_ci remains = priv->fw->size; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887)); 52562306a36Sopenharmony_ci memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE); 52662306a36Sopenharmony_ci hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR); 52762306a36Sopenharmony_ci hdr->fw_length = cpu_to_le32(priv->fw->size); 52862306a36Sopenharmony_ci hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr, 52962306a36Sopenharmony_ci sizeof(u32)*2)); 53062306a36Sopenharmony_ci left -= sizeof(*hdr); 53162306a36Sopenharmony_ci tmp += sizeof(*hdr); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci while (remains) { 53462306a36Sopenharmony_ci while (left--) { 53562306a36Sopenharmony_ci if (carry) { 53662306a36Sopenharmony_ci *tmp++ = carry; 53762306a36Sopenharmony_ci carry = 0; 53862306a36Sopenharmony_ci remains--; 53962306a36Sopenharmony_ci continue; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci switch (*data) { 54262306a36Sopenharmony_ci case '~': 54362306a36Sopenharmony_ci *tmp++ = '}'; 54462306a36Sopenharmony_ci carry = '^'; 54562306a36Sopenharmony_ci break; 54662306a36Sopenharmony_ci case '}': 54762306a36Sopenharmony_ci *tmp++ = '}'; 54862306a36Sopenharmony_ci carry = ']'; 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci default: 55162306a36Sopenharmony_ci *tmp++ = *data; 55262306a36Sopenharmony_ci remains--; 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci data++; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size); 55962306a36Sopenharmony_ci if (err) { 56062306a36Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware " 56162306a36Sopenharmony_ci "upload failed!\n"); 56262306a36Sopenharmony_ci goto err_upload_failed; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci tmp = buf; 56662306a36Sopenharmony_ci left = block_size = min((unsigned int)P54U_FW_BLOCK, remains); 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data, 57062306a36Sopenharmony_ci priv->fw->size)); 57162306a36Sopenharmony_ci err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32)); 57262306a36Sopenharmony_ci if (err) { 57362306a36Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n"); 57462306a36Sopenharmony_ci goto err_upload_failed; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(1000); 57762306a36Sopenharmony_ci while (!(err = usb_bulk_msg(priv->udev, 57862306a36Sopenharmony_ci usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { 57962306a36Sopenharmony_ci if (alen > 2 && !memcmp(buf, "OK", 2)) 58062306a36Sopenharmony_ci break; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (alen > 5 && !memcmp(buf, "ERROR", 5)) { 58362306a36Sopenharmony_ci err = -EINVAL; 58462306a36Sopenharmony_ci break; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 58862306a36Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware boot " 58962306a36Sopenharmony_ci "timed out!\n"); 59062306a36Sopenharmony_ci err = -ETIMEDOUT; 59162306a36Sopenharmony_ci break; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci if (err) { 59562306a36Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n"); 59662306a36Sopenharmony_ci goto err_upload_failed; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci buf[0] = 'g'; 60062306a36Sopenharmony_ci buf[1] = '\r'; 60162306a36Sopenharmony_ci err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2); 60262306a36Sopenharmony_ci if (err) { 60362306a36Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware boot failed!\n"); 60462306a36Sopenharmony_ci goto err_upload_failed; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(1000); 60862306a36Sopenharmony_ci while (!(err = usb_bulk_msg(priv->udev, 60962306a36Sopenharmony_ci usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { 61062306a36Sopenharmony_ci if (alen > 0 && buf[0] == 'g') 61162306a36Sopenharmony_ci break; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 61462306a36Sopenharmony_ci err = -ETIMEDOUT; 61562306a36Sopenharmony_ci break; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci if (err) 61962306a36Sopenharmony_ci goto err_upload_failed; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cierr_upload_failed: 62262306a36Sopenharmony_ci kfree(buf); 62362306a36Sopenharmony_ci return err; 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct p54u_priv *priv = dev->priv; 62962306a36Sopenharmony_ci const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE; 63062306a36Sopenharmony_ci int err, alen; 63162306a36Sopenharmony_ci void *buf; 63262306a36Sopenharmony_ci __le32 reg; 63362306a36Sopenharmony_ci unsigned int remains, offset; 63462306a36Sopenharmony_ci const u8 *data; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci buf = kmalloc(512, GFP_KERNEL); 63762306a36Sopenharmony_ci if (!buf) 63862306a36Sopenharmony_ci return -ENOMEM; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci#define P54U_WRITE(type, addr, data) \ 64162306a36Sopenharmony_ci do {\ 64262306a36Sopenharmony_ci err = p54u_write(priv, buf, type,\ 64362306a36Sopenharmony_ci cpu_to_le32((u32)(unsigned long)addr), data);\ 64462306a36Sopenharmony_ci if (err) \ 64562306a36Sopenharmony_ci goto fail;\ 64662306a36Sopenharmony_ci } while (0) 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci#define P54U_READ(type, addr) \ 64962306a36Sopenharmony_ci do {\ 65062306a36Sopenharmony_ci err = p54u_read(priv, buf, type,\ 65162306a36Sopenharmony_ci cpu_to_le32((u32)(unsigned long)addr), ®);\ 65262306a36Sopenharmony_ci if (err)\ 65362306a36Sopenharmony_ci goto fail;\ 65462306a36Sopenharmony_ci } while (0) 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* power down net2280 bridge */ 65762306a36Sopenharmony_ci P54U_READ(NET2280_BRG_U32, NET2280_GPIOCTL); 65862306a36Sopenharmony_ci reg |= cpu_to_le32(P54U_BRG_POWER_DOWN); 65962306a36Sopenharmony_ci reg &= cpu_to_le32(~P54U_BRG_POWER_UP); 66062306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci mdelay(100); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* power up bridge */ 66562306a36Sopenharmony_ci reg |= cpu_to_le32(P54U_BRG_POWER_UP); 66662306a36Sopenharmony_ci reg &= cpu_to_le32(~P54U_BRG_POWER_DOWN); 66762306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci mdelay(100); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_DEVINIT, 67262306a36Sopenharmony_ci cpu_to_le32(NET2280_CLK_30Mhz | 67362306a36Sopenharmony_ci NET2280_PCI_ENABLE | 67462306a36Sopenharmony_ci NET2280_PCI_SOFT_RESET)); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci mdelay(20); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_CFG_U16, PCI_COMMAND, 67962306a36Sopenharmony_ci cpu_to_le32(PCI_COMMAND_MEMORY | 68062306a36Sopenharmony_ci PCI_COMMAND_MASTER)); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_0, 68362306a36Sopenharmony_ci cpu_to_le32(NET2280_BASE)); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci P54U_READ(NET2280_BRG_CFG_U16, PCI_STATUS); 68662306a36Sopenharmony_ci reg |= cpu_to_le32(PCI_STATUS_REC_MASTER_ABORT); 68762306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_CFG_U16, PCI_STATUS, reg); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci // TODO: we really need this? 69062306a36Sopenharmony_ci P54U_READ(NET2280_BRG_U32, NET2280_RELNUM); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_RSP, 69362306a36Sopenharmony_ci cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); 69462306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_EPC_RSP, 69562306a36Sopenharmony_ci cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_2, 69862306a36Sopenharmony_ci cpu_to_le32(NET2280_BASE2)); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci /* finally done setting up the bridge */ 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | PCI_COMMAND, 70362306a36Sopenharmony_ci cpu_to_le32(PCI_COMMAND_MEMORY | 70462306a36Sopenharmony_ci PCI_COMMAND_MASTER)); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | 0x40 /* TRDY timeout */, 0); 70762306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_CFG_U32, 0x10000 | PCI_BASE_ADDRESS_0, 70862306a36Sopenharmony_ci cpu_to_le32(P54U_DEV_BASE)); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); 71162306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, 71262306a36Sopenharmony_ci cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci /* do romboot */ 71562306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 0); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); 71862306a36Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); 71962306a36Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT); 72062306a36Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); 72162306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci mdelay(20); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); 72662306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci mdelay(20); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); 73162306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci mdelay(100); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci P54U_READ(NET2280_DEV_U32, &devreg->int_ident); 73662306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* finally, we can upload firmware now! */ 73962306a36Sopenharmony_ci remains = priv->fw->size; 74062306a36Sopenharmony_ci data = priv->fw->data; 74162306a36Sopenharmony_ci offset = ISL38XX_DEV_FIRMWARE_ADDR; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci while (remains) { 74462306a36Sopenharmony_ci unsigned int block_len = min(remains, (unsigned int)512); 74562306a36Sopenharmony_ci memcpy(buf, data, block_len); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len); 74862306a36Sopenharmony_ci if (err) { 74962306a36Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware block " 75062306a36Sopenharmony_ci "upload failed\n"); 75162306a36Sopenharmony_ci goto fail; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->direct_mem_base, 75562306a36Sopenharmony_ci cpu_to_le32(0xc0000f00)); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, 75862306a36Sopenharmony_ci 0x0020 | (unsigned long)&devreg->direct_mem_win, 0); 75962306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, 76062306a36Sopenharmony_ci 0x0020 | (unsigned long)&devreg->direct_mem_win, 76162306a36Sopenharmony_ci cpu_to_le32(1)); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, 76462306a36Sopenharmony_ci 0x0024 | (unsigned long)&devreg->direct_mem_win, 76562306a36Sopenharmony_ci cpu_to_le32(block_len)); 76662306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, 76762306a36Sopenharmony_ci 0x0028 | (unsigned long)&devreg->direct_mem_win, 76862306a36Sopenharmony_ci cpu_to_le32(offset)); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->dma_addr, 77162306a36Sopenharmony_ci cpu_to_le32(NET2280_EPA_FIFO_PCI_ADDR)); 77262306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->dma_len, 77362306a36Sopenharmony_ci cpu_to_le32(block_len >> 2)); 77462306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->dma_ctrl, 77562306a36Sopenharmony_ci cpu_to_le32(ISL38XX_DMA_MASTER_CONTROL_TRIGGER)); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci mdelay(10); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci P54U_READ(NET2280_DEV_U32, 78062306a36Sopenharmony_ci 0x002C | (unsigned long)&devreg->direct_mem_win); 78162306a36Sopenharmony_ci if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) || 78262306a36Sopenharmony_ci !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) { 78362306a36Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) firmware DMA " 78462306a36Sopenharmony_ci "transfer failed\n"); 78562306a36Sopenharmony_ci goto fail; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_STAT, 78962306a36Sopenharmony_ci cpu_to_le32(NET2280_FIFO_FLUSH)); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci remains -= block_len; 79262306a36Sopenharmony_ci data += block_len; 79362306a36Sopenharmony_ci offset += block_len; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* do ramboot */ 79762306a36Sopenharmony_ci P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); 79862306a36Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); 79962306a36Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); 80062306a36Sopenharmony_ci reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT); 80162306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci mdelay(20); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); 80662306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); 80962306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci mdelay(100); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci P54U_READ(NET2280_DEV_U32, &devreg->int_ident); 81462306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* start up the firmware */ 81762306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 81862306a36Sopenharmony_ci cpu_to_le32(ISL38XX_INT_IDENT_INIT)); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, 82162306a36Sopenharmony_ci cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 82462306a36Sopenharmony_ci cpu_to_le32(NET2280_PCI_INTA_INTERRUPT_ENABLE | 82562306a36Sopenharmony_ci NET2280_USB_INTERRUPT_ENABLE)); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->dev_int, 82862306a36Sopenharmony_ci cpu_to_le32(ISL38XX_DEV_INT_RESET)); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci err = usb_interrupt_msg(priv->udev, 83162306a36Sopenharmony_ci usb_rcvbulkpipe(priv->udev, P54U_PIPE_INT), 83262306a36Sopenharmony_ci buf, sizeof(__le32), &alen, 1000); 83362306a36Sopenharmony_ci if (err || alen != sizeof(__le32)) 83462306a36Sopenharmony_ci goto fail; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci P54U_READ(NET2280_DEV_U32, &devreg->int_ident); 83762306a36Sopenharmony_ci P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (!(reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT))) 84062306a36Sopenharmony_ci err = -EINVAL; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); 84362306a36Sopenharmony_ci P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, 84462306a36Sopenharmony_ci cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci#undef P54U_WRITE 84762306a36Sopenharmony_ci#undef P54U_READ 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cifail: 85062306a36Sopenharmony_ci kfree(buf); 85162306a36Sopenharmony_ci return err; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic int p54_find_type(struct p54u_priv *priv) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci int i; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci for (i = 0; i < __NUM_P54U_HWTYPES; i++) 85962306a36Sopenharmony_ci if (p54u_fwlist[i].type == priv->hw_type) 86062306a36Sopenharmony_ci break; 86162306a36Sopenharmony_ci if (i == __NUM_P54U_HWTYPES) 86262306a36Sopenharmony_ci return -EOPNOTSUPP; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci return i; 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic int p54u_start_ops(struct p54u_priv *priv) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci struct ieee80211_hw *dev = priv->common.hw; 87062306a36Sopenharmony_ci int ret; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci ret = p54_parse_firmware(dev, priv->fw); 87362306a36Sopenharmony_ci if (ret) 87462306a36Sopenharmony_ci goto err_out; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci ret = p54_find_type(priv); 87762306a36Sopenharmony_ci if (ret < 0) 87862306a36Sopenharmony_ci goto err_out; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (priv->common.fw_interface != p54u_fwlist[ret].intf) { 88162306a36Sopenharmony_ci dev_err(&priv->udev->dev, "wrong firmware, please get " 88262306a36Sopenharmony_ci "a firmware for \"%s\" and try again.\n", 88362306a36Sopenharmony_ci p54u_fwlist[ret].hw); 88462306a36Sopenharmony_ci ret = -ENODEV; 88562306a36Sopenharmony_ci goto err_out; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci ret = priv->upload_fw(dev); 88962306a36Sopenharmony_ci if (ret) 89062306a36Sopenharmony_ci goto err_out; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci ret = p54u_open(dev); 89362306a36Sopenharmony_ci if (ret) 89462306a36Sopenharmony_ci goto err_out; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci ret = p54_read_eeprom(dev); 89762306a36Sopenharmony_ci if (ret) 89862306a36Sopenharmony_ci goto err_stop; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci p54u_stop(dev); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci ret = p54_register_common(dev, &priv->udev->dev); 90362306a36Sopenharmony_ci if (ret) 90462306a36Sopenharmony_ci goto err_stop; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci return 0; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cierr_stop: 90962306a36Sopenharmony_ci p54u_stop(dev); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cierr_out: 91262306a36Sopenharmony_ci /* 91362306a36Sopenharmony_ci * p54u_disconnect will do the rest of the 91462306a36Sopenharmony_ci * cleanup 91562306a36Sopenharmony_ci */ 91662306a36Sopenharmony_ci return ret; 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cistatic void p54u_load_firmware_cb(const struct firmware *firmware, 92062306a36Sopenharmony_ci void *context) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci struct p54u_priv *priv = context; 92362306a36Sopenharmony_ci struct usb_device *udev = priv->udev; 92462306a36Sopenharmony_ci struct usb_interface *intf = priv->intf; 92562306a36Sopenharmony_ci int err; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (firmware) { 92862306a36Sopenharmony_ci priv->fw = firmware; 92962306a36Sopenharmony_ci err = p54u_start_ops(priv); 93062306a36Sopenharmony_ci } else { 93162306a36Sopenharmony_ci err = -ENOENT; 93262306a36Sopenharmony_ci dev_err(&udev->dev, "Firmware not found.\n"); 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci complete(&priv->fw_wait_load); 93662306a36Sopenharmony_ci /* 93762306a36Sopenharmony_ci * At this point p54u_disconnect may have already freed 93862306a36Sopenharmony_ci * the "priv" context. Do not use it anymore! 93962306a36Sopenharmony_ci */ 94062306a36Sopenharmony_ci priv = NULL; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci if (err) { 94362306a36Sopenharmony_ci dev_err(&intf->dev, "failed to initialize device (%d)\n", err); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci usb_lock_device(udev); 94662306a36Sopenharmony_ci usb_driver_release_interface(&p54u_driver, intf); 94762306a36Sopenharmony_ci usb_unlock_device(udev); 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci usb_put_intf(intf); 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic int p54u_load_firmware(struct ieee80211_hw *dev, 95462306a36Sopenharmony_ci struct usb_interface *intf) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 95762306a36Sopenharmony_ci struct p54u_priv *priv = dev->priv; 95862306a36Sopenharmony_ci struct device *device = &udev->dev; 95962306a36Sopenharmony_ci int err, i; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci init_completion(&priv->fw_wait_load); 96462306a36Sopenharmony_ci i = p54_find_type(priv); 96562306a36Sopenharmony_ci if (i < 0) 96662306a36Sopenharmony_ci return i; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci dev_info(&priv->udev->dev, "Loading firmware file %s\n", 96962306a36Sopenharmony_ci p54u_fwlist[i].fw); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci usb_get_intf(intf); 97262306a36Sopenharmony_ci err = request_firmware_nowait(THIS_MODULE, 1, p54u_fwlist[i].fw, 97362306a36Sopenharmony_ci device, GFP_KERNEL, priv, 97462306a36Sopenharmony_ci p54u_load_firmware_cb); 97562306a36Sopenharmony_ci if (err) { 97662306a36Sopenharmony_ci dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s " 97762306a36Sopenharmony_ci "(%d)!\n", p54u_fwlist[i].fw, err); 97862306a36Sopenharmony_ci usb_put_intf(intf); 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci return err; 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic int p54u_probe(struct usb_interface *intf, 98562306a36Sopenharmony_ci const struct usb_device_id *id) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 98862306a36Sopenharmony_ci struct ieee80211_hw *dev; 98962306a36Sopenharmony_ci struct p54u_priv *priv; 99062306a36Sopenharmony_ci int err; 99162306a36Sopenharmony_ci unsigned int i, recognized_pipes; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci dev = p54_init_common(sizeof(*priv)); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci if (!dev) { 99662306a36Sopenharmony_ci dev_err(&udev->dev, "(p54usb) ieee80211 alloc failed\n"); 99762306a36Sopenharmony_ci return -ENOMEM; 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci priv = dev->priv; 100162306a36Sopenharmony_ci priv->hw_type = P54U_INVALID_HW; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci SET_IEEE80211_DEV(dev, &intf->dev); 100462306a36Sopenharmony_ci usb_set_intfdata(intf, dev); 100562306a36Sopenharmony_ci priv->udev = udev; 100662306a36Sopenharmony_ci priv->intf = intf; 100762306a36Sopenharmony_ci skb_queue_head_init(&priv->rx_queue); 100862306a36Sopenharmony_ci init_usb_anchor(&priv->submitted); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci /* really lazy and simple way of figuring out if we're a 3887 */ 101162306a36Sopenharmony_ci /* TODO: should just stick the identification in the device table */ 101262306a36Sopenharmony_ci i = intf->altsetting->desc.bNumEndpoints; 101362306a36Sopenharmony_ci recognized_pipes = 0; 101462306a36Sopenharmony_ci while (i--) { 101562306a36Sopenharmony_ci switch (intf->altsetting->endpoint[i].desc.bEndpointAddress) { 101662306a36Sopenharmony_ci case P54U_PIPE_DATA: 101762306a36Sopenharmony_ci case P54U_PIPE_MGMT: 101862306a36Sopenharmony_ci case P54U_PIPE_BRG: 101962306a36Sopenharmony_ci case P54U_PIPE_DEV: 102062306a36Sopenharmony_ci case P54U_PIPE_DATA | USB_DIR_IN: 102162306a36Sopenharmony_ci case P54U_PIPE_MGMT | USB_DIR_IN: 102262306a36Sopenharmony_ci case P54U_PIPE_BRG | USB_DIR_IN: 102362306a36Sopenharmony_ci case P54U_PIPE_DEV | USB_DIR_IN: 102462306a36Sopenharmony_ci case P54U_PIPE_INT | USB_DIR_IN: 102562306a36Sopenharmony_ci recognized_pipes++; 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci priv->common.open = p54u_open; 102962306a36Sopenharmony_ci priv->common.stop = p54u_stop; 103062306a36Sopenharmony_ci if (recognized_pipes < P54U_PIPE_NUMBER) { 103162306a36Sopenharmony_ci#ifdef CONFIG_PM 103262306a36Sopenharmony_ci /* ISL3887 needs a full reset on resume */ 103362306a36Sopenharmony_ci udev->reset_resume = 1; 103462306a36Sopenharmony_ci#endif /* CONFIG_PM */ 103562306a36Sopenharmony_ci err = p54u_device_reset(dev); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci priv->hw_type = P54U_3887; 103862306a36Sopenharmony_ci dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr); 103962306a36Sopenharmony_ci priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr); 104062306a36Sopenharmony_ci priv->common.tx = p54u_tx_lm87; 104162306a36Sopenharmony_ci priv->upload_fw = p54u_upload_firmware_3887; 104262306a36Sopenharmony_ci } else { 104362306a36Sopenharmony_ci priv->hw_type = P54U_NET2280; 104462306a36Sopenharmony_ci dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr); 104562306a36Sopenharmony_ci priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr); 104662306a36Sopenharmony_ci priv->common.tx = p54u_tx_net2280; 104762306a36Sopenharmony_ci priv->upload_fw = p54u_upload_firmware_net2280; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci err = p54u_load_firmware(dev, intf); 105062306a36Sopenharmony_ci if (err) 105162306a36Sopenharmony_ci p54_free_common(dev); 105262306a36Sopenharmony_ci return err; 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_cistatic void p54u_disconnect(struct usb_interface *intf) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci struct ieee80211_hw *dev = usb_get_intfdata(intf); 105862306a36Sopenharmony_ci struct p54u_priv *priv; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (!dev) 106162306a36Sopenharmony_ci return; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci priv = dev->priv; 106462306a36Sopenharmony_ci wait_for_completion(&priv->fw_wait_load); 106562306a36Sopenharmony_ci p54_unregister_common(dev); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci release_firmware(priv->fw); 106862306a36Sopenharmony_ci p54_free_common(dev); 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_cistatic int p54u_pre_reset(struct usb_interface *intf) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci struct ieee80211_hw *dev = usb_get_intfdata(intf); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (!dev) 107662306a36Sopenharmony_ci return -ENODEV; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci p54u_stop(dev); 107962306a36Sopenharmony_ci return 0; 108062306a36Sopenharmony_ci} 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_cistatic int p54u_resume(struct usb_interface *intf) 108362306a36Sopenharmony_ci{ 108462306a36Sopenharmony_ci struct ieee80211_hw *dev = usb_get_intfdata(intf); 108562306a36Sopenharmony_ci struct p54u_priv *priv; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (!dev) 108862306a36Sopenharmony_ci return -ENODEV; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci priv = dev->priv; 109162306a36Sopenharmony_ci if (unlikely(!(priv->upload_fw && priv->fw))) 109262306a36Sopenharmony_ci return 0; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci return priv->upload_fw(dev); 109562306a36Sopenharmony_ci} 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic int p54u_post_reset(struct usb_interface *intf) 109862306a36Sopenharmony_ci{ 109962306a36Sopenharmony_ci struct ieee80211_hw *dev = usb_get_intfdata(intf); 110062306a36Sopenharmony_ci struct p54u_priv *priv; 110162306a36Sopenharmony_ci int err; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci err = p54u_resume(intf); 110462306a36Sopenharmony_ci if (err) 110562306a36Sopenharmony_ci return err; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci /* reinitialize old device state */ 110862306a36Sopenharmony_ci priv = dev->priv; 110962306a36Sopenharmony_ci if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED) 111062306a36Sopenharmony_ci ieee80211_restart_hw(dev); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci return 0; 111362306a36Sopenharmony_ci} 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci#ifdef CONFIG_PM 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_cistatic int p54u_suspend(struct usb_interface *intf, pm_message_t message) 111862306a36Sopenharmony_ci{ 111962306a36Sopenharmony_ci return p54u_pre_reset(intf); 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci#endif /* CONFIG_PM */ 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cistatic struct usb_driver p54u_driver = { 112562306a36Sopenharmony_ci .name = "p54usb", 112662306a36Sopenharmony_ci .id_table = p54u_table, 112762306a36Sopenharmony_ci .probe = p54u_probe, 112862306a36Sopenharmony_ci .disconnect = p54u_disconnect, 112962306a36Sopenharmony_ci .pre_reset = p54u_pre_reset, 113062306a36Sopenharmony_ci .post_reset = p54u_post_reset, 113162306a36Sopenharmony_ci#ifdef CONFIG_PM 113262306a36Sopenharmony_ci .suspend = p54u_suspend, 113362306a36Sopenharmony_ci .resume = p54u_resume, 113462306a36Sopenharmony_ci .reset_resume = p54u_resume, 113562306a36Sopenharmony_ci#endif /* CONFIG_PM */ 113662306a36Sopenharmony_ci .soft_unbind = 1, 113762306a36Sopenharmony_ci .disable_hub_initiated_lpm = 1, 113862306a36Sopenharmony_ci}; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_cimodule_usb_driver(p54u_driver); 1141