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