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), &reg);\
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