linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices
       [not found] <20200924151910.21693-1-srini.raju@purelifi.com>
@ 2020-09-28 10:19 ` Srinivasan Raju
  2020-09-28 12:07   ` Joe Perches
                     ` (19 more replies)
  0 siblings, 20 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-09-28 10:19 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Mauro Carvalho Chehab, Rob Herring, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices, which provide lightweight, highspeed secure and
fully networked wireless communications via light.

This driver implementation has been based on the zd1211rw driver

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management
The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
---
 MAINTAINERS                                   |    5 +
 drivers/net/wireless/Kconfig                  |    1 +
 drivers/net/wireless/Makefile                 |    1 +
 drivers/net/wireless/purelifi/Kconfig         |   38 +
 drivers/net/wireless/purelifi/Makefile        |    3 +
 drivers/net/wireless/purelifi/chip.c          |  120 ++
 drivers/net/wireless/purelifi/chip.h          |   81 +
 drivers/net/wireless/purelifi/def.h           |   46 +
 drivers/net/wireless/purelifi/log.h           |   15 +
 drivers/net/wireless/purelifi/mac.c           |  957 +++++++++
 drivers/net/wireless/purelifi/mac.h           |  178 ++
 .../net/wireless/purelifi/mac_usb_interface.h |   38 +
 drivers/net/wireless/purelifi/usb.c           | 1872 +++++++++++++++++
 drivers/net/wireless/purelifi/usb.h           |  148 ++
 14 files changed, 3503 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/chip.c
 create mode 100644 drivers/net/wireless/purelifi/chip.h
 create mode 100644 drivers/net/wireless/purelifi/def.h
 create mode 100644 drivers/net/wireless/purelifi/log.h
 create mode 100644 drivers/net/wireless/purelifi/mac.c
 create mode 100644 drivers/net/wireless/purelifi/mac.h
 create mode 100644 drivers/net/wireless/purelifi/mac_usb_interface.h
 create mode 100644 drivers/net/wireless/purelifi/usb.c
 create mode 100644 drivers/net/wireless/purelifi/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 42c69d2eeece..0e8cd1decafe 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14098,6 +14098,11 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PUREILIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+S:	Maintained
+F:	drivers/net/wireless/purelifi
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 170a64e67709..b87da3139f94 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -48,6 +48,7 @@ source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
 source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 
 config PCMCIA_RAYCS
 	tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..c403dda7a14e 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
 obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
+obj-$(WLAN_VENDOR_PURELIFI) += purelifi/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..ff05eaf0a8d4
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+config PURELIFI
+
+	tristate "pureLiFi device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   Say Y if you want to use LiFi.
+
+	   This driver makes the adapter appear as a normal WLAN interface.
+
+	   The pureLiFi device requires external STA firmware to be loaded.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called purelifi.
+
+config PURELIFI_AP
+
+	tristate "pureLiFi device Access Point support"
+	depends on PURELIFI
+	help
+	   Say Y if you want to use LiFi in Access Point mode.
+
+	   The pureLiFi device requires external AP firmware to be loaded.
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..1f20055e741f
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PURELIFI)		:= purelifi.o
+purelifi-objs 		+= chip.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
new file mode 100644
index 000000000000..32e6984703b6
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "def.h"
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+#include "log.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	PURELIFI_ASSERT(!mutex_is_locked(&chip->mutex));
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval || (chip->beacon_set &&
+			  chip->beacon_interval == interval)) {
+		return 0;
+	}
+
+	chip->beacon_interval = interval;
+	chip->beacon_set = true;
+	return usb_write_req((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+static void print_chip_info(struct purelifi_chip *chip)
+{
+	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %04hx:%04hx v%04hx  %02x-%02x-%02x %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr[0], addr[1], addr[2],
+		speed(udev->speed));
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	print_chip_info(chip);
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
+{
+	int r;
+
+	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		pl_dev_err(purelifi_chip_dev(chip), "%s::r=%d",
+			   __func__, r);
+	return r;
+}
+
+int purelifi_chip_switch_radio_on(struct purelifi_chip *chip)
+{
+	return purelifi_chip_switch_radio(chip, 1);
+}
+
+int purelifi_chip_switch_radio_off(struct purelifi_chip *chip)
+{
+	return purelifi_chip_switch_radio(chip, 0);
+}
+
+void purelifi_chip_enable_rxtx_urb_complete(struct urb *urb_struct)
+{
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	u32 value;
+
+	value = 0;
+	usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u32 rate)
+{
+	int r;
+	static struct purelifi_chip *chip_p;
+
+	if (chip)
+		chip_p = chip;
+	if (!chip_p)
+		return -EINVAL;
+
+	r = usb_write_req((const u8 *)&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		pl_dev_err(purelifi_chip_dev(chip), "%s::r=%d",
+			   __func__, r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/chip.h b/drivers/net/wireless/purelifi/chip.h
new file mode 100644
index 000000000000..892ecaf7d5d3
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	u16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb
+							)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+void purelifi_chip_release(struct purelifi_chip *chip);
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+/* Locking functions for reading and writing registers.
+ * The different parameters are intentional.
+ */
+int purelifi_chip_switch_radio_on(struct purelifi_chip *chip);
+int purelifi_chip_switch_radio_off(struct purelifi_chip *chip);
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u32 rate);
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr
+					)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1 << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/def.h b/drivers/net/wireless/purelifi/def.h
new file mode 100644
index 000000000000..295dfb45b568
--- /dev/null
+++ b/drivers/net/wireless/purelifi/def.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_DEF_H
+#define _PURELIFI_DEF_H
+
+#include <linux/kernel.h>
+#include <linux/stringify.h>
+#include <linux/device.h>
+
+typedef u16 __nocast purelifi_addr_t;
+
+#define dev_printk_f(level, dev, fmt, args...) \
+	dev_printk(level, dev, "%s() " fmt, __func__, ##args)
+
+#ifdef DEBUG
+#  define dev_dbg_f(dev, fmt, args...) \
+	  dev_printk_f(KERN_DEBUG, dev, fmt, ## args)
+#  define dev_dbg_f_limit(dev, fmt, args...) do { \
+	if (net_ratelimit()) \
+		dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \
+} while (0)
+#  define dev_dbg_f_cond(dev, cond, fmt, args...) ({ \
+	bool __cond = !!(cond); \
+	if (unlikely(__cond)) \
+		dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \
+})
+#else
+#  define dev_dbg_f(dev, fmt, args...)  (void)(dev)
+#  define dev_dbg_f_limit(dev, fmt, args...) (void)(dev)
+#  define dev_dbg_f_cond(dev, cond, fmt, args...) (void)(dev)
+#endif /* DEBUG */
+
+#ifdef DEBUG
+#  define PURELIFI_ASSERT(x) \
+do { \
+	if (unlikely(!(x))) { \
+		pr_debug("%s:%d ASSERT %s VIOLATED!\n", \
+			__FILE__, __LINE__, __stringify(x)); \
+		dump_stack(); \
+	} \
+} while (0)
+#else
+#  define PURELIFI_ASSERT(x) do { } while (0)
+#endif
+
+#endif /* _PURELIFI_DEF_H */
diff --git a/drivers/net/wireless/purelifi/log.h b/drivers/net/wireless/purelifi/log.h
new file mode 100644
index 000000000000..5e275da9ba12
--- /dev/null
+++ b/drivers/net/wireless/purelifi/log.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_LOG
+#define _PURELIFI_LOG
+
+#ifdef PL_DEBUG
+#define pl_dev_info(dev, fmt, arg...) dev_info(dev, fmt, ##arg)
+#define pl_dev_warn(dev, fmt, arg...) dev_warn(dev, fmt, ##arg)
+#define  pl_dev_err(dev, fmt, arg...) dev_err(dev, fmt, ##arg)
+#else
+#define pl_dev_info(dev, fmt, arg...) (void)(dev)
+#define pl_dev_warn(dev, fmt, arg...) (void)(dev)
+#define  pl_dev_err(dev, fmt, arg...) (void)(dev)
+#endif
+#endif
diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
new file mode 100644
index 000000000000..c6be289f8431
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.c
@@ -0,0 +1,957 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "def.h"
+#include "chip.h"
+#include "mac.h"
+#include "log.h"
+
+#ifndef IEEE80211_BAND_2GHZ
+#define IEEE80211_BAND_2GHZ NL80211_BAND_2GHZ
+#endif
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M, },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 },
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
+{
+#ifdef CONFIG_PURELIFI_AP
+	struct ieee80211_vif *vif = purelifi_usb_to_mac(usb)->vif;
+	struct ieee80211_sta *sta = ieee80211_find_sta(vif, mac);
+
+	if (!sta) {
+		//Print Debugs
+	} else {
+		ieee80211_sta_block_awake(purelifi_usb_to_hw(usb), sta,
+					  block);
+	}
+#else
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+#endif
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		pl_dev_warn(purelifi_mac_dev(mac), "%s::purelifi_chip_init_hw	failed. (%d)\n",
+			    __func__, r);
+		goto out;
+	}
+	PURELIFI_ASSERT(!irqs_disabled());
+
+	r = regulatory_hint(hw->wiphy, "CA");
+out:
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	regulatory_hint(hw->wiphy, "EU");
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+	struct sk_buff *skb;
+	struct sk_buff_head *ack_wait_queue = &mac->ack_wait_queue;
+
+	pl_dev_info(purelifi_mac_dev(mac), "%s", __func__);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	/* The order here deliberately is a little different from the open()
+	 * method, since we need to make sure there is no opportunity for RX
+	 * frames to be processed by mac80211 after we have stopped it.
+	 */
+
+	return;  //don't allow stop for debugging rx_urb_complete failure
+	purelifi_chip_switch_radio_off(chip);
+
+	while ((skb = skb_dequeue(ack_wait_queue)))
+		dev_kfree_skb_any(skb);
+	return;  //don't allow stop for debugging rx_urb_complete failure
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	struct purelifi_mc_hash multicast_hash;
+	int beacon_interval, beacon_period;
+
+	dev_dbg_f(purelifi_mac_dev(mac), "\n");
+
+	spin_lock_irq(&mac->lock);
+	multicast_hash = mac->multicast_hash;
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type == NL80211_IFTYPE_MESH_POINT ||
+	    mac->type == NL80211_IFTYPE_ADHOC ||
+			mac->type == NL80211_IFTYPE_AP) {
+		if (mac->vif) {
+			beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+			if (beacon) {
+				purelifi_mac_config_beacon(mac->hw, beacon,
+							   false);
+				kfree_skb(beacon);
+				/* Returned skb is used only once and lowlevel
+				 *  driver is responsible for freeing it.
+				 */
+			}
+		}
+
+		purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+					     beacon_period, mac->type);
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+	}
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status - reports tx status of a packet if required
+ * @hw - a &struct ieee80211_hw pointer
+ * @skb - a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success - True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+		int ackssi, struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1, retry = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status) {
+		success = !tx_status->failure;
+		retry = tx_status->retry + success;
+	}
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		//FIXME : do we need to fill in anything ?
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		// ieee80211_tx_status_irqsafe(hw, skb);
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					mac->ack_signal : 0,
+					NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr)
+{
+	return usb_write_req((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) >= sizeof(struct purelifi_ctrlset)) {
+		cs = (struct purelifi_ctrlset *)skb_push(skb,
+				sizeof(struct purelifi_ctrlset));
+	} else {
+		pl_dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs->id = USB_REQ_DATA_TX;
+	cs->payload_len_nw = frag_len;
+	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
+		- sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned */
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) >= (3 - tmp)) {
+			skb_put(skb, 4 - tmp);
+		} else {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				return 1;
+			}
+		}
+		cs->len +=  4 - tmp;
+	}
+
+	//check if not multiple of 512
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) >= 4) {
+			skb_put(skb, 4);
+		} else {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen b/c
+				 * sufficient headroom was reserved
+				 */
+				return 1;
+			}
+		}
+
+		cs->len +=  4;
+	}
+
+	cs->id = TO_NETWORK(cs->id);
+	cs->len = TO_NETWORK(cs->len);
+	cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
+
+	return 0;
+}
+
+/**
+ * purelifi_op_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+extern void send_packet_from_data_queue(struct purelifi_usb *usb);
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+	info->rate_driver_data[0] = hw;
+
+	if (skb->data[24] != 0x08 && skb->len == 116)
+		pl_dev_info(purelifi_mac_dev(mac), "%s::ping\n", __func__);
+
+	if (skb->data[24] != 0x08) {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
+					USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	} else {
+		u8 dst_mac[ETH_ALEN];
+		u8 sidx;
+		bool found = false;
+
+		memcpy(dst_mac, &skb->data[28], ETH_ALEN);
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (usb->tx.station[sidx].flag &
+					STATION_CONNECTED_FLAG) {
+				if (!memcmp(usb->tx.station[sidx].mac,
+					    dst_mac, ETH_ALEN)) {
+					found = true;
+					break;
+				}
+			}
+		}
+
+		// Default to broadcast address for unknown MACs.
+
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		// Stop OS from sending any packets,
+		// if the queue is half full.
+		if (skb_queue_len(&usb->tx.station[sidx].data_list) > 60)
+			block_queue(usb, usb->tx.station[sidx].mac, true);
+
+		// Schedule packet for transmission if queue is not full
+		if (skb_queue_len(&usb->tx.station[sidx].data_list) < 256) {
+			skb_queue_tail(&usb->tx.station[sidx].data_list, skb);
+			send_packet_from_data_queue(usb);
+		} else {
+			dev_kfree_skb(skb);
+		}
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+		      struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	int found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+	pl_dev_info(purelifi_mac_dev(mac), "%s::ACK Received", __func__);
+	return 0;
+
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++) {
+			skb = __skb_dequeue(q);
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					mac->ack_signal : 0,
+					NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = 1;
+		mac->ack_signal = stats->signal;
+
+		/* Prevent pending tx-packet on AP-mode */
+		if (mac->type == NL80211_IFTYPE_AP) {
+			skb = __skb_dequeue(q);
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_signal, NULL);
+			mac->ack_pending = 0;
+		}
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	u32 crc_error_cnt_low, crc_error_cnt_high;
+	int sidx;
+
+	// Packet blockade during disabled interface.
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = IEEE80211_BAND_2GHZ;
+	mac->rssi      = -15 * ntohs(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	crc_error_cnt_low = status->crc_error_count;
+	crc_error_cnt_high = status->crc_error_count >> 32;
+	mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
+		ntohl(crc_error_cnt_high);
+
+	if (!bad_frame &&
+	    filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = TO_HOST(*(u32 *)buffer);
+
+	// MTU = 1500, MAC header = 36, CRC = 4, sum = 1540
+	if (payload_length > 1560) {
+		pl_dev_info(purelifi_mac_dev(mac), "%s::payload_length=%u\n",
+			    __func__, payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!memcmp(&buffer[10],
+			    mac->chip.usb.tx.station[sidx].mac,
+					ETH_ALEN)) {
+			if (mac->chip.usb.tx.station[sidx].flag &
+					STATION_CONNECTED_FLAG) {
+				mac->chip.usb.tx.station[sidx].flag |=
+					STATION_HEARTBEAT_FLAG;
+				break;
+			}
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (!(mac->chip.usb.tx.station[sidx].flag &
+						STATION_CONNECTED_FLAG
+			     )) {
+				memcpy(mac->chip.usb.tx.station[sidx].mac,
+				       &buffer[10],
+						ETH_ALEN);
+				mac->chip.usb.tx.station[sidx].flag |=
+					STATION_CONNECTED_FLAG;
+				mac->chip.usb.tx.station[sidx].flag |=
+					STATION_HEARTBEAT_FLAG;
+				break;
+			}
+		}
+	}
+
+	if (buffer[0] == 0xb0) {
+		pl_dev_info(purelifi_mac_dev(mac), "%s:Authentication req\n",
+			    __func__);
+	} else if (buffer[0] == 0x40) {
+		pl_dev_info(purelifi_mac_dev(mac), "%s:Probe request\n",
+			    __func__);
+	} else if (buffer[0] == 0x0) {
+		pl_dev_info(purelifi_mac_dev(mac), "%s:Association request\n",
+			    __func__);
+	}
+
+	if (buffer[0] == 0xb0) {
+		min_exp_seq_nmb = 0;
+	} else if (buffer[0] == 0x08) {
+		unsigned short int seq_nmb = (buffer[23] << 4) |
+			((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000)) {
+			// return 0;
+		} else {
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		}
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+	if (need_padding) {
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+	}
+
+	/* FIXME : could we avoid this big memcpy ? */
+	memcpy(skb_put(skb, payload_length), buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	pl_dev_info(purelifi_mac_dev(mac), "%s\n", __func__);
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_MONITOR:
+		pl_dev_info(purelifi_mac_dev(mac),
+			    "%s::NL80211_IFTYPE_MONITOR\n",
+				__func__);
+		break;
+	case NL80211_IFTYPE_MESH_POINT:
+		pl_dev_info(purelifi_mac_dev(mac),
+			    "%s::NL80211_IFTYPE_MESH_POINT\n",
+				__func__);
+		break;
+	case NL80211_IFTYPE_STATION:
+		pl_dev_info(purelifi_mac_dev(mac),
+			    "%s::NL80211_IFTYPE_STATION\n",
+				__func__);
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		pl_dev_info(purelifi_mac_dev(mac),
+			    "%s::NL80211_IFTYPE_ADHOC\n",
+				__func__);
+		break;
+	case NL80211_IFTYPE_AP:
+		pl_dev_info(purelifi_mac_dev(mac),
+			    "%s::vif->type=NL80211_IFTYPE_AP\n",
+				__func__);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+	mac->type = vif->type;
+	mac->vif = vif;
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	pl_dev_info(purelifi_mac_dev(mac), "%s\n", __func__);
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	spin_lock_irq(&mac->lock);
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+		unsigned int *new_flags,
+		u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+	int r;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	//purelifi_chip_set_multicast_hash(&mac->chip, &hash);
+
+	if (changed_flags & FIF_CONTROL) {
+		/* TODO: Re-enable these lines?
+		 * r = set_rx_filter(mac);
+		 * if (r)
+		 * pl_dev_err(purelifi_mac_dev(mac),
+		 *  "set_rx_filter error %d\n", r);
+		 */
+		r = 0;
+	}
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+		struct ieee80211_bss_conf *bss_conf,
+		u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	pl_dev_info(purelifi_mac_dev(mac), "%s\n", __func__);
+	pl_dev_info(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type == NL80211_IFTYPE_MESH_POINT ||
+	    mac->type == NL80211_IFTYPE_ADHOC ||
+	    mac->type == NL80211_IFTYPE_AP) {
+		associated = true;
+		if (changes & BSS_CHANGED_BEACON) {
+			struct sk_buff *beacon = ieee80211_beacon_get(hw,
+					vif);
+
+			if (beacon) {
+				purelifi_mac_config_beacon(hw, beacon, false);
+				kfree_skb(beacon);
+				/*Returned skb is used only once and
+				 * low-level driver is
+				 * responsible for freeing it.
+				 */
+			}
+		}
+
+		if (changes & BSS_CHANGED_BEACON_ENABLED) {
+			u16 interval = 0;
+			u8 period = 0;
+
+			if (bss_conf->enable_beacon) {
+				period = bss_conf->dtim_period;
+				interval = bss_conf->beacon_int;
+			}
+
+			spin_lock_irq(&mac->lock);
+			mac->beacon.period = period;
+			mac->beacon.interval = interval;
+			mac->beacon.last_update = jiffies;
+			spin_unlock_irq(&mac->lock);
+
+			purelifi_set_beacon_interval(&mac->chip, interval,
+						     period, mac->type);
+		}
+	} else {
+		associated = is_valid_ether_addr(bss_conf->bssid);
+	}
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+		struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg_f(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band;
+
+#ifdef ieee80211_hw_set
+	_ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
+	_ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
+	_ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
+	_ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);
+#else
+	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+		IEEE80211_HW_SIGNAL_DBM |
+		IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+		IEEE80211_HW_MFP_CAPABLE;
+#endif
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_MESH_POINT) |
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC) |
+		BIT(NL80211_IFTYPE_AP);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	//4 for 32 bit alignment if no tailroom
+
+	//Tell mac80211 that we support multi rate retries
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/mac.h b/drivers/net/wireless/purelifi/mac.h
new file mode 100644
index 000000000000..35e28cc4dffb
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define MODULATION_RATE_BPSK_1_2  0
+#define MODULATION_RATE_BPSK_3_4  (MODULATION_RATE_BPSK_1_2  + 1)
+#define MODULATION_RATE_QPSK_1_2  (MODULATION_RATE_BPSK_3_4  + 1)
+#define MODULATION_RATE_QPSK_3_4  (MODULATION_RATE_QPSK_1_2  + 1)
+#define MODULATION_RATE_QAM16_1_2 (MODULATION_RATE_QPSK_3_4  + 1)
+#define MODULATION_RATE_QAM16_3_4 (MODULATION_RATE_QAM16_1_2 + 1)
+#define MODULATION_RATE_QAM64_1_2 (MODULATION_RATE_QAM16_3_4 + 1)
+#define MODULATION_RATE_QAM64_3_4 (MODULATION_RATE_QAM64_1_2 + 1)
+#define MODULATION_RATE_AUTO      (MODULATION_RATE_QAM64_3_4 + 1)
+#define MODULATION_RATE_NUM       (MODULATION_RATE_AUTO      + 1)
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             modulation;
+	u8             control;
+	u8             service;
+	u8             pad;
+	__le16         packet_length;
+	__le16         current_length;
+	__le16          next_frame_length;
+	__le16         tx_length;
+	u32            payload_len_nw;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	spinlock_t intr_lock; /* not used : interrupt lock for mac */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	unsigned int ack_pending:1;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
+		*chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block);
+
+#ifdef DEBUG
+void purelifi_dump_rx_status(const struct rx_status *status);
+#else
+#define purelifi_dump_rx_status(status)
+#endif
+
+#endif
diff --git a/drivers/net/wireless/purelifi/mac_usb_interface.h b/drivers/net/wireless/purelifi/mac_usb_interface.h
new file mode 100644
index 000000000000..20e96671e0a7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac_usb_interface.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	u16 rssi;
+	u8  rate_idx;
+	u8  pad;
+	u64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             buf[512];
+};
+
+#endif
diff --git a/drivers/net/wireless/purelifi/usb.c b/drivers/net/wireless/purelifi/usb.c
new file mode 100644
index 000000000000..759095910175
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.c
@@ -0,0 +1,1872 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "def.h"
+#include "mac.h"
+#include "usb.h"
+#include "log.h"
+
+#define FCS_LEN 4
+
+#define PURELIFI_X_VENDOR_ID_0  0x16C1
+#define PURELIFI_X_PRODUCT_ID_0 0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0 0x2EF5
+
+#ifdef CONFIG_PURELIFI_AP
+#define PURELIFI_XL_PRODUCT_ID_0 0x000B
+#else
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A
+#endif
+
+#undef  LOAD_MAC_AND_SERIAL_FROM_FILE
+#undef  LOAD_MAC_AND_SERIAL_FROM_FLASH
+#define LOAD_MAC_AND_SERIAL_FROM_EP0
+
+#define PROC_FOLDER "purelifi"
+#define MODULATION_FILENAME "modulation"
+#define QUEUE_MANAGEMENT_DEBUG 0
+
+struct proc_dir_entry *proc_folder;
+struct proc_dir_entry *modulation_proc_entry;
+static atomic_t data_queue_flag;
+
+#define USB_ALLOC_URB(a, b) usb_alloc_urb(a, b)
+#define USB_ALLOC_COHERENT(a, b, c, d) usb_alloc_coherent(a, b, c, d)
+#define USB_FREE_COHERENT(a, b, c, d) usb_free_coherent(a, b, c, d)
+#define USB_FREE_URB(a) usb_free_urb(a)
+
+//Define the duration of the Tx retry backoff timer (in milliseconds).
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+//Define the duration of the Tx retry backoff timer (in jiffies).
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+MODULE_FIRMWARE("purelifi/li_fi_x/fpga.bit");
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+
+	spin_lock_irqsave(&usb->tx.lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if ((usb->tx.station[sidx].flag &
+					STATION_CONNECTED_FLAG)) {
+			if (!(usb->tx.station[sidx].flag &
+						STATION_FIFO_FULL_FLAG)) {
+				skb = skb_peek(&usb->tx.station
+						[sidx].data_list);
+			}
+		}
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&usb->tx.station[sidx].data_list);
+		usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				    tx_urb_complete, skb);
+		if (skb_queue_len(&usb->tx.station[sidx].data_list)
+				<= 60) {
+			block_queue(usb, usb->tx.station[sidx].mac,
+				    false);
+		}
+	}
+	spin_unlock_irqrestore(&usb->tx.lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+int rx_usb_enabled;
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_rx *rx;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+
+	if (!urb) {
+		pl_dev_err(purelifi_usb_dev(usb),
+			   "%s:URB is NULL.\n", __func__);
+		return;
+	} else if (!urb->context) {
+		pl_dev_err(purelifi_usb_dev(usb),
+			   "%s:!urb->context is NULL.\n", __func__);
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb,
+			  urb->status);
+		return;
+	default:
+		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb,
+			  urb->status);
+			goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
+
+	rx = &usb->rx;
+	tx = &usb->tx;
+
+	if (urb->actual_length == 8) {
+		u16 status = buffer[7];//SM (buffer[6] << 8) | buffer[7];
+		u8 sidx;
+
+		pl_dev_info(&usb->intf->dev, "%s::Received status=%u\n",
+			    __func__, status
+			   );
+		pl_dev_info(&usb->intf->dev,
+			    "%s::Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+			    __func__,
+			    buffer[0],
+			    buffer[1],
+			    buffer[2],
+			    buffer[3],
+			    buffer[4],
+			    buffer[5]
+			  );
+		if (status == STATION_FIFO_ALMOST_FULL_NOT_MESSAGE) {
+			pl_dev_info(&usb->intf->dev,
+				    "FIFO full packet receipt.\n"
+				   );
+#ifdef CONFIG_PURELIFI_AP
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (usb->tx.station[sidx].flag &
+					STATION_CONNECTED_FLAG &&
+			    !memcmp(usb->tx.station[sidx].mac, buffer,
+				    ETH_ALEN)) {
+				pl_dev_info(&usb->intf->dev,
+					    "%s:: FIFO at sidx =%d\n",
+						__func__, sidx);
+					usb->tx.station[sidx].flag |=
+						STATION_FIFO_FULL_FLAG;
+				break;
+			} else if (usb->tx.station[sidx].flag &
+					STATION_CONNECTED_FLAG) {
+				pl_dev_info(&usb->intf->dev,
+					    "st[%d].mac=%x:%x:%x:%x:%x:%x\n",
+						sidx,
+						usb->tx.station[sidx].mac[0],
+						usb->tx.station[sidx].mac[1],
+						usb->tx.station[sidx].mac[2],
+						usb->tx.station[sidx].mac[3],
+						usb->tx.station[sidx].mac[4],
+						usb->tx.station[sidx].mac[5]
+					   );
+			}
+		}
+		if (sidx == MAX_STA_NUM) {
+			pl_dev_warn(&usb->intf->dev,
+				    "%s::Station not found.\n",
+					__func__);
+		}
+#else
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			usb->tx.station[sidx].flag |=
+				STATION_FIFO_FULL_FLAG;
+		}
+#endif
+	} else if (status == STATION_FIFO_ALMOST_FULL_MESSAGE) {
+		pl_dev_info(&usb->intf->dev,
+			    "FIFO full packet receipt.\n");
+#ifdef CONFIG_PURELIFI_AP
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (usb->tx.station[sidx].flag &
+					STATION_CONNECTED_FLAG &&
+					!memcmp(usb->tx.station[sidx].mac,
+						buffer, ETH_ALEN)) {
+				pl_dev_info(&usb->intf->dev,
+					    "%s::Remove FIFO flag at sidx = %d\n",
+					__func__,
+					sidx
+	     );
+				usb->tx.station[sidx].flag &= 0xFD;
+				break;
+			}
+		}
+#else
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			usb->tx.station[sidx].flag &= 0xFD;
+#endif
+			send_packet_from_data_queue(usb);
+		} else if (status == STATION_CONNECT_MESSAGE) {
+			fpga_link_connection_f = 1;
+			pl_dev_info(&usb->intf->dev,
+				    "ST_CONNECT_MSG packet receipt.\n");
+		} else if (status == STATION_DISCONNECT_MESSAGE) {
+			fpga_link_connection_f = 0;
+			pl_dev_info(&usb->intf->dev,
+				    "ST_DISCONN_MSG packet receipt.\n");
+		}
+	} else {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg_f(urb_dev(urb), "urb %p resubmit error %d\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = USB_ALLOC_URB(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+	buffer = USB_ALLOC_COHERENT(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		USB_FREE_URB(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	USB_FREE_COHERENT(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	USB_FREE_URB(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	pl_dev_info(&usb->intf->dev, "%s:\n", __func__);
+
+	dev_dbg_f(purelifi_usb_dev(usb), "\n");
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	PURELIFI_ASSERT(!irqs_disabled());
+	spin_lock_irq(&rx->lock);
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0;
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		rx_usb_enabled = 1;
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	pl_dev_info(&usb->intf->dev, "%s\n", __func__);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	pl_dev_info(&usb->intf->dev, "%s\n", __func__);
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+
+	//tasklet_kill(&rx->reset_timer_tasklet);
+	//cancel_delayed_work_sync(&rx->idle_work);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+	tx = &usb->tx;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb,
+			  urb->status);
+		break;
+	default:
+		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb,
+			  urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	send_packet_from_data_queue(usb);
+	USB_FREE_URB(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = USB_ALLOC_URB(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg_f(purelifi_usb_dev(usb), "error submit urb %p %d\n",
+			  urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	USB_FREE_URB(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	PURELIFI_ASSERT(rx->fragment_length == 0);
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+const char *speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		pl_dev_err(purelifi_usb_dev(usb),
+			   "couldn't reset configuration. Error %d\n",
+				r);
+		return r;
+	}
+	return 0;
+}
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			usb_rcvctrlpipe(udev, 0),
+			request,
+			0xC0,
+			0,
+			0,
+			buffer,
+			buffer_size,
+			1000
+			);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			usb_sndctrlpipe(udev, 0),
+			request,
+			0x40,
+			0,
+			0,
+			buffer,
+			buffer_size,
+			1000
+			);
+}
+
+static int download_fpga(struct usb_interface *intf)
+{
+	int r, actual_length;
+	struct firmware *fw = NULL;
+	unsigned char *fw_data;
+	unsigned char fpga_setting[2];
+	unsigned char fpga_state[9];
+	const char *fw_name;//[] = "purelifi/li_fi_x/fpga.bit";
+	int fw_data_i;
+	unsigned char *fpga_dmabuff;
+	int blk_tran_len = 16384;
+
+	fpga_dmabuff = NULL;
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+			 PURELIFI_X_VENDOR_ID_0) &&
+			 (le16_to_cpu(interface_to_usbdev
+			 (intf)->descriptor.idProduct) ==
+			  PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga.bit";
+		pl_dev_info(&intf->dev,
+			    "%s::bit file for X selected.\n", __func__);
+	} else if ((PURELIFI_XC_VENDOR_ID_0 ==
+				le16_to_cpu(interface_to_usbdev
+				(intf)->descriptor.idVendor)) &&
+				(le16_to_cpu(interface_to_usbdev
+					     (intf)->descriptor.idProduct) ==
+				PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga_xc.bit";
+		pl_dev_info(&intf->dev,
+			    "%s::bit filefor XC selected.\n", __func__);
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware((const struct firmware **)&fw, fw_name,
+			     &intf->dev);
+	if (r) {
+		pl_dev_err(&intf->dev, "%s::request_firmware failed.(%d)\n",
+			   __func__, r);
+		goto error;
+	}
+	/* WARNING: unnecessary cast may hide bugs
+	 */
+	fpga_dmabuff = kmalloc(2, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(interface_to_usbdev(intf),
+			    0x33,
+			    fpga_dmabuff,//fpga_setting,
+			    sizeof(fpga_setting)
+			   );
+
+	memcpy(fpga_setting, fpga_dmabuff, 2);
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(interface_to_usbdev(intf),
+			    0x34,
+			    NULL,
+			    0
+			  );
+
+	if (fpga_setting[0] != 6) {
+		pl_dev_err(&intf->dev,
+			   "%s::fpga_setting[0] wrong.\n", __func__);
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmalloc(blk_tran_len, GFP_KERNEL);
+
+		memcpy(fw_data, &fw->data[fw_data_i], blk_tran_len);
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			fw_data[tbuf_idx] =
+				((fw_data[tbuf_idx] & 128) >> 7) |
+				((fw_data[tbuf_idx] &  64) >> 5) |
+				((fw_data[tbuf_idx] &  32) >> 3) |
+				((fw_data[tbuf_idx] &  16) >> 1) |
+				((fw_data[tbuf_idx] &   8) << 1) |
+				((fw_data[tbuf_idx] &   4) << 3) |
+				((fw_data[tbuf_idx] &   2) << 5) |
+				((fw_data[tbuf_idx] &   1) << 7);
+		}
+		r = usb_bulk_msg(interface_to_usbdev(intf),
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_setting[0] & 0xFF),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2000
+				);
+
+		if (r) {
+			pl_dev_err(&intf->dev, "%s::bulk msg failed. (%d)\n",
+				   __func__, r
+				  );
+		}
+
+		kfree(fw_data);
+
+		fw_data_i += blk_tran_len;
+	}
+
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(9, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xFF, 9);
+
+	send_vendor_request(interface_to_usbdev(intf),
+			    0x30,
+			    fpga_dmabuff,//fpga_state,
+			    sizeof(fpga_state)
+			   );
+	pl_dev_info(&intf->dev,
+		    "fpga_status %x %x %x %x %x %x %x %x %x\n",
+		    fpga_dmabuff[0],
+		    fpga_dmabuff[1],
+		    fpga_dmabuff[2],
+		    fpga_dmabuff[3],
+		    fpga_dmabuff[4],
+		    fpga_dmabuff[5],
+		    fpga_dmabuff[6],
+		    fpga_dmabuff[7],
+		    fpga_dmabuff[8],
+		    __func__
+		   );
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		kfree(fpga_dmabuff);
+		goto error_free_fw;
+	}
+
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(interface_to_usbdev(intf),
+			    0x35,
+			    NULL,
+			    0
+			   );
+
+	//wait because the EZ-USB FW also waits
+	msleep(200);
+
+error_free_fw:
+	release_firmware(fw);
+error:
+	return r;
+}
+
+static int download_XL_FW(struct usb_interface *intf)
+{
+	int r;
+
+	struct firmware *fw_packed = NULL;
+	int step;
+	u8 *buf;
+	u32 size, nmb_of_control_packets, i, no_of_files;
+	u32 tot_size, start_addr;
+	const char *fw_pack;
+
+	r = send_vendor_command(interface_to_usbdev(intf),
+				0x80,
+				NULL,
+				0
+				);
+	msleep(100);
+	// Code for single pack file download
+	fw_pack = "purelifi/li_fi_x/Li-Fi-XL.bin";
+	r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
+			     &intf->dev);
+	if (r) {
+		pl_dev_err(&intf->dev, "%s::request_firmware failed.(%d)\n",
+			   __func__, r
+			   );
+		goto error;
+	}
+	no_of_files = *(u32 *)&fw_packed->data[0];
+	tot_size = fw_packed->size;
+	pl_dev_err(&intf->dev,
+		   "XL Firmware file(%d, %d)\n", no_of_files, tot_size);
+
+	buf = kmalloc(64, GFP_KERNEL);
+	r = -ENOMEM;
+	if (!buf)
+		goto error;
+	memset(buf, 0, 64);
+
+	for (step = 0; step < no_of_files; step++) {
+		buf[0] = step;
+		r = send_vendor_command(interface_to_usbdev(intf),
+					0x82,
+					buf,
+					64
+					);
+
+		if (step < no_of_files - 1)
+			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
+				- *(u32 *)&fw_packed->data[4 + (step) * 4];
+		else
+			size = tot_size -
+				*(u32 *)&fw_packed->data[4 + (step) * 4];
+
+		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
+
+		if ((size % 64) && step < 2)
+			size += 64 - size % 64;
+		nmb_of_control_packets = size / 64;
+
+		for (i = 0; i < nmb_of_control_packets; i++) {
+			memcpy(buf,
+			       &fw_packed->data[start_addr + (i * 64)], 64);
+			r = send_vendor_command(interface_to_usbdev(intf),
+						0x81,
+						buf,
+						64
+					       );
+		}
+	  dev_info(&intf->dev,
+		   "%s::Downloading firmware step %d,r=%d size %d\n",
+			  __func__, step, r, size);
+	}
+	release_firmware(fw_packed);
+	kfree(buf);
+	// Code for single pack file download ends
+	//fw download finish
+	r = send_vendor_command(interface_to_usbdev(intf),
+				0x83,
+				NULL,
+				0
+			       );
+	dev_info(&intf->dev, "download_fpga (4), r=%d\n", r);
+error:
+	return r;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+static void pretty_print_mac(struct usb_interface *intf, char *serial_number,
+			     unsigned char *hw_address
+			    )
+{
+	unsigned char i;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		pl_dev_info(&intf->dev, "%s::hw_address[%d]=%x\n",
+			    __func__, i, hw_address[i]);
+}
+
+static int upload_mac_and_serial_number(struct usb_interface *intf,
+					unsigned char *hw_address,
+					unsigned char *serial_number)
+{
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FILE
+	struct firmware *fw = NULL;
+	int r;
+	char *mac_file_name = "purelifi/li_fi_x/mac.ascii";
+
+	r = request_firmware((const struct firmware **)&fw, mac_file_name,
+			     &intf->dev);
+	if (r) {
+		pl_dev_err(&intf->dev, "%s::request_firmware fail.(%d)\n",
+			   __func__, r);
+		goto ERROR;
+	}
+	pl_dev_info(&intf->dev, "%s::fw->data=%s\n", __func__, fw->data);
+	r = sscanf(fw->data,
+		   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+			&hw_address[0],
+			&hw_address[1],
+			&hw_address[2],
+			&hw_address[3],
+			&hw_address[4],
+			&hw_address[5]);
+	if (r != 6) {
+		pl_dev_err(&intf->dev, "%s::fw_dw usage :args fail.(%d)\n",
+			   __func__, r);
+		goto ERROR;
+	}
+	release_firmware(fw);
+	char *serial_file_name = "purelifi/li_fi_x/serial.ascii";
+
+	r = request_firmware((const struct firmware **)&fw,
+			     serial_file_name,
+			&intf->dev);
+	if (r) {
+		pl_dev_err(&intf->dev, "%s::request_firmware fail.(%d)\n",
+			   __func__, r);
+		goto ERROR;
+	}
+	pl_dev_info(&intf->dev, "%s::fw->data=%s\n", __func__, fw->data);
+	strcpy(serial_number, fw->data);
+	pl_dev_info(&intf->dev, "%s::serial_number=%s\n", __func__,
+		    serial_number);
+	release_firmware(fw);
+	pretty_print_mac(intf, serial_number, hw_address);
+ERROR:
+	return r;
+#endif
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FLASH
+	struct flash_t flash;
+	unsigned char *flash_sector_buf;
+	unsigned int sector_index;
+
+#define FLASH_EC_BUSY 3
+	do {
+		unsigned char buf[8];
+
+		msleep(200);
+
+		send_vendor_request(interface_to_usbdev(intf),
+				    0x40,
+				    buf,
+				    sizeof(buf)
+				   );
+		flash.enabled = buf[0] & 0xFF;
+
+		if (flash.enabled) {
+			flash.sectors = ((buf[6] & 255) << 24) |
+				((buf[5] & 255) << 16) |
+				((buf[4] & 255) <<  8) |
+				(buf[3] & 255);
+			flash.sector_size =
+				((buf[2] & 255) << 8) | (buf[1] & 255);
+		} else {
+			flash.sectors = 0;
+			flash.sector_size = 0;
+		}
+
+		if (flash.sector_size & 0x8000)
+			flash.sector_size = 1 << (flash.sector_size & 0x7FFF);
+
+		flash.ec = buf[7] & 0xFF;
+	} while (flash.ec == FLASH_EC_BUSY);
+
+	flash_sector_buf = kmalloc(flash.sector_size,
+				   GFP_KERNEL);
+	if (!flash_sector_buf)
+		return -ENOMEM;
+
+	if (!flash.enabled) {
+		pl_dev_err(&intf->dev, "%s:Flash is not enabled", __func__);
+		return -EINVAL;
+	}
+
+	read_flash(intf, &flash, 0, flash_sector_buf);
+	sector_index = *((unsigned int *)flash_sector_buf);
+
+	read_flash(intf, &flash, sector_index, flash_sector_buf);
+	memcpy(hw_address, flash_sector_buf, ETH_ALEN);
+	strncpy(serial_number, &flash_sector_buf[ETH_ALEN], 256);
+
+	pretty_print_mac(intf, serial_number, hw_address);
+	kfree(flash_sector_buf);
+	return 0;
+#endif
+#ifdef LOAD_MAC_AND_SERIAL_FROM_EP0
+#define USB_MAC_VENDOR_REQUEST 0x36
+#define USB_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define USB_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define SERIAL_NUMBER_LENGTH 14
+	int r = 0;
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(14, GFP_KERNEL);
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+	send_vendor_request(interface_to_usbdev(intf),
+			    USB_MAC_VENDOR_REQUEST,
+			    dma_buffer,//hw_address_dma_buffer,
+			    ETH_ALEN
+			   );
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(interface_to_usbdev(intf),
+			    USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer,//serial_number_dma_buffer,
+			    14
+			   );
+	send_vendor_request(interface_to_usbdev(intf),
+			    USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer,//serial_number_dma_buffer,
+			    14
+			   );
+	memcpy(serial_number, dma_buffer, SERIAL_NUMBER_LENGTH);
+
+	memset(dma_buffer, 0x00, 14);
+	send_vendor_request(interface_to_usbdev(intf),
+			    USB_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer,
+			    8
+			   );
+	memcpy(&firmware_version, dma_buffer, sizeof(unsigned long long));
+	dev_info(&intf->dev, "Firmware Version: %llu, Revision: 16418\n",
+		 firmware_version);
+	kfree(dma_buffer);
+
+	pretty_print_mac(intf, serial_number, hw_address);
+error:
+	return r;
+#endif
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	u32 payload_len_nw = TO_NETWORK((buffer_len + FCS_LEN));
+
+	usb_req->id = usb_req_id;
+	usb_req->len  = 0;
+
+	// Copy buffer length into the transmitted buffer, as it is important
+	// for the Rx MAC to know its exact length.
+	if (usb_req->id == USB_REQ_BEACON_WR) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		usb_req->len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	usb_req->len +=  buffer_len;
+
+	// Set the FCS_LEN (4) bytes as 0 for CRC checking.
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	usb_req->len += FCS_LEN;
+
+	// Round the packet to be transmitted to 4 bytes.
+	if (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+				(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+		usb_req->len += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->id = TO_NETWORK(usb_req->id);
+	usb_req->len = TO_NETWORK(usb_req->len);
+}
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+		usb_complete_t complete_fn,
+		void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb,
+			  udev,
+			  usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer,
+			  buffer_len,
+			  complete_fn,
+			  context
+			);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		pl_dev_err(&udev->dev, "%s::r = %d\n", __func__, r);
+
+	return r;
+}
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(usb_req.id) + sizeof(usb_req.len) +
+		TO_HOST(usb_req.len);
+
+	dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+	memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer,//&usb_req,
+			 usb_bulk_msg_len,
+			 &actual_length,
+			 USB_BULK_MSG_TIMEOUT_MS
+			);
+	kfree(dma_buffer);
+error:
+	if (r) {
+		pl_dev_err(&udev->dev, "%s::usb_bulk_msg unsuccesful.(%d)\n",
+			   __func__, r);
+	}
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	send_packet_from_data_queue(usb);
+	usb->tx.tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	add_timer(&usb->tx.tx_retry_timer);
+}
+
+#if QUEUE_MANAGEMENT_DEBUG
+static void tx_station_pretty_print(struct purelifi_usb *usb)
+{
+	int sidx;
+	const char *heading =
+		" | Queue | Heartbeat | FIFO full | Connected | MAC\n"
+	pl_dev_info(&usb->intf->dev, heading);
+	for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+		pl_dev_info(&usb->intf->dev,
+			    " |   %d   |     %d     |     %d      |     %d  |"
+			    sidx,
+			    ((usb->tx.station[sidx].flag &
+			    STATION_HEARTBEAT_FLAG) != 0),
+			    ((usb->tx.station[sidx].flag &
+			    STATION_FIFO_FULL_FLAG) != 0),
+			    ((usb->tx.station[sidx].flag &
+			    STATION_CONNECTED_FLAG) != 0),
+		pl_dev_info(&usb->intf->dev,
+			    " %hhx:%hhx:%hhx:%hhx:%hhx:%hhx\n",
+			    usb->tx.station[sidx].mac[0] & 0xFF,
+			    usb->tx.station[sidx].mac[1] & 0xFF,
+			    usb->tx.station[sidx].mac[2] & 0xFF,
+			    usb->tx.station[sidx].mac[3] & 0xFF,
+			    usb->tx.station[sidx].mac[4] & 0xFF,
+			    usb->tx.station[sidx].mac[5] & 0xFF
+			   );
+	}
+}
+#endif
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (usb->tx.station[sidx].flag & STATION_CONNECTED_FLAG) {
+			if (usb->tx.station[sidx].flag &
+					STATION_HEARTBEAT_FLAG) {
+				usb->tx.station[sidx].flag ^=
+					STATION_HEARTBEAT_FLAG;
+			} else {
+				memset(usb->tx.station[sidx].mac, 0,
+				       ETH_ALEN);
+				usb->tx.station[sidx].flag = 0;
+			}
+		}
+	}
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb,
+			0);
+	add_timer(&usb->sta_queue_cleanup);
+}
+
+static enum unit_type_t get_unit_type(char *serial_number,
+				      struct usb_interface *intf)
+{
+#ifdef CONFIG_PURELIFI_AP
+	pl_dev_info(&intf->dev, "%s::Unit-type is an AP!\n", __func__);
+	return AP;
+#else
+	pl_dev_info(&intf->dev, "%s::Unit-type is an STA!\n", __func__);
+	return STA;
+#endif
+}
+
+char usr_input[8] = {0};
+
+static ssize_t purelifi_store_frequency(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t len)
+{
+	int i, r, row, col, predivider, feedback_divider, output_div_0;
+	int output_div_1, output_div_2, clkout3_phase;
+	char values[6][4] = {{0}, {0} };
+	bool valid = false;
+	int rv = 0;
+
+	row = 0; col = 0;
+	for (i = 0; i < min(len, sizeof(values)); i++) {
+		values[row][col] = buf[i];
+		col++;
+		if (buf[i] == 0x20) {
+			row++;
+			col = 0;
+		} else if (buf[i] == 0x0A) {
+			if (row == 5 && col > 0) {
+				rv = sscanf((char *)&values[0][0],
+					    "%d", &predivider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[1][0],
+					    "%d", &feedback_divider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[2][0],
+					    "%d", &output_div_0);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[3][0],
+					    "%d", &output_div_1);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[4][0],
+					    "%d", &output_div_2);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[5][0],
+					    "%d", &clkout3_phase);
+				if (rv != 1)
+					valid = false;
+				valid = true;
+
+				break;
+			}
+		}
+	}
+
+	if (valid) {
+		usr_input[0] = (char)predivider;
+		usr_input[1] = (char)feedback_divider;
+		usr_input[2] = (char)output_div_0;
+		usr_input[3] = (char)output_div_1;
+		usr_input[4] = (char)output_div_2;
+		usr_input[5] = (char)clkout3_phase;
+		pl_dev_err(dev,
+			   "options IP_DIV: %d VCO_MULT: %d OP_DIV_PHY: %d",
+			    usr_input[0], usr_input[1], usr_input[2]);
+		pl_dev_err(dev,
+			   "OP_DIV_CPU: %d OP_DIV_USB: %d CLK3_PH: %d\n",
+			   usr_input[3], usr_input[4], usr_input[5]);
+		r = usb_write_req((const u8 *)&usr_input[0],
+				  sizeof(usr_input), USB_REQ_SET_FREQ);
+	} else {
+		pl_dev_err(dev, "Your options are invalid\n");
+	}
+
+	return len;
+}
+
+static ssize_t purelifi_show_sysfs(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	return 0;
+}
+
+static const struct device_attribute purelifi_attr_frequency = {
+	  .attr = {.name = "frequency", .mode = (0666)},
+	  .show = purelifi_show_sysfs,
+	  .store = purelifi_store_frequency,
+};
+
+static ssize_t purelifi_store_modulation(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t len)
+{
+	int rate, i;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	for (i = 0; i < min(len, sizeof(usr_input)); i++)
+		usr_input[i] = buf[i];
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return len;
+}
+
+static ssize_t purelifi_show_modul(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	return 0;
+}
+
+static struct device_attribute purelifi_attr_modulation = {
+	.attr = {.name = "modulation", .mode = (0666)},
+	.show = purelifi_show_modul,
+	.store = purelifi_store_modulation,
+};
+
+static void create_config(struct usb_interface *intf)
+{
+	struct device *dev;
+
+	dev = &intf->dev;
+	if (device_create_file(dev, &purelifi_attr_modulation)) {
+		pl_dev_err(&intf->dev,
+			   "failed to create modulation sysfs entry\n");
+		return;
+	}
+	if (device_create_file(dev, &purelifi_attr_frequency)) {
+		device_remove_file(dev, &purelifi_attr_modulation);
+		pl_dev_err(&intf->dev,
+			   "failed to create frequency sysfs entry\n");
+	}
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_usb *usb;
+	struct purelifi_chip *chip;
+	struct ieee80211_hw *hw = NULL;
+	static unsigned char hw_address[ETH_ALEN];
+	static unsigned char serial_number[256];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	create_config(intf);
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+
+	r = upload_mac_and_serial_number(intf, hw_address, serial_number);
+	if (r) {
+		pl_dev_err(&intf->dev,
+			   "%s:MAC and Serial upload failed%d\n",
+				__func__, r);
+	} else {
+		pl_dev_info(&intf->dev,
+			    "%s:MAC and Serial upload succeeded%d\n",
+				__func__, r);
+	}
+	chip->unit_type = get_unit_type(serial_number, intf);
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg_f(&intf->dev, "init mac,line %d Error %d\n",
+			  __LINE__, r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg_f(&intf->dev, "register device Error%d\n", r);
+		goto error;
+	}
+	pl_dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+			(le16_to_cpu(interface_to_usbdev(intf)->
+				     descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_XL_FW(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "%s:FPGA download failure.(%d)\n",
+			__func__, r);
+		goto error;
+	} else {
+		dev_info(&intf->dev, "%s::FPGA download success.\n",
+			 __func__);
+	}
+	usb->tx.mac_fifo_full = 0;
+	spin_lock_init(&usb->tx.lock);
+
+	msleep(200);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		pl_dev_info(&intf->dev, "%s::usb_init_hw fail (%d)\n",
+			    __func__, r);
+		goto error;
+	} else {
+		pl_dev_info(&intf->dev, "%s::usb_init_hw success (%d)\n",
+			    __func__, r);
+	}
+
+	msleep(200);
+	r = purelifi_chip_switch_radio_on(chip);
+	if (r < 0) {
+		pl_dev_info(&intf->dev,
+			    "%s::chip_switch_radio_on failed (%d)\n",
+				__func__, r);
+		goto error;
+	} else {
+		pl_dev_info(&intf->dev,
+			    "%s::chip_switch_radio_on success (%d)\n",
+				__func__, r);
+	}
+
+	msleep(200);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		pl_dev_info(&intf->dev,
+			    "%s::purelifi_chip_set_rate fail (%d)\n",
+				__func__, r);
+		goto error;
+	} else {
+		pl_dev_info(&intf->dev,
+			    "%s::purelifi_chip_set_rate succeeded.\n",
+				__func__);
+	}
+
+	msleep(200);
+	r = usb_write_req(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		pl_dev_info(&intf->dev,
+			    "%s::USB_REQ_MAC_WR failure. (%d)\n",
+				__func__, r);
+		goto error;
+	} else {
+		pl_dev_info(&intf->dev,
+			    "%s::USB_REQ_MAC_WR success. (%d)\n",
+				__func__, r);
+	}
+	purelifi_chip_enable_rxtx(chip);
+
+	//Initialise the data plane Tx queue.
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&usb->tx.station[i].data_list);
+		usb->tx.station[i].flag = 0;
+	}
+	usb->tx.station[STA_BROADCAST_INDEX].flag |=
+		STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		usb->tx.station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	usb->tx.tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&usb->tx.tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	pl_dev_info(&intf->dev, "%s\n", __func__);
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer(&usb->tx.tx_retry_timer);
+	del_timer(&usb->sta_queue_cleanup);
+
+	dev_dbg_f(purelifi_usb_dev(usb), "\n");
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	device_remove_file(&intf->dev, &purelifi_attr_modulation);
+	device_remove_file(&intf->dev, &purelifi_attr_frequency);
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		pl_dev_warn(purelifi_usb_dev(usb),
+			    "Device resume fail error %d. Retry...\n",
+				r);
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"restore error, %d\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	pl_dev_info(&usb->intf->dev, "%s\n", __func__);
+
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	pl_dev_info(&intf->dev, "%s\n", __func__);
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	mutex_lock(&mac->chip.mutex);
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	pl_dev_info(&intf->dev, "%s\n", __func__);
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	mutex_unlock(&mac->chip.mutex);
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#else
+
+#define suspend NULL
+#define resume  NULL
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+	.suspend        = suspend,
+	.resume         = resume,
+	.disable_hub_initiated_lpm = 1,
+};
+
+struct workqueue_struct *purelifi_workqueue;
+
+static int modulation_open(struct inode *inode_p, struct file *file_p)
+{
+	return 0;
+}
+
+static ssize_t modulation_read(struct file *file_p,
+			       char __user *user_p,
+		size_t offset, loff_t *off_p)
+{
+	return 0;
+}
+
+ssize_t modulation_write(struct file *file_p,
+			 const char __user *buffer,
+		size_t count, loff_t *actual_data_written)
+{
+	int rate;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	if (copy_from_user(usr_input, buffer, min(count, sizeof(usr_input))))
+		return -EFAULT;
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return count;
+}
+
+static const struct proc_ops  modulation_fops = {
+	.proc_open  = modulation_open,
+	.proc_read  = modulation_read,
+	.proc_write = modulation_write
+};
+
+static int create_proc_entry_e(struct proc_dir_entry *entry,
+			       size_t size,
+			       mode_t mode,
+			       const struct proc_ops *fops,
+			       const char *entry_name,
+			       struct proc_dir_entry *parent
+			     )
+{
+	entry = proc_create(entry_name, mode, parent, fops);
+	if (!(entry))
+		return -ENOMEM;
+	return 0;
+}
+
+static int __init usb_init(void)
+{
+	int r;
+
+	proc_folder = proc_mkdir(PROC_FOLDER, NULL);
+	if (!proc_folder) {
+		pr_err("Error creating proc folder.\n");
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = create_proc_entry_e(modulation_proc_entry, sizeof(u32), 0666,
+				&modulation_fops, MODULATION_FILENAME,
+				proc_folder);
+	if (r)
+		goto remove_proc_dir;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n",
+		       driver.name);
+		r = -ENOMEM;
+		goto remove_modulation_proc_file;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed. Error  %d\n",
+		       driver.name, r);
+		return r;
+	}
+
+	pr_debug("%s:Initialized\n", driver.name);
+	return 0;
+
+remove_modulation_proc_file:
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+remove_proc_dir:
+	remove_proc_entry(PROC_FOLDER, NULL);
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	pr_debug("%s %s\n", driver.name, __func__);
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+	remove_proc_entry(PROC_FOLDER, NULL);
+	if (ez_usb_interface) {
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_modulation);
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_frequency);
+	}
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi Ltd");
+MODULE_VERSION("1.0");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/usb.h b/drivers/net/wireless/purelifi/usb.h
new file mode 100644
index 000000000000..f438f26a406b
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "def.h"
+#include "mac_usb_interface.h"
+
+#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
+		      (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
+
+#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
+
+#define TO_NETWORK_32(X) TO_NETWORK(X)
+
+#define TO_HOST(X)    TO_NETWORK(X)
+#define TO_HOST_16(X) TO_NETWORK_16(X)
+#define TO_HOST_32(X) TO_NETWORK_32(X)
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2     |     1     |     0     |
+   // Reserved  | Hearbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	u8 initialized:1, was_running:1;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+		usb_complete_t complete_fn, void *context);
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
+		*usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *purelifi_intf_to_hw(struct usb_interface
+		*intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
+		*usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+const char *speed(enum usb_device_speed speed);
+#endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
@ 2020-09-28 12:07   ` Joe Perches
  2020-09-28 12:53     ` Srinivasan Raju
  2020-09-30  5:16   ` Leon Romanovsky
                     ` (18 subsequent siblings)
  19 siblings, 1 reply; 108+ messages in thread
From: Joe Perches @ 2020-09-28 12:07 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

On Mon, 2020-09-28 at 15:49 +0530, Srinivasan Raju wrote:
> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices, which provide lightweight, highspeed secure and
> fully networked wireless communications via light.

trivial notes:

> diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
[]
> +static void print_chip_info(struct purelifi_chip *chip)
> +{
> +	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
> +	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
> +
> +	pr_info("purelifi chip %04hx:%04hx v%04hx  %02x-%02x-%02x %s\n",

You don't need to use %04hx for u16's
any more than you need to use %02hhx for u8's.

	pr_info("purelifi chip %04x:%04x v%04x  %02x-%02x-%02x %s\n",

> +		le16_to_cpu(udev->descriptor.idVendor),
> +		le16_to_cpu(udev->descriptor.idProduct),
> +		le16_to_cpu(udev->descriptor.bcdDevice),
> +		addr[0], addr[1], addr[2],
> +		speed(udev->speed));
> +}
> 

> +static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
> +					u8 *addr
> +					)

Can you use normal close parenthesis locations?

> diff --git a/drivers/net/wireless/purelifi/def.h b/drivers/net/wireless/purelifi/def.h
> new file mode 100644
> index 000000000000..295dfb45b568
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/def.h
> @@ -0,0 +1,46 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef _PURELIFI_DEF_H
> +#define _PURELIFI_DEF_H
> +
> +#include <linux/kernel.h>
> +#include <linux/stringify.h>
> +#include <linux/device.h>
> +
> +typedef u16 __nocast purelifi_addr_t;
> +
> +#define dev_printk_f(level, dev, fmt, args...) \
> +	dev_printk(level, dev, "%s() " fmt, __func__, ##args)

If you _really_w want __func__ output you could use

#define dev_fmt "%s(): " fmt, __func__

> +
> +#ifdef DEBUG
> +#  define dev_dbg_f(dev, fmt, args...) \
> +	  dev_printk_f(KERN_DEBUG, dev, fmt, ## args)
> +#  define dev_dbg_f_limit(dev, fmt, args...) do { \
> +	if (net_ratelimit()) \
> +		dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \
> +} while (0)
> +#  define dev_dbg_f_cond(dev, cond, fmt, args...) ({ \
> +	bool __cond = !!(cond); \
> +	if (unlikely(__cond)) \
> +		dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \
> +})
> +#else
> +#  define dev_dbg_f(dev, fmt, args...)  (void)(dev)

	no_printk

> +#  define dev_dbg_f_limit(dev, fmt, args...) (void)(dev)
> +#  define dev_dbg_f_cond(dev, cond, fmt, args...) (void)(dev)
> +#endif /* DEBUG */
> 
[]
> +++ b/drivers/net/wireless/purelifi/log.h
> @@ -0,0 +1,15 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef _PURELIFI_LOG
> +#define _PURELIFI_LOG
> +
> +#ifdef PL_DEBUG
> +#define pl_dev_info(dev, fmt, arg...) dev_info(dev, fmt, ##arg)
> +#define pl_dev_warn(dev, fmt, arg...) dev_warn(dev, fmt, ##arg)
> +#define  pl_dev_err(dev, fmt, arg...) dev_err(dev, fmt, ##arg)

Seems completely pointless.
Debugging output should be at KERN_DEBUG

> +#else
> +#define pl_dev_info(dev, fmt, arg...) (void)(dev)
> +#define pl_dev_warn(dev, fmt, arg...) (void)(dev)
> +#define  pl_dev_err(dev, fmt, arg...) (void)(dev)
> +#endif
> +#endif
> diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
[]
> +int purelifi_mac_init_hw(struct ieee80211_hw *hw)
> +{
> +	int r;
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct purelifi_chip *chip = &mac->chip;
> +
> +	r = purelifi_chip_init_hw(chip);
> +	if (r) {
> +		pl_dev_warn(purelifi_mac_dev(mac), "%s::purelifi_chip_init_hw	failed. (%d)\n",
> +			    __func__, r);

Odd double colon with odd spacing too.

> +		goto out;
> +	}
> +	PURELIFI_ASSERT(!irqs_disabled());
> +
> +	r = regulatory_hint(hw->wiphy, "CA");
> +out:
> +	return r;
> +}
> +
> +void purelifi_mac_release(struct purelifi_mac *mac)
> +{
> +	purelifi_chip_release(&mac->chip);
> +	lockdep_assert_held(&mac->lock);
> +}
> +
> +int purelifi_op_start(struct ieee80211_hw *hw)
> +{
> +	regulatory_hint(hw->wiphy, "EU");
> +	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
> +	return 0;
> +}
> +
> +void purelifi_op_stop(struct ieee80211_hw *hw)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct purelifi_chip *chip = &mac->chip;
> +	struct sk_buff *skb;
> +	struct sk_buff_head *ack_wait_queue = &mac->ack_wait_queue;
> +
> +	pl_dev_info(purelifi_mac_dev(mac), "%s", __func__);

Unnecessary as ftrace works well

[]

> +static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
> +{
> +	unsigned int frag_len = skb->len;
> +	unsigned int tmp;
> +	struct purelifi_ctrlset *cs;
> +
> +	if (skb_headroom(skb) >= sizeof(struct purelifi_ctrlset)) {
> +		cs = (struct purelifi_ctrlset *)skb_push(skb,
> +				sizeof(struct purelifi_ctrlset));
> +	} else {
> +		pl_dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
> +		return 1;
> +	}

Reverse the test please

	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
		pl_dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
		return 1;
	}

	cs = (struct purelifi_ctrlset *)skb_push(skb, sizeof(struct purelifi_ctrlset));

> +
> +	cs->id = USB_REQ_DATA_TX;
> +	cs->payload_len_nw = frag_len;
> +	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
> +		- sizeof(cs->id) - sizeof(cs->len);

[]

> +	/*check if 32 bit aligned */
> +	tmp = skb->len & 3;
> +	if (tmp) {
> +		if (skb_tailroom(skb) >= (3 - tmp)) {
> +			skb_put(skb, 4 - tmp);
> +		} else {
> +			if (skb_headroom(skb) >= 4 - tmp) {
> +				u8 len;
> +				u8 *src_pt;
> +				u8 *dest_pt;
> +
> +				len = skb->len;
> +				src_pt = skb->data;
> +				dest_pt = skb_push(skb, 4 - tmp);
> +				memcpy(dest_pt, src_pt, len);
> +			} else {
> +				return 1;

and reverse the test here and use and unindent

> +			}
> +		}
> +		cs->len +=  4 - tmp;
> +	}
> +
> +	//check if not multiple of 512
> +	tmp = skb->len & 0x1ff;
> +	if (!tmp) {
> +		if (skb_tailroom(skb) >= 4) {
> +			skb_put(skb, 4);
> +		} else {
> +			if (skb_headroom(skb) >= 4) {
> +				u8 len = skb->len;
> +				u8 *src_pt = skb->data;
> +				u8 *dest_pt = skb_push(skb, 4);
> +
> +				memcpy(dest_pt, src_pt, len);
> +			} else {
> +				/* should never happen b/c
> +				 * sufficient headroom was reserved
> +				 */
> +				return 1;
> +			}
> +		}
> +

and here

[]

> +static int purelifi_op_add_interface(struct ieee80211_hw *hw,
> +				     struct ieee80211_vif *vif)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +
> +	pl_dev_info(purelifi_mac_dev(mac), "%s\n", __func__);
> +
> +	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
> +		return -EOPNOTSUPP;
> +
> +	switch (vif->type) {
> +	case NL80211_IFTYPE_MONITOR:
> +		pl_dev_info(purelifi_mac_dev(mac),
> +			    "%s::NL80211_IFTYPE_MONITOR\n",
> +				__func__);
> +		break;
> +	case NL80211_IFTYPE_MESH_POINT:
> +		pl_dev_info(purelifi_mac_dev(mac),
> +			    "%s::NL80211_IFTYPE_MESH_POINT\n",
> +				__func__);
> +		break;
> +	case NL80211_IFTYPE_STATION:
> +		pl_dev_info(purelifi_mac_dev(mac),
> +			    "%s::NL80211_IFTYPE_STATION\n",
> +				__func__);
> +		break;
> +	case NL80211_IFTYPE_ADHOC:
> +		pl_dev_info(purelifi_mac_dev(mac),
> +			    "%s::NL80211_IFTYPE_ADHOC\n",
> +				__func__);
> +		break;
> +	case NL80211_IFTYPE_AP:
> +		pl_dev_info(purelifi_mac_dev(mac),
> +			    "%s::vif->type=NL80211_IFTYPE_AP\n",
> +				__func__);
> +		break;

Create and use a lookup table and use a single
output

> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +	mac->type = vif->type;
> +	mac->vif = vif;
> +	return 0;
> +}
> 
[]
> +static void purelifi_get_et_stats(struct ieee80211_hw *hw,
> +				  struct ieee80211_vif *vif,
> +		struct ethtool_stats *stats, u64 *data)

odd alignment.

Didn't look at the rest


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices
  2020-09-28 12:07   ` Joe Perches
@ 2020-09-28 12:53     ` Srinivasan Raju
  0 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-09-28 12:53 UTC (permalink / raw)
  To: Joe Perches
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS



> Didn't look at the rest

Thanks for your comments Joe, I will refactor the code for clarity, remove redundancy and address your comments.

Regards
Srini


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
  2020-09-28 12:07   ` Joe Perches
@ 2020-09-30  5:16   ` Leon Romanovsky
  2020-09-30  5:29     ` Srinivasan Raju
                       ` (2 more replies)
  2020-10-14  6:19   ` [PATCH] [PATCH] [v3] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
                     ` (17 subsequent siblings)
  19 siblings, 3 replies; 108+ messages in thread
From: Leon Romanovsky @ 2020-09-30  5:16 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, linux-kernel, linux-wireless,
	netdev

On Mon, Sep 28, 2020 at 03:49:49PM +0530, Srinivasan Raju wrote:
> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices, which provide lightweight, highspeed secure and
> fully networked wireless communications via light.
>
> This driver implementation has been based on the zd1211rw driver
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
> ---
>  MAINTAINERS                                   |    5 +
>  drivers/net/wireless/Kconfig                  |    1 +
>  drivers/net/wireless/Makefile                 |    1 +
>  drivers/net/wireless/purelifi/Kconfig         |   38 +
>  drivers/net/wireless/purelifi/Makefile        |    3 +
>  drivers/net/wireless/purelifi/chip.c          |  120 ++
>  drivers/net/wireless/purelifi/chip.h          |   81 +
>  drivers/net/wireless/purelifi/def.h           |   46 +
>  drivers/net/wireless/purelifi/log.h           |   15 +
>  drivers/net/wireless/purelifi/mac.c           |  957 +++++++++
>  drivers/net/wireless/purelifi/mac.h           |  178 ++
>  .../net/wireless/purelifi/mac_usb_interface.h |   38 +
>  drivers/net/wireless/purelifi/usb.c           | 1872 +++++++++++++++++
>  drivers/net/wireless/purelifi/usb.h           |  148 ++
>  14 files changed, 3503 insertions(+)
>  create mode 100644 drivers/net/wireless/purelifi/Kconfig
>  create mode 100644 drivers/net/wireless/purelifi/Makefile
>  create mode 100644 drivers/net/wireless/purelifi/chip.c
>  create mode 100644 drivers/net/wireless/purelifi/chip.h
>  create mode 100644 drivers/net/wireless/purelifi/def.h
>  create mode 100644 drivers/net/wireless/purelifi/log.h
>  create mode 100644 drivers/net/wireless/purelifi/mac.c
>  create mode 100644 drivers/net/wireless/purelifi/mac.h
>  create mode 100644 drivers/net/wireless/purelifi/mac_usb_interface.h
>  create mode 100644 drivers/net/wireless/purelifi/usb.c
>  create mode 100644 drivers/net/wireless/purelifi/usb.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 42c69d2eeece..0e8cd1decafe 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14098,6 +14098,11 @@ T:	git git://linuxtv.org/media_tree.git
>  F:	Documentation/admin-guide/media/pulse8-cec.rst
>  F:	drivers/media/cec/usb/pulse8/
>
> +PUREILIFI USB DRIVER
> +M:	Srinivasan Raju <srini.raju@purelifi.com>
> +S:	Maintained
> +F:	drivers/net/wireless/purelifi
> +
>  PVRUSB2 VIDEO4LINUX DRIVER
>  M:	Mike Isely <isely@pobox.com>
>  L:	pvrusb2@isely.net	(subscribers-only)
> diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
> index 170a64e67709..b87da3139f94 100644
> --- a/drivers/net/wireless/Kconfig
> +++ b/drivers/net/wireless/Kconfig
> @@ -48,6 +48,7 @@ source "drivers/net/wireless/st/Kconfig"
>  source "drivers/net/wireless/ti/Kconfig"
>  source "drivers/net/wireless/zydas/Kconfig"
>  source "drivers/net/wireless/quantenna/Kconfig"
> +source "drivers/net/wireless/purelifi/Kconfig"
>
>  config PCMCIA_RAYCS
>  	tristate "Aviator/Raytheon 2.4GHz wireless support"
> diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
> index 80b324499786..c403dda7a14e 100644
> --- a/drivers/net/wireless/Makefile
> +++ b/drivers/net/wireless/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
>  obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
>  obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
>  obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
> +obj-$(WLAN_VENDOR_PURELIFI) += purelifi/

It should be CONFIG_WLAN_VENDOR_PURELIFI

>
>  # 16-bit wireless PCMCIA client drivers
>  obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
> diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
> new file mode 100644
> index 000000000000..ff05eaf0a8d4
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/Kconfig
> @@ -0,0 +1,38 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config WLAN_VENDOR_PURELIFI
> +	bool "pureLiFi devices"
> +	default y

"N" is preferred default.

> +	help
> +	  If you have a pureLiFi device, say Y.
> +
> +	  Note that the answer to this question doesn't directly affect the
> +	  kernel: saying N will just cause the configurator to skip all the
> +	  questions about these cards. If you say Y, you will be asked for
> +	  your specific card in the following questions.

The text above makes no sense. Of course, it makes a lot of sense to
disable this device for whole world.

> +
> +if WLAN_VENDOR_PURELIFI
> +
> +config PURELIFI
> +
> +	tristate "pureLiFi device support"
> +	depends on CFG80211 && MAC80211 && USB
> +	help
> +	   Say Y if you want to use LiFi.
> +
> +	   This driver makes the adapter appear as a normal WLAN interface.
> +
> +	   The pureLiFi device requires external STA firmware to be loaded.
> +
> +	   To compile this driver as a module, choose M here: the module will
> +	   be called purelifi.
> +
> +config PURELIFI_AP
> +
> +	tristate "pureLiFi device Access Point support"
> +	depends on PURELIFI
> +	help
> +	   Say Y if you want to use LiFi in Access Point mode.
> +
> +	   The pureLiFi device requires external AP firmware to be loaded.
> +
> +endif # WLAN_VENDOR_PURELIFI
> diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
> new file mode 100644
> index 000000000000..1f20055e741f
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_PURELIFI)		:= purelifi.o

Please refactor the code to place PURELIFI_AP separately and not as
it is placed now interleaved with PURELIFI.

> +purelifi-objs 		+= chip.o usb.o mac.o
> diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
> new file mode 100644
> index 000000000000..32e6984703b6
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/chip.c
> @@ -0,0 +1,120 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +
> +#include "def.h"
> +#include "chip.h"
> +#include "mac.h"
> +#include "usb.h"
> +#include "log.h"
> +
> +void purelifi_chip_init(struct purelifi_chip *chip,
> +			struct ieee80211_hw *hw,
> +			struct usb_interface *intf)
> +{
> +	memset(chip, 0, sizeof(*chip));
> +	mutex_init(&chip->mutex);
> +	purelifi_usb_init(&chip->usb, hw, intf);
> +}
> +
> +void purelifi_chip_release(struct purelifi_chip *chip)
> +{
> +	PURELIFI_ASSERT(!mutex_is_locked(&chip->mutex));
> +	purelifi_usb_release(&chip->usb);
> +	mutex_destroy(&chip->mutex);
> +}

All those one time called functions are better to be removed and
embedded into the callers.

> +
> +int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
> +				 u8 dtim_period, int type)
> +{
> +	if (!interval || (chip->beacon_set &&
> +			  chip->beacon_interval == interval)) {
> +		return 0;
> +	}
> +
> +	chip->beacon_interval = interval;
> +	chip->beacon_set = true;
> +	return usb_write_req((const u8 *)&chip->beacon_interval,
> +			     sizeof(chip->beacon_interval),
> +			     USB_REQ_BEACON_INTERVAL_WR);
> +}
> +
> +static void print_chip_info(struct purelifi_chip *chip)
> +{
> +	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
> +	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
> +
> +	pr_info("purelifi chip %04hx:%04hx v%04hx  %02x-%02x-%02x %s\n",
> +		le16_to_cpu(udev->descriptor.idVendor),
> +		le16_to_cpu(udev->descriptor.idProduct),
> +		le16_to_cpu(udev->descriptor.bcdDevice),
> +		addr[0], addr[1], addr[2],
> +		speed(udev->speed));
> +}

Another one time function.

> +
> +int purelifi_chip_init_hw(struct purelifi_chip *chip)
> +{
> +	print_chip_info(chip);
> +	return purelifi_set_beacon_interval(chip, 100, 0, 0);
> +}
> +
> +int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
> +{
> +	int r;
> +
> +	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
> +	if (r)
> +		pl_dev_err(purelifi_chip_dev(chip), "%s::r=%d",
> +			   __func__, r);
> +	return r;
> +}
> +
> +int purelifi_chip_switch_radio_on(struct purelifi_chip *chip)
> +{
> +	return purelifi_chip_switch_radio(chip, 1);
> +}
> +
> +int purelifi_chip_switch_radio_off(struct purelifi_chip *chip)
> +{
> +	return purelifi_chip_switch_radio(chip, 0);
> +}

Ditto

> +
> +void purelifi_chip_enable_rxtx_urb_complete(struct urb *urb_struct)
> +{
> +}

This function is not used.

> +
> +int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
> +{
> +	purelifi_usb_enable_tx(&chip->usb);
> +	return purelifi_usb_enable_rx(&chip->usb);
> +}
> +
> +void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
> +{
> +	u32 value;
> +
> +	value = 0;
> +	usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_RXTX_WR);

You declared value as u32, but forwarded u8, doesn't sound right.

> +	purelifi_usb_disable_rx(&chip->usb);
> +	purelifi_usb_disable_tx(&chip->usb);
> +}
> +
> +int purelifi_chip_set_rate(struct purelifi_chip *chip, u32 rate)
> +{
> +	int r;
> +	static struct purelifi_chip *chip_p;
> +
> +	if (chip)
> +		chip_p = chip;
> +	if (!chip_p)
> +		return -EINVAL;
> +
> +	r = usb_write_req((const u8 *)&rate, sizeof(rate), USB_REQ_RATE_WR);

You better have proper types from the beginning.

> +	if (r)
> +		pl_dev_err(purelifi_chip_dev(chip), "%s::r=%d",
> +			   __func__, r);
> +	return r;
> +}
> +
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/net/wireless/purelifi/chip.h b/drivers/net/wireless/purelifi/chip.h
> new file mode 100644
> index 000000000000..892ecaf7d5d3
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/chip.h
> @@ -0,0 +1,81 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef _LF_X_CHIP_H
> +#define _LF_X_CHIP_H
> +
> +#include <net/mac80211.h>
> +
> +#include "usb.h"
> +
> +enum unit_type_t {
> +	STA = 0,
> +	AP = 1,
> +};
> +
> +struct purelifi_chip {
> +	struct purelifi_usb usb;
> +	struct mutex mutex; /* lock to protect chip data */
> +	enum unit_type_t unit_type;
> +	u16 link_led;
> +	u8  beacon_set;
> +	u16 beacon_interval;
> +};
> +
> +struct purelifi_mc_hash {
> +	u32 low;
> +	u32 high;
> +};
> +
> +#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
> +
> +static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
> +							 *usb
> +							)
> +{
> +	return container_of(usb, struct purelifi_chip, usb);
> +}
> +
> +void purelifi_chip_init(struct purelifi_chip *chip,
> +			struct ieee80211_hw *hw,
> +			struct usb_interface *intf);
> +void purelifi_chip_release(struct purelifi_chip *chip);
> +int purelifi_chip_init_hw(struct purelifi_chip *chip);
> +
> +/* Locking functions for reading and writing registers.
> + * The different parameters are intentional.

This comment is useless.

> + */
> +int purelifi_chip_switch_radio_on(struct purelifi_chip *chip);
> +int purelifi_chip_switch_radio_off(struct purelifi_chip *chip);
> +int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
> +void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
> +int purelifi_chip_set_rate(struct purelifi_chip *chip, u32 rate);
> +int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
> +				 u8 dtim_period, int type);
> +
> +static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
> +{
> +	hash->low = 0;
> +	/* The interfaces must always received broadcasts.
> +	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
> +	 */
> +	hash->high = 0x80000000;
> +}
> +
> +static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
> +{
> +	hash->low = 0xffffffff;
> +	hash->high = 0xffffffff;
> +}
> +
> +static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
> +					u8 *addr
> +					)
> +{
> +	unsigned int i = addr[5] >> 2;
> +
> +	if (i < 32)
> +		hash->low |= 1 << i;
> +	else
> +		hash->high |= 1 << (i - 32);
> +}
> +#endif /* _LF_X_CHIP_H */
> diff --git a/drivers/net/wireless/purelifi/def.h b/drivers/net/wireless/purelifi/def.h
> new file mode 100644
> index 000000000000..295dfb45b568
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/def.h
> @@ -0,0 +1,46 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef _PURELIFI_DEF_H
> +#define _PURELIFI_DEF_H
> +
> +#include <linux/kernel.h>
> +#include <linux/stringify.h>
> +#include <linux/device.h>
> +
> +typedef u16 __nocast purelifi_addr_t;

?????

> +
> +#define dev_printk_f(level, dev, fmt, args...) \
> +	dev_printk(level, dev, "%s() " fmt, __func__, ##args)
> +
> +#ifdef DEBUG
> +#  define dev_dbg_f(dev, fmt, args...) \
> +	  dev_printk_f(KERN_DEBUG, dev, fmt, ## args)
> +#  define dev_dbg_f_limit(dev, fmt, args...) do { \
> +	if (net_ratelimit()) \
> +		dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \
> +} while (0)
> +#  define dev_dbg_f_cond(dev, cond, fmt, args...) ({ \
> +	bool __cond = !!(cond); \
> +	if (unlikely(__cond)) \
> +		dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \
> +})
> +#else
> +#  define dev_dbg_f(dev, fmt, args...)  (void)(dev)
> +#  define dev_dbg_f_limit(dev, fmt, args...) (void)(dev)
> +#  define dev_dbg_f_cond(dev, cond, fmt, args...) (void)(dev)
> +#endif /* DEBUG */
> +
> +#ifdef DEBUG
> +#  define PURELIFI_ASSERT(x) \
> +do { \
> +	if (unlikely(!(x))) { \
> +		pr_debug("%s:%d ASSERT %s VIOLATED!\n", \
> +			__FILE__, __LINE__, __stringify(x)); \
> +		dump_stack(); \
> +	} \
> +} while (0)
> +#else
> +#  define PURELIFI_ASSERT(x) do { } while (0)
> +#endif

No to everything above, please don't obscure kernel primitives.

> +
> +#endif /* _PURELIFI_DEF_H */
> diff --git a/drivers/net/wireless/purelifi/log.h b/drivers/net/wireless/purelifi/log.h
> new file mode 100644
> index 000000000000..5e275da9ba12
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/log.h
> @@ -0,0 +1,15 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef _PURELIFI_LOG
> +#define _PURELIFI_LOG
> +
> +#ifdef PL_DEBUG
> +#define pl_dev_info(dev, fmt, arg...) dev_info(dev, fmt, ##arg)
> +#define pl_dev_warn(dev, fmt, arg...) dev_warn(dev, fmt, ##arg)
> +#define  pl_dev_err(dev, fmt, arg...) dev_err(dev, fmt, ##arg)
> +#else
> +#define pl_dev_info(dev, fmt, arg...) (void)(dev)
> +#define pl_dev_warn(dev, fmt, arg...) (void)(dev)
> +#define  pl_dev_err(dev, fmt, arg...) (void)(dev)
> +#endif
> +#endif

Ditto

> diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
> new file mode 100644
> index 000000000000..c6be289f8431
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/mac.c
> @@ -0,0 +1,957 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/slab.h>
> +#include <linux/usb.h>
> +#include <linux/gpio.h>
> +#include <linux/jiffies.h>
> +#include <net/ieee80211_radiotap.h>
> +
> +#include "def.h"
> +#include "chip.h"
> +#include "mac.h"
> +#include "log.h"
> +
> +#ifndef IEEE80211_BAND_2GHZ
> +#define IEEE80211_BAND_2GHZ NL80211_BAND_2GHZ
> +#endif
> +
> +static const struct ieee80211_rate purelifi_rates[] = {
> +	{ .bitrate = 10,
> +		.hw_value = PURELIFI_CCK_RATE_1M, },
> +	{ .bitrate = 20,
> +		.hw_value = PURELIFI_CCK_RATE_2M,
> +		.hw_value_short = PURELIFI_CCK_RATE_2M
> +			| PURELIFI_CCK_PREA_SHORT,
> +		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
> +	{ .bitrate = 55,
> +		.hw_value = PURELIFI_CCK_RATE_5_5M,
> +		.hw_value_short = PURELIFI_CCK_RATE_5_5M
> +			| PURELIFI_CCK_PREA_SHORT,
> +		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
> +	{ .bitrate = 110,
> +		.hw_value = PURELIFI_CCK_RATE_11M,
> +		.hw_value_short = PURELIFI_CCK_RATE_11M
> +			| PURELIFI_CCK_PREA_SHORT,
> +		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
> +	{ .bitrate = 60,
> +		.hw_value = PURELIFI_OFDM_RATE_6M,
> +		.flags = 0 },
> +	{ .bitrate = 90,
> +		.hw_value = PURELIFI_OFDM_RATE_9M,
> +		.flags = 0 },
> +	{ .bitrate = 120,
> +		.hw_value = PURELIFI_OFDM_RATE_12M,
> +		.flags = 0 },
> +	{ .bitrate = 180,
> +		.hw_value = PURELIFI_OFDM_RATE_18M,
> +		.flags = 0 },
> +	{ .bitrate = 240,
> +		.hw_value = PURELIFI_OFDM_RATE_24M,
> +		.flags = 0 },
> +	{ .bitrate = 360,
> +		.hw_value = PURELIFI_OFDM_RATE_36M,
> +		.flags = 0 },
> +	{ .bitrate = 480,
> +		.hw_value = PURELIFI_OFDM_RATE_48M,
> +		.flags = 0 },
> +	{ .bitrate = 540,
> +		.hw_value = PURELIFI_OFDM_RATE_54M,
> +		.flags = 0 },
> +};
> +
> +static const struct ieee80211_channel purelifi_channels[] = {
> +	{ .center_freq = 2412, .hw_value = 1 },
> +	{ .center_freq = 2417, .hw_value = 2 },
> +	{ .center_freq = 2422, .hw_value = 3 },
> +	{ .center_freq = 2427, .hw_value = 4 },
> +	{ .center_freq = 2432, .hw_value = 5 },
> +	{ .center_freq = 2437, .hw_value = 6 },
> +	{ .center_freq = 2442, .hw_value = 7 },
> +	{ .center_freq = 2447, .hw_value = 8 },
> +	{ .center_freq = 2452, .hw_value = 9 },
> +	{ .center_freq = 2457, .hw_value = 10 },
> +	{ .center_freq = 2462, .hw_value = 11 },
> +	{ .center_freq = 2467, .hw_value = 12 },
> +	{ .center_freq = 2472, .hw_value = 13 },
> +	{ .center_freq = 2484, .hw_value = 14 },
> +};
> +
> +static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
> +				      struct sk_buff *beacon, bool in_intr);
> +
> +int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
> +{
> +	SET_IEEE80211_PERM_ADDR(hw, hw_address);
> +	return 0;
> +}
> +
> +void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
> +{
> +#ifdef CONFIG_PURELIFI_AP
> +	struct ieee80211_vif *vif = purelifi_usb_to_mac(usb)->vif;
> +	struct ieee80211_sta *sta = ieee80211_find_sta(vif, mac);
> +
> +	if (!sta) {
> +		//Print Debugs

The code submitted to the kernel should be in release quality with
minimal debug prints, no C++ comments and really hard-to-do and not
important TODOs.

> +	} else {
> +		ieee80211_sta_block_awake(purelifi_usb_to_hw(usb), sta,
> +					  block);
> +	}
> +#else
> +	if (block)
> +		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
> +	else
> +		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
> +#endif
> +}
> +
> +int purelifi_mac_init_hw(struct ieee80211_hw *hw)
> +{
> +	int r;
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct purelifi_chip *chip = &mac->chip;
> +
> +	r = purelifi_chip_init_hw(chip);
> +	if (r) {
> +		pl_dev_warn(purelifi_mac_dev(mac), "%s::purelifi_chip_init_hw	failed. (%d)\n",
> +			    __func__, r);
> +		goto out;
> +	}
> +	PURELIFI_ASSERT(!irqs_disabled());
> +
> +	r = regulatory_hint(hw->wiphy, "CA");
> +out:
> +	return r;
> +}
> +
> +void purelifi_mac_release(struct purelifi_mac *mac)
> +{
> +	purelifi_chip_release(&mac->chip);
> +	lockdep_assert_held(&mac->lock);
> +}
> +
> +int purelifi_op_start(struct ieee80211_hw *hw)
> +{
> +	regulatory_hint(hw->wiphy, "EU");
> +	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
> +	return 0;
> +}
> +
> +void purelifi_op_stop(struct ieee80211_hw *hw)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct purelifi_chip *chip = &mac->chip;
> +	struct sk_buff *skb;
> +	struct sk_buff_head *ack_wait_queue = &mac->ack_wait_queue;
> +
> +	pl_dev_info(purelifi_mac_dev(mac), "%s", __func__);
> +
> +	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
> +
> +	/* The order here deliberately is a little different from the open()
> +	 * method, since we need to make sure there is no opportunity for RX
> +	 * frames to be processed by mac80211 after we have stopped it.
> +	 */
> +
> +	return;  //don't allow stop for debugging rx_urb_complete failure

Did you test the code?

> +	purelifi_chip_switch_radio_off(chip);
> +
> +	while ((skb = skb_dequeue(ack_wait_queue)))
> +		dev_kfree_skb_any(skb);
> +	return;  //don't allow stop for debugging rx_urb_complete failure
> +}
> +
> +int purelifi_restore_settings(struct purelifi_mac *mac)
> +{
> +	struct sk_buff *beacon;
> +	struct purelifi_mc_hash multicast_hash;
> +	int beacon_interval, beacon_period;
> +
> +	dev_dbg_f(purelifi_mac_dev(mac), "\n");
> +
> +	spin_lock_irq(&mac->lock);
> +	multicast_hash = mac->multicast_hash;
> +	beacon_interval = mac->beacon.interval;
> +	beacon_period = mac->beacon.period;
> +	spin_unlock_irq(&mac->lock);
> +
> +	if (mac->type == NL80211_IFTYPE_MESH_POINT ||
> +	    mac->type == NL80211_IFTYPE_ADHOC ||
> +			mac->type == NL80211_IFTYPE_AP) {
> +		if (mac->vif) {
> +			beacon = ieee80211_beacon_get(mac->hw, mac->vif);
> +			if (beacon) {
> +				purelifi_mac_config_beacon(mac->hw, beacon,
> +							   false);
> +				kfree_skb(beacon);
> +				/* Returned skb is used only once and lowlevel
> +				 *  driver is responsible for freeing it.
> +				 */
> +			}
> +		}
> +
> +		purelifi_set_beacon_interval(&mac->chip, beacon_interval,
> +					     beacon_period, mac->type);
> +
> +		spin_lock_irq(&mac->lock);
> +		mac->beacon.last_update = jiffies;
> +		spin_unlock_irq(&mac->lock);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * purelifi_mac_tx_status - reports tx status of a packet if required
> + * @hw - a &struct ieee80211_hw pointer
> + * @skb - a sk-buffer
> + * @flags: extra flags to set in the TX status info
> + * @ackssi: ACK signal strength
> + * @success - True for successful transmission of the frame
> + *
> + * This information calls ieee80211_tx_status_irqsafe() if required by the
> + * control information. It copies the control information into the status
> + * information.
> + *
> + * If no status information has been requested, the skb is freed.
> + */
> +static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
> +				   struct sk_buff *skb,
> +		int ackssi, struct tx_status *tx_status)
> +{
> +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +	int success = 1, retry = 1;
> +
> +	ieee80211_tx_info_clear_status(info);
> +
> +	if (tx_status) {
> +		success = !tx_status->failure;
> +		retry = tx_status->retry + success;
> +	}
> +
> +	if (success)
> +		info->flags |= IEEE80211_TX_STAT_ACK;
> +	else
> +		info->flags &= ~IEEE80211_TX_STAT_ACK;
> +
> +	info->status.ack_signal = 50;
> +	ieee80211_tx_status_irqsafe(hw, skb);
> +}
> +
> +/**
> + * purelifi_mac_tx_to_dev - callback for USB layer
> + * @skb: a &sk_buff pointer
> + * @error: error value, 0 if transmission successful
> + *
> + * Informs the MAC layer that the frame has successfully transferred to the
> + * device. If an ACK is required and the transfer to the device has been
> + * successful, the packets are put on the @ack_wait_queue with
> + * the control set removed.
> + */
> +void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
> +{
> +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +	struct ieee80211_hw *hw = info->rate_driver_data[0];
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +
> +	ieee80211_tx_info_clear_status(info);
> +	skb_pull(skb, sizeof(struct purelifi_ctrlset));
> +
> +	if (unlikely(error ||
> +		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
> +		//FIXME : do we need to fill in anything ?

Linux kernel is written in C, no C++ code, please.

> +		ieee80211_tx_status_irqsafe(hw, skb);
> +	} else {
> +		// ieee80211_tx_status_irqsafe(hw, skb);
> +		struct sk_buff_head *q = &mac->ack_wait_queue;
> +
> +		skb_queue_tail(q, skb);
> +		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
> +			purelifi_mac_tx_status(hw, skb_dequeue(q),
> +					       mac->ack_pending ?
> +					mac->ack_signal : 0,
> +					NULL);
> +			mac->ack_pending = 0;
> +		}
> +	}
> +}
> +
> +static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
> +				      struct sk_buff *beacon, bool in_intr)
> +{
> +	return usb_write_req((const u8 *)beacon->data, beacon->len,
> +			USB_REQ_BEACON_WR);
> +}
> +
> +static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
> +{
> +	unsigned int frag_len = skb->len;
> +	unsigned int tmp;
> +	struct purelifi_ctrlset *cs;
> +
> +	if (skb_headroom(skb) >= sizeof(struct purelifi_ctrlset)) {
> +		cs = (struct purelifi_ctrlset *)skb_push(skb,
> +				sizeof(struct purelifi_ctrlset));
> +	} else {
> +		pl_dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
> +		return 1;
> +	}
> +
> +	cs->id = USB_REQ_DATA_TX;
> +	cs->payload_len_nw = frag_len;
> +	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
> +		- sizeof(cs->id) - sizeof(cs->len);
> +
> +	/* data packet lengths must be multiple of four bytes
> +	 * and must not be a multiple of 512
> +	 * bytes. First, it is attempted to append the
> +	 * data packet in the tailroom of the skb. In rare
> +	 * ocasions, the tailroom is too small. In this case,
> +	 * the content of the packet is shifted into
> +	 * the headroom of the skb by memcpy. Headroom is allocated
> +	 * at startup (below in this file). Therefore,
> +	 * there will be always enough headroom. The call skb_headroom
> +	 * is an additional safety which might be
> +	 * dropped.
> +	 */
> +
> +	/*check if 32 bit aligned */

Don't we have macro for that?

> +	tmp = skb->len & 3;
> +	if (tmp) {
> +		if (skb_tailroom(skb) >= (3 - tmp)) {
> +			skb_put(skb, 4 - tmp);
> +		} else {
> +			if (skb_headroom(skb) >= 4 - tmp) {
> +				u8 len;
> +				u8 *src_pt;
> +				u8 *dest_pt;
> +
> +				len = skb->len;
> +				src_pt = skb->data;
> +				dest_pt = skb_push(skb, 4 - tmp);
> +				memcpy(dest_pt, src_pt, len);
> +			} else {
> +				return 1;
> +			}
> +		}
> +		cs->len +=  4 - tmp;
> +	}
> +
> +	//check if not multiple of 512

Ditto

> +	tmp = skb->len & 0x1ff;
> +	if (!tmp) {
> +		if (skb_tailroom(skb) >= 4) {
> +			skb_put(skb, 4);
> +		} else {
> +			if (skb_headroom(skb) >= 4) {
> +				u8 len = skb->len;
> +				u8 *src_pt = skb->data;
> +				u8 *dest_pt = skb_push(skb, 4);
> +
> +				memcpy(dest_pt, src_pt, len);
> +			} else {
> +				/* should never happen b/c
> +				 * sufficient headroom was reserved
> +				 */
> +				return 1;
> +			}
> +		}
> +
> +		cs->len +=  4;
> +	}
> +
> +	cs->id = TO_NETWORK(cs->id);
> +	cs->len = TO_NETWORK(cs->len);
> +	cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
> +
> +	return 0;
> +}
> +
> +/**
> + * purelifi_op_tx - transmits a network frame to the device
> + *
> + * @dev: mac80211 hardware device
> + * @skb: socket buffer
> + * @control: the control structure
> + *
> + * This function transmit an IEEE 802.11 network frame to the device. The
> + * control block of the skbuff will be initialized. If necessary the incoming
> + * mac80211 queues will be stopped.
> + */
> +extern void send_packet_from_data_queue(struct purelifi_usb *usb);
> +static void purelifi_op_tx(struct ieee80211_hw *hw,
> +			   struct ieee80211_tx_control *control,
> +			   struct sk_buff *skb)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct purelifi_usb *usb = &mac->chip.usb;
> +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);

Reversed Christmas tree in all places, please.

I stopped here.

Thanks

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices
  2020-09-30  5:16   ` Leon Romanovsky
@ 2020-09-30  5:29     ` Srinivasan Raju
  2020-09-30  8:01     ` Kalle Valo
  2020-09-30  8:05     ` Kalle Valo
  2 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-09-30  5:29 UTC (permalink / raw)
  To: Leon Romanovsky
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, linux-kernel, linux-wireless,
	netdev


> I stopped here.
>
> Thanks

Thanks for your comments Leon, I will resubmit after the changes

Thanks
Srini

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices
  2020-09-30  5:16   ` Leon Romanovsky
  2020-09-30  5:29     ` Srinivasan Raju
@ 2020-09-30  8:01     ` Kalle Valo
  2020-09-30  9:55       ` Leon Romanovsky
  2020-09-30  8:05     ` Kalle Valo
  2 siblings, 1 reply; 108+ messages in thread
From: Kalle Valo @ 2020-09-30  8:01 UTC (permalink / raw)
  To: Leon Romanovsky
  Cc: Srinivasan Raju, mostafa.afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, linux-kernel, linux-wireless,
	netdev

Leon Romanovsky <leon@kernel.org> writes:

>> diff --git a/drivers/net/wireless/purelifi/Kconfig
> b/drivers/net/wireless/purelifi/Kconfig
>> new file mode 100644
>> index 000000000000..ff05eaf0a8d4
>> --- /dev/null
>> +++ b/drivers/net/wireless/purelifi/Kconfig
>> @@ -0,0 +1,38 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +config WLAN_VENDOR_PURELIFI
>> +	bool "pureLiFi devices"
>> +	default y
>
> "N" is preferred default.

In most cases that's true, but for WLAN_VENDOR_ configs 'default y'
should be used. It's the same as with NET_VENDOR_.


-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices
  2020-09-30  5:16   ` Leon Romanovsky
  2020-09-30  5:29     ` Srinivasan Raju
  2020-09-30  8:01     ` Kalle Valo
@ 2020-09-30  8:05     ` Kalle Valo
  2020-09-30 10:04       ` Leon Romanovsky
  2 siblings, 1 reply; 108+ messages in thread
From: Kalle Valo @ 2020-09-30  8:05 UTC (permalink / raw)
  To: Leon Romanovsky
  Cc: Srinivasan Raju, mostafa.afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, linux-kernel, linux-wireless,
	netdev

Leon Romanovsky <leon@kernel.org> writes:

>> --- /dev/null
>> +++ b/drivers/net/wireless/purelifi/Kconfig
>> @@ -0,0 +1,38 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +config WLAN_VENDOR_PURELIFI
>> +	bool "pureLiFi devices"
>> +	default y
>
> "N" is preferred default.
>
>> +	help
>> +	  If you have a pureLiFi device, say Y.
>> +
>> +	  Note that the answer to this question doesn't directly affect the
>> +	  kernel: saying N will just cause the configurator to skip all the
>> +	  questions about these cards. If you say Y, you will be asked for
>> +	  your specific card in the following questions.
>
> The text above makes no sense. Of course, it makes a lot of sense to
> disable this device for whole world.

This is a standard text for all vendor "groups", the actual driver
should be selected in a separate config. This text has been copied from
NET_VENDOR_ groups and used by all WLAN_VENDOR configs (or at least that
was the original plan).

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices
  2020-09-30  8:01     ` Kalle Valo
@ 2020-09-30  9:55       ` Leon Romanovsky
  2020-09-30 10:11         ` Johannes Berg
  0 siblings, 1 reply; 108+ messages in thread
From: Leon Romanovsky @ 2020-09-30  9:55 UTC (permalink / raw)
  To: Kalle Valo
  Cc: Srinivasan Raju, mostafa.afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, linux-kernel, linux-wireless,
	netdev

On Wed, Sep 30, 2020 at 11:01:27AM +0300, Kalle Valo wrote:
> Leon Romanovsky <leon@kernel.org> writes:
>
> >> diff --git a/drivers/net/wireless/purelifi/Kconfig
> > b/drivers/net/wireless/purelifi/Kconfig
> >> new file mode 100644
> >> index 000000000000..ff05eaf0a8d4
> >> --- /dev/null
> >> +++ b/drivers/net/wireless/purelifi/Kconfig
> >> @@ -0,0 +1,38 @@
> >> +# SPDX-License-Identifier: GPL-2.0
> >> +config WLAN_VENDOR_PURELIFI
> >> +	bool "pureLiFi devices"
> >> +	default y
> >
> > "N" is preferred default.
>
> In most cases that's true, but for WLAN_VENDOR_ configs 'default y'
> should be used. It's the same as with NET_VENDOR_.

I would like to challenge it, why is that?
Why do I need to set "N", every time new vendor upstreams its code?

Thanks

>
>
> --
> https://patchwork.kernel.org/project/linux-wireless/list/
>
> https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices
  2020-09-30  8:05     ` Kalle Valo
@ 2020-09-30 10:04       ` Leon Romanovsky
  0 siblings, 0 replies; 108+ messages in thread
From: Leon Romanovsky @ 2020-09-30 10:04 UTC (permalink / raw)
  To: Kalle Valo
  Cc: Srinivasan Raju, mostafa.afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, linux-kernel, linux-wireless,
	netdev

On Wed, Sep 30, 2020 at 11:05:49AM +0300, Kalle Valo wrote:
> Leon Romanovsky <leon@kernel.org> writes:
>
> >> --- /dev/null
> >> +++ b/drivers/net/wireless/purelifi/Kconfig
> >> @@ -0,0 +1,38 @@
> >> +# SPDX-License-Identifier: GPL-2.0
> >> +config WLAN_VENDOR_PURELIFI
> >> +	bool "pureLiFi devices"
> >> +	default y
> >
> > "N" is preferred default.
> >
> >> +	help
> >> +	  If you have a pureLiFi device, say Y.
> >> +
> >> +	  Note that the answer to this question doesn't directly affect the
> >> +	  kernel: saying N will just cause the configurator to skip all the
> >> +	  questions about these cards. If you say Y, you will be asked for
> >> +	  your specific card in the following questions.
> >
> > The text above makes no sense. Of course, it makes a lot of sense to
> > disable this device for whole world.
>
> This is a standard text for all vendor "groups", the actual driver
> should be selected in a separate config. This text has been copied from
> NET_VENDOR_ groups and used by all WLAN_VENDOR configs (or at least that
> was the original plan).

OK, Thanks.

>
> --
> https://patchwork.kernel.org/project/linux-wireless/list/
>
> https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices
  2020-09-30  9:55       ` Leon Romanovsky
@ 2020-09-30 10:11         ` Johannes Berg
  2020-09-30 10:44           ` Leon Romanovsky
  0 siblings, 1 reply; 108+ messages in thread
From: Johannes Berg @ 2020-09-30 10:11 UTC (permalink / raw)
  To: Leon Romanovsky, Kalle Valo
  Cc: Srinivasan Raju, mostafa.afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, linux-kernel, linux-wireless,
	netdev

On Wed, 2020-09-30 at 12:55 +0300, Leon Romanovsky wrote:
> On Wed, Sep 30, 2020 at 11:01:27AM +0300, Kalle Valo wrote:
> > Leon Romanovsky <leon@kernel.org> writes:
> > 
> > > > diff --git a/drivers/net/wireless/purelifi/Kconfig
> > > b/drivers/net/wireless/purelifi/Kconfig
> > > > new file mode 100644
> > > > index 000000000000..ff05eaf0a8d4
> > > > --- /dev/null
> > > > +++ b/drivers/net/wireless/purelifi/Kconfig
> > > > @@ -0,0 +1,38 @@
> > > > +# SPDX-License-Identifier: GPL-2.0
> > > > +config WLAN_VENDOR_PURELIFI
> > > > +	bool "pureLiFi devices"
> > > > +	default y
> > > 
> > > "N" is preferred default.
> > 
> > In most cases that's true, but for WLAN_VENDOR_ configs 'default y'
> > should be used. It's the same as with NET_VENDOR_.
> 
> I would like to challenge it, why is that?
> Why do I need to set "N", every time new vendor upstreams its code?

You don't. The WLAN_VENDOR_* settings are not supposed to affect the
build, just the Kconfig visibility.

johannes


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices
  2020-09-30 10:11         ` Johannes Berg
@ 2020-09-30 10:44           ` Leon Romanovsky
  2020-10-16  8:23             ` Kalle Valo
  0 siblings, 1 reply; 108+ messages in thread
From: Leon Romanovsky @ 2020-09-30 10:44 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Kalle Valo, Srinivasan Raju, mostafa.afgani, David S. Miller,
	Jakub Kicinski, Mauro Carvalho Chehab, Rob Herring, linux-kernel,
	linux-wireless, netdev

On Wed, Sep 30, 2020 at 12:11:24PM +0200, Johannes Berg wrote:
> On Wed, 2020-09-30 at 12:55 +0300, Leon Romanovsky wrote:
> > On Wed, Sep 30, 2020 at 11:01:27AM +0300, Kalle Valo wrote:
> > > Leon Romanovsky <leon@kernel.org> writes:
> > >
> > > > > diff --git a/drivers/net/wireless/purelifi/Kconfig
> > > > b/drivers/net/wireless/purelifi/Kconfig
> > > > > new file mode 100644
> > > > > index 000000000000..ff05eaf0a8d4
> > > > > --- /dev/null
> > > > > +++ b/drivers/net/wireless/purelifi/Kconfig
> > > > > @@ -0,0 +1,38 @@
> > > > > +# SPDX-License-Identifier: GPL-2.0
> > > > > +config WLAN_VENDOR_PURELIFI
> > > > > +	bool "pureLiFi devices"
> > > > > +	default y
> > > >
> > > > "N" is preferred default.
> > >
> > > In most cases that's true, but for WLAN_VENDOR_ configs 'default y'
> > > should be used. It's the same as with NET_VENDOR_.
> >
> > I would like to challenge it, why is that?
> > Why do I need to set "N", every time new vendor upstreams its code?
>
> You don't. The WLAN_VENDOR_* settings are not supposed to affect the
> build, just the Kconfig visibility.

Which is important to me, I'm keeping .config as minimal as possible
to simplify comparison between various builds.

Thanks

^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [PATCH] [v3] wireless: Initial driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
  2020-09-28 12:07   ` Joe Perches
  2020-09-30  5:16   ` Leon Romanovsky
@ 2020-10-14  6:19   ` Srinivasan Raju
  2020-10-14 10:17     ` kernel test robot
  2020-10-15 22:35     ` Joe Perches
  2020-10-16  6:34   ` [PATCH] [v4] " Srinivasan Raju
                     ` (16 subsequent siblings)
  19 siblings, 2 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-10-14  6:19 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Mauro Carvalho Chehab, Rob Herring,
	Lukas Bulwahn, open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
---
 MAINTAINERS                            |    5 +
 drivers/net/wireless/Kconfig           |    1 +
 drivers/net/wireless/Makefile          |    1 +
 drivers/net/wireless/purelifi/Kconfig  |   27 +
 drivers/net/wireless/purelifi/Makefile |    3 +
 drivers/net/wireless/purelifi/chip.c   |   97 ++
 drivers/net/wireless/purelifi/chip.h   |   82 ++
 drivers/net/wireless/purelifi/intf.h   |   38 +
 drivers/net/wireless/purelifi/mac.c    |  874 +++++++++++++
 drivers/net/wireless/purelifi/mac.h    |  178 +++
 drivers/net/wireless/purelifi/usb.c    | 1647 ++++++++++++++++++++++++
 drivers/net/wireless/purelifi/usb.h    |  147 +++
 12 files changed, 3100 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/chip.c
 create mode 100644 drivers/net/wireless/purelifi/chip.h
 create mode 100644 drivers/net/wireless/purelifi/intf.h
 create mode 100644 drivers/net/wireless/purelifi/mac.c
 create mode 100644 drivers/net/wireless/purelifi/mac.h
 create mode 100644 drivers/net/wireless/purelifi/usb.c
 create mode 100644 drivers/net/wireless/purelifi/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index c80f87d7258c..150f592fb6e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14108,6 +14108,11 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PUREILIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+S:	Maintained
+F:	drivers/net/wireless/purelifi
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 170a64e67709..b87da3139f94 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -48,6 +48,7 @@ source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
 source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 
 config PCMCIA_RAYCS
 	tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e9fc770026f0 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
 obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..f6630791df9d
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+config PURELIFI
+
+	tristate "pureLiFi device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This driver makes the adapter appear as a normal WLAN interface.
+
+	   The pureLiFi device requires external STA firmware to be loaded.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called purelifi.
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..1f20055e741f
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PURELIFI)		:= purelifi.o
+purelifi-objs 		+= chip.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
new file mode 100644
index 000000000000..ea1e58426c92
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval || (chip->beacon_set &&
+			  chip->beacon_interval == interval)) {
+		return 0;
+	}
+
+	chip->beacon_interval = interval;
+	chip->beacon_set = true;
+	return usb_write_req((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %02hx:%02hx v%02hx  %02x-%02x-%02x %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr[0], addr[1], addr[2],
+		speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
+{
+	int r;
+
+	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "%s r=%d", __func__, r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	u8 value;
+
+	value = 0;
+	usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+	static struct purelifi_chip *chip_p;
+
+	if (chip)
+		chip_p = chip;
+	if (!chip_p)
+		return -EINVAL;
+
+	r = usb_write_req((const u8 *)&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "set rate failed r=%d", r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/chip.h b/drivers/net/wireless/purelifi/chip.h
new file mode 100644
index 000000000000..f479f34a6503
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	u16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr
+					)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1 << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/intf.h b/drivers/net/wireless/purelifi/intf.h
new file mode 100644
index 000000000000..20e96671e0a7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/intf.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	u16 rssi;
+	u8  rate_idx;
+	u8  pad;
+	u64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             buf[512];
+};
+
+#endif
diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
new file mode 100644
index 000000000000..ff2105b7b9dc
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.c
@@ -0,0 +1,874 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+
+#ifndef IEEE80211_BAND_2GHZ
+#define IEEE80211_BAND_2GHZ NL80211_BAND_2GHZ
+#endif
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M, },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 },
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
+{
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed. (%d)\n", r);
+		goto out;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled %d", irqs_disabled());
+
+	r = regulatory_hint(hw->wiphy, "CA");
+out:
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	struct purelifi_mc_hash multicast_hash;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	multicast_hash = mac->multicast_hash;
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon, false);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status - reports tx status of a packet if required
+ * @hw - a &struct ieee80211_hw pointer
+ * @skb - a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success - True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1, retry = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status) {
+		success = !tx_status->failure;
+		retry = tx_status->retry + success;
+	}
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		/* ieee80211_tx_status_irqsafe(hw, skb); */
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr)
+{
+	return usb_write_req((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (struct purelifi_ctrlset *)skb_push(skb,
+			sizeof(struct purelifi_ctrlset));
+	cs->id = USB_REQ_DATA_TX;
+	cs->payload_len_nw = frag_len;
+	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
+		- sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned and align data*/
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		cs->len +=  4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data*/
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen b/c
+				 * sufficient headroom was reserved
+				 */
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		cs->len +=  4;
+	}
+
+	cs->id = TO_NETWORK(cs->id);
+	cs->len = TO_NETWORK(cs->len);
+	cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
+
+	return 0;
+}
+
+extern void send_packet_from_data_queue(struct purelifi_usb *usb);
+/**
+ * purelifi_op_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+	info->rate_driver_data[0] = hw;
+
+	if (skb->data[24] != 0x08 && skb->len == 116)
+		dev_info(purelifi_mac_dev(mac), "%s::ping\n", __func__);
+
+	if (skb->data[24] == 0x08) {
+		u8 dst_mac[ETH_ALEN];
+		u8 sidx;
+		bool found = false;
+
+		memcpy(dst_mac, &skb->data[28], ETH_ALEN);
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (usb->tx.station[sidx].flag &
+					STATION_CONNECTED_FLAG) {
+				if (!memcmp(usb->tx.station[sidx].mac,
+					    dst_mac, ETH_ALEN)) {
+					found = true;
+					break;
+				}
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&usb->tx.station[sidx].data_list) > 60)
+			block_queue(usb, usb->tx.station[sidx].mac, true);
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&usb->tx.station[sidx].data_list) < 256) {
+			skb_queue_tail(&usb->tx.station[sidx].data_list, skb);
+			send_packet_from_data_queue(usb);
+		} else {
+			dev_kfree_skb(skb);
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
+					USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+		      struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	int found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+	dev_info(purelifi_mac_dev(mac), "%s::ACK Received", __func__);
+	return 0;
+
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++) {
+			skb = __skb_dequeue(q);
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = 1;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	u32 crc_error_cnt_low, crc_error_cnt_high;
+	int sidx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = IEEE80211_BAND_2GHZ;
+	mac->rssi      = -15 * ntohs(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	crc_error_cnt_low = status->crc_error_count;
+	crc_error_cnt_high = status->crc_error_count >> 32;
+	mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
+		ntohl(crc_error_cnt_high);
+
+	if (!bad_frame &&
+	    filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = TO_HOST(*(u32 *)buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!memcmp(&buffer[10],
+			    mac->chip.usb.tx.station[sidx].mac,
+					ETH_ALEN)) {
+			if (mac->chip.usb.tx.station[sidx].flag &
+					STATION_CONNECTED_FLAG) {
+				mac->chip.usb.tx.station[sidx].flag |=
+					STATION_HEARTBEAT_FLAG;
+				break;
+			}
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (!(mac->chip.usb.tx.station[sidx].flag &
+						STATION_CONNECTED_FLAG
+			     )) {
+				memcpy(mac->chip.usb.tx.station[sidx].mac,
+				       &buffer[10],
+						ETH_ALEN);
+				mac->chip.usb.tx.station[sidx].flag |=
+					STATION_CONNECTED_FLAG;
+				mac->chip.usb.tx.station[sidx].flag |=
+					STATION_HEARTBEAT_FLAG;
+				break;
+			}
+		}
+	}
+
+	if (buffer[0] == 0x40)
+		dev_info(purelifi_mac_dev(mac), "Probe request\n");
+	else if (buffer[0] == 0x0)
+		dev_info(purelifi_mac_dev(mac), "Association request\n");
+
+	if (buffer[0] == 0xb0) {
+		dev_info(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+	} else if (buffer[0] == 0x08) {
+		unsigned short int seq_nmb = (buffer[23] << 4) |
+			((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000)) {
+			dev_info(purelifi_mac_dev(mac), "seq_nmb < min_exp");
+		} else {
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		}
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+	if (need_padding) {
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+	}
+
+	/* FIXME : could we avoid this big memcpy ? */
+	memcpy(skb_put(skb, payload_length), buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_info(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			 iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_info(purelifi_mac_dev(mac), "unsupported iftype");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	spin_lock_irq(&mac->lock);
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+	int r;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* purelifi_chip_set_multicast_hash(&mac->chip, &hash); */
+
+	if (changed_flags & FIF_CONTROL)
+		r = 0;
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_info(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon, false);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band;
+
+#ifdef ieee80211_hw_set
+	_ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
+	_ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
+	_ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
+	_ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);
+#else
+	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+		IEEE80211_HW_SIGNAL_DBM |
+		IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+		IEEE80211_HW_MFP_CAPABLE;
+#endif
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* 4 for 32 bit alignment if no tailroom */
+
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/mac.h b/drivers/net/wireless/purelifi/mac.h
new file mode 100644
index 000000000000..35e28cc4dffb
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define MODULATION_RATE_BPSK_1_2  0
+#define MODULATION_RATE_BPSK_3_4  (MODULATION_RATE_BPSK_1_2  + 1)
+#define MODULATION_RATE_QPSK_1_2  (MODULATION_RATE_BPSK_3_4  + 1)
+#define MODULATION_RATE_QPSK_3_4  (MODULATION_RATE_QPSK_1_2  + 1)
+#define MODULATION_RATE_QAM16_1_2 (MODULATION_RATE_QPSK_3_4  + 1)
+#define MODULATION_RATE_QAM16_3_4 (MODULATION_RATE_QAM16_1_2 + 1)
+#define MODULATION_RATE_QAM64_1_2 (MODULATION_RATE_QAM16_3_4 + 1)
+#define MODULATION_RATE_QAM64_3_4 (MODULATION_RATE_QAM64_1_2 + 1)
+#define MODULATION_RATE_AUTO      (MODULATION_RATE_QAM64_3_4 + 1)
+#define MODULATION_RATE_NUM       (MODULATION_RATE_AUTO      + 1)
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             modulation;
+	u8             control;
+	u8             service;
+	u8             pad;
+	__le16         packet_length;
+	__le16         current_length;
+	__le16          next_frame_length;
+	__le16         tx_length;
+	u32            payload_len_nw;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	spinlock_t intr_lock; /* not used : interrupt lock for mac */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	unsigned int ack_pending:1;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
+		*chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block);
+
+#ifdef DEBUG
+void purelifi_dump_rx_status(const struct rx_status *status);
+#else
+#define purelifi_dump_rx_status(status)
+#endif
+
+#endif
diff --git a/drivers/net/wireless/purelifi/usb.c b/drivers/net/wireless/purelifi/usb.c
new file mode 100644
index 000000000000..684ef61d6761
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.c
@@ -0,0 +1,1647 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+#define FCS_LEN 4
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#undef  LOAD_MAC_AND_SERIAL_FROM_FILE
+#undef  LOAD_MAC_AND_SERIAL_FROM_FLASH
+#define LOAD_MAC_AND_SERIAL_FROM_EP0
+
+#define PROC_FOLDER           "purelifi"
+#define MODULATION_FILENAME   "modulation"
+
+struct proc_dir_entry *proc_folder;
+struct proc_dir_entry *modulation_proc_entry;
+static atomic_t data_queue_flag;
+
+/*Tx retry backoff timer (in milliseconds).*/
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+
+/*Tx retry backoff timer (in jiffies).*/
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+
+	spin_lock_irqsave(&usb->tx.lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if ((usb->tx.station[sidx].flag &
+					STATION_CONNECTED_FLAG)) {
+			if (!(usb->tx.station[sidx].flag &
+						STATION_FIFO_FULL_FLAG)) {
+				skb = skb_peek(&usb->tx.station
+						[sidx].data_list);
+			}
+		}
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&usb->tx.station[sidx].data_list);
+		usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				    tx_urb_complete, skb);
+		if (skb_queue_len(&usb->tx.station[sidx].data_list)
+				<= 60) {
+			block_queue(usb, usb->tx.station[sidx].mac,
+				    false);
+		}
+	}
+	spin_unlock_irqrestore(&usb->tx.lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int rx_usb_enabled;
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_rx *rx;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
+		return;
+	} else if (!urb->context) {
+		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
+
+	rx = &usb->rx;
+	tx = &usb->tx;
+
+	if (urb->actual_length != 8) {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[7];
+
+	dev_info(&usb->intf->dev, "Recv status=%u\n", status);
+	dev_info(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+		 buffer[0], buffer[1], buffer[2], buffer[3],
+		 buffer[4], buffer[5]);
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_info(&usb->intf->dev,
+			 "FIFO full not packet receipt.\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			usb->tx.station[sidx].flag |=
+				STATION_FIFO_FULL_FLAG;
+		}
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_info(&usb->intf->dev, "FIFO full packet receipt.\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			usb->tx.station[sidx].flag &= 0xFD;
+
+		send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		fpga_link_connection_f = 1;
+		dev_info(&usb->intf->dev, "ST_CONNECT_MSG packet receipt.\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		fpga_link_connection_f = 0;
+		dev_info(&usb->intf->dev, "ST_DISCONN_MSG packet receipt.\n");
+		break;
+	default:
+		dev_info(&usb->intf->dev, "Unknown packet receipt.\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit error %d\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0; /*no error return*/
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	dev_info(&usb->intf->dev, "%s\n", __func__);
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+	tx = &usb->tx;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "error urb %p %d\n", urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "error fragment_length");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+const char *speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset error %d\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, 0x40, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+static int download_fpga(struct usb_interface *intf)
+{
+	int i, r, actual_length;
+	int fw_data_i, blk_tran_len = 16384;
+	const char *fw_name;
+	unsigned char fpga_setting[2];
+	unsigned char *fw_data;
+	unsigned char fpga_state[9];
+	unsigned char *fpga_dmabuff;
+	struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	fpga_dmabuff = NULL;
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga.bit";
+		dev_info(&intf->dev, "bit file for X selected.\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga_xc.bit";
+		dev_info(&intf->dev, "bit filefor XC selected.\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware((const struct firmware **)&fw, fw_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed.(%d)\n", r);
+		goto error;
+	}
+
+	fpga_dmabuff = kmalloc(2, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, 0x33, fpga_dmabuff, sizeof(fpga_setting));
+
+	memcpy(fpga_setting, fpga_dmabuff, 2);
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x34, NULL, 0);
+
+	if (fpga_setting[0] != 6) {
+		dev_err(&intf->dev, "%s::fpga_setting[0] wrong.\n", __func__);
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmalloc(blk_tran_len, GFP_KERNEL);
+
+		memcpy(fw_data, &fw->data[fw_data_i], blk_tran_len);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			fw_data[tbuf_idx] =
+				((fw_data[tbuf_idx] & 128) >> 7) |
+				((fw_data[tbuf_idx] &  64) >> 5) |
+				((fw_data[tbuf_idx] &  32) >> 3) |
+				((fw_data[tbuf_idx] &  16) >> 1) |
+				((fw_data[tbuf_idx] &   8) << 1) |
+				((fw_data[tbuf_idx] &   4) << 3) |
+				((fw_data[tbuf_idx] &   2) << 5) |
+				((fw_data[tbuf_idx] &   1) << 7);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_setting[0] & 0xFF),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2000);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed. (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(9, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xFF, 9);
+
+	send_vendor_request(udev, 0x30, fpga_dmabuff, sizeof(fpga_state));
+
+	dev_info(&intf->dev, "fpga_status");
+	for (i = 0; i <= 8; i++)
+		dev_info(&intf->dev, " %x ", fpga_dmabuff[i]);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		kfree(fpga_dmabuff);
+		goto error_free_fw;
+	}
+
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x35, NULL, 0);
+
+	msleep(200);
+
+error_free_fw:
+	release_firmware(fw);
+error:
+	return r;
+}
+
+static int download_XL_FW(struct usb_interface *intf)
+{
+	int r;
+
+	struct firmware *fw_packed = NULL;
+	int step;
+	u8 *buf;
+	u32 size, nmb_of_control_packets, i, no_of_files;
+	u32 tot_size, start_addr;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, 0x80, NULL, 0);
+	msleep(100);
+
+	/* Code for single pack file download */
+
+	fw_pack = "purelifi/li_fi_x/Li-Fi-XL.bin";
+
+	r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed.(%d)\n", r);
+		goto error;
+	}
+	no_of_files = *(u32 *)&fw_packed->data[0];
+	tot_size = fw_packed->size;
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n", no_of_files, tot_size);
+
+	buf = kmalloc(64, GFP_KERNEL);
+	r = -ENOMEM;
+
+	if (!buf)
+		goto error;
+
+	memset(buf, 0, 64);
+
+	for (step = 0; step < no_of_files; step++) {
+		buf[0] = step;
+		r = send_vendor_command(udev, 0x82, buf, 64);
+
+		if (step < no_of_files - 1)
+			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
+				- *(u32 *)&fw_packed->data[4 + (step) * 4];
+		else
+			size = tot_size -
+				*(u32 *)&fw_packed->data[4 + (step) * 4];
+
+		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
+
+		if ((size % 64) && step < 2)
+			size += 64 - size % 64;
+		nmb_of_control_packets = size / 64;
+
+		for (i = 0; i < nmb_of_control_packets; i++) {
+			memcpy(buf,
+			       &fw_packed->data[start_addr + (i * 64)], 64);
+			r = send_vendor_command(udev, 0x81, buf, 64);
+		}
+	  dev_info(&intf->dev, "fw-dw step %d,r=%d size %d\n", step, r, size);
+	}
+	release_firmware(fw_packed);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, 0x83, NULL, 0);
+	dev_info(&intf->dev, "download_fpga (4), r=%d\n", r);
+error:
+	return r;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+static void pretty_print_mac(struct usb_interface *intf, char *serial_number,
+			     unsigned char *hw_address
+			    )
+{
+	unsigned char i;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		dev_info(&intf->dev, "hw_address[%d]=%x\n", i, hw_address[i]);
+}
+
+static int upload_mac_and_serial_number(struct usb_interface *intf,
+					unsigned char *hw_address,
+					unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FILE
+	struct firmware *fw = NULL;
+	int r;
+	char *mac_file_name = "purelifi/li_fi_x/mac.ascii";
+
+	r = request_firmware((const struct firmware **)&fw, mac_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail.(%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	r = sscanf(fw->data,
+		   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+		    &hw_address[0], &hw_address[1],
+		    &hw_address[2], &hw_address[3],
+		    &hw_address[4], &hw_address[5]);
+	if (r != 6) {
+		dev_err(&intf->dev, "fw_dw usage :args fail.(%d)\n", r);
+		goto ERROR;
+	}
+	release_firmware(fw);
+
+	char *serial_file_name = "purelifi/li_fi_x/serial.ascii";
+
+	r = request_firmware((const struct firmware **)&fw, serial_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail.(%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	strcpy(serial_number, fw->data);
+	dev_info(&intf->dev, "serial_number=%s\n", serial_number);
+	release_firmware(fw);
+	pretty_print_mac(intf, serial_number, hw_address);
+ERROR:
+	return r;
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FLASH
+
+#define FLASH_EC_BUSY 3
+
+	struct flash_t flash;
+	unsigned char *flash_sector_buf;
+	unsigned int sector_index;
+
+	do {
+		unsigned char buf[8];
+
+		msleep(200);
+
+		send_vendor_request(udev, 0x40, buf, sizeof(buf));
+		flash.enabled = buf[0] & 0xFF;
+
+		if (flash.enabled) {
+			flash.sectors = ((buf[6] & 255) << 24) |
+				((buf[5] & 255) << 16) |
+				((buf[4] & 255) <<  8) |
+				(buf[3] & 255);
+
+			flash.sector_size =
+				((buf[2] & 255) << 8) | (buf[1] & 255);
+		} else {
+			flash.sectors = 0;
+			flash.sector_size = 0;
+		}
+
+		if (flash.sector_size & 0x8000)
+			flash.sector_size = 1 << (flash.sector_size & 0x7FFF);
+
+		flash.ec = buf[7] & 0xFF;
+	} while (flash.ec == FLASH_EC_BUSY);
+
+	flash_sector_buf = kmalloc(flash.sector_size,
+				   GFP_KERNEL);
+	if (!flash_sector_buf)
+		return -ENOMEM;
+
+	if (!flash.enabled) {
+		dev_err(&intf->dev, "Flash is not enabled");
+		return -EINVAL;
+	}
+
+	read_flash(intf, &flash, 0, flash_sector_buf);
+	sector_index = *((unsigned int *)flash_sector_buf);
+
+	read_flash(intf, &flash, sector_index, flash_sector_buf);
+	memcpy(hw_address, flash_sector_buf, ETH_ALEN);
+	strncpy(serial_number, &flash_sector_buf[ETH_ALEN], 256);
+
+	pretty_print_mac(intf, serial_number, hw_address);
+	kfree(flash_sector_buf);
+	return 0;
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_EP0
+
+#define USB_MAC_VENDOR_REQUEST 0x36
+#define USB_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define USB_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define SERIAL_NUMBER_LENGTH 14
+
+	int r = 0;
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(14, GFP_KERNEL);
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	send_vendor_request(udev, USB_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	memcpy(serial_number, dma_buffer, SERIAL_NUMBER_LENGTH);
+
+	memset(dma_buffer, 0x00, 14);
+
+	send_vendor_request(udev, USB_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, 8);
+
+	memcpy(&firmware_version, dma_buffer, sizeof(unsigned long long));
+
+	dev_info(&intf->dev, "Firmware Version: %llu, Revision: 16418\n",
+		 firmware_version);
+	kfree(dma_buffer);
+
+	pretty_print_mac(intf, serial_number, hw_address);
+error:
+	return r;
+#endif
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	u32 payload_len_nw = TO_NETWORK((buffer_len + FCS_LEN));
+
+	usb_req->id = usb_req_id;
+	usb_req->len  = 0;
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == USB_REQ_BEACON_WR) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		usb_req->len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	usb_req->len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	usb_req->len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+		usb_req->len += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->id = TO_NETWORK(usb_req->id);
+	usb_req->len = TO_NETWORK(usb_req->len);
+}
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			usb_complete_t complete_fn,
+			void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "async write submit r = %d\n", r);
+
+	return r;
+}
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(usb_req.id) + sizeof(usb_req.len) +
+			   TO_HOST(usb_req.len);
+
+	dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+	memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg unsuccesful.(%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	send_packet_from_data_queue(usb);
+	usb->tx.tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	add_timer(&usb->tx.tx_retry_timer);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (usb->tx.station[sidx].flag & STATION_CONNECTED_FLAG) {
+			if (usb->tx.station[sidx].flag &
+					STATION_HEARTBEAT_FLAG) {
+				usb->tx.station[sidx].flag ^=
+					STATION_HEARTBEAT_FLAG;
+			} else {
+				memset(usb->tx.station[sidx].mac, 0,
+				       ETH_ALEN);
+				usb->tx.station[sidx].flag = 0;
+			}
+		}
+	}
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	add_timer(&usb->sta_queue_cleanup);
+}
+
+static enum unit_type_t get_unit_type(char *serial_number,
+				      struct usb_interface *intf)
+{
+	dev_info(&intf->dev, "Unit-type is an Station (STA)\n");
+	return STA;
+}
+
+char usr_input[8] = {0};
+
+static ssize_t purelifi_store_frequency(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t len)
+{
+	int i, r, row, col, predivider, feedback_divider, output_div_0;
+	int output_div_1, output_div_2, clkout3_phase;
+	char values[6][4] = {{0}, {0} };
+	bool valid = false;
+	int rv = 0;
+
+	row = 0; col = 0;
+	for (i = 0; i < min(len, sizeof(values)); i++) {
+		values[row][col] = buf[i];
+		col++;
+		if (buf[i] == 0x20) {
+			row++;
+			col = 0;
+		} else if (buf[i] == 0x0A) {
+			if (row == 5 && col > 0) {
+				rv = sscanf((char *)&values[0][0],
+					    "%d", &predivider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[1][0],
+					    "%d", &feedback_divider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[2][0],
+					    "%d", &output_div_0);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[3][0],
+					    "%d", &output_div_1);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[4][0],
+					    "%d", &output_div_2);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[5][0],
+					    "%d", &clkout3_phase);
+				if (rv != 1)
+					valid = false;
+				valid = true;
+
+				break;
+			}
+		}
+	}
+
+	if (valid) {
+		usr_input[0] = (char)predivider;
+		usr_input[1] = (char)feedback_divider;
+		usr_input[2] = (char)output_div_0;
+		usr_input[3] = (char)output_div_1;
+		usr_input[4] = (char)output_div_2;
+		usr_input[5] = (char)clkout3_phase;
+
+		dev_err(dev, "options IP_DIV: %d VCO_MULT: %d OP_DIV_PHY: %d",
+			usr_input[0], usr_input[1], usr_input[2]);
+		dev_err(dev, "OP_DIV_CPU: %d OP_DIV_USB: %d CLK3_PH: %d\n",
+			usr_input[3], usr_input[4], usr_input[5]);
+
+		r = usb_write_req((const u8 *)&usr_input[0],
+				  sizeof(usr_input), USB_REQ_SET_FREQ);
+	} else {
+		dev_err(dev, "Your options are invalid\n");
+	}
+
+	return len;
+}
+
+static ssize_t purelifi_show_sysfs(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	return 0;
+}
+
+static const struct device_attribute purelifi_attr_frequency = {
+	  .attr = {.name = "frequency", .mode = (0666)},
+	  .show = purelifi_show_sysfs,
+	  .store = purelifi_store_frequency,
+};
+
+static ssize_t purelifi_store_modulation(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t len)
+{
+	int rate, i;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	for (i = 0; i < min(len, sizeof(usr_input)); i++)
+		usr_input[i] = buf[i];
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return len;
+}
+
+static ssize_t purelifi_show_modulation(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return 0;
+}
+
+static struct device_attribute purelifi_attr_modulation = {
+	.attr = {.name = "modulation", .mode = (0666)},
+	.show = purelifi_show_modulation,
+	.store = purelifi_store_modulation,
+};
+
+static void create_config(struct usb_interface *intf)
+{
+	struct device *dev;
+
+	dev = &intf->dev;
+	if (device_create_file(dev, &purelifi_attr_modulation)) {
+		dev_err(&intf->dev,
+			"failed to create modulation sysfs entry\n");
+		return;
+	}
+	if (device_create_file(dev, &purelifi_attr_frequency)) {
+		device_remove_file(dev, &purelifi_attr_modulation);
+		dev_err(&intf->dev,
+			"failed to create frequency sysfs entry\n");
+	}
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_usb *usb;
+	struct purelifi_chip *chip;
+	struct ieee80211_hw *hw = NULL;
+	static unsigned char hw_address[ETH_ALEN];
+	static unsigned char serial_number[256];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	create_config(intf);
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+
+	r = upload_mac_and_serial_number(intf, hw_address, serial_number);
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed %d\n", r);
+		goto error;
+	}
+	chip->unit_type = get_unit_type(serial_number, intf);
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg(&intf->dev, "init mac Error %d\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg(&intf->dev, "register device Error %d\n", r);
+		goto error;
+	}
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_XL_FW(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failure.(%d)\n", r);
+		goto error;
+	}
+
+	usb->tx.mac_fifo_full = 0;
+	spin_lock_init(&usb->tx.lock);
+
+	msleep(200);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_info(&intf->dev, "usb_init_hw fail (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_info(&intf->dev, "purelifi_chip_set_rate fail (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = usb_write_req(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_info(&intf->dev, "USB_REQ_MAC_WR failure.(%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&usb->tx.station[i].data_list);
+		usb->tx.station[i].flag = 0;
+	}
+	usb->tx.station[STA_BROADCAST_INDEX].flag |=
+		STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		usb->tx.station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	usb->tx.tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&usb->tx.tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer(&usb->tx.tx_retry_timer);
+	del_timer(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	device_remove_file(&intf->dev, &purelifi_attr_modulation);
+	device_remove_file(&intf->dev, &purelifi_attr_frequency);
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume fail error %d. Retry...\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb), "restore r: %d\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	mutex_lock(&mac->chip.mutex);
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	mutex_unlock(&mac->chip.mutex);
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#else
+
+#define suspend NULL
+#define resume  NULL
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+	.suspend        = suspend,
+	.resume         = resume,
+	.disable_hub_initiated_lpm = 1,
+};
+
+struct workqueue_struct *purelifi_workqueue;
+
+static int modulation_open(struct inode *inode_p, struct file *file_p)
+{
+	return 0;
+}
+
+static ssize_t modulation_read(struct file *file_p,
+			       char __user *user_p,
+		size_t offset, loff_t *off_p)
+{
+	return 0;
+}
+
+ssize_t modulation_write(struct file *file_p,
+			 const char __user *buffer,
+		size_t count, loff_t *actual_data_written)
+{
+	int rate;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	if (copy_from_user(usr_input, buffer, min(count, sizeof(usr_input))))
+		return -EFAULT;
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return count;
+}
+
+static const struct proc_ops  modulation_fops = {
+	.proc_open  = modulation_open,
+	.proc_read  = modulation_read,
+	.proc_write = modulation_write
+};
+
+static int create_proc_entry_e(struct proc_dir_entry *entry,
+			       size_t size,
+			       mode_t mode,
+			       const struct proc_ops *pops,
+			       const char *entry_name,
+			       struct proc_dir_entry *parent
+			     )
+{
+	entry = proc_create(entry_name, mode, parent, pops);
+	if (!(entry))
+		return -ENOMEM;
+	return 0;
+}
+
+static int __init usb_init(void)
+{
+	int r;
+
+	proc_folder = proc_mkdir(PROC_FOLDER, NULL);
+	if (!proc_folder) {
+		pr_err("Error creating proc folder.\n");
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = create_proc_entry_e(modulation_proc_entry, sizeof(u32), 0666,
+				&modulation_fops, MODULATION_FILENAME,
+				proc_folder);
+	if (r)
+		goto remove_proc_dir;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto remove_modulation_proc_file;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+remove_modulation_proc_file:
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+remove_proc_dir:
+	remove_proc_entry(PROC_FOLDER, NULL);
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+	remove_proc_entry(PROC_FOLDER, NULL);
+	if (ez_usb_interface) {
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_modulation);
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_frequency);
+	}
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi Ltd");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("purelifi/li_fi_x/fpga.bit");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/usb.h b/drivers/net/wireless/purelifi/usb.h
new file mode 100644
index 000000000000..53cbbe5b8330
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.h
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
+		      (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
+
+#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
+
+#define TO_NETWORK_32(X) TO_NETWORK(X)
+
+#define TO_HOST(X)    TO_NETWORK(X)
+#define TO_HOST_16(X) TO_NETWORK_16(X)
+#define TO_HOST_32(X) TO_NETWORK_32(X)
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2     |     1     |     0     |
+   // Reserved  | Hearbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	u8 initialized:1, was_running:1;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+		usb_complete_t complete_fn, void *context);
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
+		*usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *purelifi_intf_to_hw(struct usb_interface
+		*intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
+		*usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+const char *speed(enum usb_device_speed speed);
+#endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [PATCH] [v3] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-14  6:19   ` [PATCH] [PATCH] [v3] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
@ 2020-10-14 10:17     ` kernel test robot
  2020-10-15 22:35     ` Joe Perches
  1 sibling, 0 replies; 108+ messages in thread
From: kernel test robot @ 2020-10-14 10:17 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: kbuild-all, mostafa.afgani, Srinivasan Raju, Kalle Valo,
	Jakub Kicinski, Mauro Carvalho Chehab, linux-media, Rob Herring,
	Lukas Bulwahn, open list, open list:NETWORKING DRIVERS (WIRELESS)

[-- Attachment #1: Type: text/plain, Size: 8417 bytes --]

Hi Srinivasan,

I love your patch! Perhaps something to improve:

[auto build test WARNING on wireless-drivers-next/master]
[also build test WARNING on wireless-drivers/master net-next/master net/master linus/master v5.9 next-20201013]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Srinivasan-Raju/wireless-Initial-driver-submission-for-pureLiFi-STA-devices/20201014-142216
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next.git master
config: arc-allyesconfig (attached as .config)
compiler: arceb-elf-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/2f1cd7604f364eba9428b88e2ab38c2a42272fcd
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Srinivasan-Raju/wireless-Initial-driver-submission-for-pureLiFi-STA-devices/20201014-142216
        git checkout 2f1cd7604f364eba9428b88e2ab38c2a42272fcd
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=arc 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/net/wireless/purelifi/usb.c:74:6: warning: no previous prototype for 'send_packet_from_data_queue' [-Wmissing-prototypes]
      74 | void send_packet_from_data_queue(struct purelifi_usb *usb)
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/net/wireless/purelifi/usb.c: In function 'rx_urb_complete':
>> drivers/net/wireless/purelifi/usb.c:124:26: warning: variable 'rx' set but not used [-Wunused-but-set-variable]
     124 |  struct purelifi_usb_rx *rx;
         |                          ^~
   drivers/net/wireless/purelifi/usb.c: In function 'tx_urb_complete':
>> drivers/net/wireless/purelifi/usb.c:416:26: warning: variable 'tx' set but not used [-Wunused-but-set-variable]
     416 |  struct purelifi_usb_tx *tx;
         |                          ^~
   drivers/net/wireless/purelifi/usb.c: In function 'purelifi_store_frequency':
>> drivers/net/wireless/purelifi/usb.c:1101:9: warning: variable 'r' set but not used [-Wunused-but-set-variable]
    1101 |  int i, r, row, col, predivider, feedback_divider, output_div_0;
         |         ^
   drivers/net/wireless/purelifi/usb.c: At top level:
>> drivers/net/wireless/purelifi/usb.c:1544:9: warning: no previous prototype for 'modulation_write' [-Wmissing-prototypes]
    1544 | ssize_t modulation_write(struct file *file_p,
         |         ^~~~~~~~~~~~~~~~
--
   drivers/net/wireless/purelifi/mac.c: In function 'purelifi_restore_settings':
>> drivers/net/wireless/purelifi/mac.c:137:26: warning: variable 'multicast_hash' set but not used [-Wunused-but-set-variable]
     137 |  struct purelifi_mc_hash multicast_hash;
         |                          ^~~~~~~~~~~~~~
   drivers/net/wireless/purelifi/mac.c: In function 'purelifi_mac_tx_status':
>> drivers/net/wireless/purelifi/mac.c:190:19: warning: variable 'retry' set but not used [-Wunused-but-set-variable]
     190 |  int success = 1, retry = 1;
         |                   ^~~~~
   drivers/net/wireless/purelifi/mac.c: In function 'purelifi_op_configure_filter':
>> drivers/net/wireless/purelifi/mac.c:656:6: warning: variable 'r' set but not used [-Wunused-but-set-variable]
     656 |  int r;
         |      ^

vim +/send_packet_from_data_queue +74 drivers/net/wireless/purelifi/usb.c

    73	
  > 74	void send_packet_from_data_queue(struct purelifi_usb *usb)
    75	{
    76		struct sk_buff *skb = NULL;
    77		unsigned long flags;
    78		static u8 sidx;
    79		u8 last_served_sidx;
    80	
    81		spin_lock_irqsave(&usb->tx.lock, flags);
    82		last_served_sidx = sidx;
    83		do {
    84			sidx = (sidx + 1) % MAX_STA_NUM;
    85			if ((usb->tx.station[sidx].flag &
    86						STATION_CONNECTED_FLAG)) {
    87				if (!(usb->tx.station[sidx].flag &
    88							STATION_FIFO_FULL_FLAG)) {
    89					skb = skb_peek(&usb->tx.station
    90							[sidx].data_list);
    91				}
    92			}
    93		} while ((sidx != last_served_sidx) && (!skb));
    94	
    95		if (skb) {
    96			skb = skb_dequeue(&usb->tx.station[sidx].data_list);
    97			usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
    98					    tx_urb_complete, skb);
    99			if (skb_queue_len(&usb->tx.station[sidx].data_list)
   100					<= 60) {
   101				block_queue(usb, usb->tx.station[sidx].mac,
   102					    false);
   103			}
   104		}
   105		spin_unlock_irqrestore(&usb->tx.lock, flags);
   106	}
   107	
   108	static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
   109				     unsigned int length)
   110	{
   111		purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
   112	}
   113	
   114	#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
   115	#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
   116	#define STATION_CONNECT_MESSAGE              2
   117	#define STATION_DISCONNECT_MESSAGE           3
   118	
   119	int rx_usb_enabled;
   120	static void rx_urb_complete(struct urb *urb)
   121	{
   122		int r;
   123		struct purelifi_usb *usb;
 > 124		struct purelifi_usb_rx *rx;
   125		struct purelifi_usb_tx *tx;
   126		const u8 *buffer;
   127		static u8 fpga_link_connection_f;
   128		unsigned int length;
   129		u16 status;
   130		u8 sidx;
   131	
   132		if (!urb) {
   133			dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
   134			return;
   135		} else if (!urb->context) {
   136			dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
   137			return;
   138		}
   139		usb = urb->context;
   140	
   141		if (usb->initialized != 1)
   142			return;
   143	
   144		switch (urb->status) {
   145		case 0:
   146			break;
   147		case -ESHUTDOWN:
   148		case -EINVAL:
   149		case -ENODEV:
   150		case -ENOENT:
   151		case -ECONNRESET:
   152		case -EPIPE:
   153			dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
   154			return;
   155		default:
   156			dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
   157			goto resubmit;
   158		}
   159	
   160		buffer = urb->transfer_buffer;
   161		length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
   162	
   163		rx = &usb->rx;
   164		tx = &usb->tx;
   165	
   166		if (urb->actual_length != 8) {
   167			if (usb->initialized && fpga_link_connection_f)
   168				handle_rx_packet(usb, buffer, length);
   169			goto resubmit;
   170		}
   171	
   172		status = buffer[7];
   173	
   174		dev_info(&usb->intf->dev, "Recv status=%u\n", status);
   175		dev_info(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
   176			 buffer[0], buffer[1], buffer[2], buffer[3],
   177			 buffer[4], buffer[5]);
   178	
   179		switch (status) {
   180		case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
   181			dev_info(&usb->intf->dev,
   182				 "FIFO full not packet receipt.\n");
   183			tx->mac_fifo_full = 1;
   184			for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
   185				usb->tx.station[sidx].flag |=
   186					STATION_FIFO_FULL_FLAG;
   187			}
   188			break;
   189		case STATION_FIFO_ALMOST_FULL_MESSAGE:
   190			dev_info(&usb->intf->dev, "FIFO full packet receipt.\n");
   191	
   192			for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
   193				usb->tx.station[sidx].flag &= 0xFD;
   194	
   195			send_packet_from_data_queue(usb);
   196			break;
   197		case STATION_CONNECT_MESSAGE:
   198			fpga_link_connection_f = 1;
   199			dev_info(&usb->intf->dev, "ST_CONNECT_MSG packet receipt.\n");
   200			break;
   201		case STATION_DISCONNECT_MESSAGE:
   202			fpga_link_connection_f = 0;
   203			dev_info(&usb->intf->dev, "ST_DISCONN_MSG packet receipt.\n");
   204			break;
   205		default:
   206			dev_info(&usb->intf->dev, "Unknown packet receipt.\n");
   207			break;
   208		}
   209	
   210	resubmit:
   211		r = usb_submit_urb(urb, GFP_ATOMIC);
   212		if (r)
   213			dev_dbg(urb_dev(urb), "urb %p resubmit error %d\n", urb, r);
   214	}
   215	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 65444 bytes --]

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [PATCH] [v3] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-14  6:19   ` [PATCH] [PATCH] [v3] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  2020-10-14 10:17     ` kernel test robot
@ 2020-10-15 22:35     ` Joe Perches
  2020-10-16  6:36       ` Srinivasan Raju
  1 sibling, 1 reply; 108+ messages in thread
From: Joe Perches @ 2020-10-15 22:35 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

On Wed, 2020-10-14 at 11:49 +0530, Srinivasan Raju wrote:
> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.

trivia: netdev_<level> might be better than dev_<level>.

> diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
[]
> +int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
> +{
> +	int r;
> +
> +	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
> +	if (r)
> +		dev_err(purelifi_chip_dev(chip), "%s r=%d", __func__, r);

missing '\n' termination.

[]

> > +int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
> +{
> +	int r;
> +	static struct purelifi_chip *chip_p;
> +
> +	if (chip)
> +		chip_p = chip;
> +	if (!chip_p)
> +		return -EINVAL;
> +
> +	r = usb_write_req((const u8 *)&rate, sizeof(rate), USB_REQ_RATE_WR);
> +	if (r)
> +		dev_err(purelifi_chip_dev(chip), "set rate failed r=%d", r);

same missing newline, etc

It might also be better to use a consistent error message like

		dev_err(purelifi_chip_dev(chip), "%s: failed, r=%d\n", r);

for both.

> diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
[]
> +static const struct ieee80211_rate purelifi_rates[] = {
> +	{ .bitrate = 10,
> +		.hw_value = PURELIFI_CCK_RATE_1M, },

[]

> +	{ .bitrate = 60,
> +		.hw_value = PURELIFI_OFDM_RATE_6M,
> +		.flags = 0 },

Seems odd to set .flags to 0 for only some of the entries.
Perhaps more readable to always leave it off if unset.

> +	{ .bitrate = 90,
> +		.hw_value = PURELIFI_OFDM_RATE_9M,
> +		.flags = 0 },
> +	{ .bitrate = 120,
> +		.hw_value = PURELIFI_OFDM_RATE_12M,
> +		.flags = 0 },
> +	{ .bitrate = 180,
> +		.hw_value = PURELIFI_OFDM_RATE_18M,
> +		.flags = 0 },
> +	{ .bitrate = 240,
> +		.hw_value = PURELIFI_OFDM_RATE_24M,
> +		.flags = 0 },
> +	{ .bitrate = 360,
> +		.hw_value = PURELIFI_OFDM_RATE_36M,
> +		.flags = 0 },
> +	{ .bitrate = 480,
> +		.hw_value = PURELIFI_OFDM_RATE_48M,
> +		.flags = 0 },
> +	{ .bitrate = 540,
> +		.hw_value = PURELIFI_OFDM_RATE_54M,
> +		.flags = 0 },
> +};

[]
> +int purelifi_mac_init_hw(struct ieee80211_hw *hw)
> +{
> +	int r;
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct purelifi_chip *chip = &mac->chip;
> +
> +	r = purelifi_chip_init_hw(chip);
> +	if (r) {
> +		dev_warn(purelifi_mac_dev(mac), "init hw failed. (%d)\n", r);
> +		goto out;
> +	}
> +
> +	dev_dbg(purelifi_mac_dev(mac), "irq_disabled %d", irqs_disabled());

missing newline

> +static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
> +		      struct ieee80211_rx_status *stats)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct sk_buff *skb;
> +	struct sk_buff_head *q;
> +	unsigned long flags;
> +	int found = 0;
> +	int i, position = 0;
> +
> +	if (!ieee80211_is_ack(rx_hdr->frame_control))
> +		return 0;
> +	dev_info(purelifi_mac_dev(mac), "%s::ACK Received", __func__);

missing newline

> +static int purelifi_op_add_interface(struct ieee80211_hw *hw,
> +				     struct ieee80211_vif *vif)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	static const char * const iftype80211[] = {
> +		[NL80211_IFTYPE_STATION]	= "Station",
> +		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
> +	};
> +
> +	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
> +		return -EOPNOTSUPP;
> +
> +	if (vif->type == NL80211_IFTYPE_ADHOC ||
> +	    vif->type == NL80211_IFTYPE_STATION) {
> +		dev_info(purelifi_mac_dev(mac), "%s %s\n", __func__,
> +			 iftype80211[vif->type]);
> +		mac->type = vif->type;
> +		mac->vif = vif;
> +	} else {
> +		dev_info(purelifi_mac_dev(mac), "unsupported iftype");

etc...



^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v4] wireless: Initial driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (2 preceding siblings ...)
  2020-10-14  6:19   ` [PATCH] [PATCH] [v3] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
@ 2020-10-16  6:34   ` Srinivasan Raju
  2020-10-16  8:58     ` Joe Perches
                       ` (2 more replies)
  2020-12-03  4:38   ` [PATCH] [v8] " Srinivasan Raju
                     ` (15 subsequent siblings)
  19 siblings, 3 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-10-16  6:34 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Mauro Carvalho Chehab, Rob Herring,
	Lukas Bulwahn, open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
---
 MAINTAINERS                            |    5 +
 drivers/net/wireless/Kconfig           |    1 +
 drivers/net/wireless/Makefile          |    1 +
 drivers/net/wireless/purelifi/Kconfig  |   27 +
 drivers/net/wireless/purelifi/Makefile |    3 +
 drivers/net/wireless/purelifi/chip.c   |   97 ++
 drivers/net/wireless/purelifi/chip.h   |   82 ++
 drivers/net/wireless/purelifi/intf.h   |   38 +
 drivers/net/wireless/purelifi/mac.c    |  869 +++++++++++++
 drivers/net/wireless/purelifi/mac.h    |  178 +++
 drivers/net/wireless/purelifi/usb.c    | 1647 ++++++++++++++++++++++++
 drivers/net/wireless/purelifi/usb.h    |  148 +++
 12 files changed, 3096 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/chip.c
 create mode 100644 drivers/net/wireless/purelifi/chip.h
 create mode 100644 drivers/net/wireless/purelifi/intf.h
 create mode 100644 drivers/net/wireless/purelifi/mac.c
 create mode 100644 drivers/net/wireless/purelifi/mac.h
 create mode 100644 drivers/net/wireless/purelifi/usb.c
 create mode 100644 drivers/net/wireless/purelifi/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index c80f87d7258c..150f592fb6e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14108,6 +14108,11 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PUREILIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+S:	Maintained
+F:	drivers/net/wireless/purelifi
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 170a64e67709..b87da3139f94 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -48,6 +48,7 @@ source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
 source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 
 config PCMCIA_RAYCS
 	tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e9fc770026f0 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
 obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..f6630791df9d
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+config PURELIFI
+
+	tristate "pureLiFi device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This driver makes the adapter appear as a normal WLAN interface.
+
+	   The pureLiFi device requires external STA firmware to be loaded.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called purelifi.
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..1f20055e741f
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PURELIFI)		:= purelifi.o
+purelifi-objs 		+= chip.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
new file mode 100644
index 000000000000..9ad2664b7542
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval || (chip->beacon_set &&
+			  chip->beacon_interval == interval)) {
+		return 0;
+	}
+
+	chip->beacon_interval = interval;
+	chip->beacon_set = true;
+	return usb_write_req((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %02hx:%02hx v%02hx  %02x-%02x-%02x %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr[0], addr[1], addr[2],
+		speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
+{
+	int r;
+
+	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	u8 value;
+
+	value = 0;
+	usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+	static struct purelifi_chip *chip_p;
+
+	if (chip)
+		chip_p = chip;
+	if (!chip_p)
+		return -EINVAL;
+
+	r = usb_write_req((const u8 *)&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/chip.h b/drivers/net/wireless/purelifi/chip.h
new file mode 100644
index 000000000000..f479f34a6503
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	u16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr
+					)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1 << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/intf.h b/drivers/net/wireless/purelifi/intf.h
new file mode 100644
index 000000000000..20e96671e0a7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/intf.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	u16 rssi;
+	u8  rate_idx;
+	u8  pad;
+	u64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             buf[512];
+};
+
+#endif
diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
new file mode 100644
index 000000000000..6920a1dfd599
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.c
@@ -0,0 +1,869 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+
+#ifndef IEEE80211_BAND_2GHZ
+#define IEEE80211_BAND_2GHZ NL80211_BAND_2GHZ
+#endif
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 },
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
+{
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		goto out;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled %d\n", irqs_disabled());
+
+	r = regulatory_hint(hw->wiphy, "CA");
+out:
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon, false);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status - reports tx status of a packet if required
+ * @hw - a &struct ieee80211_hw pointer
+ * @skb - a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success - True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1, retry = 1;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status) {
+		success = !tx_status->failure;
+		retry = tx_status->retry + success;
+	}
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	dev_info(purelifi_mac_dev(mac), "retry=%d\n", retry);
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		/* ieee80211_tx_status_irqsafe(hw, skb); */
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr)
+{
+	return usb_write_req((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (struct purelifi_ctrlset *)skb_push(skb,
+			sizeof(struct purelifi_ctrlset));
+	cs->id = USB_REQ_DATA_TX;
+	cs->payload_len_nw = frag_len;
+	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
+		- sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned and align data*/
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		cs->len +=  4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data*/
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen b/c
+				 * sufficient headroom was reserved
+				 */
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		cs->len +=  4;
+	}
+
+	cs->id = TO_NETWORK(cs->id);
+	cs->len = TO_NETWORK(cs->len);
+	cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
+
+	return 0;
+}
+
+extern void send_packet_from_data_queue(struct purelifi_usb *usb);
+/**
+ * purelifi_op_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+	info->rate_driver_data[0] = hw;
+
+	if (skb->data[24] != 0x08 && skb->len == 116)
+		dev_info(purelifi_mac_dev(mac), "%s ping\n", __func__);
+
+	if (skb->data[24] == 0x08) {
+		u8 dst_mac[ETH_ALEN];
+		u8 sidx;
+		bool found = false;
+
+		memcpy(dst_mac, &skb->data[28], ETH_ALEN);
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (usb->tx.station[sidx].flag &
+					STATION_CONNECTED_FLAG) {
+				if (!memcmp(usb->tx.station[sidx].mac,
+					    dst_mac, ETH_ALEN)) {
+					found = true;
+					break;
+				}
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&usb->tx.station[sidx].data_list) > 60)
+			block_queue(usb, usb->tx.station[sidx].mac, true);
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&usb->tx.station[sidx].data_list) < 256) {
+			skb_queue_tail(&usb->tx.station[sidx].data_list, skb);
+			send_packet_from_data_queue(usb);
+		} else {
+			dev_kfree_skb(skb);
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
+					USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+		      struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	int found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+	dev_info(purelifi_mac_dev(mac), "%s ACK Received\n", __func__);
+	return 0;
+
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++) {
+			skb = __skb_dequeue(q);
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = 1;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	u32 crc_error_cnt_low, crc_error_cnt_high;
+	int sidx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = IEEE80211_BAND_2GHZ;
+	mac->rssi      = -15 * ntohs(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	crc_error_cnt_low = status->crc_error_count;
+	crc_error_cnt_high = status->crc_error_count >> 32;
+	mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
+		ntohl(crc_error_cnt_high);
+
+	if (!bad_frame &&
+	    filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = TO_HOST(*(u32 *)buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!memcmp(&buffer[10],
+			    mac->chip.usb.tx.station[sidx].mac,
+					ETH_ALEN)) {
+			if (mac->chip.usb.tx.station[sidx].flag &
+					STATION_CONNECTED_FLAG) {
+				mac->chip.usb.tx.station[sidx].flag |=
+					STATION_HEARTBEAT_FLAG;
+				break;
+			}
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (!(mac->chip.usb.tx.station[sidx].flag &
+						STATION_CONNECTED_FLAG
+			     )) {
+				memcpy(mac->chip.usb.tx.station[sidx].mac,
+				       &buffer[10],
+						ETH_ALEN);
+				mac->chip.usb.tx.station[sidx].flag |=
+					STATION_CONNECTED_FLAG;
+				mac->chip.usb.tx.station[sidx].flag |=
+					STATION_HEARTBEAT_FLAG;
+				break;
+			}
+		}
+	}
+
+	if (buffer[0] == 0x40)
+		dev_info(purelifi_mac_dev(mac), "Probe request\n");
+	else if (buffer[0] == 0x0)
+		dev_info(purelifi_mac_dev(mac), "Association request\n");
+
+	if (buffer[0] == 0xb0) {
+		dev_info(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+	} else if (buffer[0] == 0x08) {
+		unsigned short int seq_nmb = (buffer[23] << 4) |
+			((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000)) {
+			dev_info(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
+		} else {
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		}
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+	if (need_padding) {
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+	}
+
+	/* FIXME : could we avoid this big memcpy ? */
+	memcpy(skb_put(skb, payload_length), buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_info(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			 iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_info(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	spin_lock_irq(&mac->lock);
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_info(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon, false);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band;
+
+#ifdef ieee80211_hw_set
+	_ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
+	_ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
+	_ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
+	_ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);
+#else
+	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+		IEEE80211_HW_SIGNAL_DBM |
+		IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+		IEEE80211_HW_MFP_CAPABLE;
+#endif
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* 4 for 32 bit alignment if no tailroom */
+
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/mac.h b/drivers/net/wireless/purelifi/mac.h
new file mode 100644
index 000000000000..35e28cc4dffb
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define MODULATION_RATE_BPSK_1_2  0
+#define MODULATION_RATE_BPSK_3_4  (MODULATION_RATE_BPSK_1_2  + 1)
+#define MODULATION_RATE_QPSK_1_2  (MODULATION_RATE_BPSK_3_4  + 1)
+#define MODULATION_RATE_QPSK_3_4  (MODULATION_RATE_QPSK_1_2  + 1)
+#define MODULATION_RATE_QAM16_1_2 (MODULATION_RATE_QPSK_3_4  + 1)
+#define MODULATION_RATE_QAM16_3_4 (MODULATION_RATE_QAM16_1_2 + 1)
+#define MODULATION_RATE_QAM64_1_2 (MODULATION_RATE_QAM16_3_4 + 1)
+#define MODULATION_RATE_QAM64_3_4 (MODULATION_RATE_QAM64_1_2 + 1)
+#define MODULATION_RATE_AUTO      (MODULATION_RATE_QAM64_3_4 + 1)
+#define MODULATION_RATE_NUM       (MODULATION_RATE_AUTO      + 1)
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             modulation;
+	u8             control;
+	u8             service;
+	u8             pad;
+	__le16         packet_length;
+	__le16         current_length;
+	__le16          next_frame_length;
+	__le16         tx_length;
+	u32            payload_len_nw;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	spinlock_t intr_lock; /* not used : interrupt lock for mac */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	unsigned int ack_pending:1;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
+		*chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block);
+
+#ifdef DEBUG
+void purelifi_dump_rx_status(const struct rx_status *status);
+#else
+#define purelifi_dump_rx_status(status)
+#endif
+
+#endif
diff --git a/drivers/net/wireless/purelifi/usb.c b/drivers/net/wireless/purelifi/usb.c
new file mode 100644
index 000000000000..730adbf786da
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.c
@@ -0,0 +1,1647 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+#define FCS_LEN 4
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#undef  LOAD_MAC_AND_SERIAL_FROM_FILE
+#undef  LOAD_MAC_AND_SERIAL_FROM_FLASH
+#define LOAD_MAC_AND_SERIAL_FROM_EP0
+
+#define PROC_FOLDER           "purelifi"
+#define MODULATION_FILENAME   "modulation"
+
+struct proc_dir_entry *proc_folder;
+struct proc_dir_entry *modulation_proc_entry;
+static atomic_t data_queue_flag;
+
+/*Tx retry backoff timer (in milliseconds).*/
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+
+/*Tx retry backoff timer (in jiffies).*/
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+
+	spin_lock_irqsave(&usb->tx.lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if ((usb->tx.station[sidx].flag &
+					STATION_CONNECTED_FLAG)) {
+			if (!(usb->tx.station[sidx].flag &
+						STATION_FIFO_FULL_FLAG)) {
+				skb = skb_peek(&usb->tx.station
+						[sidx].data_list);
+			}
+		}
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&usb->tx.station[sidx].data_list);
+		usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				    tx_urb_complete, skb);
+		if (skb_queue_len(&usb->tx.station[sidx].data_list)
+				<= 60) {
+			block_queue(usb, usb->tx.station[sidx].mac,
+				    false);
+		}
+	}
+	spin_unlock_irqrestore(&usb->tx.lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int rx_usb_enabled;
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
+		return;
+	} else if (!urb->context) {
+		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
+
+	tx = &usb->tx;
+
+	if (urb->actual_length != 8) {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[7];
+
+	dev_info(&usb->intf->dev, "Recv status=%u\n", status);
+	dev_info(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+		 buffer[0], buffer[1], buffer[2], buffer[3],
+		 buffer[4], buffer[5]);
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_info(&usb->intf->dev,
+			 "FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			usb->tx.station[sidx].flag |=
+				STATION_FIFO_FULL_FLAG;
+		}
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_info(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			usb->tx.station[sidx].flag &= 0xFD;
+
+		send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		fpga_link_connection_f = 1;
+		dev_info(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		fpga_link_connection_f = 0;
+		dev_info(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_info(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit failed (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0; /*no error return*/
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+const char *speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, 0x40, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+static int download_fpga(struct usb_interface *intf)
+{
+	int i, r, actual_length;
+	int fw_data_i, blk_tran_len = 16384;
+	const char *fw_name;
+	unsigned char fpga_setting[2];
+	unsigned char *fw_data;
+	unsigned char fpga_state[9];
+	unsigned char *fpga_dmabuff;
+	struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	fpga_dmabuff = NULL;
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga.bit";
+		dev_info(&intf->dev, "bit file for X selected.\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga_xc.bit";
+		dev_info(&intf->dev, "bit filefor XC selected.\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware((const struct firmware **)&fw, fw_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+
+	fpga_dmabuff = kmalloc(2, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, 0x33, fpga_dmabuff, sizeof(fpga_setting));
+
+	memcpy(fpga_setting, fpga_dmabuff, 2);
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x34, NULL, 0);
+
+	if (fpga_setting[0] != 6) {
+		dev_err(&intf->dev, "fpga_setting[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmalloc(blk_tran_len, GFP_KERNEL);
+
+		memcpy(fw_data, &fw->data[fw_data_i], blk_tran_len);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			fw_data[tbuf_idx] =
+				((fw_data[tbuf_idx] & 128) >> 7) |
+				((fw_data[tbuf_idx] &  64) >> 5) |
+				((fw_data[tbuf_idx] &  32) >> 3) |
+				((fw_data[tbuf_idx] &  16) >> 1) |
+				((fw_data[tbuf_idx] &   8) << 1) |
+				((fw_data[tbuf_idx] &   4) << 3) |
+				((fw_data[tbuf_idx] &   2) << 5) |
+				((fw_data[tbuf_idx] &   1) << 7);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_setting[0] & 0xFF),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2000);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(9, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xFF, 9);
+
+	send_vendor_request(udev, 0x30, fpga_dmabuff, sizeof(fpga_state));
+
+	dev_info(&intf->dev, "fpga_status");
+	for (i = 0; i <= 8; i++)
+		dev_info(&intf->dev, " %x ", fpga_dmabuff[i]);
+	dev_info(&intf->dev, "\n");
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		kfree(fpga_dmabuff);
+		goto error_free_fw;
+	}
+
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x35, NULL, 0);
+
+	msleep(200);
+
+error_free_fw:
+	release_firmware(fw);
+error:
+	return r;
+}
+
+static int download_XL_FW(struct usb_interface *intf)
+{
+	int r;
+
+	struct firmware *fw_packed = NULL;
+	int step;
+	u8 *buf;
+	u32 size, nmb_of_control_packets, i, no_of_files;
+	u32 tot_size, start_addr;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, 0x80, NULL, 0);
+	msleep(100);
+
+	/* Code for single pack file download */
+
+	fw_pack = "purelifi/li_fi_x/Li-Fi-XL.bin";
+
+	r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	no_of_files = *(u32 *)&fw_packed->data[0];
+	tot_size = fw_packed->size;
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n", no_of_files, tot_size);
+
+	buf = kmalloc(64, GFP_KERNEL);
+	r = -ENOMEM;
+
+	if (!buf)
+		goto error;
+
+	memset(buf, 0, 64);
+
+	for (step = 0; step < no_of_files; step++) {
+		buf[0] = step;
+		r = send_vendor_command(udev, 0x82, buf, 64);
+
+		if (step < no_of_files - 1)
+			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
+				- *(u32 *)&fw_packed->data[4 + (step) * 4];
+		else
+			size = tot_size -
+				*(u32 *)&fw_packed->data[4 + (step) * 4];
+
+		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
+
+		if ((size % 64) && step < 2)
+			size += 64 - size % 64;
+		nmb_of_control_packets = size / 64;
+
+		for (i = 0; i < nmb_of_control_packets; i++) {
+			memcpy(buf,
+			       &fw_packed->data[start_addr + (i * 64)], 64);
+			r = send_vendor_command(udev, 0x81, buf, 64);
+		}
+	  dev_info(&intf->dev, "fw-dw step %d,r=%d size %d\n", step, r, size);
+	}
+	release_firmware(fw_packed);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, 0x83, NULL, 0);
+	dev_info(&intf->dev, "download_fpga (4) (%d)\n", r);
+error:
+	return r;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+static void pretty_print_mac(struct usb_interface *intf, char *serial_number,
+			     unsigned char *hw_address
+			    )
+{
+	unsigned char i;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		dev_info(&intf->dev, "hw_address[%d]=%x\n", i, hw_address[i]);
+}
+
+static int upload_mac_and_serial_number(struct usb_interface *intf,
+					unsigned char *hw_address,
+					unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FILE
+	struct firmware *fw = NULL;
+	int r;
+	char *mac_file_name = "purelifi/li_fi_x/mac.ascii";
+
+	r = request_firmware((const struct firmware **)&fw, mac_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	r = sscanf(fw->data,
+		   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+		    &hw_address[0], &hw_address[1],
+		    &hw_address[2], &hw_address[3],
+		    &hw_address[4], &hw_address[5]);
+	if (r != 6) {
+		dev_err(&intf->dev, "fw_dw - usage args fail (%d)\n", r);
+		goto ERROR;
+	}
+	release_firmware(fw);
+
+	char *serial_file_name = "purelifi/li_fi_x/serial.ascii";
+
+	r = request_firmware((const struct firmware **)&fw, serial_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	strcpy(serial_number, fw->data);
+	dev_info(&intf->dev, "serial_number=%s\n", serial_number);
+	release_firmware(fw);
+	pretty_print_mac(intf, serial_number, hw_address);
+ERROR:
+	return r;
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FLASH
+
+#define FLASH_EC_BUSY 3
+
+	struct flash_t flash;
+	unsigned char *flash_sector_buf;
+	unsigned int sector_index;
+
+	do {
+		unsigned char buf[8];
+
+		msleep(200);
+
+		send_vendor_request(udev, 0x40, buf, sizeof(buf));
+		flash.enabled = buf[0] & 0xFF;
+
+		if (flash.enabled) {
+			flash.sectors = ((buf[6] & 255) << 24) |
+				((buf[5] & 255) << 16) |
+				((buf[4] & 255) <<  8) |
+				(buf[3] & 255);
+
+			flash.sector_size =
+				((buf[2] & 255) << 8) | (buf[1] & 255);
+		} else {
+			flash.sectors = 0;
+			flash.sector_size = 0;
+		}
+
+		if (flash.sector_size & 0x8000)
+			flash.sector_size = 1 << (flash.sector_size & 0x7FFF);
+
+		flash.ec = buf[7] & 0xFF;
+	} while (flash.ec == FLASH_EC_BUSY);
+
+	flash_sector_buf = kmalloc(flash.sector_size,
+				   GFP_KERNEL);
+	if (!flash_sector_buf)
+		return -ENOMEM;
+
+	if (!flash.enabled) {
+		dev_err(&intf->dev, "Flash is not enabled\n");
+		return -EINVAL;
+	}
+
+	read_flash(intf, &flash, 0, flash_sector_buf);
+	sector_index = *((unsigned int *)flash_sector_buf);
+
+	read_flash(intf, &flash, sector_index, flash_sector_buf);
+	memcpy(hw_address, flash_sector_buf, ETH_ALEN);
+	strncpy(serial_number, &flash_sector_buf[ETH_ALEN], 256);
+
+	pretty_print_mac(intf, serial_number, hw_address);
+	kfree(flash_sector_buf);
+	return 0;
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_EP0
+
+#define USB_MAC_VENDOR_REQUEST 0x36
+#define USB_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define USB_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define SERIAL_NUMBER_LENGTH 14
+
+	int r = 0;
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(14, GFP_KERNEL);
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	send_vendor_request(udev, USB_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	memcpy(serial_number, dma_buffer, SERIAL_NUMBER_LENGTH);
+
+	memset(dma_buffer, 0x00, 14);
+
+	send_vendor_request(udev, USB_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, 8);
+
+	memcpy(&firmware_version, dma_buffer, sizeof(unsigned long long));
+
+	dev_info(&intf->dev, "Firmware Version: %llu, Revision: 16418\n",
+		 firmware_version);
+	kfree(dma_buffer);
+
+	pretty_print_mac(intf, serial_number, hw_address);
+error:
+	return r;
+#endif
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	u32 payload_len_nw = TO_NETWORK((buffer_len + FCS_LEN));
+
+	usb_req->id = usb_req_id;
+	usb_req->len  = 0;
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == USB_REQ_BEACON_WR) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		usb_req->len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	usb_req->len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	usb_req->len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+		usb_req->len += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->id = TO_NETWORK(usb_req->id);
+	usb_req->len = TO_NETWORK(usb_req->len);
+}
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			usb_complete_t complete_fn,
+			void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(usb_req.id) + sizeof(usb_req.len) +
+			   TO_HOST(usb_req.len);
+
+	dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+	memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	send_packet_from_data_queue(usb);
+	usb->tx.tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	add_timer(&usb->tx.tx_retry_timer);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (usb->tx.station[sidx].flag & STATION_CONNECTED_FLAG) {
+			if (usb->tx.station[sidx].flag &
+					STATION_HEARTBEAT_FLAG) {
+				usb->tx.station[sidx].flag ^=
+					STATION_HEARTBEAT_FLAG;
+			} else {
+				memset(usb->tx.station[sidx].mac, 0,
+				       ETH_ALEN);
+				usb->tx.station[sidx].flag = 0;
+			}
+		}
+	}
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	add_timer(&usb->sta_queue_cleanup);
+}
+
+static enum unit_type_t get_unit_type(char *serial_number,
+				      struct usb_interface *intf)
+{
+	dev_info(&intf->dev, "Unit-type is station (STA)\n");
+	return STA;
+}
+
+char usr_input[8] = {0};
+
+static ssize_t purelifi_store_frequency(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t len)
+{
+	int i, r, row, col, predivider, feedback_divider, output_div_0;
+	int output_div_1, output_div_2, clkout3_phase;
+	char values[6][4] = {{0}, {0} };
+	bool valid = false;
+	int rv = 0;
+
+	row = 0; col = 0;
+	for (i = 0; i < min(len, sizeof(values)); i++) {
+		values[row][col] = buf[i];
+		col++;
+		if (buf[i] == 0x20) {
+			row++;
+			col = 0;
+		} else if (buf[i] == 0x0A) {
+			if (row == 5 && col > 0) {
+				rv = sscanf((char *)&values[0][0],
+					    "%d", &predivider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[1][0],
+					    "%d", &feedback_divider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[2][0],
+					    "%d", &output_div_0);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[3][0],
+					    "%d", &output_div_1);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[4][0],
+					    "%d", &output_div_2);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[5][0],
+					    "%d", &clkout3_phase);
+				if (rv != 1)
+					valid = false;
+				valid = true;
+
+				break;
+			}
+		}
+	}
+
+	if (valid) {
+		usr_input[0] = (char)predivider;
+		usr_input[1] = (char)feedback_divider;
+		usr_input[2] = (char)output_div_0;
+		usr_input[3] = (char)output_div_1;
+		usr_input[4] = (char)output_div_2;
+		usr_input[5] = (char)clkout3_phase;
+
+		dev_err(dev, "options IP_DIV: %d VCO_MULT: %d OP_DIV_PHY: %d",
+			usr_input[0], usr_input[1], usr_input[2]);
+		dev_err(dev, "OP_DIV_CPU: %d OP_DIV_USB: %d CLK3_PH: %d\n",
+			usr_input[3], usr_input[4], usr_input[5]);
+
+		r = usb_write_req((const u8 *)&usr_input[0],
+				  sizeof(usr_input), USB_REQ_SET_FREQ);
+		if (r < 0)
+			dev_err(dev, "SET_FREQ failed (%d)\n", r);
+	} else {
+		dev_err(dev, "Your options are invalid\n");
+	}
+
+	return len;
+}
+
+static ssize_t purelifi_show_sysfs(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	return 0;
+}
+
+static const struct device_attribute purelifi_attr_frequency = {
+	  .attr = {.name = "frequency", .mode = (0666)},
+	  .show = purelifi_show_sysfs,
+	  .store = purelifi_store_frequency,
+};
+
+static ssize_t purelifi_store_modulation(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t len)
+{
+	int rate, i;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	for (i = 0; i < min(len, sizeof(usr_input)); i++)
+		usr_input[i] = buf[i];
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return len;
+}
+
+static ssize_t purelifi_show_modulation(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return 0;
+}
+
+static struct device_attribute purelifi_attr_modulation = {
+	.attr = {.name = "modulation", .mode = (0666)},
+	.show = purelifi_show_modulation,
+	.store = purelifi_store_modulation,
+};
+
+static void create_config(struct usb_interface *intf)
+{
+	struct device *dev;
+
+	dev = &intf->dev;
+	if (device_create_file(dev, &purelifi_attr_modulation)) {
+		dev_err(&intf->dev,
+			"failed to create modulation sysfs entry\n");
+		return;
+	}
+	if (device_create_file(dev, &purelifi_attr_frequency)) {
+		device_remove_file(dev, &purelifi_attr_modulation);
+		dev_err(&intf->dev,
+			"failed to create frequency sysfs entry\n");
+	}
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_usb *usb;
+	struct purelifi_chip *chip;
+	struct ieee80211_hw *hw = NULL;
+	static unsigned char hw_address[ETH_ALEN];
+	static unsigned char serial_number[256];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	create_config(intf);
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+
+	r = upload_mac_and_serial_number(intf, hw_address, serial_number);
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = get_unit_type(serial_number, intf);
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg(&intf->dev, "register device failed (%d)\n", r);
+		goto error;
+	}
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_XL_FW(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	usb->tx.mac_fifo_full = 0;
+	spin_lock_init(&usb->tx.lock);
+
+	msleep(200);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_info(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = usb_write_req(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_info(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&usb->tx.station[i].data_list);
+		usb->tx.station[i].flag = 0;
+	}
+	usb->tx.station[STA_BROADCAST_INDEX].flag |=
+		STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		usb->tx.station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	usb->tx.tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&usb->tx.tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer(&usb->tx.tx_retry_timer);
+	del_timer(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	device_remove_file(&intf->dev, &purelifi_attr_modulation);
+	device_remove_file(&intf->dev, &purelifi_attr_frequency);
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	mutex_lock(&mac->chip.mutex);
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	mutex_unlock(&mac->chip.mutex);
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#else
+
+#define suspend NULL
+#define resume  NULL
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+	.suspend        = suspend,
+	.resume         = resume,
+	.disable_hub_initiated_lpm = 1,
+};
+
+struct workqueue_struct *purelifi_workqueue;
+
+static int modulation_open(struct inode *inode_p, struct file *file_p)
+{
+	return 0;
+}
+
+static ssize_t modulation_read(struct file *file_p,
+			       char __user *user_p,
+		size_t offset, loff_t *off_p)
+{
+	return 0;
+}
+
+static ssize_t modulation_write(struct file *file_p,
+				const char __user *buffer,
+				size_t count, loff_t *actual_data_written)
+{
+	int rate;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	if (copy_from_user(usr_input, buffer, min(count, sizeof(usr_input))))
+		return -EFAULT;
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return count;
+}
+
+static const struct proc_ops  modulation_fops = {
+	.proc_open  = modulation_open,
+	.proc_read  = modulation_read,
+	.proc_write = modulation_write
+};
+
+static int create_proc_entry_e(struct proc_dir_entry *entry,
+			       size_t size,
+			       mode_t mode,
+			       const struct proc_ops *pops,
+			       const char *entry_name,
+			       struct proc_dir_entry *parent
+			     )
+{
+	entry = proc_create(entry_name, mode, parent, pops);
+	if (!(entry))
+		return -ENOMEM;
+	return 0;
+}
+
+static int __init usb_init(void)
+{
+	int r;
+
+	proc_folder = proc_mkdir(PROC_FOLDER, NULL);
+	if (!proc_folder) {
+		pr_err("error creating proc folder\n");
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = create_proc_entry_e(modulation_proc_entry, sizeof(u32), 0666,
+				&modulation_fops, MODULATION_FILENAME,
+				proc_folder);
+	if (r)
+		goto remove_proc_dir;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto remove_modulation_proc_file;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+remove_modulation_proc_file:
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+remove_proc_dir:
+	remove_proc_entry(PROC_FOLDER, NULL);
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+	remove_proc_entry(PROC_FOLDER, NULL);
+	if (ez_usb_interface) {
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_modulation);
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_frequency);
+	}
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi Ltd");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("purelifi/li_fi_x/fpga.bit");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/usb.h b/drivers/net/wireless/purelifi/usb.h
new file mode 100644
index 000000000000..cc8f67cd7ad0
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
+		      (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
+
+#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
+
+#define TO_NETWORK_32(X) TO_NETWORK(X)
+
+#define TO_HOST(X)    TO_NETWORK(X)
+#define TO_HOST_16(X) TO_NETWORK_16(X)
+#define TO_HOST_32(X) TO_NETWORK_32(X)
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2     |     1     |     0     |
+   // Reserved  | Hearbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	u8 initialized:1, was_running:1;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+		usb_complete_t complete_fn, void *context);
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
+		*usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *purelifi_intf_to_hw(struct usb_interface
+		*intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
+		*usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *speed(enum usb_device_speed speed);
+#endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [PATCH] [v3] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-15 22:35     ` Joe Perches
@ 2020-10-16  6:36       ` Srinivasan Raju
  0 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-10-16  6:36 UTC (permalink / raw)
  To: Joe Perches
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS


Thanks for your comments Joe, I have resubmitted with the comments addressed

Regards,
Srini



^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices
  2020-09-30 10:44           ` Leon Romanovsky
@ 2020-10-16  8:23             ` Kalle Valo
  0 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2020-10-16  8:23 UTC (permalink / raw)
  To: Leon Romanovsky
  Cc: Johannes Berg, Srinivasan Raju, mostafa.afgani, David S. Miller,
	Jakub Kicinski, Mauro Carvalho Chehab, Rob Herring, linux-kernel,
	linux-wireless, netdev

Leon Romanovsky <leon@kernel.org> writes:

> On Wed, Sep 30, 2020 at 12:11:24PM +0200, Johannes Berg wrote:
>> On Wed, 2020-09-30 at 12:55 +0300, Leon Romanovsky wrote:
>> > On Wed, Sep 30, 2020 at 11:01:27AM +0300, Kalle Valo wrote:
>> > > Leon Romanovsky <leon@kernel.org> writes:
>> > >
>> > > > > diff --git a/drivers/net/wireless/purelifi/Kconfig
>> > > > b/drivers/net/wireless/purelifi/Kconfig
>> > > > > new file mode 100644
>> > > > > index 000000000000..ff05eaf0a8d4
>> > > > > --- /dev/null
>> > > > > +++ b/drivers/net/wireless/purelifi/Kconfig
>> > > > > @@ -0,0 +1,38 @@
>> > > > > +# SPDX-License-Identifier: GPL-2.0
>> > > > > +config WLAN_VENDOR_PURELIFI
>> > > > > +	bool "pureLiFi devices"
>> > > > > +	default y
>> > > >
>> > > > "N" is preferred default.
>> > >
>> > > In most cases that's true, but for WLAN_VENDOR_ configs 'default y'
>> > > should be used. It's the same as with NET_VENDOR_.
>> >
>> > I would like to challenge it, why is that?
>> > Why do I need to set "N", every time new vendor upstreams its code?
>>
>> You don't. The WLAN_VENDOR_* settings are not supposed to affect the
>> build, just the Kconfig visibility.
>
> Which is important to me, I'm keeping .config as minimal as possible
> to simplify comparison between various builds.

IIRC the 'default y' is to avoid breaking when updating from an old,
pre-vendor, kernel config (for example with 'make oldconfig'), otherwise
all wireless drivers would have been disabled without a warning. But as
wireless vendors were introduced back in 2015 in v4.5-rc1 I don't think
we need to worry about that anymore. But I would like to keep this
behaviour consistent across all vendors, so they all should be changed
at the same time in one patch.

-- 
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v4] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-16  6:34   ` [PATCH] [v4] " Srinivasan Raju
@ 2020-10-16  8:58     ` Joe Perches
  2020-10-16 10:13       ` Srinivasan Raju
  2020-10-19  3:17     ` [PATCH] [v5] " Srinivasan Raju
  2020-11-16  9:22     ` [PATCH] [v7] " Srinivasan Raju
  2 siblings, 1 reply; 108+ messages in thread
From: Joe Perches @ 2020-10-16  8:58 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

On Fri, 2020-10-16 at 12:04 +0530, Srinivasan Raju wrote:
> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.

Suggested neatening patch on top of this:

o Use include "usb.h" instead of direct extern
o Prefix purelifi to speed and send_packet_from_data_queue functions
  to remove generic global function naming
o Use enum instead of macro defines for MODULATION_RATE_<foo>
o Simplify the (mac->chip->)usb->tx. uses with a temporary
o Use continue in loops instead of extra indentation
o Remove unnecessary %hx printf formatting, use %x

---
 drivers/net/wireless/purelifi/chip.c |  4 +-
 drivers/net/wireless/purelifi/mac.c  | 60 +++++++++++--------------
 drivers/net/wireless/purelifi/mac.h  | 22 ++++-----
 drivers/net/wireless/purelifi/usb.c  | 86 ++++++++++++++++--------------------
 drivers/net/wireless/purelifi/usb.h  |  4 +-
 5 files changed, 80 insertions(+), 96 deletions(-)

diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
index 9ad2664b7542..cca03697cb06 100644
--- a/drivers/net/wireless/purelifi/chip.c
+++ b/drivers/net/wireless/purelifi/chip.c
@@ -42,12 +42,12 @@ int purelifi_chip_init_hw(struct purelifi_chip *chip)
 	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
 	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
 
-	pr_info("purelifi chip %02hx:%02hx v%02hx  %02x-%02x-%02x %s\n",
+	pr_info("purelifi chip %02x:%02x v%02x  %02x-%02x-%02x %s\n",
 		le16_to_cpu(udev->descriptor.idVendor),
 		le16_to_cpu(udev->descriptor.idProduct),
 		le16_to_cpu(udev->descriptor.bcdDevice),
 		addr[0], addr[1], addr[2],
-		speed(udev->speed));
+		purelifi_speed(udev->speed));
 
 	return purelifi_set_beacon_interval(chip, 100, 0, 0);
 }
diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
index 6920a1dfd599..09b7c2c0050d 100644
--- a/drivers/net/wireless/purelifi/mac.c
+++ b/drivers/net/wireless/purelifi/mac.c
@@ -10,6 +10,7 @@
 
 #include "chip.h"
 #include "mac.h"
+#include "usb.h"
 
 #ifndef IEEE80211_BAND_2GHZ
 #define IEEE80211_BAND_2GHZ NL80211_BAND_2GHZ
@@ -332,7 +333,6 @@ static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
 	return 0;
 }
 
-extern void send_packet_from_data_queue(struct purelifi_usb *usb);
 /**
  * purelifi_op_tx - transmits a network frame to the device
  *
@@ -366,16 +366,15 @@ static void purelifi_op_tx(struct ieee80211_hw *hw,
 		u8 dst_mac[ETH_ALEN];
 		u8 sidx;
 		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
 
 		memcpy(dst_mac, &skb->data[28], ETH_ALEN);
 		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
-			if (usb->tx.station[sidx].flag &
-					STATION_CONNECTED_FLAG) {
-				if (!memcmp(usb->tx.station[sidx].mac,
-					    dst_mac, ETH_ALEN)) {
-					found = true;
-					break;
-				}
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
 			}
 		}
 
@@ -384,13 +383,13 @@ static void purelifi_op_tx(struct ieee80211_hw *hw,
 			sidx = STA_BROADCAST_INDEX;
 
 		/* Stop OS from sending packets, if the queue is half full */
-		if (skb_queue_len(&usb->tx.station[sidx].data_list) > 60)
-			block_queue(usb, usb->tx.station[sidx].mac, true);
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			block_queue(usb, tx->station[sidx].mac, true);
 
 		/* Schedule packet for transmission if queue is not full */
-		if (skb_queue_len(&usb->tx.station[sidx].data_list) < 256) {
-			skb_queue_tail(&usb->tx.station[sidx].data_list, skb);
-			send_packet_from_data_queue(usb);
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
 		} else {
 			dev_kfree_skb(skb);
 		}
@@ -487,6 +486,7 @@ int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
 	static unsigned short int min_exp_seq_nmb;
 	u32 crc_error_cnt_low, crc_error_cnt_high;
 	int sidx;
+	struct purelifi_usb_tx *tx;
 
 	/* Packet blockade during disabled interface. */
 	if (!mac->vif)
@@ -530,33 +530,25 @@ int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
 	fc = get_unaligned((__le16 *)buffer);
 	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
 
+	tx = &mac->chip.usb.tx;
+
 	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
-		if (!memcmp(&buffer[10],
-			    mac->chip.usb.tx.station[sidx].mac,
-					ETH_ALEN)) {
-			if (mac->chip.usb.tx.station[sidx].flag &
-					STATION_CONNECTED_FLAG) {
-				mac->chip.usb.tx.station[sidx].flag |=
-					STATION_HEARTBEAT_FLAG;
-				break;
-			}
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
 		}
 	}
 
 	if (sidx == MAX_STA_NUM - 1) {
 		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
-			if (!(mac->chip.usb.tx.station[sidx].flag &
-						STATION_CONNECTED_FLAG
-			     )) {
-				memcpy(mac->chip.usb.tx.station[sidx].mac,
-				       &buffer[10],
-						ETH_ALEN);
-				mac->chip.usb.tx.station[sidx].flag |=
-					STATION_CONNECTED_FLAG;
-				mac->chip.usb.tx.station[sidx].flag |=
-					STATION_HEARTBEAT_FLAG;
-				break;
-			}
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
 		}
 	}
 
diff --git a/drivers/net/wireless/purelifi/mac.h b/drivers/net/wireless/purelifi/mac.h
index 35e28cc4dffb..cca1e70a6416 100644
--- a/drivers/net/wireless/purelifi/mac.h
+++ b/drivers/net/wireless/purelifi/mac.h
@@ -37,16 +37,18 @@
 #define PURELIFI_RX_ERROR		0x80
 #define PURELIFI_RX_CRC32_ERROR		0x10
 
-#define MODULATION_RATE_BPSK_1_2  0
-#define MODULATION_RATE_BPSK_3_4  (MODULATION_RATE_BPSK_1_2  + 1)
-#define MODULATION_RATE_QPSK_1_2  (MODULATION_RATE_BPSK_3_4  + 1)
-#define MODULATION_RATE_QPSK_3_4  (MODULATION_RATE_QPSK_1_2  + 1)
-#define MODULATION_RATE_QAM16_1_2 (MODULATION_RATE_QPSK_3_4  + 1)
-#define MODULATION_RATE_QAM16_3_4 (MODULATION_RATE_QAM16_1_2 + 1)
-#define MODULATION_RATE_QAM64_1_2 (MODULATION_RATE_QAM16_3_4 + 1)
-#define MODULATION_RATE_QAM64_3_4 (MODULATION_RATE_QAM64_1_2 + 1)
-#define MODULATION_RATE_AUTO      (MODULATION_RATE_QAM64_3_4 + 1)
-#define MODULATION_RATE_NUM       (MODULATION_RATE_AUTO      + 1)
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
 
 #define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
 
diff --git a/drivers/net/wireless/purelifi/usb.c b/drivers/net/wireless/purelifi/usb.c
index 730adbf786da..1354c8734d44 100644
--- a/drivers/net/wireless/purelifi/usb.c
+++ b/drivers/net/wireless/purelifi/usb.c
@@ -71,38 +71,32 @@ static inline u16 get_bcd_device(const struct usb_device *udev)
 
 #define urb_dev(urb) (&(urb)->dev->dev)
 
-void send_packet_from_data_queue(struct purelifi_usb *usb)
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
 {
 	struct sk_buff *skb = NULL;
 	unsigned long flags;
 	static u8 sidx;
 	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
 
-	spin_lock_irqsave(&usb->tx.lock, flags);
+	spin_lock_irqsave(&tx->lock, flags);
 	last_served_sidx = sidx;
 	do {
 		sidx = (sidx + 1) % MAX_STA_NUM;
-		if ((usb->tx.station[sidx].flag &
-					STATION_CONNECTED_FLAG)) {
-			if (!(usb->tx.station[sidx].flag &
-						STATION_FIFO_FULL_FLAG)) {
-				skb = skb_peek(&usb->tx.station
-						[sidx].data_list);
-			}
-		}
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
 	} while ((sidx != last_served_sidx) && (!skb));
 
 	if (skb) {
-		skb = skb_dequeue(&usb->tx.station[sidx].data_list);
+		skb = skb_dequeue(&tx->station[sidx].data_list);
 		usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
 				    tx_urb_complete, skb);
-		if (skb_queue_len(&usb->tx.station[sidx].data_list)
-				<= 60) {
-			block_queue(usb, usb->tx.station[sidx].mac,
-				    false);
-		}
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			block_queue(usb, tx->station[sidx].mac, false);
 	}
-	spin_unlock_irqrestore(&usb->tx.lock, flags);
+	spin_unlock_irqrestore(&tx->lock, flags);
 }
 
 static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
@@ -179,18 +173,16 @@ static void rx_urb_complete(struct urb *urb)
 		dev_info(&usb->intf->dev,
 			 "FIFO full not packet receipt\n");
 		tx->mac_fifo_full = 1;
-		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
-			usb->tx.station[sidx].flag |=
-				STATION_FIFO_FULL_FLAG;
-		}
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
 		break;
 	case STATION_FIFO_ALMOST_FULL_MESSAGE:
 		dev_info(&usb->intf->dev, "FIFO full packet receipt\n");
 
 		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
-			usb->tx.station[sidx].flag &= 0xFD;
+			tx->station[sidx].flag &= 0xFD;
 
-		send_packet_from_data_queue(usb);
+		purelifi_send_packet_from_data_queue(usb);
 		break;
 	case STATION_CONNECT_MESSAGE:
 		fpga_link_connection_f = 1;
@@ -435,7 +427,7 @@ void tx_urb_complete(struct urb *urb)
 	}
 
 	purelifi_mac_tx_to_dev(skb, urb->status);
-	send_packet_from_data_queue(usb);
+	purelifi_send_packet_from_data_queue(usb);
 	usb_free_urb(urb);
 }
 
@@ -537,7 +529,7 @@ void purelifi_usb_release(struct purelifi_usb *usb)
 	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
 }
 
-const char *speed(enum usb_device_speed speed)
+const char *purelifi_speed(enum usb_device_speed speed)
 {
 	switch (speed) {
 	case USB_SPEED_LOW:
@@ -1050,7 +1042,7 @@ static void slif_data_plane_sap_timer_callb(struct timer_list *t)
 {
 	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
 
-	send_packet_from_data_queue(usb);
+	purelifi_send_packet_from_data_queue(usb);
 	usb->tx.tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
 	timer_setup(&usb->tx.tx_retry_timer,
 		    slif_data_plane_sap_timer_callb, 0);
@@ -1060,19 +1052,17 @@ static void slif_data_plane_sap_timer_callb(struct timer_list *t)
 static void sta_queue_cleanup_timer_callb(struct timer_list *t)
 {
 	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
 	int sidx;
 
 	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
-		if (usb->tx.station[sidx].flag & STATION_CONNECTED_FLAG) {
-			if (usb->tx.station[sidx].flag &
-					STATION_HEARTBEAT_FLAG) {
-				usb->tx.station[sidx].flag ^=
-					STATION_HEARTBEAT_FLAG;
-			} else {
-				memset(usb->tx.station[sidx].mac, 0,
-				       ETH_ALEN);
-				usb->tx.station[sidx].flag = 0;
-			}
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
 		}
 	}
 	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
@@ -1231,8 +1221,9 @@ static int probe(struct usb_interface *intf,
 		 const struct usb_device_id *id)
 {
 	int r = 0;
-	struct purelifi_usb *usb;
 	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
 	struct ieee80211_hw *hw = NULL;
 	static unsigned char hw_address[ETH_ALEN];
 	static unsigned char serial_number[256];
@@ -1250,6 +1241,7 @@ static int probe(struct usb_interface *intf,
 
 	chip = &purelifi_hw_mac(hw)->chip;
 	usb = &chip->usb;
+	tx = &usb->tx;
 
 	r = upload_mac_and_serial_number(intf, hw_address, serial_number);
 	if (r) {
@@ -1284,8 +1276,8 @@ static int probe(struct usb_interface *intf,
 		goto error;
 	}
 
-	usb->tx.mac_fifo_full = 0;
-	spin_lock_init(&usb->tx.lock);
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
 
 	msleep(200);
 	r = purelifi_usb_init_hw(usb);
@@ -1320,19 +1312,17 @@ static int probe(struct usb_interface *intf,
 	/* Initialise the data plane Tx queue */
 	atomic_set(&data_queue_flag, 1);
 	for (i = 0; i < MAX_STA_NUM; i++) {
-		skb_queue_head_init(&usb->tx.station[i].data_list);
-		usb->tx.station[i].flag = 0;
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
 	}
-	usb->tx.station[STA_BROADCAST_INDEX].flag |=
-		STATION_CONNECTED_FLAG;
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
 	for (i = 0; i < ETH_ALEN; i++)
-		usb->tx.station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
 	atomic_set(&data_queue_flag, 0);
 
-	timer_setup(&usb->tx.tx_retry_timer,
-		    slif_data_plane_sap_timer_callb, 0);
-	usb->tx.tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
-	add_timer(&usb->tx.tx_retry_timer);
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
 
 	timer_setup(&usb->sta_queue_cleanup,
 		    sta_queue_cleanup_timer_callb, 0);
diff --git a/drivers/net/wireless/purelifi/usb.h b/drivers/net/wireless/purelifi/usb.h
index cc8f67cd7ad0..af37e7e9fa0a 100644
--- a/drivers/net/wireless/purelifi/usb.h
+++ b/drivers/net/wireless/purelifi/usb.h
@@ -136,7 +136,7 @@ static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
 
 void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
 		       struct usb_interface *intf);
-void send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
 void purelifi_usb_release(struct purelifi_usb *usb);
 void purelifi_usb_disable_rx(struct purelifi_usb *usb);
 void purelifi_usb_enable_tx(struct purelifi_usb *usb);
@@ -144,5 +144,5 @@ void purelifi_usb_disable_tx(struct purelifi_usb *usb);
 int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
 int purelifi_usb_enable_rx(struct purelifi_usb *usb);
 int purelifi_usb_init_hw(struct purelifi_usb *usb);
-const char *speed(enum usb_device_speed speed);
+const char *purelifi_speed(enum usb_device_speed speed);
 #endif



^ permalink raw reply related	[flat|nested] 108+ messages in thread

* RE: [PATCH] [v4] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-16  8:58     ` Joe Perches
@ 2020-10-16 10:13       ` Srinivasan Raju
  0 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-10-16 10:13 UTC (permalink / raw)
  To: Joe Perches
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

> Suggested neatening patch on top of this:

Thanks for the neatening patch Joe, I will resubmit patch.

Thanks
Srini

^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v5] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-16  6:34   ` [PATCH] [v4] " Srinivasan Raju
  2020-10-16  8:58     ` Joe Perches
@ 2020-10-19  3:17     ` Srinivasan Raju
  2020-10-19  4:55       ` Joe Perches
  2020-10-19  8:38       ` [PATCH] [v6] " Srinivasan Raju
  2020-11-16  9:22     ` [PATCH] [v7] " Srinivasan Raju
  2 siblings, 2 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-10-19  3:17 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Mauro Carvalho Chehab, Rob Herring,
	Lukas Bulwahn, open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
---
 MAINTAINERS                            |    5 +
 drivers/net/wireless/Kconfig           |    1 +
 drivers/net/wireless/Makefile          |    1 +
 drivers/net/wireless/purelifi/Kconfig  |   27 +
 drivers/net/wireless/purelifi/Makefile |    3 +
 drivers/net/wireless/purelifi/chip.c   |   97 ++
 drivers/net/wireless/purelifi/chip.h   |   82 ++
 drivers/net/wireless/purelifi/intf.h   |   38 +
 drivers/net/wireless/purelifi/mac.c    |  861 +++++++++++++
 drivers/net/wireless/purelifi/mac.h    |  180 +++
 drivers/net/wireless/purelifi/usb.c    | 1637 ++++++++++++++++++++++++
 drivers/net/wireless/purelifi/usb.h    |  148 +++
 12 files changed, 3080 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/chip.c
 create mode 100644 drivers/net/wireless/purelifi/chip.h
 create mode 100644 drivers/net/wireless/purelifi/intf.h
 create mode 100644 drivers/net/wireless/purelifi/mac.c
 create mode 100644 drivers/net/wireless/purelifi/mac.h
 create mode 100644 drivers/net/wireless/purelifi/usb.c
 create mode 100644 drivers/net/wireless/purelifi/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index c80f87d7258c..150f592fb6e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14108,6 +14108,11 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PUREILIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+S:	Maintained
+F:	drivers/net/wireless/purelifi
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 170a64e67709..b87da3139f94 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -48,6 +48,7 @@ source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
 source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 
 config PCMCIA_RAYCS
 	tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e9fc770026f0 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
 obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..f6630791df9d
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+config PURELIFI
+
+	tristate "pureLiFi device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This driver makes the adapter appear as a normal WLAN interface.
+
+	   The pureLiFi device requires external STA firmware to be loaded.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called purelifi.
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..1f20055e741f
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PURELIFI)		:= purelifi.o
+purelifi-objs 		+= chip.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
new file mode 100644
index 000000000000..cca03697cb06
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval || (chip->beacon_set &&
+			  chip->beacon_interval == interval)) {
+		return 0;
+	}
+
+	chip->beacon_interval = interval;
+	chip->beacon_set = true;
+	return usb_write_req((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %02x:%02x v%02x  %02x-%02x-%02x %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr[0], addr[1], addr[2],
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
+{
+	int r;
+
+	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	u8 value;
+
+	value = 0;
+	usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+	static struct purelifi_chip *chip_p;
+
+	if (chip)
+		chip_p = chip;
+	if (!chip_p)
+		return -EINVAL;
+
+	r = usb_write_req((const u8 *)&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/chip.h b/drivers/net/wireless/purelifi/chip.h
new file mode 100644
index 000000000000..f479f34a6503
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	u16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr
+					)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1 << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/intf.h b/drivers/net/wireless/purelifi/intf.h
new file mode 100644
index 000000000000..20e96671e0a7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/intf.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	u16 rssi;
+	u8  rate_idx;
+	u8  pad;
+	u64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             buf[512];
+};
+
+#endif
diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
new file mode 100644
index 000000000000..09b7c2c0050d
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.c
@@ -0,0 +1,861 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+#ifndef IEEE80211_BAND_2GHZ
+#define IEEE80211_BAND_2GHZ NL80211_BAND_2GHZ
+#endif
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 },
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
+{
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		goto out;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled %d\n", irqs_disabled());
+
+	r = regulatory_hint(hw->wiphy, "CA");
+out:
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon, false);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status - reports tx status of a packet if required
+ * @hw - a &struct ieee80211_hw pointer
+ * @skb - a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success - True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1, retry = 1;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status) {
+		success = !tx_status->failure;
+		retry = tx_status->retry + success;
+	}
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	dev_info(purelifi_mac_dev(mac), "retry=%d\n", retry);
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		/* ieee80211_tx_status_irqsafe(hw, skb); */
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr)
+{
+	return usb_write_req((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (struct purelifi_ctrlset *)skb_push(skb,
+			sizeof(struct purelifi_ctrlset));
+	cs->id = USB_REQ_DATA_TX;
+	cs->payload_len_nw = frag_len;
+	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
+		- sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned and align data*/
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		cs->len +=  4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data*/
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen b/c
+				 * sufficient headroom was reserved
+				 */
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		cs->len +=  4;
+	}
+
+	cs->id = TO_NETWORK(cs->id);
+	cs->len = TO_NETWORK(cs->len);
+	cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
+
+	return 0;
+}
+
+/**
+ * purelifi_op_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+	info->rate_driver_data[0] = hw;
+
+	if (skb->data[24] != 0x08 && skb->len == 116)
+		dev_info(purelifi_mac_dev(mac), "%s ping\n", __func__);
+
+	if (skb->data[24] == 0x08) {
+		u8 dst_mac[ETH_ALEN];
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		memcpy(dst_mac, &skb->data[28], ETH_ALEN);
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			block_queue(usb, tx->station[sidx].mac, true);
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			dev_kfree_skb(skb);
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
+					USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+		      struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	int found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+	dev_info(purelifi_mac_dev(mac), "%s ACK Received\n", __func__);
+	return 0;
+
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++) {
+			skb = __skb_dequeue(q);
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = 1;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	u32 crc_error_cnt_low, crc_error_cnt_high;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = IEEE80211_BAND_2GHZ;
+	mac->rssi      = -15 * ntohs(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	crc_error_cnt_low = status->crc_error_count;
+	crc_error_cnt_high = status->crc_error_count >> 32;
+	mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
+		ntohl(crc_error_cnt_high);
+
+	if (!bad_frame &&
+	    filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = TO_HOST(*(u32 *)buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (buffer[0] == 0x40)
+		dev_info(purelifi_mac_dev(mac), "Probe request\n");
+	else if (buffer[0] == 0x0)
+		dev_info(purelifi_mac_dev(mac), "Association request\n");
+
+	if (buffer[0] == 0xb0) {
+		dev_info(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+	} else if (buffer[0] == 0x08) {
+		unsigned short int seq_nmb = (buffer[23] << 4) |
+			((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000)) {
+			dev_info(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
+		} else {
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		}
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+	if (need_padding) {
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+	}
+
+	/* FIXME : could we avoid this big memcpy ? */
+	memcpy(skb_put(skb, payload_length), buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_info(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			 iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_info(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	spin_lock_irq(&mac->lock);
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_info(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon, false);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band;
+
+#ifdef ieee80211_hw_set
+	_ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
+	_ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
+	_ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
+	_ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);
+#else
+	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+		IEEE80211_HW_SIGNAL_DBM |
+		IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+		IEEE80211_HW_MFP_CAPABLE;
+#endif
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* 4 for 32 bit alignment if no tailroom */
+
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/mac.h b/drivers/net/wireless/purelifi/mac.h
new file mode 100644
index 000000000000..cca1e70a6416
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.h
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             modulation;
+	u8             control;
+	u8             service;
+	u8             pad;
+	__le16         packet_length;
+	__le16         current_length;
+	__le16          next_frame_length;
+	__le16         tx_length;
+	u32            payload_len_nw;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	spinlock_t intr_lock; /* not used : interrupt lock for mac */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	unsigned int ack_pending:1;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
+		*chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block);
+
+#ifdef DEBUG
+void purelifi_dump_rx_status(const struct rx_status *status);
+#else
+#define purelifi_dump_rx_status(status)
+#endif
+
+#endif
diff --git a/drivers/net/wireless/purelifi/usb.c b/drivers/net/wireless/purelifi/usb.c
new file mode 100644
index 000000000000..1354c8734d44
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.c
@@ -0,0 +1,1637 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+#define FCS_LEN 4
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#undef  LOAD_MAC_AND_SERIAL_FROM_FILE
+#undef  LOAD_MAC_AND_SERIAL_FROM_FLASH
+#define LOAD_MAC_AND_SERIAL_FROM_EP0
+
+#define PROC_FOLDER           "purelifi"
+#define MODULATION_FILENAME   "modulation"
+
+struct proc_dir_entry *proc_folder;
+struct proc_dir_entry *modulation_proc_entry;
+static atomic_t data_queue_flag;
+
+/*Tx retry backoff timer (in milliseconds).*/
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+
+/*Tx retry backoff timer (in jiffies).*/
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				    tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			block_queue(usb, tx->station[sidx].mac, false);
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int rx_usb_enabled;
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
+		return;
+	} else if (!urb->context) {
+		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
+
+	tx = &usb->tx;
+
+	if (urb->actual_length != 8) {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[7];
+
+	dev_info(&usb->intf->dev, "Recv status=%u\n", status);
+	dev_info(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+		 buffer[0], buffer[1], buffer[2], buffer[3],
+		 buffer[4], buffer[5]);
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_info(&usb->intf->dev,
+			 "FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_info(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= 0xFD;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		fpga_link_connection_f = 1;
+		dev_info(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		fpga_link_connection_f = 0;
+		dev_info(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_info(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit failed (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0; /*no error return*/
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, 0x40, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+static int download_fpga(struct usb_interface *intf)
+{
+	int i, r, actual_length;
+	int fw_data_i, blk_tran_len = 16384;
+	const char *fw_name;
+	unsigned char fpga_setting[2];
+	unsigned char *fw_data;
+	unsigned char fpga_state[9];
+	unsigned char *fpga_dmabuff;
+	struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	fpga_dmabuff = NULL;
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga.bit";
+		dev_info(&intf->dev, "bit file for X selected.\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga_xc.bit";
+		dev_info(&intf->dev, "bit filefor XC selected.\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware((const struct firmware **)&fw, fw_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+
+	fpga_dmabuff = kmalloc(2, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, 0x33, fpga_dmabuff, sizeof(fpga_setting));
+
+	memcpy(fpga_setting, fpga_dmabuff, 2);
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x34, NULL, 0);
+
+	if (fpga_setting[0] != 6) {
+		dev_err(&intf->dev, "fpga_setting[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmalloc(blk_tran_len, GFP_KERNEL);
+
+		memcpy(fw_data, &fw->data[fw_data_i], blk_tran_len);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			fw_data[tbuf_idx] =
+				((fw_data[tbuf_idx] & 128) >> 7) |
+				((fw_data[tbuf_idx] &  64) >> 5) |
+				((fw_data[tbuf_idx] &  32) >> 3) |
+				((fw_data[tbuf_idx] &  16) >> 1) |
+				((fw_data[tbuf_idx] &   8) << 1) |
+				((fw_data[tbuf_idx] &   4) << 3) |
+				((fw_data[tbuf_idx] &   2) << 5) |
+				((fw_data[tbuf_idx] &   1) << 7);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_setting[0] & 0xFF),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2000);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(9, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xFF, 9);
+
+	send_vendor_request(udev, 0x30, fpga_dmabuff, sizeof(fpga_state));
+
+	dev_info(&intf->dev, "fpga_status");
+	for (i = 0; i <= 8; i++)
+		dev_info(&intf->dev, " %x ", fpga_dmabuff[i]);
+	dev_info(&intf->dev, "\n");
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		kfree(fpga_dmabuff);
+		goto error_free_fw;
+	}
+
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x35, NULL, 0);
+
+	msleep(200);
+
+error_free_fw:
+	release_firmware(fw);
+error:
+	return r;
+}
+
+static int download_XL_FW(struct usb_interface *intf)
+{
+	int r;
+
+	struct firmware *fw_packed = NULL;
+	int step;
+	u8 *buf;
+	u32 size, nmb_of_control_packets, i, no_of_files;
+	u32 tot_size, start_addr;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, 0x80, NULL, 0);
+	msleep(100);
+
+	/* Code for single pack file download */
+
+	fw_pack = "purelifi/li_fi_x/Li-Fi-XL.bin";
+
+	r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	no_of_files = *(u32 *)&fw_packed->data[0];
+	tot_size = fw_packed->size;
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n", no_of_files, tot_size);
+
+	buf = kmalloc(64, GFP_KERNEL);
+	r = -ENOMEM;
+
+	if (!buf)
+		goto error;
+
+	memset(buf, 0, 64);
+
+	for (step = 0; step < no_of_files; step++) {
+		buf[0] = step;
+		r = send_vendor_command(udev, 0x82, buf, 64);
+
+		if (step < no_of_files - 1)
+			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
+				- *(u32 *)&fw_packed->data[4 + (step) * 4];
+		else
+			size = tot_size -
+				*(u32 *)&fw_packed->data[4 + (step) * 4];
+
+		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
+
+		if ((size % 64) && step < 2)
+			size += 64 - size % 64;
+		nmb_of_control_packets = size / 64;
+
+		for (i = 0; i < nmb_of_control_packets; i++) {
+			memcpy(buf,
+			       &fw_packed->data[start_addr + (i * 64)], 64);
+			r = send_vendor_command(udev, 0x81, buf, 64);
+		}
+	  dev_info(&intf->dev, "fw-dw step %d,r=%d size %d\n", step, r, size);
+	}
+	release_firmware(fw_packed);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, 0x83, NULL, 0);
+	dev_info(&intf->dev, "download_fpga (4) (%d)\n", r);
+error:
+	return r;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+static void pretty_print_mac(struct usb_interface *intf, char *serial_number,
+			     unsigned char *hw_address
+			    )
+{
+	unsigned char i;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		dev_info(&intf->dev, "hw_address[%d]=%x\n", i, hw_address[i]);
+}
+
+static int upload_mac_and_serial_number(struct usb_interface *intf,
+					unsigned char *hw_address,
+					unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FILE
+	struct firmware *fw = NULL;
+	int r;
+	char *mac_file_name = "purelifi/li_fi_x/mac.ascii";
+
+	r = request_firmware((const struct firmware **)&fw, mac_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	r = sscanf(fw->data,
+		   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+		    &hw_address[0], &hw_address[1],
+		    &hw_address[2], &hw_address[3],
+		    &hw_address[4], &hw_address[5]);
+	if (r != 6) {
+		dev_err(&intf->dev, "fw_dw - usage args fail (%d)\n", r);
+		goto ERROR;
+	}
+	release_firmware(fw);
+
+	char *serial_file_name = "purelifi/li_fi_x/serial.ascii";
+
+	r = request_firmware((const struct firmware **)&fw, serial_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	strcpy(serial_number, fw->data);
+	dev_info(&intf->dev, "serial_number=%s\n", serial_number);
+	release_firmware(fw);
+	pretty_print_mac(intf, serial_number, hw_address);
+ERROR:
+	return r;
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FLASH
+
+#define FLASH_EC_BUSY 3
+
+	struct flash_t flash;
+	unsigned char *flash_sector_buf;
+	unsigned int sector_index;
+
+	do {
+		unsigned char buf[8];
+
+		msleep(200);
+
+		send_vendor_request(udev, 0x40, buf, sizeof(buf));
+		flash.enabled = buf[0] & 0xFF;
+
+		if (flash.enabled) {
+			flash.sectors = ((buf[6] & 255) << 24) |
+				((buf[5] & 255) << 16) |
+				((buf[4] & 255) <<  8) |
+				(buf[3] & 255);
+
+			flash.sector_size =
+				((buf[2] & 255) << 8) | (buf[1] & 255);
+		} else {
+			flash.sectors = 0;
+			flash.sector_size = 0;
+		}
+
+		if (flash.sector_size & 0x8000)
+			flash.sector_size = 1 << (flash.sector_size & 0x7FFF);
+
+		flash.ec = buf[7] & 0xFF;
+	} while (flash.ec == FLASH_EC_BUSY);
+
+	flash_sector_buf = kmalloc(flash.sector_size,
+				   GFP_KERNEL);
+	if (!flash_sector_buf)
+		return -ENOMEM;
+
+	if (!flash.enabled) {
+		dev_err(&intf->dev, "Flash is not enabled\n");
+		return -EINVAL;
+	}
+
+	read_flash(intf, &flash, 0, flash_sector_buf);
+	sector_index = *((unsigned int *)flash_sector_buf);
+
+	read_flash(intf, &flash, sector_index, flash_sector_buf);
+	memcpy(hw_address, flash_sector_buf, ETH_ALEN);
+	strncpy(serial_number, &flash_sector_buf[ETH_ALEN], 256);
+
+	pretty_print_mac(intf, serial_number, hw_address);
+	kfree(flash_sector_buf);
+	return 0;
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_EP0
+
+#define USB_MAC_VENDOR_REQUEST 0x36
+#define USB_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define USB_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define SERIAL_NUMBER_LENGTH 14
+
+	int r = 0;
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(14, GFP_KERNEL);
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	send_vendor_request(udev, USB_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	memcpy(serial_number, dma_buffer, SERIAL_NUMBER_LENGTH);
+
+	memset(dma_buffer, 0x00, 14);
+
+	send_vendor_request(udev, USB_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, 8);
+
+	memcpy(&firmware_version, dma_buffer, sizeof(unsigned long long));
+
+	dev_info(&intf->dev, "Firmware Version: %llu, Revision: 16418\n",
+		 firmware_version);
+	kfree(dma_buffer);
+
+	pretty_print_mac(intf, serial_number, hw_address);
+error:
+	return r;
+#endif
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	u32 payload_len_nw = TO_NETWORK((buffer_len + FCS_LEN));
+
+	usb_req->id = usb_req_id;
+	usb_req->len  = 0;
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == USB_REQ_BEACON_WR) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		usb_req->len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	usb_req->len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	usb_req->len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+		usb_req->len += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->id = TO_NETWORK(usb_req->id);
+	usb_req->len = TO_NETWORK(usb_req->len);
+}
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			usb_complete_t complete_fn,
+			void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(usb_req.id) + sizeof(usb_req.len) +
+			   TO_HOST(usb_req.len);
+
+	dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+	memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	usb->tx.tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	add_timer(&usb->tx.tx_retry_timer);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	add_timer(&usb->sta_queue_cleanup);
+}
+
+static enum unit_type_t get_unit_type(char *serial_number,
+				      struct usb_interface *intf)
+{
+	dev_info(&intf->dev, "Unit-type is station (STA)\n");
+	return STA;
+}
+
+char usr_input[8] = {0};
+
+static ssize_t purelifi_store_frequency(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t len)
+{
+	int i, r, row, col, predivider, feedback_divider, output_div_0;
+	int output_div_1, output_div_2, clkout3_phase;
+	char values[6][4] = {{0}, {0} };
+	bool valid = false;
+	int rv = 0;
+
+	row = 0; col = 0;
+	for (i = 0; i < min(len, sizeof(values)); i++) {
+		values[row][col] = buf[i];
+		col++;
+		if (buf[i] == 0x20) {
+			row++;
+			col = 0;
+		} else if (buf[i] == 0x0A) {
+			if (row == 5 && col > 0) {
+				rv = sscanf((char *)&values[0][0],
+					    "%d", &predivider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[1][0],
+					    "%d", &feedback_divider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[2][0],
+					    "%d", &output_div_0);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[3][0],
+					    "%d", &output_div_1);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[4][0],
+					    "%d", &output_div_2);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[5][0],
+					    "%d", &clkout3_phase);
+				if (rv != 1)
+					valid = false;
+				valid = true;
+
+				break;
+			}
+		}
+	}
+
+	if (valid) {
+		usr_input[0] = (char)predivider;
+		usr_input[1] = (char)feedback_divider;
+		usr_input[2] = (char)output_div_0;
+		usr_input[3] = (char)output_div_1;
+		usr_input[4] = (char)output_div_2;
+		usr_input[5] = (char)clkout3_phase;
+
+		dev_err(dev, "options IP_DIV: %d VCO_MULT: %d OP_DIV_PHY: %d",
+			usr_input[0], usr_input[1], usr_input[2]);
+		dev_err(dev, "OP_DIV_CPU: %d OP_DIV_USB: %d CLK3_PH: %d\n",
+			usr_input[3], usr_input[4], usr_input[5]);
+
+		r = usb_write_req((const u8 *)&usr_input[0],
+				  sizeof(usr_input), USB_REQ_SET_FREQ);
+		if (r < 0)
+			dev_err(dev, "SET_FREQ failed (%d)\n", r);
+	} else {
+		dev_err(dev, "Your options are invalid\n");
+	}
+
+	return len;
+}
+
+static ssize_t purelifi_show_sysfs(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	return 0;
+}
+
+static const struct device_attribute purelifi_attr_frequency = {
+	  .attr = {.name = "frequency", .mode = (0666)},
+	  .show = purelifi_show_sysfs,
+	  .store = purelifi_store_frequency,
+};
+
+static ssize_t purelifi_store_modulation(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t len)
+{
+	int rate, i;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	for (i = 0; i < min(len, sizeof(usr_input)); i++)
+		usr_input[i] = buf[i];
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return len;
+}
+
+static ssize_t purelifi_show_modulation(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return 0;
+}
+
+static struct device_attribute purelifi_attr_modulation = {
+	.attr = {.name = "modulation", .mode = (0666)},
+	.show = purelifi_show_modulation,
+	.store = purelifi_store_modulation,
+};
+
+static void create_config(struct usb_interface *intf)
+{
+	struct device *dev;
+
+	dev = &intf->dev;
+	if (device_create_file(dev, &purelifi_attr_modulation)) {
+		dev_err(&intf->dev,
+			"failed to create modulation sysfs entry\n");
+		return;
+	}
+	if (device_create_file(dev, &purelifi_attr_frequency)) {
+		device_remove_file(dev, &purelifi_attr_modulation);
+		dev_err(&intf->dev,
+			"failed to create frequency sysfs entry\n");
+	}
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	static unsigned char hw_address[ETH_ALEN];
+	static unsigned char serial_number[256];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	create_config(intf);
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial_number(intf, hw_address, serial_number);
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = get_unit_type(serial_number, intf);
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg(&intf->dev, "register device failed (%d)\n", r);
+		goto error;
+	}
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_XL_FW(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(200);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_info(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = usb_write_req(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_info(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer(&usb->tx.tx_retry_timer);
+	del_timer(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	device_remove_file(&intf->dev, &purelifi_attr_modulation);
+	device_remove_file(&intf->dev, &purelifi_attr_frequency);
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	mutex_lock(&mac->chip.mutex);
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	mutex_unlock(&mac->chip.mutex);
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#else
+
+#define suspend NULL
+#define resume  NULL
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+	.suspend        = suspend,
+	.resume         = resume,
+	.disable_hub_initiated_lpm = 1,
+};
+
+struct workqueue_struct *purelifi_workqueue;
+
+static int modulation_open(struct inode *inode_p, struct file *file_p)
+{
+	return 0;
+}
+
+static ssize_t modulation_read(struct file *file_p,
+			       char __user *user_p,
+		size_t offset, loff_t *off_p)
+{
+	return 0;
+}
+
+static ssize_t modulation_write(struct file *file_p,
+				const char __user *buffer,
+				size_t count, loff_t *actual_data_written)
+{
+	int rate;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	if (copy_from_user(usr_input, buffer, min(count, sizeof(usr_input))))
+		return -EFAULT;
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return count;
+}
+
+static const struct proc_ops  modulation_fops = {
+	.proc_open  = modulation_open,
+	.proc_read  = modulation_read,
+	.proc_write = modulation_write
+};
+
+static int create_proc_entry_e(struct proc_dir_entry *entry,
+			       size_t size,
+			       mode_t mode,
+			       const struct proc_ops *pops,
+			       const char *entry_name,
+			       struct proc_dir_entry *parent
+			     )
+{
+	entry = proc_create(entry_name, mode, parent, pops);
+	if (!(entry))
+		return -ENOMEM;
+	return 0;
+}
+
+static int __init usb_init(void)
+{
+	int r;
+
+	proc_folder = proc_mkdir(PROC_FOLDER, NULL);
+	if (!proc_folder) {
+		pr_err("error creating proc folder\n");
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = create_proc_entry_e(modulation_proc_entry, sizeof(u32), 0666,
+				&modulation_fops, MODULATION_FILENAME,
+				proc_folder);
+	if (r)
+		goto remove_proc_dir;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto remove_modulation_proc_file;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+remove_modulation_proc_file:
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+remove_proc_dir:
+	remove_proc_entry(PROC_FOLDER, NULL);
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+	remove_proc_entry(PROC_FOLDER, NULL);
+	if (ez_usb_interface) {
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_modulation);
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_frequency);
+	}
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi Ltd");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("purelifi/li_fi_x/fpga.bit");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/usb.h b/drivers/net/wireless/purelifi/usb.h
new file mode 100644
index 000000000000..af37e7e9fa0a
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
+		      (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
+
+#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
+
+#define TO_NETWORK_32(X) TO_NETWORK(X)
+
+#define TO_HOST(X)    TO_NETWORK(X)
+#define TO_HOST_16(X) TO_NETWORK_16(X)
+#define TO_HOST_32(X) TO_NETWORK_32(X)
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2     |     1     |     0     |
+   // Reserved  | Hearbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	u8 initialized:1, was_running:1;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+		usb_complete_t complete_fn, void *context);
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
+		*usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *purelifi_intf_to_hw(struct usb_interface
+		*intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
+		*usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+#endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v5] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-19  3:17     ` [PATCH] [v5] " Srinivasan Raju
@ 2020-10-19  4:55       ` Joe Perches
  2020-10-19  6:05         ` Srinivasan Raju
  2020-10-19  8:38       ` [PATCH] [v6] " Srinivasan Raju
  1 sibling, 1 reply; 108+ messages in thread
From: Joe Perches @ 2020-10-19  4:55 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

On Mon, 2020-10-19 at 08:47 +0530, Srinivasan Raju wrote:
> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.

Mostly trivial comments:

> diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
[]
> +int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
> +{
> +	int r;
> +	static struct purelifi_chip *chip_p;

Isn't chip_p pointless?

> +
> +	if (chip)
> +		chip_p = chip;
> +	if (!chip_p)
> +		return -EINVAL;
> +

chip_p is otherwise unused.

> diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
[]
> +int purelifi_mac_init_hw(struct ieee80211_hw *hw)
> +{
> +	int r;
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct purelifi_chip *chip = &mac->chip;
> +
> +	r = purelifi_chip_init_hw(chip);
> +	if (r) {
> +		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
> +		goto out;
> +	}
> +
> +	dev_dbg(purelifi_mac_dev(mac), "irq_disabled %d\n", irqs_disabled());
> +
> +	r = regulatory_hint(hw->wiphy, "CA");
> +out:
> +	return r;
> +}

Simpler without the out: label and a direct return

	if (r) {
		dev_warn(...)
		return r;
	}

	...

	return regulator_hint(hw->wiphy, "CA");
}

> +static int download_fpga(struct usb_interface *intf)
> +{
[]
> +	if ((le16_to_cpu(udev->descriptor.idVendor) ==
> +				PURELIFI_X_VENDOR_ID_0) &&
> +	    (le16_to_cpu(udev->descriptor.idProduct) ==
> +				PURELIFI_X_PRODUCT_ID_0)) {
> +		fw_name = "purelifi/li_fi_x/fpga.bit";
> +		dev_info(&intf->dev, "bit file for X selected.\n");
> +
> +	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
> +					PURELIFI_XC_VENDOR_ID_0 &&
> +		   (le16_to_cpu(udev->descriptor.idProduct) ==
> +					PURELIFI_XC_PRODUCT_ID_0)) {
> +		fw_name = "purelifi/li_fi_x/fpga_xc.bit";
> +		dev_info(&intf->dev, "bit filefor XC selected.\n");

Inconsistent dev_info spacing: "file for" vs "filefor"

> +	for (fw_data_i = 0; fw_data_i < fw->size;) {
> +		int tbuf_idx;
> +
> +		if ((fw->size - fw_data_i) < blk_tran_len)
> +			blk_tran_len = fw->size - fw_data_i;
> +
> +		fw_data = kmalloc(blk_tran_len, GFP_KERNEL);
> +
> +		memcpy(fw_data, &fw->data[fw_data_i], blk_tran_len);
> +
> +		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
> +			fw_data[tbuf_idx] =
> +				((fw_data[tbuf_idx] & 128) >> 7) |
> +				((fw_data[tbuf_idx] &  64) >> 5) |
> +				((fw_data[tbuf_idx] &  32) >> 3) |
> +				((fw_data[tbuf_idx] &  16) >> 1) |
> +				((fw_data[tbuf_idx] &   8) << 1) |
> +				((fw_data[tbuf_idx] &   4) << 3) |
> +				((fw_data[tbuf_idx] &   2) << 5) |
> +				((fw_data[tbuf_idx] &   1) << 7);
> +		}

pity there isn't an u8_bit_reverse function/mechanism

> +static void pretty_print_mac(struct usb_interface *intf, char *serial_number,
> +			     unsigned char *hw_address
> +			    )
> +{
> +	unsigned char i;
> +
> +	for (i = 0; i < ETH_ALEN; i++)
> +		dev_info(&intf->dev, "hw_address[%d]=%x\n", i, hw_address[i]);

I don't think this prettier than %pM

> +}
> +
> +static int upload_mac_and_serial_number(struct usb_interface *intf,
> +					unsigned char *hw_address,
> +					unsigned char *serial_number)
> +{
[]
> +	do {
> +		unsigned char buf[8];
> +
> +		msleep(200);
> +
> +		send_vendor_request(udev, 0x40, buf, sizeof(buf));
> +		flash.enabled = buf[0] & 0xFF;
> +
> +		if (flash.enabled) {
> +			flash.sectors = ((buf[6] & 255) << 24) |

buf is unsigned char[], rather pointless using & 255

> diff --git a/drivers/net/wireless/purelifi/usb.h b/drivers/net/wireless/purelifi/usb.h
[]
> +struct station_t {
> +   //  7...3    |    2     |     1     |     0     |
> +   // Reserved  | Hearbeat | FIFO full | Connected |

heartbeat



^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v5] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-19  4:55       ` Joe Perches
@ 2020-10-19  6:05         ` Srinivasan Raju
  0 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-10-19  6:05 UTC (permalink / raw)
  To: Joe Perches
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS


Mostly trivial comments:

>> Ok Thanks, I will address them

^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v6] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-19  3:17     ` [PATCH] [v5] " Srinivasan Raju
  2020-10-19  4:55       ` Joe Perches
@ 2020-10-19  8:38       ` Srinivasan Raju
  2020-10-19 16:07         ` Krishna Chaitanya
  1 sibling, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2020-10-19  8:38 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Mauro Carvalho Chehab, Rob Herring,
	Lukas Bulwahn, open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
---
 MAINTAINERS                            |    5 +
 drivers/net/wireless/Kconfig           |    1 +
 drivers/net/wireless/Makefile          |    1 +
 drivers/net/wireless/purelifi/Kconfig  |   27 +
 drivers/net/wireless/purelifi/Makefile |    3 +
 drivers/net/wireless/purelifi/chip.c   |   94 ++
 drivers/net/wireless/purelifi/chip.h   |   82 ++
 drivers/net/wireless/purelifi/intf.h   |   38 +
 drivers/net/wireless/purelifi/mac.c    |  860 +++++++++++++
 drivers/net/wireless/purelifi/mac.h    |  180 +++
 drivers/net/wireless/purelifi/usb.c    | 1627 ++++++++++++++++++++++++
 drivers/net/wireless/purelifi/usb.h    |  148 +++
 12 files changed, 3066 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/chip.c
 create mode 100644 drivers/net/wireless/purelifi/chip.h
 create mode 100644 drivers/net/wireless/purelifi/intf.h
 create mode 100644 drivers/net/wireless/purelifi/mac.c
 create mode 100644 drivers/net/wireless/purelifi/mac.h
 create mode 100644 drivers/net/wireless/purelifi/usb.c
 create mode 100644 drivers/net/wireless/purelifi/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index c80f87d7258c..150f592fb6e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14108,6 +14108,11 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PUREILIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+S:	Maintained
+F:	drivers/net/wireless/purelifi
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 170a64e67709..b87da3139f94 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -48,6 +48,7 @@ source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
 source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 
 config PCMCIA_RAYCS
 	tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e9fc770026f0 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
 obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..f6630791df9d
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+config PURELIFI
+
+	tristate "pureLiFi device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This driver makes the adapter appear as a normal WLAN interface.
+
+	   The pureLiFi device requires external STA firmware to be loaded.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called purelifi.
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..1f20055e741f
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PURELIFI)		:= purelifi.o
+purelifi-objs 		+= chip.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
new file mode 100644
index 000000000000..4282ce1d92b4
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval || (chip->beacon_set &&
+			  chip->beacon_interval == interval)) {
+		return 0;
+	}
+
+	chip->beacon_interval = interval;
+	chip->beacon_set = true;
+	return usb_write_req((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %02x:%02x v%02x  %02x-%02x-%02x %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr[0], addr[1], addr[2],
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
+{
+	int r;
+
+	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	u8 value;
+
+	value = 0;
+	usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = usb_write_req((const u8 *)&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/chip.h b/drivers/net/wireless/purelifi/chip.h
new file mode 100644
index 000000000000..f479f34a6503
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	u16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr
+					)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1 << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/intf.h b/drivers/net/wireless/purelifi/intf.h
new file mode 100644
index 000000000000..20e96671e0a7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/intf.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	u16 rssi;
+	u8  rate_idx;
+	u8  pad;
+	u64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             buf[512];
+};
+
+#endif
diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
new file mode 100644
index 000000000000..3fb280820950
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.c
@@ -0,0 +1,860 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+#ifndef IEEE80211_BAND_2GHZ
+#define IEEE80211_BAND_2GHZ NL80211_BAND_2GHZ
+#endif
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 },
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
+{
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+
+	r = regulatory_hint(hw->wiphy, "CA");
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon, false);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status - reports tx status of a packet if required
+ * @hw - a &struct ieee80211_hw pointer
+ * @skb - a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success - True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1, retry = 1;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status) {
+		success = !tx_status->failure;
+		retry = tx_status->retry + success;
+	}
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	dev_info(purelifi_mac_dev(mac), "retry=%d\n", retry);
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		/* ieee80211_tx_status_irqsafe(hw, skb); */
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr)
+{
+	return usb_write_req((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (struct purelifi_ctrlset *)skb_push(skb,
+			sizeof(struct purelifi_ctrlset));
+	cs->id = USB_REQ_DATA_TX;
+	cs->payload_len_nw = frag_len;
+	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
+		- sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned and align data*/
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		cs->len +=  4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data*/
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen b/c
+				 * sufficient headroom was reserved
+				 */
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		cs->len +=  4;
+	}
+
+	cs->id = TO_NETWORK(cs->id);
+	cs->len = TO_NETWORK(cs->len);
+	cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
+
+	return 0;
+}
+
+/**
+ * purelifi_op_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+	info->rate_driver_data[0] = hw;
+
+	if (skb->data[24] != 0x08 && skb->len == 116)
+		dev_info(purelifi_mac_dev(mac), "%s ping\n", __func__);
+
+	if (skb->data[24] == 0x08) {
+		u8 dst_mac[ETH_ALEN];
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		memcpy(dst_mac, &skb->data[28], ETH_ALEN);
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			block_queue(usb, tx->station[sidx].mac, true);
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			dev_kfree_skb(skb);
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
+					USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+		      struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	int found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+	dev_info(purelifi_mac_dev(mac), "%s ACK Received\n", __func__);
+	return 0;
+
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++) {
+			skb = __skb_dequeue(q);
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = 1;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	u32 crc_error_cnt_low, crc_error_cnt_high;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = IEEE80211_BAND_2GHZ;
+	mac->rssi      = -15 * ntohs(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	crc_error_cnt_low = status->crc_error_count;
+	crc_error_cnt_high = status->crc_error_count >> 32;
+	mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
+		ntohl(crc_error_cnt_high);
+
+	if (!bad_frame &&
+	    filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = TO_HOST(*(u32 *)buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (buffer[0] == 0x40)
+		dev_info(purelifi_mac_dev(mac), "Probe request\n");
+	else if (buffer[0] == 0x0)
+		dev_info(purelifi_mac_dev(mac), "Association request\n");
+
+	if (buffer[0] == 0xb0) {
+		dev_info(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+	} else if (buffer[0] == 0x08) {
+		unsigned short int seq_nmb = (buffer[23] << 4) |
+			((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000)) {
+			dev_info(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
+		} else {
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		}
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+	if (need_padding) {
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+	}
+
+	/* FIXME : could we avoid this big memcpy ? */
+	memcpy(skb_put(skb, payload_length), buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_info(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			 iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_info(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	spin_lock_irq(&mac->lock);
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_info(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon, false);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band;
+
+#ifdef ieee80211_hw_set
+	_ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
+	_ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
+	_ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
+	_ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);
+#else
+	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+		IEEE80211_HW_SIGNAL_DBM |
+		IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+		IEEE80211_HW_MFP_CAPABLE;
+#endif
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* 4 for 32 bit alignment if no tailroom */
+
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/mac.h b/drivers/net/wireless/purelifi/mac.h
new file mode 100644
index 000000000000..cca1e70a6416
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.h
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             modulation;
+	u8             control;
+	u8             service;
+	u8             pad;
+	__le16         packet_length;
+	__le16         current_length;
+	__le16          next_frame_length;
+	__le16         tx_length;
+	u32            payload_len_nw;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	spinlock_t intr_lock; /* not used : interrupt lock for mac */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	unsigned int ack_pending:1;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
+		*chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block);
+
+#ifdef DEBUG
+void purelifi_dump_rx_status(const struct rx_status *status);
+#else
+#define purelifi_dump_rx_status(status)
+#endif
+
+#endif
diff --git a/drivers/net/wireless/purelifi/usb.c b/drivers/net/wireless/purelifi/usb.c
new file mode 100644
index 000000000000..f405434bd148
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.c
@@ -0,0 +1,1627 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+#define FCS_LEN 4
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#undef  LOAD_MAC_AND_SERIAL_FROM_FILE
+#undef  LOAD_MAC_AND_SERIAL_FROM_FLASH
+#define LOAD_MAC_AND_SERIAL_FROM_EP0
+
+#define PROC_FOLDER           "purelifi"
+#define MODULATION_FILENAME   "modulation"
+
+struct proc_dir_entry *proc_folder;
+struct proc_dir_entry *modulation_proc_entry;
+static atomic_t data_queue_flag;
+
+/*Tx retry backoff timer (in milliseconds).*/
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+
+/*Tx retry backoff timer (in jiffies).*/
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				    tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			block_queue(usb, tx->station[sidx].mac, false);
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int rx_usb_enabled;
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
+		return;
+	} else if (!urb->context) {
+		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
+
+	tx = &usb->tx;
+
+	if (urb->actual_length != 8) {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[7];
+
+	dev_info(&usb->intf->dev, "Recv status=%u\n", status);
+	dev_info(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+		 buffer[0], buffer[1], buffer[2], buffer[3],
+		 buffer[4], buffer[5]);
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_info(&usb->intf->dev,
+			 "FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_info(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= 0xFD;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		fpga_link_connection_f = 1;
+		dev_info(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		fpga_link_connection_f = 0;
+		dev_info(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_info(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit failed (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0; /*no error return*/
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, 0x40, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+static int download_fpga(struct usb_interface *intf)
+{
+	int i, r, actual_length;
+	int fw_data_i, blk_tran_len = 16384;
+	const char *fw_name;
+	unsigned char fpga_setting[2];
+	unsigned char *fw_data;
+	unsigned char fpga_state[9];
+	unsigned char *fpga_dmabuff;
+	struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	fpga_dmabuff = NULL;
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga.bit";
+		dev_info(&intf->dev, "bit file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga_xc.bit";
+		dev_info(&intf->dev, "bit file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware((const struct firmware **)&fw, fw_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+
+	fpga_dmabuff = kmalloc(2, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, 0x33, fpga_dmabuff, sizeof(fpga_setting));
+
+	memcpy(fpga_setting, fpga_dmabuff, 2);
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x34, NULL, 0);
+
+	if (fpga_setting[0] != 6) {
+		dev_err(&intf->dev, "fpga_setting[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmalloc(blk_tran_len, GFP_KERNEL);
+
+		memcpy(fw_data, &fw->data[fw_data_i], blk_tran_len);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			fw_data[tbuf_idx] =
+				((fw_data[tbuf_idx] & 128) >> 7) |
+				((fw_data[tbuf_idx] &  64) >> 5) |
+				((fw_data[tbuf_idx] &  32) >> 3) |
+				((fw_data[tbuf_idx] &  16) >> 1) |
+				((fw_data[tbuf_idx] &   8) << 1) |
+				((fw_data[tbuf_idx] &   4) << 3) |
+				((fw_data[tbuf_idx] &   2) << 5) |
+				((fw_data[tbuf_idx] &   1) << 7);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_setting[0] & 0xFF),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2000);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(9, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xFF, 9);
+
+	send_vendor_request(udev, 0x30, fpga_dmabuff, sizeof(fpga_state));
+
+	dev_info(&intf->dev, "fpga_status");
+	for (i = 0; i <= 8; i++)
+		dev_info(&intf->dev, " %x ", fpga_dmabuff[i]);
+	dev_info(&intf->dev, "\n");
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		kfree(fpga_dmabuff);
+		goto error_free_fw;
+	}
+
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x35, NULL, 0);
+
+	msleep(200);
+
+error_free_fw:
+	release_firmware(fw);
+error:
+	return r;
+}
+
+static int download_XL_FW(struct usb_interface *intf)
+{
+	int r;
+
+	struct firmware *fw_packed = NULL;
+	int step;
+	u8 *buf;
+	u32 size, nmb_of_control_packets, i, no_of_files;
+	u32 tot_size, start_addr;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, 0x80, NULL, 0);
+	msleep(100);
+
+	/* Code for single pack file download */
+
+	fw_pack = "purelifi/li_fi_x/Li-Fi-XL.bin";
+
+	r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	no_of_files = *(u32 *)&fw_packed->data[0];
+	tot_size = fw_packed->size;
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n", no_of_files, tot_size);
+
+	buf = kmalloc(64, GFP_KERNEL);
+	r = -ENOMEM;
+
+	if (!buf)
+		goto error;
+
+	memset(buf, 0, 64);
+
+	for (step = 0; step < no_of_files; step++) {
+		buf[0] = step;
+		r = send_vendor_command(udev, 0x82, buf, 64);
+
+		if (step < no_of_files - 1)
+			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
+				- *(u32 *)&fw_packed->data[4 + (step) * 4];
+		else
+			size = tot_size -
+				*(u32 *)&fw_packed->data[4 + (step) * 4];
+
+		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
+
+		if ((size % 64) && step < 2)
+			size += 64 - size % 64;
+		nmb_of_control_packets = size / 64;
+
+		for (i = 0; i < nmb_of_control_packets; i++) {
+			memcpy(buf,
+			       &fw_packed->data[start_addr + (i * 64)], 64);
+			r = send_vendor_command(udev, 0x81, buf, 64);
+		}
+	  dev_info(&intf->dev, "fw-dw step %d,r=%d size %d\n", step, r, size);
+	}
+	release_firmware(fw_packed);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, 0x83, NULL, 0);
+	dev_info(&intf->dev, "download_fpga (4) (%d)\n", r);
+error:
+	return r;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+static int upload_mac_and_serial_number(struct usb_interface *intf,
+					unsigned char *hw_address,
+					unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FILE
+	struct firmware *fw = NULL;
+	int r;
+	char *mac_file_name = "purelifi/li_fi_x/mac.ascii";
+
+	r = request_firmware((const struct firmware **)&fw, mac_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	r = sscanf(fw->data,
+		   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+		    &hw_address[0], &hw_address[1],
+		    &hw_address[2], &hw_address[3],
+		    &hw_address[4], &hw_address[5]);
+	if (r != 6) {
+		dev_err(&intf->dev, "fw_dw - usage args fail (%d)\n", r);
+		goto ERROR;
+	}
+	release_firmware(fw);
+
+	char *serial_file_name = "purelifi/li_fi_x/serial.ascii";
+
+	r = request_firmware((const struct firmware **)&fw, serial_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	strcpy(serial_number, fw->data);
+	dev_info(&intf->dev, "serial_number=%s\n", serial_number);
+	release_firmware(fw);
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+ERROR:
+	return r;
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FLASH
+
+#define FLASH_EC_BUSY 3
+
+	struct flash_t flash;
+	unsigned char *flash_sector_buf;
+	unsigned int sector_index;
+
+	do {
+		unsigned char buf[8];
+
+		msleep(200);
+
+		send_vendor_request(udev, 0x40, buf, sizeof(buf));
+		flash.enabled = buf[0] & 0xFF;
+
+		if (flash.enabled) {
+			flash.sectors = ((buf[6] & 255) << 24) |
+				((buf[5] & 255) << 16) |
+				((buf[4] & 255) <<  8) |
+				(buf[3] & 255);
+
+			flash.sector_size =
+				((buf[2] & 255) << 8) | (buf[1] & 255);
+		} else {
+			flash.sectors = 0;
+			flash.sector_size = 0;
+		}
+
+		if (flash.sector_size & 0x8000)
+			flash.sector_size = 1 << (flash.sector_size & 0x7FFF);
+
+		flash.ec = buf[7] & 0xFF;
+	} while (flash.ec == FLASH_EC_BUSY);
+
+	flash_sector_buf = kmalloc(flash.sector_size,
+				   GFP_KERNEL);
+	if (!flash_sector_buf)
+		return -ENOMEM;
+
+	if (!flash.enabled) {
+		dev_err(&intf->dev, "Flash is not enabled\n");
+		return -EINVAL;
+	}
+
+	read_flash(intf, &flash, 0, flash_sector_buf);
+	sector_index = *((unsigned int *)flash_sector_buf);
+
+	read_flash(intf, &flash, sector_index, flash_sector_buf);
+	memcpy(hw_address, flash_sector_buf, ETH_ALEN);
+	strncpy(serial_number, &flash_sector_buf[ETH_ALEN], 256);
+
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+	kfree(flash_sector_buf);
+	return 0;
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_EP0
+
+#define USB_MAC_VENDOR_REQUEST 0x36
+#define USB_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define USB_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define SERIAL_NUMBER_LENGTH 14
+
+	int r = 0;
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(14, GFP_KERNEL);
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	send_vendor_request(udev, USB_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	memcpy(serial_number, dma_buffer, SERIAL_NUMBER_LENGTH);
+
+	memset(dma_buffer, 0x00, 14);
+
+	send_vendor_request(udev, USB_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, 8);
+
+	memcpy(&firmware_version, dma_buffer, sizeof(unsigned long long));
+
+	dev_info(&intf->dev, "Firmware Version: %llu, Revision: 16418\n",
+		 firmware_version);
+	kfree(dma_buffer);
+
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+error:
+	return r;
+#endif
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	u32 payload_len_nw = TO_NETWORK((buffer_len + FCS_LEN));
+
+	usb_req->id = usb_req_id;
+	usb_req->len  = 0;
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == USB_REQ_BEACON_WR) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		usb_req->len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	usb_req->len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	usb_req->len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+		usb_req->len += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->id = TO_NETWORK(usb_req->id);
+	usb_req->len = TO_NETWORK(usb_req->len);
+}
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			usb_complete_t complete_fn,
+			void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(usb_req.id) + sizeof(usb_req.len) +
+			   TO_HOST(usb_req.len);
+
+	dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+	memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	usb->tx.tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	add_timer(&usb->tx.tx_retry_timer);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	add_timer(&usb->sta_queue_cleanup);
+}
+
+static enum unit_type_t get_unit_type(char *serial_number,
+				      struct usb_interface *intf)
+{
+	dev_info(&intf->dev, "Unit-type is station (STA)\n");
+	return STA;
+}
+
+char usr_input[8] = {0};
+
+static ssize_t purelifi_store_frequency(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t len)
+{
+	int i, r, row, col, predivider, feedback_divider, output_div_0;
+	int output_div_1, output_div_2, clkout3_phase;
+	char values[6][4] = {{0}, {0} };
+	bool valid = false;
+	int rv = 0;
+
+	row = 0; col = 0;
+	for (i = 0; i < min(len, sizeof(values)); i++) {
+		values[row][col] = buf[i];
+		col++;
+		if (buf[i] == 0x20) {
+			row++;
+			col = 0;
+		} else if (buf[i] == 0x0A) {
+			if (row == 5 && col > 0) {
+				rv = sscanf((char *)&values[0][0],
+					    "%d", &predivider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[1][0],
+					    "%d", &feedback_divider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[2][0],
+					    "%d", &output_div_0);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[3][0],
+					    "%d", &output_div_1);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[4][0],
+					    "%d", &output_div_2);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[5][0],
+					    "%d", &clkout3_phase);
+				if (rv != 1)
+					valid = false;
+				valid = true;
+
+				break;
+			}
+		}
+	}
+
+	if (valid) {
+		usr_input[0] = (char)predivider;
+		usr_input[1] = (char)feedback_divider;
+		usr_input[2] = (char)output_div_0;
+		usr_input[3] = (char)output_div_1;
+		usr_input[4] = (char)output_div_2;
+		usr_input[5] = (char)clkout3_phase;
+
+		dev_err(dev, "options IP_DIV: %d VCO_MULT: %d OP_DIV_PHY: %d",
+			usr_input[0], usr_input[1], usr_input[2]);
+		dev_err(dev, "OP_DIV_CPU: %d OP_DIV_USB: %d CLK3_PH: %d\n",
+			usr_input[3], usr_input[4], usr_input[5]);
+
+		r = usb_write_req((const u8 *)&usr_input[0],
+				  sizeof(usr_input), USB_REQ_SET_FREQ);
+		if (r < 0)
+			dev_err(dev, "SET_FREQ failed (%d)\n", r);
+	} else {
+		dev_err(dev, "Your options are invalid\n");
+	}
+
+	return len;
+}
+
+static ssize_t purelifi_show_sysfs(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	return 0;
+}
+
+static const struct device_attribute purelifi_attr_frequency = {
+	  .attr = {.name = "frequency", .mode = (0666)},
+	  .show = purelifi_show_sysfs,
+	  .store = purelifi_store_frequency,
+};
+
+static ssize_t purelifi_store_modulation(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t len)
+{
+	int rate, i;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	for (i = 0; i < min(len, sizeof(usr_input)); i++)
+		usr_input[i] = buf[i];
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return len;
+}
+
+static ssize_t purelifi_show_modulation(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return 0;
+}
+
+static struct device_attribute purelifi_attr_modulation = {
+	.attr = {.name = "modulation", .mode = (0666)},
+	.show = purelifi_show_modulation,
+	.store = purelifi_store_modulation,
+};
+
+static void create_config(struct usb_interface *intf)
+{
+	struct device *dev;
+
+	dev = &intf->dev;
+	if (device_create_file(dev, &purelifi_attr_modulation)) {
+		dev_err(&intf->dev,
+			"failed to create modulation sysfs entry\n");
+		return;
+	}
+	if (device_create_file(dev, &purelifi_attr_frequency)) {
+		device_remove_file(dev, &purelifi_attr_modulation);
+		dev_err(&intf->dev,
+			"failed to create frequency sysfs entry\n");
+	}
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	static unsigned char hw_address[ETH_ALEN];
+	static unsigned char serial_number[256];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	create_config(intf);
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial_number(intf, hw_address, serial_number);
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = get_unit_type(serial_number, intf);
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg(&intf->dev, "register device failed (%d)\n", r);
+		goto error;
+	}
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_XL_FW(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(200);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_info(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = usb_write_req(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_info(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer(&usb->tx.tx_retry_timer);
+	del_timer(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	device_remove_file(&intf->dev, &purelifi_attr_modulation);
+	device_remove_file(&intf->dev, &purelifi_attr_frequency);
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	mutex_lock(&mac->chip.mutex);
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	mutex_unlock(&mac->chip.mutex);
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#else
+
+#define suspend NULL
+#define resume  NULL
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+	.suspend        = suspend,
+	.resume         = resume,
+	.disable_hub_initiated_lpm = 1,
+};
+
+struct workqueue_struct *purelifi_workqueue;
+
+static int modulation_open(struct inode *inode_p, struct file *file_p)
+{
+	return 0;
+}
+
+static ssize_t modulation_read(struct file *file_p,
+			       char __user *user_p,
+		size_t offset, loff_t *off_p)
+{
+	return 0;
+}
+
+static ssize_t modulation_write(struct file *file_p,
+				const char __user *buffer,
+				size_t count, loff_t *actual_data_written)
+{
+	int rate;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	if (copy_from_user(usr_input, buffer, min(count, sizeof(usr_input))))
+		return -EFAULT;
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return count;
+}
+
+static const struct proc_ops  modulation_fops = {
+	.proc_open  = modulation_open,
+	.proc_read  = modulation_read,
+	.proc_write = modulation_write
+};
+
+static int create_proc_entry_e(struct proc_dir_entry *entry,
+			       size_t size,
+			       mode_t mode,
+			       const struct proc_ops *pops,
+			       const char *entry_name,
+			       struct proc_dir_entry *parent
+			     )
+{
+	entry = proc_create(entry_name, mode, parent, pops);
+	if (!(entry))
+		return -ENOMEM;
+	return 0;
+}
+
+static int __init usb_init(void)
+{
+	int r;
+
+	proc_folder = proc_mkdir(PROC_FOLDER, NULL);
+	if (!proc_folder) {
+		pr_err("error creating proc folder\n");
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = create_proc_entry_e(modulation_proc_entry, sizeof(u32), 0666,
+				&modulation_fops, MODULATION_FILENAME,
+				proc_folder);
+	if (r)
+		goto remove_proc_dir;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto remove_modulation_proc_file;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+remove_modulation_proc_file:
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+remove_proc_dir:
+	remove_proc_entry(PROC_FOLDER, NULL);
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+	remove_proc_entry(PROC_FOLDER, NULL);
+	if (ez_usb_interface) {
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_modulation);
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_frequency);
+	}
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi Ltd");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("purelifi/li_fi_x/fpga.bit");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/usb.h b/drivers/net/wireless/purelifi/usb.h
new file mode 100644
index 000000000000..3a6cae971e50
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
+		      (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
+
+#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
+
+#define TO_NETWORK_32(X) TO_NETWORK(X)
+
+#define TO_HOST(X)    TO_NETWORK(X)
+#define TO_HOST_16(X) TO_NETWORK_16(X)
+#define TO_HOST_32(X) TO_NETWORK_32(X)
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2      |     1     |     0	    |
+   // Reserved  | Heartbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	u8 initialized:1, was_running:1;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+		usb_complete_t complete_fn, void *context);
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
+		*usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *purelifi_intf_to_hw(struct usb_interface
+		*intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
+		*usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+#endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v6] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-19  8:38       ` [PATCH] [v6] " Srinivasan Raju
@ 2020-10-19 16:07         ` Krishna Chaitanya
  2020-10-19 16:40           ` Srinivasan Raju
  0 siblings, 1 reply; 108+ messages in thread
From: Krishna Chaitanya @ 2020-10-19 16:07 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

On Mon, Oct 19, 2020 at 4:01 PM Srinivasan Raju <srini.raju@purelifi.com> wrote:
>
> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
>
> This driver implementation has been based on the zd1211rw driver.
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
>
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
>
> Reported-by: kernel test robot <lkp@intel.com>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
> ---
>  MAINTAINERS                            |    5 +
>  drivers/net/wireless/Kconfig           |    1 +
>  drivers/net/wireless/Makefile          |    1 +
>  drivers/net/wireless/purelifi/Kconfig  |   27 +
>  drivers/net/wireless/purelifi/Makefile |    3 +
>  drivers/net/wireless/purelifi/chip.c   |   94 ++
>  drivers/net/wireless/purelifi/chip.h   |   82 ++
>  drivers/net/wireless/purelifi/intf.h   |   38 +
>  drivers/net/wireless/purelifi/mac.c    |  860 +++++++++++++
>  drivers/net/wireless/purelifi/mac.h    |  180 +++
>  drivers/net/wireless/purelifi/usb.c    | 1627 ++++++++++++++++++++++++
>  drivers/net/wireless/purelifi/usb.h    |  148 +++
>  12 files changed, 3066 insertions(+)
>  create mode 100644 drivers/net/wireless/purelifi/Kconfig
>  create mode 100644 drivers/net/wireless/purelifi/Makefile
>  create mode 100644 drivers/net/wireless/purelifi/chip.c
>  create mode 100644 drivers/net/wireless/purelifi/chip.h
>  create mode 100644 drivers/net/wireless/purelifi/intf.h
>  create mode 100644 drivers/net/wireless/purelifi/mac.c
>  create mode 100644 drivers/net/wireless/purelifi/mac.h
>  create mode 100644 drivers/net/wireless/purelifi/usb.c
>  create mode 100644 drivers/net/wireless/purelifi/usb.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c80f87d7258c..150f592fb6e4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14108,6 +14108,11 @@ T:     git git://linuxtv.org/media_tree.git
>  F:     Documentation/admin-guide/media/pulse8-cec.rst
>  F:     drivers/media/cec/usb/pulse8/
>
> +PUREILIFI USB DRIVER
> +M:     Srinivasan Raju <srini.raju@purelifi.com>
> +S:     Maintained
> +F:     drivers/net/wireless/purelifi
> +
>  PVRUSB2 VIDEO4LINUX DRIVER
>  M:     Mike Isely <isely@pobox.com>
>  L:     pvrusb2@isely.net       (subscribers-only)
> diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
> index 170a64e67709..b87da3139f94 100644
> --- a/drivers/net/wireless/Kconfig
> +++ b/drivers/net/wireless/Kconfig
> @@ -48,6 +48,7 @@ source "drivers/net/wireless/st/Kconfig"
>  source "drivers/net/wireless/ti/Kconfig"
>  source "drivers/net/wireless/zydas/Kconfig"
>  source "drivers/net/wireless/quantenna/Kconfig"
> +source "drivers/net/wireless/purelifi/Kconfig"
>
>  config PCMCIA_RAYCS
>         tristate "Aviator/Raytheon 2.4GHz wireless support"
> diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
> index 80b324499786..e9fc770026f0 100644
> --- a/drivers/net/wireless/Makefile
> +++ b/drivers/net/wireless/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
>  obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
>  obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
>  obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
> +obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
>
>  # 16-bit wireless PCMCIA client drivers
>  obj-$(CONFIG_PCMCIA_RAYCS)     += ray_cs.o
> diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
> new file mode 100644
> index 000000000000..f6630791df9d
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/Kconfig
> @@ -0,0 +1,27 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config WLAN_VENDOR_PURELIFI
> +       bool "pureLiFi devices"
> +       default y
> +       help
> +         If you have a pureLiFi device, say Y.
> +
> +         Note that the answer to this question doesn't directly affect the
> +         kernel: saying N will just cause the configurator to skip all the
> +         questions about these cards. If you say Y, you will be asked for
> +         your specific card in the following questions.
> +
> +if WLAN_VENDOR_PURELIFI
> +
> +config PURELIFI
> +
> +       tristate "pureLiFi device support"
> +       depends on CFG80211 && MAC80211 && USB
> +       help
> +          This driver makes the adapter appear as a normal WLAN interface.
> +
> +          The pureLiFi device requires external STA firmware to be loaded.
> +
> +          To compile this driver as a module, choose M here: the module will
> +          be called purelifi.
> +
> +endif # WLAN_VENDOR_PURELIFI
> diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
> new file mode 100644
> index 000000000000..1f20055e741f
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_PURELIFI)         := purelifi.o
> +purelifi-objs          += chip.o usb.o mac.o
> diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
> new file mode 100644
> index 000000000000..4282ce1d92b4
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/chip.c
> @@ -0,0 +1,94 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +
> +#include "chip.h"
> +#include "mac.h"
> +#include "usb.h"
> +
> +void purelifi_chip_init(struct purelifi_chip *chip,
> +                       struct ieee80211_hw *hw,
> +                       struct usb_interface *intf)
> +{
> +       memset(chip, 0, sizeof(*chip));
> +       mutex_init(&chip->mutex);
> +       purelifi_usb_init(&chip->usb, hw, intf);
> +}
> +
> +void purelifi_chip_release(struct purelifi_chip *chip)
> +{
> +       purelifi_usb_release(&chip->usb);
> +       mutex_destroy(&chip->mutex);
> +}
> +
> +int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
> +                                u8 dtim_period, int type)
> +{
> +       if (!interval || (chip->beacon_set &&
> +                         chip->beacon_interval == interval)) {
> +               return 0;
> +       }
> +
> +       chip->beacon_interval = interval;
> +       chip->beacon_set = true;
> +       return usb_write_req((const u8 *)&chip->beacon_interval,
> +                            sizeof(chip->beacon_interval),
> +                            USB_REQ_BEACON_INTERVAL_WR);
> +}
> +
> +int purelifi_chip_init_hw(struct purelifi_chip *chip)
> +{
> +       u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
> +       struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
> +
> +       pr_info("purelifi chip %02x:%02x v%02x  %02x-%02x-%02x %s\n",
> +               le16_to_cpu(udev->descriptor.idVendor),
> +               le16_to_cpu(udev->descriptor.idProduct),
> +               le16_to_cpu(udev->descriptor.bcdDevice),
> +               addr[0], addr[1], addr[2],
> +               purelifi_speed(udev->speed));
> +
> +       return purelifi_set_beacon_interval(chip, 100, 0, 0);
> +}
> +
> +int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
> +{
> +       int r;
> +
> +       r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
> +       if (r)
> +               dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
> +       return r;
> +}
> +
> +int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
> +{
> +       purelifi_usb_enable_tx(&chip->usb);
> +       return purelifi_usb_enable_rx(&chip->usb);
> +}
> +
> +void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
> +{
> +       u8 value;
> +
> +       value = 0;
> +       usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_RXTX_WR);
> +       purelifi_usb_disable_rx(&chip->usb);
> +       purelifi_usb_disable_tx(&chip->usb);
> +}
> +
> +int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
> +{
> +       int r;
> +
> +       if (!chip)
> +               return -EINVAL;
> +
> +       r = usb_write_req((const u8 *)&rate, sizeof(rate), USB_REQ_RATE_WR);
> +       if (r)
> +               dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
> +       return r;
> +}
> +
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/net/wireless/purelifi/chip.h b/drivers/net/wireless/purelifi/chip.h
> new file mode 100644
> index 000000000000..f479f34a6503
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/chip.h
> @@ -0,0 +1,82 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef _LF_X_CHIP_H
> +#define _LF_X_CHIP_H
> +
> +#include <net/mac80211.h>
> +
> +#include "usb.h"
> +
> +enum unit_type_t {
> +       STA = 0,
> +       AP = 1,
> +};
> +
> +struct purelifi_chip {
> +       struct purelifi_usb usb;
> +       struct mutex mutex; /* lock to protect chip data */
> +       enum unit_type_t unit_type;
> +       u16 link_led;
> +       u8  beacon_set;
> +       u16 beacon_interval;
> +};
> +
> +struct purelifi_mc_hash {
> +       u32 low;
> +       u32 high;
> +};
> +
> +#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
> +
> +void purelifi_chip_init(struct purelifi_chip *chip,
> +                       struct ieee80211_hw *hw,
> +                       struct usb_interface *intf);
> +
> +void purelifi_chip_release(struct purelifi_chip *chip);
> +
> +void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
> +
> +int purelifi_chip_init_hw(struct purelifi_chip *chip);
> +
> +int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
> +
> +int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
> +
> +int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
> +                                u8 dtim_period, int type);
> +
> +int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value);
> +
> +static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
> +                                                        *usb)
> +{
> +       return container_of(usb, struct purelifi_chip, usb);
> +}
> +
> +static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
> +{
> +       hash->low = 0;
> +       /* The interfaces must always received broadcasts.
> +        * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
> +        */
> +       hash->high = 0x80000000;
> +}
> +
> +static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
> +{
> +       hash->low = 0xffffffff;
> +       hash->high = 0xffffffff;
> +}
> +
> +static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
> +                                       u8 *addr
> +                                       )
> +{
> +       unsigned int i = addr[5] >> 2;
> +
> +       if (i < 32)
> +               hash->low |= 1 << i;
> +       else
> +               hash->high |= 1 << (i - 32);
> +}
> +#endif /* _LF_X_CHIP_H */
> diff --git a/drivers/net/wireless/purelifi/intf.h b/drivers/net/wireless/purelifi/intf.h
> new file mode 100644
> index 000000000000..20e96671e0a7
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/intf.h
> @@ -0,0 +1,38 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef _PURELIFI_MAC_DRIVER_USB_INTERFACE
> +#define _PURELIFI_MAC_DRIVER_USB_INTERFACE
> +#define PURELIFI_BYTE_NUM_ALIGNMENT 4
> +#ifndef ETH_ALEN
> +#define ETH_ALEN 6
> +#endif
> +#define AP_USER_LIMIT 8
> +
> +struct rx_status {
> +       u16 rssi;
> +       u8  rate_idx;
> +       u8  pad;
> +       u64 crc_error_count;
> +} __packed;
> +
> +enum usb_req_enum_t {
> +       USB_REQ_TEST_WR            = 0,
> +       USB_REQ_MAC_WR             = 1,
> +       USB_REQ_POWER_WR           = 2,
> +       USB_REQ_RXTX_WR            = 3,
> +       USB_REQ_BEACON_WR          = 4,
> +       USB_REQ_BEACON_INTERVAL_WR = 5,
> +       USB_REQ_RTS_CTS_RATE_WR    = 6,
> +       USB_REQ_HASH_WR            = 7,
> +       USB_REQ_DATA_TX            = 8,
> +       USB_REQ_RATE_WR            = 9,
> +       USB_REQ_SET_FREQ           = 15
> +};
> +
> +struct usb_req_t {
> +       enum usb_req_enum_t id;
> +       u32            len;
> +       u8             buf[512];
> +};
> +
> +#endif
> diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
> new file mode 100644
> index 000000000000..3fb280820950
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/mac.c
> @@ -0,0 +1,860 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/slab.h>
> +#include <linux/usb.h>
> +#include <linux/gpio.h>
> +#include <linux/jiffies.h>
> +#include <net/ieee80211_radiotap.h>
> +
> +#include "chip.h"
> +#include "mac.h"
> +#include "usb.h"
> +
> +#ifndef IEEE80211_BAND_2GHZ
> +#define IEEE80211_BAND_2GHZ NL80211_BAND_2GHZ
> +#endif
Why can't we directly use the NL80211 define? and
in-tree drivers needn't worry about kernel compat.
> +
> +static const struct ieee80211_rate purelifi_rates[] = {
> +       { .bitrate = 10,
> +               .hw_value = PURELIFI_CCK_RATE_1M,
> +               .flags = 0 },
> +       { .bitrate = 20,
> +               .hw_value = PURELIFI_CCK_RATE_2M,
> +               .hw_value_short = PURELIFI_CCK_RATE_2M
> +                       | PURELIFI_CCK_PREA_SHORT,
> +               .flags = IEEE80211_RATE_SHORT_PREAMBLE },
> +       { .bitrate = 55,
> +               .hw_value = PURELIFI_CCK_RATE_5_5M,
> +               .hw_value_short = PURELIFI_CCK_RATE_5_5M
> +                       | PURELIFI_CCK_PREA_SHORT,
> +               .flags = IEEE80211_RATE_SHORT_PREAMBLE },
> +       { .bitrate = 110,
> +               .hw_value = PURELIFI_CCK_RATE_11M,
> +               .hw_value_short = PURELIFI_CCK_RATE_11M
> +                       | PURELIFI_CCK_PREA_SHORT,
> +               .flags = IEEE80211_RATE_SHORT_PREAMBLE },
> +       { .bitrate = 60,
> +               .hw_value = PURELIFI_OFDM_RATE_6M,
> +               .flags = 0 },
> +       { .bitrate = 90,
> +               .hw_value = PURELIFI_OFDM_RATE_9M,
> +               .flags = 0 },
> +       { .bitrate = 120,
> +               .hw_value = PURELIFI_OFDM_RATE_12M,
> +               .flags = 0 },
> +       { .bitrate = 180,
> +               .hw_value = PURELIFI_OFDM_RATE_18M,
> +               .flags = 0 },
> +       { .bitrate = 240,
> +               .hw_value = PURELIFI_OFDM_RATE_24M,
> +               .flags = 0 },
> +       { .bitrate = 360,
> +               .hw_value = PURELIFI_OFDM_RATE_36M,
> +               .flags = 0 },
> +       { .bitrate = 480,
> +               .hw_value = PURELIFI_OFDM_RATE_48M,
> +               .flags = 0 },
> +       { .bitrate = 540,
> +               .hw_value = PURELIFI_OFDM_RATE_54M,
> +               .flags = 0 },
> +};
> +
> +static const struct ieee80211_channel purelifi_channels[] = {
> +       { .center_freq = 2412, .hw_value = 1 },
> +       { .center_freq = 2417, .hw_value = 2 },
> +       { .center_freq = 2422, .hw_value = 3 },
> +       { .center_freq = 2427, .hw_value = 4 },
> +       { .center_freq = 2432, .hw_value = 5 },
> +       { .center_freq = 2437, .hw_value = 6 },
> +       { .center_freq = 2442, .hw_value = 7 },
> +       { .center_freq = 2447, .hw_value = 8 },
> +       { .center_freq = 2452, .hw_value = 9 },
> +       { .center_freq = 2457, .hw_value = 10 },
> +       { .center_freq = 2462, .hw_value = 11 },
> +       { .center_freq = 2467, .hw_value = 12 },
> +       { .center_freq = 2472, .hw_value = 13 },
> +       { .center_freq = 2484, .hw_value = 14 },
Does the chip has the ability to follow the regulatory rules for 13 and 14?
Esp. the default hint given is CA.
> +};
> +
> +static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
> +                                     struct sk_buff *beacon, bool in_intr);
> +
> +int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
> +{
> +       SET_IEEE80211_PERM_ADDR(hw, hw_address);
> +       return 0;
> +}
> +
> +void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
> +{
> +       if (block)
> +               ieee80211_stop_queues(purelifi_usb_to_hw(usb));
> +       else
> +               ieee80211_wake_queues(purelifi_usb_to_hw(usb));
> +}
> +
> +int purelifi_mac_init_hw(struct ieee80211_hw *hw)
> +{
> +       int r;
> +       struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +       struct purelifi_chip *chip = &mac->chip;
> +
> +       r = purelifi_chip_init_hw(chip);
> +       if (r) {
> +               dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
> +               return r;
> +       }
> +
> +       dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
> +
> +       r = regulatory_hint(hw->wiphy, "CA");
> +       return r;
> +}
> +
> +void purelifi_mac_release(struct purelifi_mac *mac)
> +{
> +       purelifi_chip_release(&mac->chip);
> +       lockdep_assert_held(&mac->lock);
> +}
> +
> +int purelifi_op_start(struct ieee80211_hw *hw)
> +{
> +       purelifi_hw_mac(hw)->chip.usb.initialized = 1;
> +       return 0;
> +}
> +
> +void purelifi_op_stop(struct ieee80211_hw *hw)
> +{
> +       struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +
> +       clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
> +}
> +
> +int purelifi_restore_settings(struct purelifi_mac *mac)
> +{
> +       struct sk_buff *beacon;
> +       int beacon_interval, beacon_period;
> +
> +       spin_lock_irq(&mac->lock);
> +       beacon_interval = mac->beacon.interval;
> +       beacon_period = mac->beacon.period;
> +       spin_unlock_irq(&mac->lock);
> +
> +       if (mac->type != NL80211_IFTYPE_ADHOC)
> +               return 0;
> +
> +       if (mac->vif) {
> +               beacon = ieee80211_beacon_get(mac->hw, mac->vif);
> +               if (beacon) {
> +                       purelifi_mac_config_beacon(mac->hw, beacon, false);
> +                       kfree_skb(beacon);
> +                       /* Returned skb is used only once and lowlevel
> +                        *  driver is responsible for freeing it.
> +                        */
> +               }
> +       }
> +
> +       purelifi_set_beacon_interval(&mac->chip, beacon_interval,
> +                                    beacon_period, mac->type);
> +
> +       spin_lock_irq(&mac->lock);
> +       mac->beacon.last_update = jiffies;
> +       spin_unlock_irq(&mac->lock);
> +
> +       return 0;
> +}
> +
> +/**
> + * purelifi_mac_tx_status - reports tx status of a packet if required
> + * @hw - a &struct ieee80211_hw pointer
> + * @skb - a sk-buffer
> + * @flags: extra flags to set in the TX status info
> + * @ackssi: ACK signal strength
> + * @success - True for successful transmission of the frame
> + *
> + * This information calls ieee80211_tx_status_irqsafe() if required by the
> + * control information. It copies the control information into the status
> + * information.
> + *
> + * If no status information has been requested, the skb is freed.
> + */
> +static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
> +                                  struct sk_buff *skb,
> +                                  int ackssi,
> +                                  struct tx_status *tx_status)
> +{
> +       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +       int success = 1, retry = 1;
> +       struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +
> +       ieee80211_tx_info_clear_status(info);
> +
> +       if (tx_status) {
> +               success = !tx_status->failure;
> +               retry = tx_status->retry + success;
> +       }
> +
> +       if (success)
> +               info->flags |= IEEE80211_TX_STAT_ACK;
> +       else
> +               info->flags &= ~IEEE80211_TX_STAT_ACK;
> +
> +       dev_info(purelifi_mac_dev(mac), "retry=%d\n", retry);
> +       info->status.ack_signal = 50;
> +       ieee80211_tx_status_irqsafe(hw, skb);
> +}
> +
> +/**
> + * purelifi_mac_tx_to_dev - callback for USB layer
> + * @skb: a &sk_buff pointer
> + * @error: error value, 0 if transmission successful
> + *
> + * Informs the MAC layer that the frame has successfully transferred to the
> + * device. If an ACK is required and the transfer to the device has been
> + * successful, the packets are put on the @ack_wait_queue with
> + * the control set removed.
> + */
> +void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
> +{
> +       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +       struct ieee80211_hw *hw = info->rate_driver_data[0];
> +       struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +
> +       ieee80211_tx_info_clear_status(info);
> +       skb_pull(skb, sizeof(struct purelifi_ctrlset));
> +
> +       if (unlikely(error ||
> +                    (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
> +               ieee80211_tx_status_irqsafe(hw, skb);
> +       } else {
> +               /* ieee80211_tx_status_irqsafe(hw, skb); */
> +               struct sk_buff_head *q = &mac->ack_wait_queue;
> +
> +               skb_queue_tail(q, skb);
> +               while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
> +                       purelifi_mac_tx_status(hw, skb_dequeue(q),
> +                                              mac->ack_pending ?
> +                                              mac->ack_signal : 0,
> +                                              NULL);
> +                       mac->ack_pending = 0;
> +               }
> +       }
> +}
> +
> +static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
> +                                     struct sk_buff *beacon, bool in_intr)
> +{
> +       return usb_write_req((const u8 *)beacon->data, beacon->len,
> +                       USB_REQ_BEACON_WR);
> +}
> +
> +static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
> +{
> +       unsigned int frag_len = skb->len;
> +       unsigned int tmp;
> +       struct purelifi_ctrlset *cs;
> +
> +       if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
> +               dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
> +               return 1;
> +       }
> +
> +       cs = (struct purelifi_ctrlset *)skb_push(skb,
> +                       sizeof(struct purelifi_ctrlset));
> +       cs->id = USB_REQ_DATA_TX;
> +       cs->payload_len_nw = frag_len;
> +       cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
> +               - sizeof(cs->id) - sizeof(cs->len);
> +
> +       /* data packet lengths must be multiple of four bytes
> +        * and must not be a multiple of 512
> +        * bytes. First, it is attempted to append the
> +        * data packet in the tailroom of the skb. In rare
> +        * ocasions, the tailroom is too small. In this case,
> +        * the content of the packet is shifted into
> +        * the headroom of the skb by memcpy. Headroom is allocated
> +        * at startup (below in this file). Therefore,
> +        * there will be always enough headroom. The call skb_headroom
> +        * is an additional safety which might be
> +        * dropped.
> +        */
> +
> +       /*check if 32 bit aligned and align data*/
> +       tmp = skb->len & 3;
> +       if (tmp) {
> +               if (skb_tailroom(skb) < (3 - tmp)) {
> +                       if (skb_headroom(skb) >= 4 - tmp) {
> +                               u8 len;
> +                               u8 *src_pt;
> +                               u8 *dest_pt;
> +
> +                               len = skb->len;
> +                               src_pt = skb->data;
> +                               dest_pt = skb_push(skb, 4 - tmp);
> +                               memcpy(dest_pt, src_pt, len);
> +                       } else {
> +                               return 1;
> +                       }
> +               } else {
> +                       skb_put(skb, 4 - tmp);
> +               }
> +               cs->len +=  4 - tmp;
> +       }
> +
> +       /* check if not multiple of 512 and align data*/
> +       tmp = skb->len & 0x1ff;
> +       if (!tmp) {
> +               if (skb_tailroom(skb) < 4) {
> +                       if (skb_headroom(skb) >= 4) {
> +                               u8 len = skb->len;
> +                               u8 *src_pt = skb->data;
> +                               u8 *dest_pt = skb_push(skb, 4);
> +
> +                               memcpy(dest_pt, src_pt, len);
> +                       } else {
> +                               /* should never happen b/c
> +                                * sufficient headroom was reserved
> +                                */
> +                               return 1;
> +                       }
> +               } else {
> +                       skb_put(skb, 4);
> +               }
> +               cs->len +=  4;
> +       }
> +
> +       cs->id = TO_NETWORK(cs->id);
> +       cs->len = TO_NETWORK(cs->len);
> +       cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
> +
> +       return 0;
> +}
> +
> +/**
> + * purelifi_op_tx - transmits a network frame to the device
> + *
> + * @dev: mac80211 hardware device
> + * @skb: socket buffer
> + * @control: the control structure
> + *
> + * This function transmit an IEEE 802.11 network frame to the device. The
> + * control block of the skbuff will be initialized. If necessary the incoming
> + * mac80211 queues will be stopped.
> + */
> +static void purelifi_op_tx(struct ieee80211_hw *hw,
> +                          struct ieee80211_tx_control *control,
> +                          struct sk_buff *skb)
> +{
> +       struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +       struct purelifi_usb *usb = &mac->chip.usb;
> +       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +       unsigned long flags;
> +       int r;
> +
> +       r = fill_ctrlset(mac, skb);
> +       if (r)
> +               goto fail;
> +       info->rate_driver_data[0] = hw;
> +
> +       if (skb->data[24] != 0x08 && skb->len == 116)
> +               dev_info(purelifi_mac_dev(mac), "%s ping\n", __func__);
> +
> +       if (skb->data[24] == 0x08) {
Can use ieee80211_get_qos_ctrl and its related flags instead of these
magic numbers,
Should also take care of endianness here.
> +               u8 dst_mac[ETH_ALEN];
> +               u8 sidx;
> +               bool found = false;
> +               struct purelifi_usb_tx *tx = &usb->tx;
> +
> +               memcpy(dst_mac, &skb->data[28], ETH_ALEN);
> +               for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
> +                       if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
> +                               continue;
> +                       if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
> +                               found = true;
> +                               break;
> +                       }
> +               }
> +
> +               /* Default to broadcast address for unknown MACs */
> +               if (!found)
> +                       sidx = STA_BROADCAST_INDEX;
> +
> +               /* Stop OS from sending packets, if the queue is half full */
> +               if (skb_queue_len(&tx->station[sidx].data_list) > 60)
> +                       block_queue(usb, tx->station[sidx].mac, true);
> +
> +               /* Schedule packet for transmission if queue is not full */
> +               if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
> +                       skb_queue_tail(&tx->station[sidx].data_list, skb);
> +                       purelifi_send_packet_from_data_queue(usb);
> +               } else {
> +                       dev_kfree_skb(skb);
> +               }
> +       } else {
> +               spin_lock_irqsave(&usb->tx.lock, flags);
> +               r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
> +                                       USB_REQ_DATA_TX, tx_urb_complete, skb);
> +               spin_unlock_irqrestore(&usb->tx.lock, flags);
> +               if (r)
> +                       goto fail;
> +       }
> +       return;
> +
> +fail:
> +       dev_kfree_skb(skb);
> +}
> +
> +/**
> + * filter_ack - filters incoming packets for acknowledgements
> + * @dev: the mac80211 device
> + * @rx_hdr: received header
> + * @stats: the status for the received packet
> + *
> + * This functions looks for ACK packets and tries to match them with the
> + * frames in the tx queue. If a match is found the frame will be dequeued and
> + * the upper layers is informed about the successful transmission. If
> + * mac80211 queues have been stopped and the number of frames still to be
> + * transmitted is low the queues will be opened again.
> + *
> + * Returns 1 if the frame was an ACK, 0 if it was ignored.
> + */
> +static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
> +                     struct ieee80211_rx_status *stats)
> +{
> +       struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +       struct sk_buff *skb;
> +       struct sk_buff_head *q;
> +       unsigned long flags;
> +       int found = 0;
> +       int i, position = 0;
> +
> +       if (!ieee80211_is_ack(rx_hdr->frame_control))
> +               return 0;
> +       dev_info(purelifi_mac_dev(mac), "%s ACK Received\n", __func__);
> +       return 0;
Is this a mistake?
> +
> +       q = &mac->ack_wait_queue;
> +       spin_lock_irqsave(&q->lock, flags);
> +
> +       skb_queue_walk(q, skb) {
> +               struct ieee80211_hdr *tx_hdr;
> +
> +               position++;
> +
> +               if (mac->ack_pending && skb_queue_is_first(q, skb))
> +                       continue;
> +
Shouldn't we break if ack_pending == 0 here?

> +               tx_hdr = (struct ieee80211_hdr *)skb->data;
> +               if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
> +                       found = 1;
> +                       break;
> +               }
> +       }
> +
> +       if (found) {
> +               for (i = 1; i < position; i++) {
> +                       skb = __skb_dequeue(q);
> +                       purelifi_mac_tx_status(hw, skb,
> +                                              mac->ack_pending ?
> +                                              mac->ack_signal : 0,
Shouldn't we use if (i == position) check?
> +                                              NULL);
> +                       mac->ack_pending = 0;
> +               }
> +
> +               mac->ack_pending = 1;
Shouldn't this be below?
mac->ack_pending = skb_queue_len(ack_wait_queue) ? 1 : 0
> +               mac->ack_signal = stats->signal;
> +       }
The logic here is confusing, we have a global ack_pending status which
is set based on
each SKB? IIUC, if we find an SKB with matching ACK, then all previous
frames are assumed to be
lost and are passed up the stack.

> +
> +       spin_unlock_irqrestore(&q->lock, flags);
> +       return 1;
> +}
> +
> +int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
> +                   unsigned int length)
> +{
> +       struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +       struct ieee80211_rx_status stats;
should be status.
> +       const struct rx_status *status;
> +       struct sk_buff *skb;
> +       int bad_frame = 0;
> +       __le16 fc;
> +       int need_padding;
> +       unsigned int payload_length;
> +       static unsigned short int min_exp_seq_nmb;
> +       u32 crc_error_cnt_low, crc_error_cnt_high;
> +       int sidx;
> +       struct purelifi_usb_tx *tx;
> +
> +       /* Packet blockade during disabled interface. */
> +       if (!mac->vif)
> +               return 0;
> +
> +       memset(&stats, 0, sizeof(stats));
> +       status = (struct rx_status *)buffer;
> +
> +       stats.flag     = 0;
> +       stats.freq     = 2412;
No channel information from the Chip?
> +       stats.band     = IEEE80211_BAND_2GHZ;
> +       mac->rssi      = -15 * ntohs(status->rssi) / 10;
> +
> +       stats.signal   = mac->rssi;
> +
> +       if (status->rate_idx > 7)
> +               stats.rate_idx = 0;
> +       else
> +               stats.rate_idx = status->rate_idx;
> +
> +       crc_error_cnt_low = status->crc_error_count;
> +       crc_error_cnt_high = status->crc_error_count >> 32;
> +       mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
> +               ntohl(crc_error_cnt_high);
> +
> +       if (!bad_frame &&
> +           filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
> +           !mac->pass_ctrl)
> +               return 0;
> +
> +       buffer += sizeof(struct rx_status);
> +       payload_length = TO_HOST(*(u32 *)buffer);
> +
> +       /* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
> +       if (payload_length > 1560) {
> +               dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
> +               return 0;
> +       }
> +       buffer += sizeof(u32);
> +
> +       fc = get_unaligned((__le16 *)buffer);
> +       need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
> +
> +       tx = &mac->chip.usb.tx;
> +
> +       for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
> +               if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
> +                       continue;
> +               if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
> +                       tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
> +                       break;
> +               }
> +       }
> +
> +       if (sidx == MAX_STA_NUM - 1) {
> +               for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
> +                       if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
> +                               continue;
> +                       memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
> +                       tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
> +                       tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
> +                       break;
> +               }
> +       }
> +
> +       if (buffer[0] == 0x40)
> +               dev_info(purelifi_mac_dev(mac), "Probe request\n");
> +       else if (buffer[0] == 0x0)
> +               dev_info(purelifi_mac_dev(mac), "Association request\n");
> +
> +       if (buffer[0] == 0xb0) {
> +               dev_info(purelifi_mac_dev(mac), "Authentication req\n");
> +               min_exp_seq_nmb = 0;
> +       } else if (buffer[0] == 0x08) {
> +               unsigned short int seq_nmb = (buffer[23] << 4) |
> +                       ((buffer[22] & 0xF0) >> 4);
> +
Avoid magic numbers and use IEEE80211 macros of helpers wherever applicable.
> +               if (seq_nmb < min_exp_seq_nmb &&
> +                   ((min_exp_seq_nmb - seq_nmb) < 3000)) {
> +                       dev_info(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
> +               } else {
> +                       min_exp_seq_nmb = (seq_nmb + 1) % 4096;
> +               }
> +       }
> +
> +       skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
> +       if (!skb)
> +               return -ENOMEM;
> +       if (need_padding) {
> +               /* Make sure that the payload data is 4 byte aligned. */
> +               skb_reserve(skb, 2);
> +       }
> +
> +       /* FIXME : could we avoid this big memcpy ? */
Pre-allocate an RX ring buffer?
> +       memcpy(skb_put(skb, payload_length), buffer, payload_length);
> +       memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
> +       ieee80211_rx_irqsafe(hw, skb);
> +       return 0;
> +}
> +
> +static int purelifi_op_add_interface(struct ieee80211_hw *hw,
> +                                    struct ieee80211_vif *vif)
> +{
> +       struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +       static const char * const iftype80211[] = {
> +               [NL80211_IFTYPE_STATION]        = "Station",
> +               [NL80211_IFTYPE_ADHOC]          = "Adhoc"
> +       };
> +
> +       if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
> +               return -EOPNOTSUPP;
> +
> +       if (vif->type == NL80211_IFTYPE_ADHOC ||
> +           vif->type == NL80211_IFTYPE_STATION) {
> +               dev_info(purelifi_mac_dev(mac), "%s %s\n", __func__,
> +                        iftype80211[vif->type]);
> +               mac->type = vif->type;
> +               mac->vif = vif;
> +       } else {
> +               dev_info(purelifi_mac_dev(mac), "unsupported iftype\n");
> +               return -EOPNOTSUPP;
> +       }
> +
> +       return 0;
> +}
> +
> +static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
> +                                        struct ieee80211_vif *vif)
> +{
> +       struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +
> +       mac->type = NL80211_IFTYPE_UNSPECIFIED;
> +       mac->vif = NULL;
> +}
> +
> +static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
> +{
> +       struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +
> +       spin_lock_irq(&mac->lock);
> +       spin_unlock_irq(&mac->lock);
> +
Can remove ?
> +       return 0;
> +}
> +
> +#define SUPPORTED_FIF_FLAGS \
> +       (FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
> +        FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
> +static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
> +                                        unsigned int changed_flags,
> +                                        unsigned int *new_flags,
> +                                        u64 multicast)
> +{
> +       struct purelifi_mc_hash hash = {
> +               .low = multicast,
> +               .high = multicast >> 32,
> +       };
> +       struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +       unsigned long flags;
> +
> +       /* Only deal with supported flags */
> +       changed_flags &= SUPPORTED_FIF_FLAGS;
> +       *new_flags &= SUPPORTED_FIF_FLAGS;
> +
> +       /* If multicast parameter
> +        * (as returned by purelifi_op_prepare_multicast)
> +        * has changed, no bit in changed_flags is set. To handle this
> +        * situation, we do not return if changed_flags is 0. If we do so,
> +        * we will have some issue with IPv6 which uses multicast for link
> +        * layer address resolution.
> +        */
> +       if (*new_flags & (FIF_ALLMULTI))
> +               purelifi_mc_add_all(&hash);
> +
> +       spin_lock_irqsave(&mac->lock, flags);
> +       mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
> +       mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
> +       mac->multicast_hash = hash;
> +       spin_unlock_irqrestore(&mac->lock, flags);
> +
> +       /* no handling required for FIF_OTHER_BSS as we don't currently
> +        * do BSSID filtering
> +        */
> +       /* FIXME: in future it would be nice to enable the probe response
> +        * filter (so that the driver doesn't see them) until
> +        * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
> +        * have to schedule work to enable prbresp reception, which might
> +        * happen too late. For now we'll just listen and forward them all the
> +        * time.
> +        */
> +}
> +
> +static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
> +                                        struct ieee80211_vif *vif,
> +                                        struct ieee80211_bss_conf *bss_conf,
> +                                        u32 changes)
> +{
> +       struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +       int associated;
> +
> +       dev_info(purelifi_mac_dev(mac), "changes: %x\n", changes);
> +
> +       if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
> +               associated = is_valid_ether_addr(bss_conf->bssid);
> +               goto exit_all;
> +       }
> +       /* for ADHOC */
> +       associated = true;
> +       if (changes & BSS_CHANGED_BEACON) {
> +               struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
> +
> +               if (beacon) {
> +                       purelifi_mac_config_beacon(hw, beacon, false);
> +                       kfree_skb(beacon);
> +                       /*Returned skb is used only once and
> +                        * low-level driver is
> +                        * responsible for freeing it.
> +                        */
> +               }
> +       }
> +
> +       if (changes & BSS_CHANGED_BEACON_ENABLED) {
> +               u16 interval = 0;
> +               u8 period = 0;
> +
> +               if (bss_conf->enable_beacon) {
> +                       period = bss_conf->dtim_period;
> +                       interval = bss_conf->beacon_int;
> +               }
> +
> +               spin_lock_irq(&mac->lock);
> +               mac->beacon.period = period;
> +               mac->beacon.interval = interval;
> +               mac->beacon.last_update = jiffies;
> +               spin_unlock_irq(&mac->lock);
> +
> +               purelifi_set_beacon_interval(&mac->chip, interval,
> +                                            period, mac->type);
> +       }
> +exit_all:
> +       spin_lock_irq(&mac->lock);
> +       mac->associated = associated;
> +       spin_unlock_irq(&mac->lock);
> +}
> +
> +static int purelifi_get_stats(struct ieee80211_hw *hw,
> +                             struct ieee80211_low_level_stats *stats)
> +{
> +       stats->dot11ACKFailureCount = 0;
> +       stats->dot11RTSFailureCount = 0;
> +       stats->dot11FCSErrorCount = 0;
> +       stats->dot11RTSSuccessCount = 0;
> +       return 0;
> +}
> +
> +const char et_strings[][ETH_GSTRING_LEN] = {
> +       "phy_rssi",
> +       "phy_rx_crc_err"
> +};
> +
> +static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
> +                                     struct ieee80211_vif *vif, int sset)
> +{
> +       if (sset == ETH_SS_STATS)
> +               return ARRAY_SIZE(et_strings);
> +
> +       return 0;
> +}
> +
> +static void purelifi_get_et_strings(struct ieee80211_hw *hw,
> +                                   struct ieee80211_vif *vif,
> +                                   u32 sset, u8 *data)
> +{
> +       if (sset == ETH_SS_STATS)
> +               memcpy(data, *et_strings, sizeof(et_strings));
> +}
> +
> +static void purelifi_get_et_stats(struct ieee80211_hw *hw,
> +                                 struct ieee80211_vif *vif,
> +                                 struct ethtool_stats *stats, u64 *data)
> +{
> +       struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +
> +       data[0] = mac->rssi;
> +       data[1] = mac->crc_errors;
> +}
> +
> +static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
> +{
> +       return 0;
> +}
> +
> +static const struct ieee80211_ops purelifi_ops = {
> +       .tx                 = purelifi_op_tx,
> +       .start              = purelifi_op_start,
> +       .stop               = purelifi_op_stop,
> +       .add_interface      = purelifi_op_add_interface,
> +       .remove_interface   = purelifi_op_remove_interface,
> +       .set_rts_threshold  = purelifi_set_rts_threshold,
> +       .config             = purelifi_op_config,
> +       .configure_filter   = purelifi_op_configure_filter,
> +       .bss_info_changed   = purelifi_op_bss_info_changed,
> +       .get_stats          = purelifi_get_stats,
> +       .get_et_sset_count  = purelifi_get_et_sset_count,
> +       .get_et_stats       = purelifi_get_et_stats,
> +       .get_et_strings     = purelifi_get_et_strings,
> +};
> +
> +struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
> +{
> +       struct purelifi_mac *mac;
> +       struct ieee80211_hw *hw;
> +
> +       hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
> +       if (!hw) {
> +               dev_dbg(&intf->dev, "out of memory\n");
> +               return NULL;
> +       }
> +       set_wiphy_dev(hw->wiphy, &intf->dev);
> +
> +       mac = purelifi_hw_mac(hw);
> +
> +       memset(mac, 0, sizeof(*mac));
> +       spin_lock_init(&mac->lock);
> +       mac->hw = hw;
> +
> +       mac->type = NL80211_IFTYPE_UNSPECIFIED;
> +
> +       memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
> +       memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
> +       mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
> +       mac->band.bitrates = mac->rates;
> +       mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
> +       mac->band.channels = mac->channels;
> +
> +       hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band;
> +
> +#ifdef ieee80211_hw_set
> +       _ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
> +       _ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
> +       _ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
> +       _ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);
> +#else
> +       hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
> +               IEEE80211_HW_SIGNAL_DBM |
> +               IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
> +               IEEE80211_HW_MFP_CAPABLE;
> +#endif
in-tree drivers needn't worry about kernel compat.
> +
> +       hw->wiphy->interface_modes =
> +               BIT(NL80211_IFTYPE_STATION) |
> +               BIT(NL80211_IFTYPE_ADHOC);
> +
> +       hw->max_signal = 100;
> +       hw->queues = 1;
> +       hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
> +       /* 4 for 32 bit alignment if no tailroom */
> +
> +       /* Tell mac80211 that we support multi rate retries */
> +       hw->max_rates = IEEE80211_TX_MAX_RATES;
> +       hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
> +
> +       skb_queue_head_init(&mac->ack_wait_queue);
> +       mac->ack_pending = 0;
> +
> +       purelifi_chip_init(&mac->chip, hw, intf);
> +
> +       SET_IEEE80211_DEV(hw, &intf->dev);
> +       return hw;
> +}
> +
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/net/wireless/purelifi/mac.h b/drivers/net/wireless/purelifi/mac.h
> new file mode 100644
> index 000000000000..cca1e70a6416
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/mac.h
> @@ -0,0 +1,180 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef _PURELIFI_MAC_H
> +#define _PURELIFI_MAC_H
> +
> +#include <linux/kernel.h>
> +#include <net/mac80211.h>
> +
> +#include "chip.h"
> +
> +#define PURELIFI_CCK                  0x00
> +#define PURELIFI_OFDM                 0x10
> +#define PURELIFI_CCK_PREA_SHORT       0x20
> +
> +#define PURELIFI_OFDM_PLCP_RATE_6M     0xb
> +#define PURELIFI_OFDM_PLCP_RATE_9M     0xf
> +#define PURELIFI_OFDM_PLCP_RATE_12M    0xa
> +#define PURELIFI_OFDM_PLCP_RATE_18M    0xe
> +#define PURELIFI_OFDM_PLCP_RATE_24M    0x9
> +#define PURELIFI_OFDM_PLCP_RATE_36M    0xd
> +#define PURELIFI_OFDM_PLCP_RATE_48M    0x8
> +#define PURELIFI_OFDM_PLCP_RATE_54M    0xc
> +
> +#define PURELIFI_CCK_RATE_1M   (PURELIFI_CCK | 0x00)
> +#define PURELIFI_CCK_RATE_2M   (PURELIFI_CCK | 0x01)
> +#define PURELIFI_CCK_RATE_5_5M (PURELIFI_CCK | 0x02)
> +#define PURELIFI_CCK_RATE_11M  (PURELIFI_CCK | 0x03)
> +#define PURELIFI_OFDM_RATE_6M  (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
> +#define PURELIFI_OFDM_RATE_9M  (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
> +#define PURELIFI_OFDM_RATE_12M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
> +#define PURELIFI_OFDM_RATE_18M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
> +#define PURELIFI_OFDM_RATE_24M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
> +#define PURELIFI_OFDM_RATE_36M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
> +#define PURELIFI_OFDM_RATE_48M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
> +#define PURELIFI_OFDM_RATE_54M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
> +
> +#define PURELIFI_RX_ERROR              0x80
> +#define PURELIFI_RX_CRC32_ERROR                0x10
> +
> +enum {
> +       MODULATION_RATE_BPSK_1_2 = 0,
> +       MODULATION_RATE_BPSK_3_4,
> +       MODULATION_RATE_QPSK_1_2,
> +       MODULATION_RATE_QPSK_3_4,
> +       MODULATION_RATE_QAM16_1_2,
> +       MODULATION_RATE_QAM16_3_4,
> +       MODULATION_RATE_QAM64_1_2,
> +       MODULATION_RATE_QAM64_3_4,
> +       MODULATION_RATE_AUTO,
> +       MODULATION_RATE_NUM
> +};
> +
> +#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
> +
> +#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
> +#define PURELIFI_MAC_MAX_ACK_WAITERS 50
> +
> +struct purelifi_ctrlset {
> +       enum usb_req_enum_t id;
> +       u32            len;
> +       u8             modulation;
> +       u8             control;
> +       u8             service;
> +       u8             pad;
> +       __le16         packet_length;
> +       __le16         current_length;
> +       __le16          next_frame_length;
> +       __le16         tx_length;
> +       u32            payload_len_nw;
> +} __packed;
> +
> +struct tx_status {
> +       u8 type;
> +       u8 id;
> +       u8 rate;
> +       u8 pad;
> +       u8 mac[ETH_ALEN];
> +       u8 retry;
> +       u8 failure;
> +} __packed;
> +
> +enum mac_flags {
> +       MAC_FIXED_CHANNEL = 0x01,
> +};
> +
> +struct beacon {
> +       struct delayed_work watchdog_work;
> +       struct sk_buff *cur_beacon;
> +       unsigned long last_update;
> +       u16 interval;
> +       u8 period;
> +};
> +
> +enum purelifi_device_flags {
> +       PURELIFI_DEVICE_RUNNING,
> +};
> +
> +struct purelifi_mac {
> +       struct purelifi_chip chip;
> +       spinlock_t lock; /* lock for mac data */
> +       spinlock_t intr_lock; /* not used : interrupt lock for mac */
> +       struct ieee80211_hw *hw;
> +       struct ieee80211_vif *vif;
> +       struct beacon beacon;
> +       struct work_struct set_rts_cts_work;
> +       struct work_struct process_intr;
> +       struct purelifi_mc_hash multicast_hash;
> +       u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
> +       u8 regdomain;
> +       u8 default_regdomain;
> +       u8 channel;
> +       int type;
> +       int associated;
> +       unsigned long flags;
> +       struct sk_buff_head ack_wait_queue;
> +       struct ieee80211_channel channels[14];
> +       struct ieee80211_rate rates[12];
> +       struct ieee80211_supported_band band;
> +
> +       /* whether to pass frames with CRC errors to stack */
> +       unsigned int pass_failed_fcs:1;
> +
> +       /* whether to pass control frames to stack */
> +       unsigned int pass_ctrl:1;
> +
> +       /* whether we have received a 802.11 ACK that is pending */
> +       unsigned int ack_pending:1;
Why not use a bool? from the naming it doesn't look like this will be
extended in the future.
> +
> +       /* signal strength of the last 802.11 ACK received */
> +       int ack_signal;
> +
> +       unsigned char hw_address[ETH_ALEN];
> +       char serial_number[256];
> +       u64 crc_errors;
> +       u64 rssi;
> +};
> +
> +static inline struct purelifi_mac *purelifi_hw_mac(struct ieee80211_hw *hw)
> +{
> +       return hw->priv;
> +}
> +
> +static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
> +               *chip)
> +{
> +       return container_of(chip, struct purelifi_mac, chip);
> +}
> +
> +static inline struct purelifi_mac *purelifi_usb_to_mac(struct purelifi_usb *usb)
> +{
> +       return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
> +}
> +
> +static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
> +{
> +       return mac->hw->wiphy->perm_addr;
> +}
> +
> +struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
> +void purelifi_mac_release(struct purelifi_mac *mac);
> +
> +int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address);
> +int purelifi_mac_init_hw(struct ieee80211_hw *hw);
> +
> +int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
> +                   unsigned int length);
> +void purelifi_mac_tx_failed(struct urb *urb);
> +void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
> +int purelifi_op_start(struct ieee80211_hw *hw);
> +void purelifi_op_stop(struct ieee80211_hw *hw);
> +int purelifi_restore_settings(struct purelifi_mac *mac);
> +void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block);
> +
> +#ifdef DEBUG
> +void purelifi_dump_rx_status(const struct rx_status *status);
> +#else
> +#define purelifi_dump_rx_status(status)
> +#endif
> +
> +#endif
> diff --git a/drivers/net/wireless/purelifi/usb.c b/drivers/net/wireless/purelifi/usb.c
> new file mode 100644
> index 000000000000..f405434bd148
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/usb.c
> @@ -0,0 +1,1627 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/firmware.h>
> +#include <linux/device.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/skbuff.h>
> +#include <linux/usb.h>
> +#include <linux/workqueue.h>
> +#include <linux/string.h>
> +#include <linux/proc_fs.h>
> +#include <linux/fs.h>
> +#include <linux/module.h>
> +#include <net/mac80211.h>
> +#include <asm/unaligned.h>
> +#include <linux/version.h>
> +#include <linux/sysfs.h>
> +
> +#include "mac.h"
> +#include "usb.h"
> +
> +#define FCS_LEN 4
> +
> +#define PURELIFI_X_VENDOR_ID_0   0x16C1
> +#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
> +#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
> +#define PURELIFI_XC_PRODUCT_ID_0 0x0008
> +#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
> +#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
> +
> +#undef  LOAD_MAC_AND_SERIAL_FROM_FILE
> +#undef  LOAD_MAC_AND_SERIAL_FROM_FLASH
> +#define LOAD_MAC_AND_SERIAL_FROM_EP0
> +
> +#define PROC_FOLDER           "purelifi"
> +#define MODULATION_FILENAME   "modulation"
> +
> +struct proc_dir_entry *proc_folder;
> +struct proc_dir_entry *modulation_proc_entry;
> +static atomic_t data_queue_flag;
> +
> +/*Tx retry backoff timer (in milliseconds).*/
> +# define TX_RETRY_BACKOFF_MS 10
> +# define STA_QUEUE_CLEANUP_MS 5000
> +
> +/*Tx retry backoff timer (in jiffies).*/
> +# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
> +# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
> +
> +static struct usb_device_id usb_ids[] = {
> +       { USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
> +         .driver_info = DEVICE_LIFI_X },
> +       { USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
> +         .driver_info = DEVICE_LIFI_XC },
> +       { USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
> +         .driver_info = DEVICE_LIFI_XL },
> +       {}
> +};
> +
> +struct usb_interface *ez_usb_interface;
> +
> +static inline u16 get_bcd_device(const struct usb_device *udev)
> +{
> +       return le16_to_cpu(udev->descriptor.bcdDevice);
> +}
> +
> +/* Ensures that MAX_TRANSFER_SIZE is even. */
> +#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
> +
> +#define urb_dev(urb) (&(urb)->dev->dev)
> +
> +void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
> +{
> +       struct sk_buff *skb = NULL;
> +       unsigned long flags;
> +       static u8 sidx;
> +       u8 last_served_sidx;
> +       struct purelifi_usb_tx *tx = &usb->tx;
> +
> +       spin_lock_irqsave(&tx->lock, flags);
> +       last_served_sidx = sidx;
> +       do {
> +               sidx = (sidx + 1) % MAX_STA_NUM;
> +               if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
> +                       continue;
> +               if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
> +                       skb = skb_peek(&tx->station[sidx].data_list);
> +       } while ((sidx != last_served_sidx) && (!skb));
> +
> +       if (skb) {
> +               skb = skb_dequeue(&tx->station[sidx].data_list);
> +               usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
> +                                   tx_urb_complete, skb);
> +               if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
> +                       block_queue(usb, tx->station[sidx].mac, false);
> +       }
> +       spin_unlock_irqrestore(&tx->lock, flags);
> +}
> +
> +static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
> +                            unsigned int length)
> +{
> +       purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
> +}
> +
> +#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
> +#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
> +#define STATION_CONNECT_MESSAGE              2
> +#define STATION_DISCONNECT_MESSAGE           3
> +
> +int rx_usb_enabled;
> +static void rx_urb_complete(struct urb *urb)
> +{
> +       int r;
> +       struct purelifi_usb *usb;
> +       struct purelifi_usb_tx *tx;
> +       const u8 *buffer;
> +       static u8 fpga_link_connection_f;
> +       unsigned int length;
> +       u16 status;
> +       u8 sidx;
> +
> +       if (!urb) {
> +               dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
> +               return;
> +       } else if (!urb->context) {
> +               dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
> +               return;
> +       }
> +       usb = urb->context;
> +
> +       if (usb->initialized != 1)
> +               return;
> +
> +       switch (urb->status) {
> +       case 0:
> +               break;
> +       case -ESHUTDOWN:
> +       case -EINVAL:
> +       case -ENODEV:
> +       case -ENOENT:
> +       case -ECONNRESET:
> +       case -EPIPE:
> +               dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
> +               return;
> +       default:
> +               dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
> +               goto resubmit;
> +       }
> +
> +       buffer = urb->transfer_buffer;
> +       length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
> +
> +       tx = &usb->tx;
> +
> +       if (urb->actual_length != 8) {
> +               if (usb->initialized && fpga_link_connection_f)
> +                       handle_rx_packet(usb, buffer, length);
> +               goto resubmit;
> +       }
> +
> +       status = buffer[7];
> +
> +       dev_info(&usb->intf->dev, "Recv status=%u\n", status);
> +       dev_info(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
> +                buffer[0], buffer[1], buffer[2], buffer[3],
> +                buffer[4], buffer[5]);
> +
> +       switch (status) {
> +       case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
> +               dev_info(&usb->intf->dev,
> +                        "FIFO full not packet receipt\n");
> +               tx->mac_fifo_full = 1;
> +               for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
> +                       tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
> +               break;
> +       case STATION_FIFO_ALMOST_FULL_MESSAGE:
> +               dev_info(&usb->intf->dev, "FIFO full packet receipt\n");
> +
> +               for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
> +                       tx->station[sidx].flag &= 0xFD;
> +
> +               purelifi_send_packet_from_data_queue(usb);
> +               break;
> +       case STATION_CONNECT_MESSAGE:
> +               fpga_link_connection_f = 1;
> +               dev_info(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
> +               break;
> +       case STATION_DISCONNECT_MESSAGE:
> +               fpga_link_connection_f = 0;
> +               dev_info(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
> +               break;
> +       default:
> +               dev_info(&usb->intf->dev, "Unknown packet receipt\n");
> +               break;
> +       }
> +
> +resubmit:
> +       r = usb_submit_urb(urb, GFP_ATOMIC);
> +       if (r)
> +               dev_dbg(urb_dev(urb), "urb %p resubmit failed (%d)\n", urb, r);
> +}
> +
> +static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
> +{
> +       struct usb_device *udev = purelifi_usb_to_usbdev(usb);
> +       struct urb *urb;
> +       void *buffer;
> +
> +       urb = usb_alloc_urb(0, GFP_KERNEL);
> +       if (!urb)
> +               return NULL;
> +
> +       buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
> +                                   &urb->transfer_dma);
> +       if (!buffer) {
> +               usb_free_urb(urb);
> +               return NULL;
> +       }
> +
> +       usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
> +                         buffer, USB_MAX_RX_SIZE,
> +                         rx_urb_complete, usb);
> +       urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +
> +       return urb;
> +}
> +
> +static void free_rx_urb(struct urb *urb)
> +{
> +       if (!urb)
> +               return;
> +       usb_free_coherent(urb->dev, urb->transfer_buffer_length,
> +                         urb->transfer_buffer, urb->transfer_dma);
> +       usb_free_urb(urb);
> +}
> +
> +static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
> +{
> +       int i, r;
> +       struct purelifi_usb_rx *rx = &usb->rx;
> +       struct urb **urbs;
> +
> +       r = -ENOMEM;
> +       urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
> +       if (!urbs)
> +               goto error;
> +
> +       for (i = 0; i < RX_URBS_COUNT; i++) {
> +               urbs[i] = alloc_rx_urb(usb);
> +               if (!urbs[i])
> +                       goto error;
> +       }
> +
> +       spin_lock_irq(&rx->lock);
> +
> +       dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
> +
> +       if (rx->urbs) {
> +               spin_unlock_irq(&rx->lock);
> +               r = 0;
> +               goto error;
> +       }
> +       rx->urbs = urbs;
> +       rx->urbs_count = RX_URBS_COUNT;
> +       spin_unlock_irq(&rx->lock);
> +
> +       for (i = 0; i < RX_URBS_COUNT; i++) {
> +               r = usb_submit_urb(urbs[i], GFP_KERNEL);
> +               if (r)
> +                       goto error_submit;
> +       }
> +
> +       return 0; /*no error return*/
> +
> +error_submit:
> +       for (i = 0; i < RX_URBS_COUNT; i++)
> +               usb_kill_urb(urbs[i]);
> +       spin_lock_irq(&rx->lock);
> +       rx->urbs = NULL;
> +       rx->urbs_count = 0;
> +       spin_unlock_irq(&rx->lock);
> +error:
> +       if (urbs) {
> +               for (i = 0; i < RX_URBS_COUNT; i++)
> +                       free_rx_urb(urbs[i]);
> +       }
> +       return r;
> +}
> +
> +int purelifi_usb_enable_rx(struct purelifi_usb *usb)
> +{
> +       int r;
> +       struct purelifi_usb_rx *rx = &usb->rx;
> +
> +       mutex_lock(&rx->setup_mutex);
> +       r = __lf_x_usb_enable_rx(usb);
> +       if (!r)
> +               rx_usb_enabled = 1;
> +
> +       mutex_unlock(&rx->setup_mutex);
> +
> +       return r;
> +}
> +
> +static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
> +{
> +       int i;
> +       unsigned long flags;
> +       struct urb **urbs;
> +       unsigned int count;
> +       struct purelifi_usb_rx *rx = &usb->rx;
> +
> +       spin_lock_irqsave(&rx->lock, flags);
> +       urbs = rx->urbs;
> +       count = rx->urbs_count;
> +       spin_unlock_irqrestore(&rx->lock, flags);
> +
> +       if (!urbs)
> +               return;
> +
> +       for (i = 0; i < count; i++) {
> +               usb_kill_urb(urbs[i]);
> +               free_rx_urb(urbs[i]);
> +       }
> +       kfree(urbs);
> +
> +       spin_lock_irqsave(&rx->lock, flags);
> +       rx->urbs = NULL;
> +       rx->urbs_count = 0;
> +       spin_unlock_irqrestore(&rx->lock, flags);
> +}
> +
> +void purelifi_usb_disable_rx(struct purelifi_usb *usb)
> +{
> +       struct purelifi_usb_rx *rx = &usb->rx;
> +
> +       mutex_lock(&rx->setup_mutex);
> +       __lf_x_usb_disable_rx(usb);
> +       rx_usb_enabled = 0;
> +       mutex_unlock(&rx->setup_mutex);
> +}
> +
> +/**
> + * purelifi_usb_disable_tx - disable transmission
> + * @usb: the driver USB structure
> + *
> + * Frees all URBs in the free list and marks the transmission as disabled.
> + */
> +void purelifi_usb_disable_tx(struct purelifi_usb *usb)
> +{
> +       struct purelifi_usb_tx *tx = &usb->tx;
> +       unsigned long flags;
> +
> +       atomic_set(&tx->enabled, 0);
> +
> +       /* kill all submitted tx-urbs */
> +       usb_kill_anchored_urbs(&tx->submitted);
> +
> +       spin_lock_irqsave(&tx->lock, flags);
> +       WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
> +       WARN_ON(tx->submitted_urbs != 0);
> +       tx->submitted_urbs = 0;
> +       spin_unlock_irqrestore(&tx->lock, flags);
> +
> +       /* The stopped state is ignored, relying on ieee80211_wake_queues()
> +        * in a potentionally following purelifi_usb_enable_tx().
> +        */
> +}
> +
> +/**
> + * purelifi_usb_enable_tx - enables transmission
> + * @usb: a &struct purelifi_usb pointer
> + *
> + * This function enables transmission and prepares the &purelifi_usb_tx data
> + * structure.
> + */
> +void purelifi_usb_enable_tx(struct purelifi_usb *usb)
> +{
> +       unsigned long flags;
> +       struct purelifi_usb_tx *tx = &usb->tx;
> +
> +       spin_lock_irqsave(&tx->lock, flags);
> +       atomic_set(&tx->enabled, 1);
> +       tx->submitted_urbs = 0;
> +       ieee80211_wake_queues(purelifi_usb_to_hw(usb));
> +       tx->stopped = 0;
> +       spin_unlock_irqrestore(&tx->lock, flags);
> +}
> +
> +/**
> + * tx_urb_complete - completes the execution of an URB
> + * @urb: a URB
> + *
> + * This function is called if the URB has been transferred to a device or an
> + * error has happened.
> + */
> +void tx_urb_complete(struct urb *urb)
> +{
> +       struct sk_buff *skb;
> +       struct ieee80211_tx_info *info;
> +       struct purelifi_usb *usb;
> +
> +       skb = (struct sk_buff *)urb->context;
> +       info = IEEE80211_SKB_CB(skb);
> +       /* grab 'usb' pointer before handing off the skb (since
> +        * it might be freed by purelifi_mac_tx_to_dev or mac80211)
> +        */
> +       usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
> +
> +       switch (urb->status) {
> +       case 0:
> +               break;
> +       case -ESHUTDOWN:
> +       case -EINVAL:
> +       case -ENODEV:
> +       case -ENOENT:
> +       case -ECONNRESET:
> +       case -EPIPE:
> +               dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
> +               break;
> +       default:
> +               dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
> +               return;
> +       }
> +
> +       purelifi_mac_tx_to_dev(skb, urb->status);
> +       purelifi_send_packet_from_data_queue(usb);
> +       usb_free_urb(urb);
> +}
> +
> +/**
> + * purelifi_usb_tx: initiates transfer of a frame of the device
> + *
> + * @usb: the driver USB structure
> + * @skb: a &struct sk_buff pointer
> + *
> + * This function tranmits a frame to the device. It doesn't wait for
> + * completion. The frame must contain the control set and have all the
> + * control set information available.
> + *
> + * The function returns 0 if the transfer has been successfully initiated.
> + */
> +int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
> +{
> +       int r;
> +       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +       struct usb_device *udev = purelifi_usb_to_usbdev(usb);
> +       struct urb *urb;
> +       struct purelifi_usb_tx *tx = &usb->tx;
> +
> +       if (!atomic_read(&tx->enabled)) {
> +               r = -ENOENT;
> +               goto out;
> +       }
> +
> +       urb = usb_alloc_urb(0, GFP_ATOMIC);
> +       if (!urb) {
> +               r = -ENOMEM;
> +               goto out;
> +       }
> +
> +       usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
> +                         skb->data, skb->len, tx_urb_complete, skb);
> +
> +       info->rate_driver_data[1] = (void *)jiffies;
> +       skb_queue_tail(&tx->submitted_skbs, skb);
> +       usb_anchor_urb(urb, &tx->submitted);
> +
> +       r = usb_submit_urb(urb, GFP_ATOMIC);
> +       if (r) {
> +               dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
> +                       urb, r);
> +               usb_unanchor_urb(urb);
> +               skb_unlink(skb, &tx->submitted_skbs);
> +               goto error;
> +       }
> +       return 0;
> +error:
> +       usb_free_urb(urb);
> +out:
> +       return r;
> +}
> +
> +static inline void init_usb_rx(struct purelifi_usb *usb)
> +{
> +       struct purelifi_usb_rx *rx = &usb->rx;
> +
> +       spin_lock_init(&rx->lock);
> +       mutex_init(&rx->setup_mutex);
> +
> +       if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
> +               rx->usb_packet_size = 512;
> +       else
> +               rx->usb_packet_size = 64;
> +
> +       if (rx->fragment_length != 0)
> +               dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
> +}
> +
> +static inline void init_usb_tx(struct purelifi_usb *usb)
> +{
> +       struct purelifi_usb_tx *tx = &usb->tx;
> +
> +       spin_lock_init(&tx->lock);
> +       atomic_set(&tx->enabled, 0);
> +       tx->stopped = 0;
> +       skb_queue_head_init(&tx->submitted_skbs);
> +       init_usb_anchor(&tx->submitted);
> +}
> +
> +void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
> +                      struct usb_interface *intf)
> +{
> +       memset(usb, 0, sizeof(*usb));
> +       usb->intf = usb_get_intf(intf);
> +       usb_set_intfdata(usb->intf, hw);
> +       hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
> +       init_usb_tx(usb);
> +       init_usb_rx(usb);
> +}
> +
> +void purelifi_usb_release(struct purelifi_usb *usb)
> +{
> +       usb_set_intfdata(usb->intf, NULL);
> +       usb_put_intf(usb->intf);
> +       /* FIXME: usb_interrupt, usb_tx, usb_rx? */
> +}
> +
> +const char *purelifi_speed(enum usb_device_speed speed)
> +{
> +       switch (speed) {
> +       case USB_SPEED_LOW:
> +               return "low";
> +       case USB_SPEED_FULL:
> +               return "full";
> +       case USB_SPEED_HIGH:
> +               return "high";
> +       default:
> +               return "unknown";
> +       }
> +}
> +
> +int purelifi_usb_init_hw(struct purelifi_usb *usb)
> +{
> +       int r;
> +
> +       r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
> +       if (r) {
> +               dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
> +               return r;
> +       }
> +       return 0;
> +}
> +
> +static int send_vendor_request(struct usb_device *udev, int request,
> +                              unsigned char *buffer, int buffer_size)
> +{
> +       return usb_control_msg(udev,
> +                              usb_rcvctrlpipe(udev, 0),
> +                              request, 0xC0, 0, 0,
> +                              buffer, buffer_size, 1000);
> +}
> +
> +static int send_vendor_command(struct usb_device *udev, int request,
> +                              unsigned char *buffer, int buffer_size)
> +{
> +       return usb_control_msg(udev,
> +                              usb_sndctrlpipe(udev, 0),
> +                              request, 0x40, 0, 0,
> +                              buffer, buffer_size, 1000);
> +}
> +
> +static int download_fpga(struct usb_interface *intf)
> +{
> +       int i, r, actual_length;
> +       int fw_data_i, blk_tran_len = 16384;
> +       const char *fw_name;
> +       unsigned char fpga_setting[2];
> +       unsigned char *fw_data;
> +       unsigned char fpga_state[9];
> +       unsigned char *fpga_dmabuff;
> +       struct firmware *fw = NULL;
> +       struct usb_device *udev = interface_to_usbdev(intf);
> +
> +       fpga_dmabuff = NULL;
> +
> +       if ((le16_to_cpu(udev->descriptor.idVendor) ==
> +                               PURELIFI_X_VENDOR_ID_0) &&
> +           (le16_to_cpu(udev->descriptor.idProduct) ==
> +                               PURELIFI_X_PRODUCT_ID_0)) {
> +               fw_name = "purelifi/li_fi_x/fpga.bit";
> +               dev_info(&intf->dev, "bit file for X selected\n");
> +
> +       } else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
> +                                       PURELIFI_XC_VENDOR_ID_0 &&
> +                  (le16_to_cpu(udev->descriptor.idProduct) ==
> +                                       PURELIFI_XC_PRODUCT_ID_0)) {
> +               fw_name = "purelifi/li_fi_x/fpga_xc.bit";
> +               dev_info(&intf->dev, "bit file for XC selected\n");
> +
> +       } else {
> +               r = -EINVAL;
> +               goto error;
> +       }
> +
> +       r = request_firmware((const struct firmware **)&fw, fw_name,
> +                            &intf->dev);
> +       if (r) {
> +               dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
> +               goto error;
> +       }
> +
> +       fpga_dmabuff = kmalloc(2, GFP_KERNEL);
> +
Why can't we directly use fpga_setting here?
> +       if (!fpga_dmabuff) {
> +               r = -ENOMEM;
> +               goto error_free_fw;
> +       }
> +       send_vendor_request(udev, 0x33, fpga_dmabuff, sizeof(fpga_setting));
> +
> +       memcpy(fpga_setting, fpga_dmabuff, 2);
> +       kfree(fpga_dmabuff);
> +
> +       send_vendor_command(udev, 0x34, NULL, 0);
> +
> +       if (fpga_setting[0] != 6) {
> +               dev_err(&intf->dev, "fpga_setting[0] is wrong\n");
> +               r = -EINVAL;
> +               goto error_free_fw;
> +       }
> +
> +       for (fw_data_i = 0; fw_data_i < fw->size;) {
> +               int tbuf_idx;
> +
> +               if ((fw->size - fw_data_i) < blk_tran_len)
> +                       blk_tran_len = fw->size - fw_data_i;
> +
> +               fw_data = kmalloc(blk_tran_len, GFP_KERNEL);
> +
> +               memcpy(fw_data, &fw->data[fw_data_i], blk_tran_len);
> +
> +               for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
> +                       fw_data[tbuf_idx] =
> +                               ((fw_data[tbuf_idx] & 128) >> 7) |
> +                               ((fw_data[tbuf_idx] &  64) >> 5) |
> +                               ((fw_data[tbuf_idx] &  32) >> 3) |
> +                               ((fw_data[tbuf_idx] &  16) >> 1) |
> +                               ((fw_data[tbuf_idx] &   8) << 1) |
> +                               ((fw_data[tbuf_idx] &   4) << 3) |
> +                               ((fw_data[tbuf_idx] &   2) << 5) |
> +                               ((fw_data[tbuf_idx] &   1) << 7);
> +               }
> +               r = usb_bulk_msg(udev,
> +                                usb_sndbulkpipe(interface_to_usbdev(intf),
> +                                                fpga_setting[0] & 0xFF),
> +                                fw_data,
> +                                blk_tran_len,
> +                                &actual_length,
> +                                2000);
> +
> +               if (r)
> +                       dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
> +
> +               kfree(fw_data);
> +               fw_data_i += blk_tran_len;
> +       }
> +
> +       fpga_dmabuff = NULL;
> +       fpga_dmabuff = kmalloc(9, GFP_KERNEL);
> +       if (!fpga_dmabuff) {
> +               r = -ENOMEM;
> +               goto error_free_fw;
> +       }
> +       memset(fpga_dmabuff, 0xFF, 9);
> +
> +       send_vendor_request(udev, 0x30, fpga_dmabuff, sizeof(fpga_state));
> +
> +       dev_info(&intf->dev, "fpga_status");
> +       for (i = 0; i <= 8; i++)
> +               dev_info(&intf->dev, " %x ", fpga_dmabuff[i]);
> +       dev_info(&intf->dev, "\n");
> +
> +       if (fpga_dmabuff[0] != 0) {
> +               r = -EINVAL;
> +               kfree(fpga_dmabuff);
> +               goto error_free_fw;
> +       }
> +
> +       kfree(fpga_dmabuff);
> +
> +       send_vendor_command(udev, 0x35, NULL, 0);
> +
> +       msleep(200);
> +
> +error_free_fw:
> +       release_firmware(fw);
> +error:
> +       return r;
> +}
> +
> +static int download_XL_FW(struct usb_interface *intf)
> +{
> +       int r;
> +
> +       struct firmware *fw_packed = NULL;
> +       int step;
> +       u8 *buf;
> +       u32 size, nmb_of_control_packets, i, no_of_files;
> +       u32 tot_size, start_addr;
> +       const char *fw_pack;
> +       struct usb_device *udev = interface_to_usbdev(intf);
> +
> +       r = send_vendor_command(udev, 0x80, NULL, 0);
> +       msleep(100);
> +
> +       /* Code for single pack file download */
> +
> +       fw_pack = "purelifi/li_fi_x/Li-Fi-XL.bin";
> +
> +       r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
> +                            &intf->dev);
> +       if (r) {
> +               dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
> +               goto error;
> +       }
> +       no_of_files = *(u32 *)&fw_packed->data[0];
> +       tot_size = fw_packed->size;
> +
> +       dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n", no_of_files, tot_size);
> +
> +       buf = kmalloc(64, GFP_KERNEL);
> +       r = -ENOMEM;
> +
> +       if (!buf)
> +               goto error;
> +
> +       memset(buf, 0, 64);
kzalloc should suffice.
> +
> +       for (step = 0; step < no_of_files; step++) {
> +               buf[0] = step;
> +               r = send_vendor_command(udev, 0x82, buf, 64);
> +
> +               if (step < no_of_files - 1)
> +                       size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
> +                               - *(u32 *)&fw_packed->data[4 + (step) * 4];
> +               else
> +                       size = tot_size -
> +                               *(u32 *)&fw_packed->data[4 + (step) * 4];
> +
> +               start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
> +
> +               if ((size % 64) && step < 2)
> +                       size += 64 - size % 64;
> +               nmb_of_control_packets = size / 64;
> +
> +               for (i = 0; i < nmb_of_control_packets; i++) {
> +                       memcpy(buf,
> +                              &fw_packed->data[start_addr + (i * 64)], 64);
> +                       r = send_vendor_command(udev, 0x81, buf, 64);
> +               }
> +         dev_info(&intf->dev, "fw-dw step %d,r=%d size %d\n", step, r, size);
> +       }
> +       release_firmware(fw_packed);
> +       kfree(buf);
> +
> +       /* Code for single pack file download ends fw download finish*/
> +
> +       r = send_vendor_command(udev, 0x83, NULL, 0);
> +       dev_info(&intf->dev, "download_fpga (4) (%d)\n", r);
> +error:
> +       return r;
> +}
> +
> +struct flash_t {
> +       unsigned char enabled;
> +       unsigned int sector_size;
> +       unsigned int sectors;
> +       unsigned char ec;
> +};
> +
> +static int upload_mac_and_serial_number(struct usb_interface *intf,
> +                                       unsigned char *hw_address,
> +                                       unsigned char *serial_number)
> +{
> +       struct usb_device *udev = interface_to_usbdev(intf);
> +#ifdef LOAD_MAC_AND_SERIAL_FROM_FILE
> +       struct firmware *fw = NULL;
> +       int r;
> +       char *mac_file_name = "purelifi/li_fi_x/mac.ascii";
> +
> +       r = request_firmware((const struct firmware **)&fw, mac_file_name,
> +                            &intf->dev);
> +       if (r) {
> +               dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
> +               goto ERROR;
> +       }
> +       dev_info(&intf->dev, "fw->data=%s\n", fw->data);
> +       r = sscanf(fw->data,
> +                  "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
> +                   &hw_address[0], &hw_address[1],
> +                   &hw_address[2], &hw_address[3],
> +                   &hw_address[4], &hw_address[5]);
> +       if (r != 6) {
> +               dev_err(&intf->dev, "fw_dw - usage args fail (%d)\n", r);
> +               goto ERROR;
> +       }
> +       release_firmware(fw);
> +
> +       char *serial_file_name = "purelifi/li_fi_x/serial.ascii";
> +
> +       r = request_firmware((const struct firmware **)&fw, serial_file_name,
> +                            &intf->dev);
> +       if (r) {
> +               dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
> +               goto ERROR;
> +       }
> +       dev_info(&intf->dev, "fw->data=%s\n", fw->data);
> +       strcpy(serial_number, fw->data);
strlcpy as we are reading from a file with unknown data.
> +       dev_info(&intf->dev, "serial_number=%s\n", serial_number);
> +       release_firmware(fw);
> +       dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
> +ERROR:
> +       return r;
> +#endif
> +
> +#ifdef LOAD_MAC_AND_SERIAL_FROM_FLASH
> +
> +#define FLASH_EC_BUSY 3
> +
> +       struct flash_t flash;
> +       unsigned char *flash_sector_buf;
> +       unsigned int sector_index;
> +
> +       do {
> +               unsigned char buf[8];
> +
> +               msleep(200);
> +
> +               send_vendor_request(udev, 0x40, buf, sizeof(buf));
> +               flash.enabled = buf[0] & 0xFF;
> +
> +               if (flash.enabled) {
> +                       flash.sectors = ((buf[6] & 255) << 24) |
> +                               ((buf[5] & 255) << 16) |
> +                               ((buf[4] & 255) <<  8) |
> +                               (buf[3] & 255);
> +
> +                       flash.sector_size =
> +                               ((buf[2] & 255) << 8) | (buf[1] & 255);
> +               } else {
> +                       flash.sectors = 0;
> +                       flash.sector_size = 0;
> +               }
> +
> +               if (flash.sector_size & 0x8000)
> +                       flash.sector_size = 1 << (flash.sector_size & 0x7FFF);
> +
> +               flash.ec = buf[7] & 0xFF;
> +       } while (flash.ec == FLASH_EC_BUSY);
> +
> +       flash_sector_buf = kmalloc(flash.sector_size,
> +                                  GFP_KERNEL);
> +       if (!flash_sector_buf)
> +               return -ENOMEM;
> +
> +       if (!flash.enabled) {
> +               dev_err(&intf->dev, "Flash is not enabled\n");
> +               return -EINVAL;
> +       }
> +
> +       read_flash(intf, &flash, 0, flash_sector_buf);
> +       sector_index = *((unsigned int *)flash_sector_buf);
> +
> +       read_flash(intf, &flash, sector_index, flash_sector_buf);
> +       memcpy(hw_address, flash_sector_buf, ETH_ALEN);
> +       strncpy(serial_number, &flash_sector_buf[ETH_ALEN], 256);
> +
> +       dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
> +       kfree(flash_sector_buf);
> +       return 0;
> +#endif
> +
> +#ifdef LOAD_MAC_AND_SERIAL_FROM_EP0
> +
> +#define USB_MAC_VENDOR_REQUEST 0x36
> +#define USB_SERIAL_NUMBER_VENDOR_REQUEST 0x37
> +#define USB_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
> +#define SERIAL_NUMBER_LENGTH 14
> +
> +       int r = 0;
> +       unsigned char *dma_buffer = NULL;
> +       unsigned long long firmware_version;
> +
> +       dma_buffer = kmalloc(14, GFP_KERNEL);
> +       if (!dma_buffer) {
> +               r = -ENOMEM;
> +               goto error;
> +       }
> +
> +       send_vendor_request(udev, USB_MAC_VENDOR_REQUEST, dma_buffer,
> +                           ETH_ALEN);
> +
> +       memcpy(hw_address, dma_buffer, ETH_ALEN);
> +
> +       send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
> +                           dma_buffer, 14);
> +
> +       send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
> +                           dma_buffer, 14);
> +
> +       memcpy(serial_number, dma_buffer, SERIAL_NUMBER_LENGTH);
> +
> +       memset(dma_buffer, 0x00, 14);
> +
> +       send_vendor_request(udev, USB_FIRMWARE_VERSION_VENDOR_REQUEST,
> +                           (unsigned char *)dma_buffer, 8);
> +
> +       memcpy(&firmware_version, dma_buffer, sizeof(unsigned long long));
> +
> +       dev_info(&intf->dev, "Firmware Version: %llu, Revision: 16418\n",
> +                firmware_version);
> +       kfree(dma_buffer);
> +
> +       dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
> +error:
> +       return r;
> +#endif
> +}
> +
> +static void get_usb_req(struct usb_device *udev, const u8 *buffer,
> +                       int buffer_len, enum usb_req_enum_t usb_req_id,
> +                       struct usb_req_t *usb_req)
> +{
> +       u8 *buffer_dst_p = usb_req->buf;
> +       const u8 *buffer_src_p = buffer;
> +       u32 payload_len_nw = TO_NETWORK((buffer_len + FCS_LEN));
> +
> +       usb_req->id = usb_req_id;
> +       usb_req->len  = 0;
> +
> +       /* Copy buffer length into the transmitted buffer, as it is important
> +        * for the Rx MAC to know its exact length.
> +        */
> +       if (usb_req->id == USB_REQ_BEACON_WR) {
> +               memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
> +               buffer_dst_p += sizeof(payload_len_nw);
> +               usb_req->len += sizeof(payload_len_nw);
> +       }
> +
> +       memcpy(buffer_dst_p, buffer_src_p, buffer_len);
> +       buffer_dst_p += buffer_len;
> +       buffer_src_p += buffer_len;
> +       usb_req->len +=  buffer_len;
> +
> +       /* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
> +       memset(buffer_dst_p, 0, FCS_LEN);
> +       buffer_dst_p += FCS_LEN;
> +       usb_req->len += FCS_LEN;
> +
> +       /* Round the packet to be transmitted to 4 bytes. */
> +       if (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT) {
> +               memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
> +                      (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT));
> +               buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
> +                       (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
> +               usb_req->len += PURELIFI_BYTE_NUM_ALIGNMENT -
> +                       (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
> +       }
> +
> +       usb_req->id = TO_NETWORK(usb_req->id);
> +       usb_req->len = TO_NETWORK(usb_req->len);
> +}
> +
> +int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
> +                       int buffer_len, enum usb_req_enum_t usb_req_id,
> +                       usb_complete_t complete_fn,
> +                       void *context)
> +{
> +       int r;
> +       struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
> +       struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
> +
> +       usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
> +                         (void *)buffer, buffer_len, complete_fn, context);
> +
> +       r = usb_submit_urb(urb, GFP_ATOMIC);
> +
> +       if (r)
> +               dev_err(&udev->dev, "async write submit failed (%d)\n", r);
> +
> +       return r;
> +}
> +
> +int usb_write_req(const u8 *buffer, int buffer_len,
> +                 enum usb_req_enum_t usb_req_id)
> +{
> +       int r;
> +       int actual_length;
> +       int usb_bulk_msg_len;
> +       unsigned char *dma_buffer = NULL;
> +       struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
> +       struct usb_req_t usb_req;
> +
> +       get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
> +       usb_bulk_msg_len = sizeof(usb_req.id) + sizeof(usb_req.len) +
> +                          TO_HOST(usb_req.len);
> +
> +       dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
> +
> +       if (!dma_buffer) {
> +               r = -ENOMEM;
> +               goto error;
> +       }
> +       memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);
> +
> +       r = usb_bulk_msg(udev,
> +                        usb_sndbulkpipe(udev, EP_DATA_OUT),
> +                        dma_buffer, usb_bulk_msg_len,
> +                        &actual_length, USB_BULK_MSG_TIMEOUT_MS);
> +       kfree(dma_buffer);
> +error:
> +       if (r)
> +               dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
> +
> +       return r;
> +}
> +
> +static void slif_data_plane_sap_timer_callb(struct timer_list *t)
> +{
> +       struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
> +
> +       purelifi_send_packet_from_data_queue(usb);
> +       usb->tx.tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
> +       timer_setup(&usb->tx.tx_retry_timer,
> +                   slif_data_plane_sap_timer_callb, 0);
> +       add_timer(&usb->tx.tx_retry_timer);
Shouldn't mod_timer() suffice here?
> +}
> +
> +static void sta_queue_cleanup_timer_callb(struct timer_list *t)
> +{
> +       struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
> +       struct purelifi_usb_tx *tx = &usb->tx;
> +       int sidx;
> +
> +       for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
> +               if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
> +                       continue;
> +               if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
> +                       tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
> +               } else {
> +                       memset(tx->station[sidx].mac, 0, ETH_ALEN);
> +                       tx->station[sidx].flag = 0;
> +               }
> +       }
> +       usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
> +       timer_setup(&usb->sta_queue_cleanup,
> +                   sta_queue_cleanup_timer_callb, 0);
> +       add_timer(&usb->sta_queue_cleanup);
same, mod_timer should suffice.
> +}
> +
> +static enum unit_type_t get_unit_type(char *serial_number,
> +                                     struct usb_interface *intf)
> +{
> +       dev_info(&intf->dev, "Unit-type is station (STA)\n");
> +       return STA;
> +}
> +
> +char usr_input[8] = {0};
> +
> +static ssize_t purelifi_store_frequency(struct device *dev,
> +                                       struct device_attribute *attr,
> +                                       const char *buf,
> +                                       size_t len)
> +{
> +       int i, r, row, col, predivider, feedback_divider, output_div_0;
> +       int output_div_1, output_div_2, clkout3_phase;
> +       char values[6][4] = {{0}, {0} };
> +       bool valid = false;
> +       int rv = 0;
> +
> +       row = 0; col = 0;
> +       for (i = 0; i < min(len, sizeof(values)); i++) {
> +               values[row][col] = buf[i];
> +               col++;
> +               if (buf[i] == 0x20) {
> +                       row++;
> +                       col = 0;
> +               } else if (buf[i] == 0x0A) {
> +                       if (row == 5 && col > 0) {
> +                               rv = sscanf((char *)&values[0][0],
> +                                           "%d", &predivider);
> +                               if (rv != 1)
> +                                       valid = false;
> +                               rv = sscanf((char *)&values[1][0],
> +                                           "%d", &feedback_divider);
> +                               if (rv != 1)
> +                                       valid = false;
> +                               rv = sscanf((char *)&values[2][0],
> +                                           "%d", &output_div_0);
> +                               if (rv != 1)
> +                                       valid = false;
> +                               rv = sscanf((char *)&values[3][0],
> +                                           "%d", &output_div_1);
> +                               if (rv != 1)
> +                                       valid = false;
> +                               rv = sscanf((char *)&values[4][0],
> +                                           "%d", &output_div_2);
> +                               if (rv != 1)
> +                                       valid = false;
> +                               rv = sscanf((char *)&values[5][0],
> +                                           "%d", &clkout3_phase);
> +                               if (rv != 1)
> +                                       valid = false;
> +                               valid = true;
> +
> +                               break;
> +                       }
> +               }
> +       }
> +
> +       if (valid) {
> +               usr_input[0] = (char)predivider;
> +               usr_input[1] = (char)feedback_divider;
> +               usr_input[2] = (char)output_div_0;
> +               usr_input[3] = (char)output_div_1;
> +               usr_input[4] = (char)output_div_2;
> +               usr_input[5] = (char)clkout3_phase;
> +
> +               dev_err(dev, "options IP_DIV: %d VCO_MULT: %d OP_DIV_PHY: %d",
> +                       usr_input[0], usr_input[1], usr_input[2]);
> +               dev_err(dev, "OP_DIV_CPU: %d OP_DIV_USB: %d CLK3_PH: %d\n",
> +                       usr_input[3], usr_input[4], usr_input[5]);
> +
> +               r = usb_write_req((const u8 *)&usr_input[0],
> +                                 sizeof(usr_input), USB_REQ_SET_FREQ);
> +               if (r < 0)
> +                       dev_err(dev, "SET_FREQ failed (%d)\n", r);
> +       } else {
> +               dev_err(dev, "Your options are invalid\n");
> +       }
> +
> +       return len;
> +}
> +
maybe move these to a separate debugfs.c file?
> +static ssize_t purelifi_show_sysfs(struct device *dev,
> +                                  struct device_attribute *attr,
> +                                  char *buf)
> +{
> +       return 0;
> +}
> +
> +static const struct device_attribute purelifi_attr_frequency = {
> +         .attr = {.name = "frequency", .mode = (0666)},
> +         .show = purelifi_show_sysfs,
> +         .store = purelifi_store_frequency,
> +};
> +
> +static ssize_t purelifi_store_modulation(struct device *dev,
> +                                        struct device_attribute *attr,
> +                                        const char *buf,
> +                                        size_t len)
> +{
> +       int rate, i;
> +       char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
> +       int rv = 0;
> +
> +       for (i = 0; i < min(len, sizeof(usr_input)); i++)
> +               usr_input[i] = buf[i];
> +       usr_input[7] = 0;
> +       rv = sscanf((char *)&usr_input[0], "%d", &rate);
> +       if (rv != 1)
> +               return 0;
> +       purelifi_chip_set_rate(NULL, rate);
> +       return len;
> +}
> +
> +static ssize_t purelifi_show_modulation(struct device *dev,
> +                                       struct device_attribute *attr,
> +                                       char *buf)
> +{
> +       return 0;
> +}
> +
> +static struct device_attribute purelifi_attr_modulation = {
> +       .attr = {.name = "modulation", .mode = (0666)},
> +       .show = purelifi_show_modulation,
> +       .store = purelifi_store_modulation,
> +};
> +
> +static void create_config(struct usb_interface *intf)
> +{
> +       struct device *dev;
> +
> +       dev = &intf->dev;
> +       if (device_create_file(dev, &purelifi_attr_modulation)) {
> +               dev_err(&intf->dev,
> +                       "failed to create modulation sysfs entry\n");
> +               return;
> +       }
> +       if (device_create_file(dev, &purelifi_attr_frequency)) {
> +               device_remove_file(dev, &purelifi_attr_modulation);
> +               dev_err(&intf->dev,
> +                       "failed to create frequency sysfs entry\n");
> +       }
> +}
> +
> +static int probe(struct usb_interface *intf,
> +                const struct usb_device_id *id)
> +{
> +       int r = 0;
> +       struct purelifi_chip *chip;
> +       struct purelifi_usb *usb;
> +       struct purelifi_usb_tx *tx;
> +       struct ieee80211_hw *hw = NULL;
> +       static unsigned char hw_address[ETH_ALEN];
> +       static unsigned char serial_number[256];
> +       unsigned int i;
> +
> +       ez_usb_interface = intf;
> +
> +       create_config(intf);
> +       hw = purelifi_mac_alloc_hw(intf);
> +
> +       if (!hw) {
> +               r = -ENOMEM;
> +               goto error;
> +       }
> +
> +       chip = &purelifi_hw_mac(hw)->chip;
> +       usb = &chip->usb;
> +       tx = &usb->tx;
> +
> +       r = upload_mac_and_serial_number(intf, hw_address, serial_number);
> +       if (r) {
> +               dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
> +               goto error;
> +       }
> +       chip->unit_type = get_unit_type(serial_number, intf);
> +
> +       r = purelifi_mac_preinit_hw(hw, hw_address);
> +       if (r) {
> +               dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
> +               goto error;
> +       }
> +
> +       r = ieee80211_register_hw(hw);
> +       if (r) {
> +               dev_dbg(&intf->dev, "register device failed (%d)\n", r);
> +               goto error;
> +       }
> +       dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
> +
> +       if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
> +                               PURELIFI_XL_VENDOR_ID_0) &&
> +           (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
> +                               PURELIFI_XL_PRODUCT_ID_0)) {
> +               r = download_XL_FW(intf);
> +       } else {
> +               r = download_fpga(intf);
> +       }
> +       if (r != 0) {
> +               dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
> +               goto error;
> +       }
> +
> +       tx->mac_fifo_full = 0;
> +       spin_lock_init(&tx->lock);
> +
> +       msleep(200);
> +       r = purelifi_usb_init_hw(usb);
> +       if (r < 0) {
> +               dev_info(&intf->dev, "usb_init_hw failed (%d)\n", r);
> +               goto error;
> +       }
> +
> +       msleep(200);
> +       r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
> +       if (r < 0) {
> +               dev_info(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
> +               goto error;
> +       }
> +
> +       msleep(200);
> +       r = purelifi_chip_set_rate(chip, 8);
> +       if (r < 0) {
> +               dev_info(&intf->dev, "chip_set_rate failed (%d)\n", r);
> +               goto error;
> +       }
> +
> +       msleep(200);
> +       r = usb_write_req(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
> +       if (r < 0) {
> +               dev_info(&intf->dev, "MAC_WR failure (%d)\n", r);
> +               goto error;
> +       }
> +
> +       purelifi_chip_enable_rxtx(chip);
> +
> +       /* Initialise the data plane Tx queue */
> +       atomic_set(&data_queue_flag, 1);
> +       for (i = 0; i < MAX_STA_NUM; i++) {
> +               skb_queue_head_init(&tx->station[i].data_list);
> +               tx->station[i].flag = 0;
> +       }
> +       tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
> +       for (i = 0; i < ETH_ALEN; i++)
> +               tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
> +       atomic_set(&data_queue_flag, 0);
> +
> +       timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
> +       tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
> +       add_timer(&tx->tx_retry_timer);
> +
> +       timer_setup(&usb->sta_queue_cleanup,
> +                   sta_queue_cleanup_timer_callb, 0);
> +       usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
> +       add_timer(&usb->sta_queue_cleanup);
> +
> +       usb->initialized = 1;
> +       return 0;
> +error:
> +       if (hw) {
> +               purelifi_mac_release(purelifi_hw_mac(hw));
> +               ieee80211_unregister_hw(hw);
> +               ieee80211_free_hw(hw);
> +       }
> +       return r;
> +}
> +
> +static void disconnect(struct usb_interface *intf)
> +{
> +       struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
> +       struct purelifi_mac *mac;
> +       struct purelifi_usb *usb;
> +
> +       /* Either something really bad happened, or
> +        * we're just dealing with
> +        * a DEVICE_INSTALLER.
> +        */
> +       if (!hw)
> +               return;
> +
> +       mac = purelifi_hw_mac(hw);
> +       usb = &mac->chip.usb;
> +
> +       del_timer(&usb->tx.tx_retry_timer);
In the callback we are rearming timer unconditionally, so, this is not enough.
Need to conditionally re-arm and use del_timer_sync here.
> +       del_timer(&usb->sta_queue_cleanup);
Same.

> +
> +       ieee80211_unregister_hw(hw);
> +
> +       purelifi_usb_disable_tx(usb);
> +       purelifi_usb_disable_rx(usb);
> +
> +       /* If the disconnect has been caused by a removal of the
> +        * driver module, the reset allows reloading of the driver. If the
> +        * reset will not be executed here,
> +        * the upload of the firmware in the
> +        * probe function caused by the reloading of the driver will fail.
> +        */
> +       usb_reset_device(interface_to_usbdev(intf));
> +
> +       device_remove_file(&intf->dev, &purelifi_attr_modulation);
> +       device_remove_file(&intf->dev, &purelifi_attr_frequency);
> +
> +       purelifi_mac_release(mac);
> +       ieee80211_free_hw(hw);
> +}
> +
> +static void purelifi_usb_resume(struct purelifi_usb *usb)
> +{
> +       struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
> +       int r;
> +
> +       r = purelifi_op_start(purelifi_usb_to_hw(usb));
> +       if (r < 0) {
> +               dev_warn(purelifi_usb_dev(usb),
> +                        "Device resume failed (%d)\n", r);
> +
> +               if (usb->was_running)
> +                       set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
> +
> +               usb_queue_reset_device(usb->intf);
> +               return;
> +       }
> +
> +       if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
> +               r = purelifi_restore_settings(mac);
> +               if (r < 0) {
> +                       dev_dbg(purelifi_usb_dev(usb),
> +                               "restore failed (%d)\n", r);
> +                       return;
> +               }
> +       }
> +}
> +
> +static void purelifi_usb_stop(struct purelifi_usb *usb)
> +{
> +       purelifi_op_stop(purelifi_usb_to_hw(usb));
> +       purelifi_usb_disable_tx(usb);
> +       purelifi_usb_disable_rx(usb);
> +
> +       usb->initialized = 0;
> +}
> +
> +static int pre_reset(struct usb_interface *intf)
> +{
> +       struct ieee80211_hw *hw = usb_get_intfdata(intf);
> +       struct purelifi_mac *mac;
> +       struct purelifi_usb *usb;
> +
> +       if (!hw || intf->condition != USB_INTERFACE_BOUND)
> +               return 0;
> +
> +       mac = purelifi_hw_mac(hw);
> +       usb = &mac->chip.usb;
> +
> +       usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
> +
> +       purelifi_usb_stop(usb);
> +
> +       mutex_lock(&mac->chip.mutex);
> +       return 0;
> +}
> +
> +static int post_reset(struct usb_interface *intf)
> +{
> +       struct ieee80211_hw *hw = usb_get_intfdata(intf);
> +       struct purelifi_mac *mac;
> +       struct purelifi_usb *usb;
> +
> +       if (!hw || intf->condition != USB_INTERFACE_BOUND)
> +               return 0;
> +
> +       mac = purelifi_hw_mac(hw);
> +       usb = &mac->chip.usb;
> +
> +       mutex_unlock(&mac->chip.mutex);
> +
> +       if (usb->was_running)
> +               purelifi_usb_resume(usb);
> +       return 0;
> +}
> +
> +static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
> +{
> +       struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
> +       struct purelifi_mac *mac;
> +
> +       /* Either something really bad happened, or
> +        * we're just dealing with
> +        * a DEVICE_INSTALLER.
> +        */
> +       if (!hw)
> +               return NULL;
> +
> +       mac = purelifi_hw_mac(hw);
> +       return &mac->chip.usb;
> +}
> +
> +#ifdef CONFIG_PM
> +
> +static int suspend(struct usb_interface *interface,
> +                  pm_message_t message)
> +{
> +       struct purelifi_usb *pl = get_purelifi_usb(interface);
> +       struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
> +
> +       if (!pl || !purelifi_usb_dev(pl))
> +               return -ENODEV;
> +       if (pl->initialized == 0)
> +               return 0;
> +       pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
> +       purelifi_usb_stop(pl);
> +       return 0;
> +}
> +
> +static int resume(struct usb_interface *interface)
> +{
> +       struct purelifi_usb *pl = get_purelifi_usb(interface);
> +
> +       if (!pl || !purelifi_usb_dev(pl))
> +               return -ENODEV;
> +       if (pl->was_running)
> +               purelifi_usb_resume(pl);
> +       return 0;
> +}
> +
> +#else
> +
> +#define suspend NULL
> +#define resume  NULL
> +
> +#endif
> +
> +static struct usb_driver driver = {
> +       .name        = KBUILD_MODNAME,
> +       .id_table    = usb_ids,
> +       .probe        = probe,
> +       .disconnect    = disconnect,
> +       .pre_reset    = pre_reset,
> +       .post_reset    = post_reset,
> +       .suspend        = suspend,
> +       .resume         = resume,
> +       .disable_hub_initiated_lpm = 1,
> +};
> +
> +struct workqueue_struct *purelifi_workqueue;
> +
> +static int modulation_open(struct inode *inode_p, struct file *file_p)
> +{
> +       return 0;
> +}
> +
> +static ssize_t modulation_read(struct file *file_p,
> +                              char __user *user_p,
> +               size_t offset, loff_t *off_p)
> +{
> +       return 0;
> +}
> +
> +static ssize_t modulation_write(struct file *file_p,
> +                               const char __user *buffer,
> +                               size_t count, loff_t *actual_data_written)
> +{
> +       int rate;
> +       char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
> +       int rv = 0;
> +
> +       if (copy_from_user(usr_input, buffer, min(count, sizeof(usr_input))))
> +               return -EFAULT;
> +       usr_input[7] = 0;
> +       rv = sscanf((char *)&usr_input[0], "%d", &rate);
> +       if (rv != 1)
> +               return 0;
> +       purelifi_chip_set_rate(NULL, rate);
> +       return count;
> +}
> +
> +static const struct proc_ops  modulation_fops = {
> +       .proc_open  = modulation_open,
> +       .proc_read  = modulation_read,
> +       .proc_write = modulation_write
> +};
> +
> +static int create_proc_entry_e(struct proc_dir_entry *entry,
> +                              size_t size,
> +                              mode_t mode,
> +                              const struct proc_ops *pops,
> +                              const char *entry_name,
> +                              struct proc_dir_entry *parent
> +                            )
> +{
> +       entry = proc_create(entry_name, mode, parent, pops);
> +       if (!(entry))
> +               return -ENOMEM;
> +       return 0;
> +}
> +
> +static int __init usb_init(void)
> +{
> +       int r;
> +
> +       proc_folder = proc_mkdir(PROC_FOLDER, NULL);
> +       if (!proc_folder) {
> +               pr_err("error creating proc folder\n");
> +               r = -ENOMEM;
> +               goto error;
> +       }
> +
> +       r = create_proc_entry_e(modulation_proc_entry, sizeof(u32), 0666,
> +                               &modulation_fops, MODULATION_FILENAME,
> +                               proc_folder);
> +       if (r)
> +               goto remove_proc_dir;
> +
> +       purelifi_workqueue = create_singlethread_workqueue(driver.name);
> +       if (!purelifi_workqueue) {
> +               pr_err("%s couldn't create workqueue\n", driver.name);
> +               r = -ENOMEM;
> +               goto remove_modulation_proc_file;
> +       }
> +
> +       r = usb_register(&driver);
> +       if (r) {
> +               destroy_workqueue(purelifi_workqueue);
> +               pr_err("%s usb_register() failed %d\n", driver.name, r);
> +               return r;
> +       }
> +
> +       pr_debug("Driver initialized :%s\n", driver.name);
> +       return 0;
> +
> +remove_modulation_proc_file:
> +       remove_proc_entry(MODULATION_FILENAME, proc_folder);
> +remove_proc_dir:
> +       remove_proc_entry(PROC_FOLDER, NULL);
> +error:
> +       return r;
> +}
> +
> +static void __exit usb_exit(void)
> +{
> +       remove_proc_entry(MODULATION_FILENAME, proc_folder);
> +       remove_proc_entry(PROC_FOLDER, NULL);
> +       if (ez_usb_interface) {
> +               device_remove_file(&ez_usb_interface->dev,
> +                                  &purelifi_attr_modulation);
> +               device_remove_file(&ez_usb_interface->dev,
> +                                  &purelifi_attr_frequency);
> +       }
> +       usb_deregister(&driver);
> +       destroy_workqueue(purelifi_workqueue);
> +       pr_debug("%s %s\n", driver.name, __func__);
> +}
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("USB driver for pureLiFi devices");
> +MODULE_AUTHOR("pureLiFi Ltd");
> +MODULE_VERSION("1.0");
> +MODULE_FIRMWARE("purelifi/li_fi_x/fpga.bit");
@kalle, what is the policy on upstreaming drivers without an actual product?
Do we allow prototypes stage chips like this using FPGA?
> +MODULE_DEVICE_TABLE(usb, usb_ids);
> +
> +module_init(usb_init);
> +module_exit(usb_exit);
> diff --git a/drivers/net/wireless/purelifi/usb.h b/drivers/net/wireless/purelifi/usb.h
> new file mode 100644
> index 000000000000..3a6cae971e50
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/usb.h
> @@ -0,0 +1,148 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef _PURELIFI_USB_H
> +#define _PURELIFI_USB_H
> +
> +#include <linux/completion.h>
> +#include <linux/netdevice.h>
> +#include <linux/spinlock.h>
> +#include <linux/skbuff.h>
> +#include <linux/usb.h>
> +
> +#include "intf.h"
> +
> +#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
> +                     (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
> +
> +#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
> +
> +#define TO_NETWORK_32(X) TO_NETWORK(X)
> +
> +#define TO_HOST(X)    TO_NETWORK(X)
> +#define TO_HOST_16(X) TO_NETWORK_16(X)
> +#define TO_HOST_32(X) TO_NETWORK_32(X)
> +
> +#define USB_BULK_MSG_TIMEOUT_MS 2000
> +
> +int usb_write_req(const u8 *buffer, int buffer_len,
> +                 enum usb_req_enum_t usb_req_id);
> +void tx_urb_complete(struct urb *urb);
> +
> +enum {
> +       USB_MAX_RX_SIZE       = 4800,
> +       USB_MAX_EP_INT_BUFFER = 64,
> +};
> +
> +/* USB interrupt */
> +struct purelifi_usb_interrupt {
> +       spinlock_t lock;/* spin lock for usb interrupt buffer */
> +       struct urb *urb;
> +       void *buffer;
> +       int interval;
> +};
> +
> +#define RX_URBS_COUNT 5
> +
> +struct purelifi_usb_rx {
> +       spinlock_t lock;/* spin lock for rx urb */
> +       struct mutex setup_mutex; /* mutex lockt for rx urb */
> +       u8 fragment[2 * USB_MAX_RX_SIZE];
> +       unsigned int fragment_length;
> +       unsigned int usb_packet_size;
> +       struct urb **urbs;
> +       int urbs_count;
> +};
> +
> +struct station_t {
> +   //  7...3    |    2      |     1     |     0            |
> +   // Reserved  | Heartbeat | FIFO full | Connected |
> +       unsigned char flag;
> +       unsigned char mac[ETH_ALEN];
> +       struct sk_buff_head data_list;
> +};
> +
> +#define STATION_CONNECTED_FLAG 0x1
> +#define STATION_FIFO_FULL_FLAG 0x2
> +#define STATION_HEARTBEAT_FLAG 0x4
> +
> +/**
> + * struct purelifi_usb_tx - structure used for transmitting frames
> + * @enabled: atomic enabled flag, indicates whether tx is enabled
> + * @lock: lock for transmission
> + * @submitted: anchor for URBs sent to device
> + * @submitted_urbs: atomic integer that counts the URBs having sent to the
> + *   device, which haven't been completed
> + * @stopped: indicates whether higher level tx queues are stopped
> + */
> +#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
> +#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
> +struct purelifi_usb_tx {
> +       atomic_t enabled;
> +       spinlock_t lock; /*spinlock for USB tx */
> +       u8 mac_fifo_full;
> +       struct sk_buff_head submitted_skbs;
> +       struct usb_anchor submitted;
> +       int submitted_urbs;
> +       u8 stopped:1;
> +       struct timer_list tx_retry_timer;
> +       struct station_t station[MAX_STA_NUM];
> +};
> +
> +/* Contains the usb parts. The structure doesn't require a lock because intf
> + * will not be changed after initialization.
> + */
> +struct purelifi_usb {
> +       struct timer_list sta_queue_cleanup;
> +       struct purelifi_usb_rx rx;
> +       struct purelifi_usb_tx tx;
> +       struct usb_interface *intf;
> +       u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
> +       u8 initialized:1, was_running:1;
> +};
> +
> +enum endpoints {
> +       EP_DATA_IN  = 2,
> +       EP_DATA_OUT = 8,
> +};
> +
> +enum devicetype {
> +       DEVICE_LIFI_X  = 0,
> +       DEVICE_LIFI_XC  = 1,
> +       DEVICE_LIFI_XL  = 1,
> +};
> +
> +int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
> +                       int buffer_len, enum usb_req_enum_t usb_req_id,
> +               usb_complete_t complete_fn, void *context);
> +
> +#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
> +static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
> +               *usb)
> +{
> +       return interface_to_usbdev(usb->intf);
> +}
> +
> +static inline struct ieee80211_hw *purelifi_intf_to_hw(struct usb_interface
> +               *intf)
> +{
> +       return usb_get_intfdata(intf);
> +}
> +
> +static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
> +               *usb)
> +{
> +       return purelifi_intf_to_hw(usb->intf);
> +}
> +
> +void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
> +                      struct usb_interface *intf);
> +void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
> +void purelifi_usb_release(struct purelifi_usb *usb);
> +void purelifi_usb_disable_rx(struct purelifi_usb *usb);
> +void purelifi_usb_enable_tx(struct purelifi_usb *usb);
> +void purelifi_usb_disable_tx(struct purelifi_usb *usb);
> +int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
> +int purelifi_usb_enable_rx(struct purelifi_usb *usb);
> +int purelifi_usb_init_hw(struct purelifi_usb *usb);
> +const char *purelifi_speed(enum usb_device_speed speed);
> +#endif
> --
> 2.17.1
Overall, there are many magic numbers without comments, this makes it hard to
understand the code. Using defines with proper naming helps and for 802.11 stuff
can use ieee80211_*/IEEE80211_* should be used.

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v6] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-19 16:07         ` Krishna Chaitanya
@ 2020-10-19 16:40           ` Srinivasan Raju
  2020-10-19 16:54             ` Joe Perches
  0 siblings, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2020-10-19 16:40 UTC (permalink / raw)
  To: Krishna Chaitanya
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

> Overall, there are many magic numbers without comments, this makes it hard to
> understand the code. Using defines with proper naming helps and for 802.11 stuff
> can use ieee80211_*/IEEE80211_* should be used.

Thanks for your comments Krishna, will work on them.

Regards,
Srini

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v6] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-19 16:40           ` Srinivasan Raju
@ 2020-10-19 16:54             ` Joe Perches
  2020-10-19 17:05               ` Srinivasan Raju
  0 siblings, 1 reply; 108+ messages in thread
From: Joe Perches @ 2020-10-19 16:54 UTC (permalink / raw)
  To: Srinivasan Raju, Krishna Chaitanya
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

On Mon, 2020-10-19 at 16:40 +0000, Srinivasan Raju wrote:
> > Overall, there are many magic numbers without comments, this makes it hard to
> > understand the code. Using defines with proper naming helps and for 802.11 stuff
> > can use ieee80211_*/IEEE80211_* should be used.
> 
> Thanks for your comments Krishna, will work on them.

When you do, please start adding
changelog information below the
---
line to your patches.

It's quite a chore to figure out
what changed between revisions.



^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v6] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-19 16:54             ` Joe Perches
@ 2020-10-19 17:05               ` Srinivasan Raju
  0 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-10-19 17:05 UTC (permalink / raw)
  To: Joe Perches, Krishna Chaitanya
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS


> When you do, please start adding
> changelog information below the
> ---
> line to your patches.
>
> It's quite a chore to figure out
> what changed between revisions.

Ok, will add version logs to future revisions.

Thanks
Srini


^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v7] wireless: Initial driver submission for pureLiFi STA devices
  2020-10-16  6:34   ` [PATCH] [v4] " Srinivasan Raju
  2020-10-16  8:58     ` Joe Perches
  2020-10-19  3:17     ` [PATCH] [v5] " Srinivasan Raju
@ 2020-11-16  9:22     ` Srinivasan Raju
  2020-11-16 20:45       ` Joe Perches
                         ` (2 more replies)
  2 siblings, 3 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-11-16  9:22 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Mauro Carvalho Chehab, Rob Herring,
	Lukas Bulwahn, open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

Changes v6->v7:
- Magic numbers removed and used IEEE80211 macors
- usb.c is split into two files firmware.c and dbgfs.c
- Other code style and timer function fixes (mod_timer)
Changes v5->v6:
- Code style fix patch from Joe Perches
Changes v4->v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
Changes v3->v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
Changes v2->v3:
- Code style fixes kconfig fix
Changes v1->v2:
- v1 was submitted to staging, v2 submitted to wireless-next
- Code style fixes and copyright statement fix
---
 MAINTAINERS                              |    5 +
 drivers/net/wireless/Kconfig             |    1 +
 drivers/net/wireless/Makefile            |    1 +
 drivers/net/wireless/purelifi/Kconfig    |   27 +
 drivers/net/wireless/purelifi/Makefile   |    3 +
 drivers/net/wireless/purelifi/chip.c     |   94 ++
 drivers/net/wireless/purelifi/chip.h     |   82 ++
 drivers/net/wireless/purelifi/dbgfs.c    |  161 ++++
 drivers/net/wireless/purelifi/firmware.c |  367 ++++++++
 drivers/net/wireless/purelifi/intf.h     |   38 +
 drivers/net/wireless/purelifi/mac.c      |  876 ++++++++++++++++++
 drivers/net/wireless/purelifi/mac.h      |  189 ++++
 drivers/net/wireless/purelifi/usb.c      | 1073 ++++++++++++++++++++++
 drivers/net/wireless/purelifi/usb.h      |  197 ++++
 14 files changed, 3114 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/chip.c
 create mode 100644 drivers/net/wireless/purelifi/chip.h
 create mode 100644 drivers/net/wireless/purelifi/dbgfs.c
 create mode 100644 drivers/net/wireless/purelifi/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/intf.h
 create mode 100644 drivers/net/wireless/purelifi/mac.c
 create mode 100644 drivers/net/wireless/purelifi/mac.h
 create mode 100644 drivers/net/wireless/purelifi/usb.c
 create mode 100644 drivers/net/wireless/purelifi/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index c80f87d7258c..150f592fb6e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14108,6 +14108,11 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PUREILIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+S:	Maintained
+F:	drivers/net/wireless/purelifi
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 170a64e67709..b87da3139f94 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -48,6 +48,7 @@ source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
 source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 
 config PCMCIA_RAYCS
 	tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e9fc770026f0 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
 obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..f6630791df9d
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+config PURELIFI
+
+	tristate "pureLiFi device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This driver makes the adapter appear as a normal WLAN interface.
+
+	   The pureLiFi device requires external STA firmware to be loaded.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called purelifi.
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..cf9b931840ac
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PURELIFI)		:= purelifi.o
+purelifi-objs 		+= chip.o usb.o mac.o firmware.o dbgfs.o
diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
new file mode 100644
index 000000000000..4282ce1d92b4
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval || (chip->beacon_set &&
+			  chip->beacon_interval == interval)) {
+		return 0;
+	}
+
+	chip->beacon_interval = interval;
+	chip->beacon_set = true;
+	return usb_write_req((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %02x:%02x v%02x  %02x-%02x-%02x %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr[0], addr[1], addr[2],
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
+{
+	int r;
+
+	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	u8 value;
+
+	value = 0;
+	usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = usb_write_req((const u8 *)&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/chip.h b/drivers/net/wireless/purelifi/chip.h
new file mode 100644
index 000000000000..f479f34a6503
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	u16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr
+					)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1 << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/dbgfs.c b/drivers/net/wireless/purelifi/dbgfs.c
new file mode 100644
index 000000000000..411c0a423a23
--- /dev/null
+++ b/drivers/net/wireless/purelifi/dbgfs.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include "mac.h"
+#include "usb.h"
+
+ssize_t purelifi_store_frequency(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t len)
+{
+	int i, r, row, col, predivider, feedback_divider, output_div_0;
+	int output_div_1, output_div_2, clkout3_phase;
+	char values[6][4] = {{0}, {0} };
+	char usr_input[8] = {0};
+	bool valid = false;
+	int rv = 0;
+
+	row = 0; col = 0;
+	for (i = 0; i < min(len, sizeof(values)); i++) {
+		values[row][col] = buf[i];
+		col++;
+		if (buf[i] == 0x20) {
+			row++;
+			col = 0;
+		} else if (buf[i] == 0x0A) {
+			if (row == 5 && col > 0) {
+				rv = sscanf((char *)&values[0][0],
+					    "%d", &predivider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[1][0],
+					    "%d", &feedback_divider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[2][0],
+					    "%d", &output_div_0);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[3][0],
+					    "%d", &output_div_1);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[4][0],
+					    "%d", &output_div_2);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[5][0],
+					    "%d", &clkout3_phase);
+				if (rv != 1)
+					valid = false;
+				valid = true;
+
+				break;
+			}
+		}
+	}
+
+	if (valid) {
+		usr_input[0] = (char)predivider;
+		usr_input[1] = (char)feedback_divider;
+		usr_input[2] = (char)output_div_0;
+		usr_input[3] = (char)output_div_1;
+		usr_input[4] = (char)output_div_2;
+		usr_input[5] = (char)clkout3_phase;
+
+		dev_err(dev, "options IP_DIV: %d VCO_MULT: %d OP_DIV_PHY: %d",
+			usr_input[0], usr_input[1], usr_input[2]);
+		dev_err(dev, "OP_DIV_CPU: %d OP_DIV_USB: %d CLK3_PH: %d\n",
+			usr_input[3], usr_input[4], usr_input[5]);
+
+		r = usb_write_req((const u8 *)&usr_input[0],
+				  sizeof(usr_input), USB_REQ_SET_FREQ);
+		if (r < 0)
+			dev_err(dev, "SET_FREQ failed (%d)\n", r);
+	} else {
+		dev_err(dev, "Your options are invalid\n");
+	}
+
+	return len;
+}
+
+ssize_t purelifi_show_sysfs(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	return 0;
+}
+
+ssize_t purelifi_store_modulation(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t len)
+{
+	int rate, i;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	for (i = 0; i < min(len, sizeof(usr_input)); i++)
+		usr_input[i] = buf[i];
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return len;
+}
+
+ssize_t purelifi_show_modulation(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	return 0;
+}
+
+void create_config(struct usb_interface *intf)
+{
+	struct device *dev;
+
+	dev = &intf->dev;
+	if (device_create_file(dev, &purelifi_attr_modulation)) {
+		dev_err(&intf->dev,
+			"failed to create modulation sysfs entry\n");
+		return;
+	}
+	if (device_create_file(dev, &purelifi_attr_frequency)) {
+		device_remove_file(dev, &purelifi_attr_modulation);
+		dev_err(&intf->dev,
+			"failed to create frequency sysfs entry\n");
+	}
+}
+
+int modulation_open(struct inode *inode_p, struct file *file_p)
+{
+	return 0;
+}
+
+ssize_t modulation_read(struct file *file_p,
+			char __user *user_p,
+			size_t offset, loff_t *off_p)
+{
+	return 0;
+}
+
+ssize_t modulation_write(struct file *file_p,
+			 const char __user *buffer,
+			 size_t count, loff_t *actual_data_written)
+{
+	int rate;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	if (copy_from_user(usr_input, buffer, min(count, sizeof(usr_input))))
+		return -EFAULT;
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return count;
+}
diff --git a/drivers/net/wireless/purelifi/firmware.c b/drivers/net/wireless/purelifi/firmware.c
new file mode 100644
index 000000000000..c53e25e02b31
--- /dev/null
+++ b/drivers/net/wireless/purelifi/firmware.c
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/firmware.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+	int i, r, actual_length;
+	int fw_data_i, blk_tran_len = 16384;
+	const char *fw_name;
+	unsigned char fpga_setting[2];
+	unsigned char *fpga_dmabuff;
+	unsigned char *fw_data;
+	unsigned char fpga_state[9];
+	struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga.bit";
+		dev_info(&intf->dev, "bit file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga_xc.bit";
+		dev_info(&intf->dev, "bit file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware((const struct firmware **)&fw, fw_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+
+	send_vendor_request(udev, 0x33, fpga_setting, sizeof(fpga_setting));
+
+	send_vendor_command(udev, 0x34, NULL, 0);
+
+	if (fpga_setting[0] != 6) {
+		dev_err(&intf->dev, "fpga_setting[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmalloc(blk_tran_len, GFP_KERNEL);
+
+		memcpy(fw_data, &fw->data[fw_data_i], blk_tran_len);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			fw_data[tbuf_idx] =
+				((fw_data[tbuf_idx] & 128) >> 7) |
+				((fw_data[tbuf_idx] &  64) >> 5) |
+				((fw_data[tbuf_idx] &  32) >> 3) |
+				((fw_data[tbuf_idx] &  16) >> 1) |
+				((fw_data[tbuf_idx] &   8) << 1) |
+				((fw_data[tbuf_idx] &   4) << 3) |
+				((fw_data[tbuf_idx] &   2) << 5) |
+				((fw_data[tbuf_idx] &   1) << 7);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_setting[0] & 0xFF),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2000);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(9, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xFF, 9);
+
+	send_vendor_request(udev, 0x30, fpga_dmabuff, sizeof(fpga_state));
+
+	dev_info(&intf->dev, "fpga_status");
+	for (i = 0; i <= 8; i++)
+		dev_info(&intf->dev, " %x ", fpga_dmabuff[i]);
+	dev_info(&intf->dev, "\n");
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		kfree(fpga_dmabuff);
+		goto error_free_fw;
+	}
+
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x35, NULL, 0);
+
+	msleep(200);
+
+error_free_fw:
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+	int r;
+
+	struct firmware *fw_packed = NULL;
+	int step;
+	u8 *buf;
+	u32 size, nmb_of_control_packets, i, no_of_files;
+	u32 tot_size, start_addr;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, 0x80, NULL, 0);
+	msleep(100);
+
+	/* Code for single pack file download */
+
+	fw_pack = "purelifi/li_fi_x/Li-Fi-XL.bin";
+
+	r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	no_of_files = *(u32 *)&fw_packed->data[0];
+	tot_size = fw_packed->size;
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n", no_of_files, tot_size);
+	buf = kzalloc(64, GFP_KERNEL);
+	r = -ENOMEM;
+	if (!buf)
+		goto error;
+
+	for (step = 0; step < no_of_files; step++) {
+		buf[0] = step;
+		r = send_vendor_command(udev, 0x82, buf, 64);
+
+		if (step < no_of_files - 1)
+			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
+				- *(u32 *)&fw_packed->data[4 + (step) * 4];
+		else
+			size = tot_size -
+				*(u32 *)&fw_packed->data[4 + (step) * 4];
+
+		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
+
+		if ((size % 64) && step < 2)
+			size += 64 - size % 64;
+		nmb_of_control_packets = size / 64;
+
+		for (i = 0; i < nmb_of_control_packets; i++) {
+			memcpy(buf,
+			       &fw_packed->data[start_addr + (i * 64)], 64);
+			r = send_vendor_command(udev, 0x81, buf, 64);
+		}
+	  dev_info(&intf->dev, "fw-dw step %d,r=%d size %d\n", step, r, size);
+	}
+	release_firmware(fw_packed);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, 0x83, NULL, 0);
+	dev_info(&intf->dev, "download_fpga (4) (%d)\n", r);
+error:
+	return r;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+int upload_mac_and_serial_number(struct usb_interface *intf,
+				 unsigned char *hw_address,
+				 unsigned char *serial_number)
+{
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FILE
+	struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	int r;
+	char *mac_file_name = "purelifi/li_fi_x/mac.ascii";
+
+	r = request_firmware((const struct firmware **)&fw, mac_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	r = sscanf(fw->data,
+		   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+		    &hw_address[0], &hw_address[1],
+		    &hw_address[2], &hw_address[3],
+		    &hw_address[4], &hw_address[5]);
+	if (r != 6) {
+		dev_err(&intf->dev, "fw_dw - usage args fail (%d)\n", r);
+		goto ERROR;
+	}
+	release_firmware(fw);
+
+	char *serial_file_name = "purelifi/li_fi_x/serial.ascii";
+
+	r = request_firmware((const struct firmware **)&fw, serial_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	strlcpy(serial_number, fw->data, PURELIFI_SERIAL_LEN);
+	dev_info(&intf->dev, "serial_number=%s\n", serial_number);
+	release_firmware(fw);
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+ERROR:
+	return r;
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FLASH
+
+#define FLASH_EC_BUSY 3
+
+	struct flash_t flash;
+	unsigned char *flash_sector_buf;
+	unsigned int sector_index;
+
+	do {
+		unsigned char buf[8];
+
+		msleep(200);
+
+		send_vendor_request(udev, 0x40, buf, sizeof(buf));
+		flash.enabled = buf[0] & 0xFF;
+
+		if (flash.enabled) {
+			flash.sectors = ((buf[6] & 255) << 24) |
+				((buf[5] & 255) << 16) |
+				((buf[4] & 255) <<  8) |
+				(buf[3] & 255);
+
+			flash.sector_size =
+				((buf[2] & 255) << 8) | (buf[1] & 255);
+		} else {
+			flash.sectors = 0;
+			flash.sector_size = 0;
+		}
+
+		if (flash.sector_size & 0x8000)
+			flash.sector_size = 1 << (flash.sector_size & 0x7FFF);
+
+		flash.ec = buf[7] & 0xFF;
+	} while (flash.ec == FLASH_EC_BUSY);
+
+	flash_sector_buf = kmalloc(flash.sector_size,
+				   GFP_KERNEL);
+	if (!flash_sector_buf)
+		return -ENOMEM;
+
+	if (!flash.enabled) {
+		dev_err(&intf->dev, "Flash is not enabled\n");
+		return -EINVAL;
+	}
+
+	read_flash(intf, &flash, 0, flash_sector_buf);
+	sector_index = *((unsigned int *)flash_sector_buf);
+
+	read_flash(intf, &flash, sector_index, flash_sector_buf);
+	memcpy(hw_address, flash_sector_buf, ETH_ALEN);
+	strncpy(serial_number, &flash_sector_buf[ETH_ALEN], 256);
+
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+	kfree(flash_sector_buf);
+	return 0;
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_EP0
+
+#define USB_MAC_VENDOR_REQUEST 0x36
+#define USB_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define USB_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define SERIAL_NUMBER_LENGTH 14
+
+	int r = 0;
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(14, GFP_KERNEL);
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	send_vendor_request(udev, USB_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	memcpy(serial_number, dma_buffer, SERIAL_NUMBER_LENGTH);
+
+	memset(dma_buffer, 0x00, 14);
+
+	send_vendor_request(udev, USB_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, 8);
+
+	memcpy(&firmware_version, dma_buffer, sizeof(unsigned long long));
+
+	dev_info(&intf->dev, "Firmware Version: %llu, Revision: 16418\n",
+		 firmware_version);
+	kfree(dma_buffer);
+
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+error:
+	return r;
+#endif
+	return 0;
+}
+
diff --git a/drivers/net/wireless/purelifi/intf.h b/drivers/net/wireless/purelifi/intf.h
new file mode 100644
index 000000000000..20e96671e0a7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/intf.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	u16 rssi;
+	u8  rate_idx;
+	u8  pad;
+	u64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             buf[512];
+};
+
+#endif
diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
new file mode 100644
index 000000000000..5386ec1a7eb4
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.c
@@ -0,0 +1,876 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+struct plf_reg_alpha2_map {
+	u32 reg;
+	char alpha2[2];
+};
+
+static struct plf_reg_alpha2_map reg_alpha2_map[] = {
+	{ PLF_REGDOMAIN_FCC, "US" },
+	{ PLF_REGDOMAIN_IC, "CA" },
+	{ PLF_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
+	{ PLF_REGDOMAIN_JAPAN, "JP" },
+	{ PLF_REGDOMAIN_SPAIN, "ES" },
+	{ PLF_REGDOMAIN_FRANCE, "FR" },
+};
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 },
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr);
+
+static int plf_reg2alpha2(u8 regdomain, char *alpha2)
+{
+	unsigned int i;
+	struct plf_reg_alpha2_map *reg_map;
+		for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) {
+			reg_map = &reg_alpha2_map[i];
+			if (regdomain == reg_map->reg) {
+				alpha2[0] = reg_map->alpha2[0];
+				alpha2[1] = reg_map->alpha2[1];
+				return 0;
+			}
+	}
+	return 1;
+}
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
+{
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+	char alpha2[2];
+
+	mac->default_regdomain = PLF_REGDOMAIN_ETSI;
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	r = plf_reg2alpha2(mac->regdomain, alpha2);
+	r = regulatory_hint(hw->wiphy, alpha2);
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon, false);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status - reports tx status of a packet if required
+ * @hw - a &struct ieee80211_hw pointer
+ * @skb - a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success - True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1, retry = 1;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status) {
+		success = !tx_status->failure;
+		retry = tx_status->retry + success;
+	}
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	dev_info(purelifi_mac_dev(mac), "retry=%d\n", retry);
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		/* ieee80211_tx_status_irqsafe(hw, skb); */
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr)
+{
+	return usb_write_req((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (struct purelifi_ctrlset *)skb_push(skb,
+			sizeof(struct purelifi_ctrlset));
+	cs->id = USB_REQ_DATA_TX;
+	cs->payload_len_nw = frag_len;
+	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
+		- sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned and align data*/
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		cs->len +=  4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data*/
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen b/c
+				 * sufficient headroom was reserved
+				 */
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		cs->len +=  4;
+	}
+
+	cs->id = TO_NETWORK(cs->id);
+	cs->len = TO_NETWORK(cs->len);
+	cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
+
+	return 0;
+}
+
+/**
+ * purelifi_op_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+	info->rate_driver_data[0] = hw;
+
+	if (skb->data[24] == IEEE80211_FTYPE_DATA) {
+		u8 dst_mac[ETH_ALEN];
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		memcpy(dst_mac, &skb->data[28], ETH_ALEN);
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			block_queue(usb, tx->station[sidx].mac, true);
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			dev_kfree_skb(skb);
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
+					USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+		      struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	int found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_info(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++) {
+			skb = __skb_dequeue(q);
+			if (i == position) {
+				purelifi_mac_tx_status(hw, skb,
+						       mac->ack_pending ?
+						       mac->ack_signal : 0,
+						       NULL);
+				mac->ack_pending = 0;
+			}
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	u32 crc_error_cnt_low, crc_error_cnt_high;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_2GHZ;
+	mac->rssi      = -15 * ntohs(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	crc_error_cnt_low = status->crc_error_count;
+	crc_error_cnt_high = status->crc_error_count >> 32;
+	mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
+		ntohl(crc_error_cnt_high);
+
+	if (!bad_frame &&
+	    filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = TO_HOST(*(u32 *)buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (buffer[0] == IEEE80211_STYPE_PROBE_REQ)
+		dev_info(purelifi_mac_dev(mac), "Probe request\n");
+	else if (buffer[0] == IEEE80211_STYPE_ASSOC_REQ)
+		dev_info(purelifi_mac_dev(mac), "Association request\n");
+
+	if (buffer[0] == IEEE80211_STYPE_AUTH) {
+		dev_info(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+	} else if (buffer[0] == IEEE80211_FTYPE_DATA) {
+		unsigned short int seq_nmb = (buffer[23] << 4) |
+			((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000)) {
+			dev_info(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
+		} else {
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		}
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+	if (need_padding) {
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+	}
+
+	memcpy(skb_put(skb, payload_length), buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_info(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			 iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_info(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_info(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon, false);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;
+
+	_ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
+	_ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
+	_ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
+	_ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* 4 for 32 bit alignment if no tailroom */
+
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/mac.h b/drivers/net/wireless/purelifi/mac.h
new file mode 100644
index 000000000000..0c0a6e01a636
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             modulation;
+	u8             control;
+	u8             service;
+	u8             pad;
+	__le16         packet_length;
+	__le16         current_length;
+	__le16          next_frame_length;
+	__le16         tx_length;
+	u32            payload_len_nw;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	spinlock_t intr_lock; /* not used : interrupt lock for mac */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending:1;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
+		*chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block);
+
+#ifdef DEBUG
+void purelifi_dump_rx_status(const struct rx_status *status);
+#else
+#define purelifi_dump_rx_status(status)
+#endif
+
+#endif
diff --git a/drivers/net/wireless/purelifi/usb.c b/drivers/net/wireless/purelifi/usb.c
new file mode 100644
index 000000000000..918324fa6862
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.c
@@ -0,0 +1,1073 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+#define FCS_LEN 4
+
+#undef  LOAD_MAC_AND_SERIAL_FROM_FILE
+#undef  LOAD_MAC_AND_SERIAL_FROM_FLASH
+#define LOAD_MAC_AND_SERIAL_FROM_EP0
+
+#define PROC_FOLDER           "purelifi"
+#define MODULATION_FILENAME   "modulation"
+
+struct proc_dir_entry *proc_folder;
+struct proc_dir_entry *modulation_proc_entry;
+static atomic_t data_queue_flag;
+
+/*Tx retry backoff timer (in milliseconds).*/
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+
+/*Tx retry backoff timer (in jiffies).*/
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+const struct device_attribute purelifi_attr_frequency = {
+	  .attr = {.name = "frequency", .mode = (0666)},
+	  .show = purelifi_show_sysfs,
+	  .store = purelifi_store_frequency,
+};
+
+struct device_attribute purelifi_attr_modulation = {
+	.attr = {.name = "modulation", .mode = (0666)},
+	.show = purelifi_show_modulation,
+	.store = purelifi_store_modulation,
+};
+
+const struct proc_ops  modulation_fops = {
+	.proc_open  = modulation_open,
+	.proc_read  = modulation_read,
+	.proc_write = modulation_write
+};
+
+struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				    tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			block_queue(usb, tx->station[sidx].mac, false);
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int rx_usb_enabled;
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
+		return;
+	} else if (!urb->context) {
+		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
+
+	tx = &usb->tx;
+
+	if (urb->actual_length != 8) {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[7];
+
+	dev_info(&usb->intf->dev, "Recv status=%u\n", status);
+	dev_info(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+		 buffer[0], buffer[1], buffer[2], buffer[3],
+		 buffer[4], buffer[5]);
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_info(&usb->intf->dev,
+			 "FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_info(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= 0xFD;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		fpga_link_connection_f = 1;
+		dev_info(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		fpga_link_connection_f = 0;
+		dev_info(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_info(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit failed (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0; /*no error return*/
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	u32 payload_len_nw = TO_NETWORK((buffer_len + FCS_LEN));
+
+	usb_req->id = usb_req_id;
+	usb_req->len  = 0;
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == USB_REQ_BEACON_WR) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		usb_req->len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	usb_req->len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	usb_req->len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+		usb_req->len += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->id = TO_NETWORK(usb_req->id);
+	usb_req->len = TO_NETWORK(usb_req->len);
+}
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			usb_complete_t complete_fn,
+			void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(usb_req.id) + sizeof(usb_req.len) +
+			   TO_HOST(usb_req.len);
+
+	dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+	memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	static unsigned char hw_address[ETH_ALEN];
+	static unsigned char serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	create_config(intf);
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial_number(intf, hw_address, serial_number);
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg(&intf->dev, "register device failed (%d)\n", r);
+		goto error;
+	}
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(200);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_info(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = usb_write_req(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_info(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	device_remove_file(&intf->dev, &purelifi_attr_modulation);
+	device_remove_file(&intf->dev, &purelifi_attr_frequency);
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	mutex_lock(&mac->chip.mutex);
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	mutex_unlock(&mac->chip.mutex);
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+	.suspend        = suspend,
+	.resume         = resume,
+	.disable_hub_initiated_lpm = 1,
+};
+
+struct workqueue_struct *purelifi_workqueue;
+
+static int create_proc_entry_e(struct proc_dir_entry *entry,
+			       size_t size,
+			       mode_t mode,
+			       const struct proc_ops *pops,
+			       const char *entry_name,
+			       struct proc_dir_entry *parent
+			     )
+{
+	entry = proc_create(entry_name, mode, parent, pops);
+	if (!(entry))
+		return -ENOMEM;
+	return 0;
+}
+
+static int __init usb_init(void)
+{
+	int r;
+
+	proc_folder = proc_mkdir(PROC_FOLDER, NULL);
+	if (!proc_folder) {
+		pr_err("error creating proc folder\n");
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = create_proc_entry_e(modulation_proc_entry, sizeof(u32), 0666,
+				&modulation_fops, MODULATION_FILENAME,
+				proc_folder);
+	if (r)
+		goto remove_proc_dir;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto remove_modulation_proc_file;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+remove_modulation_proc_file:
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+remove_proc_dir:
+	remove_proc_entry(PROC_FOLDER, NULL);
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+	remove_proc_entry(PROC_FOLDER, NULL);
+	if (ez_usb_interface) {
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_modulation);
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_frequency);
+	}
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi Ltd");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("purelifi/li_fi_x/fpga.bit");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/usb.h b/drivers/net/wireless/purelifi/usb.h
new file mode 100644
index 000000000000..7bba5fef2ab2
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
+		      (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
+
+#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
+
+#define TO_NETWORK_32(X) TO_NETWORK(X)
+
+#define TO_HOST(X)    TO_NETWORK(X)
+#define TO_HOST_16(X) TO_NETWORK_16(X)
+#define TO_HOST_32(X) TO_NETWORK_32(X)
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2      |     1     |     0	    |
+   // Reserved  | Heartbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+extern const struct device_attribute purelifi_attr_frequency;
+extern struct device_attribute purelifi_attr_modulation;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+#define PURELIFI_SERIAL_LEN 256
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	u8 initialized:1, was_running:1;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+		usb_complete_t complete_fn, void *context);
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
+		*usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *purelifi_intf_to_hw(struct usb_interface
+		*intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
+		*usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/*Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+int upload_mac_and_serial_number(struct usb_interface *intf,
+				 unsigned char *hw_address,
+				 unsigned char *serial_number);
+void create_config(struct usb_interface *intf);
+
+/* Debugfs declarations */
+
+ssize_t purelifi_store_frequency(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t len);
+
+ssize_t purelifi_show_sysfs(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf);
+
+ssize_t purelifi_store_modulation(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t len);
+
+ssize_t purelifi_show_modulation(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf);
+
+int modulation_open(struct inode *inode_p, struct file *file_p);
+
+ssize_t modulation_read(struct file *file_p,
+			char __user *user_p,
+			size_t offset, loff_t *off_p);
+
+ssize_t modulation_write(struct file *file_p,
+			 const char __user *buffer,
+			 size_t count, loff_t *actual_data_written);
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v7] wireless: Initial driver submission for pureLiFi STA devices
  2020-11-16  9:22     ` [PATCH] [v7] " Srinivasan Raju
@ 2020-11-16 20:45       ` Joe Perches
  2020-11-18  3:24         ` Srinivasan Raju
  2020-11-24 14:44       ` Kalle Valo
       [not found]       ` <20201124144448.4E95EC43460@smtp.codeaurora.org>
  2 siblings, 1 reply; 108+ messages in thread
From: Joe Perches @ 2020-11-16 20:45 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

On Mon, 2020-11-16 at 14:52 +0530, Srinivasan Raju wrote:
> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
> 
> This driver implementation has been based on the zd1211rw driver.
> 
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
> 
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
> 
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

trivial notes and some style and content defects:
(I stopped reading after awhile)

Commonly this changelog would go below the --- separator line.

> 
> Changes v6->v7:
> - Magic numbers removed and used IEEE80211 macors
> - usb.c is split into two files firmware.c and dbgfs.c
> - Other code style and timer function fixes (mod_timer)
> Changes v5->v6:
> - Code style fix patch from Joe Perches
> Changes v4->v5:
> - Code refactoring for clarity and redundnacy removal
> - Fix warnings from kernel test robot
> Changes v3->v4:
> - Code refactoring based on kernel code guidelines
> - Remove multi level macors and use kernel debug macros
> Changes v2->v3:
> - Code style fixes kconfig fix
> Changes v1->v2:
> - v1 was submitted to staging, v2 submitted to wireless-next
> - Code style fixes and copyright statement fix
> ---
>  MAINTAINERS                              |    5 +

[]

> diff --git a/MAINTAINERS b/MAINTAINERS
[]
> @@ -14108,6 +14108,11 @@ T:	git git://linuxtv.org/media_tree.git
[]
> +PUREILIFI USB DRIVER
> +M:	Srinivasan Raju <srini.raju@purelifi.com>
> +S:	Maintained

If you are employed there and are paid to maintain this code the
more common S: marking is "Supported"

> diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
[]
> +++ b/drivers/net/wireless/purelifi/Kconfig
> @@ -0,0 +1,27 @@
> +# SPDX-License-Identifier: GPL-2.0

For clarity, I think it'd be nicer to use GPL-2.0-only here and elsewhere.

> diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
[]
> +int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
> +				 u8 dtim_period, int type)
> +{
> +	if (!interval || (chip->beacon_set &&
> +			  chip->beacon_interval == interval)) {
> +		return 0;
> +	}

It's ddd that checkpatch isn't complaining about single statement uses
with braces.  I would write this like the below, but there isn't really
anything wrong with what you did either.

	if (!interval ||
	    (chip->beacon_set && chip->beacon_interval == interval))
		return 0;

> +void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
> +{
> +	u8 value;
> +
> +	value = 0;

why not make this:

	static const u8 value = 0;

> +	usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_RXTX_WR);

so this is doesn't need a cast

	usb_write_req(&value, sizeof(value), USB_REQ_RXTX_WR);

> +int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
> +{
> +	int r;
> +
> +	if (!chip)
> +		return -EINVAL;
> +
> +	r = usb_write_req((const u8 *)&rate, sizeof(rate), USB_REQ_RATE_WR);

why is the cast needed here?

> +static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
> +					u8 *addr
> +					)

Odd close parenthesis location

> diff --git a/drivers/net/wireless/purelifi/dbgfs.c b/drivers/net/wireless/purelifi/dbgfs.c
[]
> +ssize_t purelifi_store_frequency(struct device *dev,
> +				 struct device_attribute *attr,
> +				 const char *buf,
> +				 size_t len)
> +{
[]
> +	if (valid) {
> +		usr_input[0] = (char)predivider;
> +		usr_input[1] = (char)feedback_divider;
> +		usr_input[2] = (char)output_div_0;
> +		usr_input[3] = (char)output_div_1;
> +		usr_input[4] = (char)output_div_2;
> +		usr_input[5] = (char)clkout3_phase;
> +
> +		dev_err(dev, "options IP_DIV: %d VCO_MULT: %d OP_DIV_PHY: %d",
> +			usr_input[0], usr_input[1], usr_input[2]);
> +		dev_err(dev, "OP_DIV_CPU: %d OP_DIV_USB: %d CLK3_PH: %d\n",
> +			usr_input[3], usr_input[4], usr_input[5]);

why is this dev_err?  It's not an error.
Shouldn't this be dev_notice or dev_info?

> +ssize_t purelifi_show_sysfs(struct device *dev,
> +			    struct device_attribute *attr,
> +			    char *buf)
> +{
> +	return 0;
> +}

This doesn't seem useful.

> +ssize_t purelifi_show_modulation(struct device *dev,
> +				 struct device_attribute *attr,
> +				 char *buf)
> +{
> +	return 0;
> +}

This either.

> diff --git a/drivers/net/wireless/purelifi/firmware.c b/drivers/net/wireless/purelifi/firmware.c
[]
> +int download_fpga(struct usb_interface *intf)
> +{
[]
> +	for (fw_data_i = 0; fw_data_i < fw->size;) {
> +		int tbuf_idx;
> +
> +		if ((fw->size - fw_data_i) < blk_tran_len)
> +			blk_tran_len = fw->size - fw_data_i;
> +
> +		fw_data = kmalloc(blk_tran_len, GFP_KERNEL);
> +
> +		memcpy(fw_data, &fw->data[fw_data_i], blk_tran_len);

Unchecked alloc, and this should probably use kmemdup()

> +	dev_info(&intf->dev, "fpga_status");
> +	for (i = 0; i <= 8; i++)
> +		dev_info(&intf->dev, " %x ", fpga_dmabuff[i]);
> +	dev_info(&intf->dev, "\n");

pr_cont rather than dev_info for the subsequent dev_info uses
otherwise these are all on separate lines.

Or just a single array print of the results and/or use print_hex_dump.

I'd just use:

	dev_info(&intf->dev, "fpga status: %x %x %x %x %x %x %x %x\n",
		  fpga_dmabuff[0], fpga_dmabuff[1],
		  fpga_dmabuff[2], fpga_dmabuff[3],
		  fpga_dmabuff[4], fpga_dmabuff[5],
		  fpga_dmabuff[6], fpga_dmabuff[7]);

[]

> +int download_xl_firmware(struct usb_interface *intf)
> +{
[]
> +	buf = kzalloc(64, GFP_KERNEL);
> +	r = -ENOMEM;
> +	if (!buf)
> +		goto error;

Odd use of setting r before if (!buf) test

> +
> +	for (step = 0; step < no_of_files; step++) {
> +		buf[0] = step;
> +		r = send_vendor_command(udev, 0x82, buf, 64);
> +
> +		if (step < no_of_files - 1)
> +			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
> +				- *(u32 *)&fw_packed->data[4 + (step) * 4];
> +		else
> +			size = tot_size -
> +				*(u32 *)&fw_packed->data[4 + (step) * 4];
> +
> +		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
> +
> +		if ((size % 64) && step < 2)
> +			size += 64 - size % 64;
> +		nmb_of_control_packets = size / 64;
> +
> +		for (i = 0; i < nmb_of_control_packets; i++) {
> +			memcpy(buf,
> +			       &fw_packed->data[start_addr + (i * 64)], 64);
> +			r = send_vendor_command(udev, 0x81, buf, 64);

unchecked return

> +		}
> +	  dev_info(&intf->dev, "fw-dw step %d,r=%d size %d\n", step, r, size);

Odd indentation too

> +int upload_mac_and_serial_number(struct usb_interface *intf,
> +				 unsigned char *hw_address,
> +				 unsigned char *serial_number)
> +{
> +#ifdef LOAD_MAC_AND_SERIAL_FROM_FILE
> +	struct firmware *fw = NULL;
> +	struct usb_device *udev = interface_to_usbdev(intf);
> +	int r;
> +	char *mac_file_name = "purelifi/li_fi_x/mac.ascii";
> +
> +	r = request_firmware((const struct firmware **)&fw, mac_file_name,
> +			     &intf->dev);
> +	if (r) {
> +		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
> +		goto ERROR;
> +	}
> +	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
> +	r = sscanf(fw->data,
> +		   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
> +		    &hw_address[0], &hw_address[1],
> +		    &hw_address[2], &hw_address[3],
> +		    &hw_address[4], &hw_address[5]);
> +	if (r != 6) {
> +		dev_err(&intf->dev, "fw_dw - usage args fail (%d)\n", r);
> +		goto ERROR;
> +	}
> +	release_firmware(fw);
> +
> +	char *serial_file_name = "purelifi/li_fi_x/serial.ascii";

Please move this to the start of the block below the #ifdef

It may make more sense to separate this into multiple functions.

> diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
[]
> +static struct plf_reg_alpha2_map reg_alpha2_map[] = {

static const?

> +static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
> +		      struct ieee80211_rx_status *stats)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct sk_buff *skb;
> +	struct sk_buff_head *q;
> +	unsigned long flags;
> +	int found = 0;

bool ?

I stopped reading here...



^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v7] wireless: Initial driver submission for pureLiFi STA devices
  2020-11-16 20:45       ` Joe Perches
@ 2020-11-18  3:24         ` Srinivasan Raju
  0 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-11-18  3:24 UTC (permalink / raw)
  To: Joe Perches
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS


> trivial notes and some style and content defects:
> (I stopped reading after awhile)

Thanks, I will address the comments.

Regards
Srini

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v7] wireless: Initial driver submission for pureLiFi STA devices
  2020-11-16  9:22     ` [PATCH] [v7] " Srinivasan Raju
  2020-11-16 20:45       ` Joe Perches
@ 2020-11-24 14:44       ` Kalle Valo
       [not found]       ` <20201124144448.4E95EC43460@smtp.codeaurora.org>
  2 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2020-11-24 14:44 UTC (permalink / raw)
  To: Srinivasan Raju

Srinivasan Raju <srini.raju@purelifi.com> wrote:

> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
> 
> This driver implementation has been based on the zd1211rw driver.
> 
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
> 
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
> 
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
> 
> Changes v6->v7:
> - Magic numbers removed and used IEEE80211 macors
> - usb.c is split into two files firmware.c and dbgfs.c
> - Other code style and timer function fixes (mod_timer)
> Changes v5->v6:
> - Code style fix patch from Joe Perches
> Changes v4->v5:
> - Code refactoring for clarity and redundnacy removal
> - Fix warnings from kernel test robot
> Changes v3->v4:
> - Code refactoring based on kernel code guidelines
> - Remove multi level macors and use kernel debug macros
> Changes v2->v3:
> - Code style fixes kconfig fix
> Changes v1->v2:
> - v1 was submitted to staging, v2 submitted to wireless-next
> - Code style fixes and copyright statement fix

I haven't had a chance to review this yet but we have some documentation for new drivers:

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches#new_driver

Is the firmware publically available?

-- 
https://patchwork.kernel.org/project/linux-wireless/patch/20201116092253.1302196-1-srini.raju@purelifi.com/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v7] wireless: Initial driver submission for pureLiFi STA devices
       [not found]       ` <20201124144448.4E95EC43460@smtp.codeaurora.org>
@ 2020-11-26  5:01         ` Srinivasan Raju
  2020-12-03  4:43           ` Srinivasan Raju
  0 siblings, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2020-11-26  5:01 UTC (permalink / raw)
  To: Kalle Valo
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS



> I haven't had a chance to review this yet but we have some documentation for new drivers:

> https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches#new_driver

> Is the firmware publically available?

Thanks Kalle, We will make the firmware available in our website for public access and share the details.

Regards
Srini


^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v8] wireless: Initial driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (3 preceding siblings ...)
  2020-10-16  6:34   ` [PATCH] [v4] " Srinivasan Raju
@ 2020-12-03  4:38   ` Srinivasan Raju
  2020-12-03  5:09   ` [PATCH] [v9] " Srinivasan Raju
                     ` (14 subsequent siblings)
  19 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-12-03  4:38 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Mauro Carvalho Chehab, Rob Herring,
	Lukas Bulwahn, open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

---
v8:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7:
- Magic numbers removed and used IEEE80211 macors
- usb.c is split into two files firmware.c and dbgfs.c
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging
---
 MAINTAINERS                              |    5 +
 drivers/net/wireless/Kconfig             |    1 +
 drivers/net/wireless/Makefile            |    1 +
 drivers/net/wireless/purelifi/Kconfig    |   27 +
 drivers/net/wireless/purelifi/Makefile   |    3 +
 drivers/net/wireless/purelifi/chip.c     |   94 ++
 drivers/net/wireless/purelifi/chip.h     |   82 ++
 drivers/net/wireless/purelifi/dbgfs.c    |  161 ++++
 drivers/net/wireless/purelifi/firmware.c |  367 ++++++++
 drivers/net/wireless/purelifi/intf.h     |   38 +
 drivers/net/wireless/purelifi/mac.c      |  876 ++++++++++++++++++
 drivers/net/wireless/purelifi/mac.h      |  189 ++++
 drivers/net/wireless/purelifi/usb.c      | 1073 ++++++++++++++++++++++
 drivers/net/wireless/purelifi/usb.h      |  197 ++++
 14 files changed, 3114 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/chip.c
 create mode 100644 drivers/net/wireless/purelifi/chip.h
 create mode 100644 drivers/net/wireless/purelifi/dbgfs.c
 create mode 100644 drivers/net/wireless/purelifi/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/intf.h
 create mode 100644 drivers/net/wireless/purelifi/mac.c
 create mode 100644 drivers/net/wireless/purelifi/mac.h
 create mode 100644 drivers/net/wireless/purelifi/usb.c
 create mode 100644 drivers/net/wireless/purelifi/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index c80f87d7258c..150f592fb6e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14108,6 +14108,11 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PUREILIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+S:	Maintained
+F:	drivers/net/wireless/purelifi
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 170a64e67709..b87da3139f94 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -48,6 +48,7 @@ source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
 source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 
 config PCMCIA_RAYCS
 	tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e9fc770026f0 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
 obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..f6630791df9d
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+config PURELIFI
+
+	tristate "pureLiFi device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This driver makes the adapter appear as a normal WLAN interface.
+
+	   The pureLiFi device requires external STA firmware to be loaded.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called purelifi.
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..cf9b931840ac
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PURELIFI)		:= purelifi.o
+purelifi-objs 		+= chip.o usb.o mac.o firmware.o dbgfs.o
diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
new file mode 100644
index 000000000000..4282ce1d92b4
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval || (chip->beacon_set &&
+			  chip->beacon_interval == interval)) {
+		return 0;
+	}
+
+	chip->beacon_interval = interval;
+	chip->beacon_set = true;
+	return usb_write_req((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %02x:%02x v%02x  %02x-%02x-%02x %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr[0], addr[1], addr[2],
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
+{
+	int r;
+
+	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	u8 value;
+
+	value = 0;
+	usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = usb_write_req((const u8 *)&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/chip.h b/drivers/net/wireless/purelifi/chip.h
new file mode 100644
index 000000000000..f479f34a6503
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	u16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr
+					)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1 << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/dbgfs.c b/drivers/net/wireless/purelifi/dbgfs.c
new file mode 100644
index 000000000000..411c0a423a23
--- /dev/null
+++ b/drivers/net/wireless/purelifi/dbgfs.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include "mac.h"
+#include "usb.h"
+
+ssize_t purelifi_store_frequency(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t len)
+{
+	int i, r, row, col, predivider, feedback_divider, output_div_0;
+	int output_div_1, output_div_2, clkout3_phase;
+	char values[6][4] = {{0}, {0} };
+	char usr_input[8] = {0};
+	bool valid = false;
+	int rv = 0;
+
+	row = 0; col = 0;
+	for (i = 0; i < min(len, sizeof(values)); i++) {
+		values[row][col] = buf[i];
+		col++;
+		if (buf[i] == 0x20) {
+			row++;
+			col = 0;
+		} else if (buf[i] == 0x0A) {
+			if (row == 5 && col > 0) {
+				rv = sscanf((char *)&values[0][0],
+					    "%d", &predivider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[1][0],
+					    "%d", &feedback_divider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[2][0],
+					    "%d", &output_div_0);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[3][0],
+					    "%d", &output_div_1);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[4][0],
+					    "%d", &output_div_2);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[5][0],
+					    "%d", &clkout3_phase);
+				if (rv != 1)
+					valid = false;
+				valid = true;
+
+				break;
+			}
+		}
+	}
+
+	if (valid) {
+		usr_input[0] = (char)predivider;
+		usr_input[1] = (char)feedback_divider;
+		usr_input[2] = (char)output_div_0;
+		usr_input[3] = (char)output_div_1;
+		usr_input[4] = (char)output_div_2;
+		usr_input[5] = (char)clkout3_phase;
+
+		dev_err(dev, "options IP_DIV: %d VCO_MULT: %d OP_DIV_PHY: %d",
+			usr_input[0], usr_input[1], usr_input[2]);
+		dev_err(dev, "OP_DIV_CPU: %d OP_DIV_USB: %d CLK3_PH: %d\n",
+			usr_input[3], usr_input[4], usr_input[5]);
+
+		r = usb_write_req((const u8 *)&usr_input[0],
+				  sizeof(usr_input), USB_REQ_SET_FREQ);
+		if (r < 0)
+			dev_err(dev, "SET_FREQ failed (%d)\n", r);
+	} else {
+		dev_err(dev, "Your options are invalid\n");
+	}
+
+	return len;
+}
+
+ssize_t purelifi_show_sysfs(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	return 0;
+}
+
+ssize_t purelifi_store_modulation(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t len)
+{
+	int rate, i;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	for (i = 0; i < min(len, sizeof(usr_input)); i++)
+		usr_input[i] = buf[i];
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return len;
+}
+
+ssize_t purelifi_show_modulation(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	return 0;
+}
+
+void create_config(struct usb_interface *intf)
+{
+	struct device *dev;
+
+	dev = &intf->dev;
+	if (device_create_file(dev, &purelifi_attr_modulation)) {
+		dev_err(&intf->dev,
+			"failed to create modulation sysfs entry\n");
+		return;
+	}
+	if (device_create_file(dev, &purelifi_attr_frequency)) {
+		device_remove_file(dev, &purelifi_attr_modulation);
+		dev_err(&intf->dev,
+			"failed to create frequency sysfs entry\n");
+	}
+}
+
+int modulation_open(struct inode *inode_p, struct file *file_p)
+{
+	return 0;
+}
+
+ssize_t modulation_read(struct file *file_p,
+			char __user *user_p,
+			size_t offset, loff_t *off_p)
+{
+	return 0;
+}
+
+ssize_t modulation_write(struct file *file_p,
+			 const char __user *buffer,
+			 size_t count, loff_t *actual_data_written)
+{
+	int rate;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	if (copy_from_user(usr_input, buffer, min(count, sizeof(usr_input))))
+		return -EFAULT;
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return count;
+}
diff --git a/drivers/net/wireless/purelifi/firmware.c b/drivers/net/wireless/purelifi/firmware.c
new file mode 100644
index 000000000000..c53e25e02b31
--- /dev/null
+++ b/drivers/net/wireless/purelifi/firmware.c
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/firmware.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+	int i, r, actual_length;
+	int fw_data_i, blk_tran_len = 16384;
+	const char *fw_name;
+	unsigned char fpga_setting[2];
+	unsigned char *fpga_dmabuff;
+	unsigned char *fw_data;
+	unsigned char fpga_state[9];
+	struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga.bit";
+		dev_info(&intf->dev, "bit file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga_xc.bit";
+		dev_info(&intf->dev, "bit file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware((const struct firmware **)&fw, fw_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+
+	send_vendor_request(udev, 0x33, fpga_setting, sizeof(fpga_setting));
+
+	send_vendor_command(udev, 0x34, NULL, 0);
+
+	if (fpga_setting[0] != 6) {
+		dev_err(&intf->dev, "fpga_setting[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmalloc(blk_tran_len, GFP_KERNEL);
+
+		memcpy(fw_data, &fw->data[fw_data_i], blk_tran_len);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			fw_data[tbuf_idx] =
+				((fw_data[tbuf_idx] & 128) >> 7) |
+				((fw_data[tbuf_idx] &  64) >> 5) |
+				((fw_data[tbuf_idx] &  32) >> 3) |
+				((fw_data[tbuf_idx] &  16) >> 1) |
+				((fw_data[tbuf_idx] &   8) << 1) |
+				((fw_data[tbuf_idx] &   4) << 3) |
+				((fw_data[tbuf_idx] &   2) << 5) |
+				((fw_data[tbuf_idx] &   1) << 7);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_setting[0] & 0xFF),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2000);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(9, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xFF, 9);
+
+	send_vendor_request(udev, 0x30, fpga_dmabuff, sizeof(fpga_state));
+
+	dev_info(&intf->dev, "fpga_status");
+	for (i = 0; i <= 8; i++)
+		dev_info(&intf->dev, " %x ", fpga_dmabuff[i]);
+	dev_info(&intf->dev, "\n");
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		kfree(fpga_dmabuff);
+		goto error_free_fw;
+	}
+
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x35, NULL, 0);
+
+	msleep(200);
+
+error_free_fw:
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+	int r;
+
+	struct firmware *fw_packed = NULL;
+	int step;
+	u8 *buf;
+	u32 size, nmb_of_control_packets, i, no_of_files;
+	u32 tot_size, start_addr;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, 0x80, NULL, 0);
+	msleep(100);
+
+	/* Code for single pack file download */
+
+	fw_pack = "purelifi/li_fi_x/Li-Fi-XL.bin";
+
+	r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	no_of_files = *(u32 *)&fw_packed->data[0];
+	tot_size = fw_packed->size;
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n", no_of_files, tot_size);
+	buf = kzalloc(64, GFP_KERNEL);
+	r = -ENOMEM;
+	if (!buf)
+		goto error;
+
+	for (step = 0; step < no_of_files; step++) {
+		buf[0] = step;
+		r = send_vendor_command(udev, 0x82, buf, 64);
+
+		if (step < no_of_files - 1)
+			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
+				- *(u32 *)&fw_packed->data[4 + (step) * 4];
+		else
+			size = tot_size -
+				*(u32 *)&fw_packed->data[4 + (step) * 4];
+
+		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
+
+		if ((size % 64) && step < 2)
+			size += 64 - size % 64;
+		nmb_of_control_packets = size / 64;
+
+		for (i = 0; i < nmb_of_control_packets; i++) {
+			memcpy(buf,
+			       &fw_packed->data[start_addr + (i * 64)], 64);
+			r = send_vendor_command(udev, 0x81, buf, 64);
+		}
+	  dev_info(&intf->dev, "fw-dw step %d,r=%d size %d\n", step, r, size);
+	}
+	release_firmware(fw_packed);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, 0x83, NULL, 0);
+	dev_info(&intf->dev, "download_fpga (4) (%d)\n", r);
+error:
+	return r;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+int upload_mac_and_serial_number(struct usb_interface *intf,
+				 unsigned char *hw_address,
+				 unsigned char *serial_number)
+{
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FILE
+	struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	int r;
+	char *mac_file_name = "purelifi/li_fi_x/mac.ascii";
+
+	r = request_firmware((const struct firmware **)&fw, mac_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	r = sscanf(fw->data,
+		   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+		    &hw_address[0], &hw_address[1],
+		    &hw_address[2], &hw_address[3],
+		    &hw_address[4], &hw_address[5]);
+	if (r != 6) {
+		dev_err(&intf->dev, "fw_dw - usage args fail (%d)\n", r);
+		goto ERROR;
+	}
+	release_firmware(fw);
+
+	char *serial_file_name = "purelifi/li_fi_x/serial.ascii";
+
+	r = request_firmware((const struct firmware **)&fw, serial_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	strlcpy(serial_number, fw->data, PURELIFI_SERIAL_LEN);
+	dev_info(&intf->dev, "serial_number=%s\n", serial_number);
+	release_firmware(fw);
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+ERROR:
+	return r;
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FLASH
+
+#define FLASH_EC_BUSY 3
+
+	struct flash_t flash;
+	unsigned char *flash_sector_buf;
+	unsigned int sector_index;
+
+	do {
+		unsigned char buf[8];
+
+		msleep(200);
+
+		send_vendor_request(udev, 0x40, buf, sizeof(buf));
+		flash.enabled = buf[0] & 0xFF;
+
+		if (flash.enabled) {
+			flash.sectors = ((buf[6] & 255) << 24) |
+				((buf[5] & 255) << 16) |
+				((buf[4] & 255) <<  8) |
+				(buf[3] & 255);
+
+			flash.sector_size =
+				((buf[2] & 255) << 8) | (buf[1] & 255);
+		} else {
+			flash.sectors = 0;
+			flash.sector_size = 0;
+		}
+
+		if (flash.sector_size & 0x8000)
+			flash.sector_size = 1 << (flash.sector_size & 0x7FFF);
+
+		flash.ec = buf[7] & 0xFF;
+	} while (flash.ec == FLASH_EC_BUSY);
+
+	flash_sector_buf = kmalloc(flash.sector_size,
+				   GFP_KERNEL);
+	if (!flash_sector_buf)
+		return -ENOMEM;
+
+	if (!flash.enabled) {
+		dev_err(&intf->dev, "Flash is not enabled\n");
+		return -EINVAL;
+	}
+
+	read_flash(intf, &flash, 0, flash_sector_buf);
+	sector_index = *((unsigned int *)flash_sector_buf);
+
+	read_flash(intf, &flash, sector_index, flash_sector_buf);
+	memcpy(hw_address, flash_sector_buf, ETH_ALEN);
+	strncpy(serial_number, &flash_sector_buf[ETH_ALEN], 256);
+
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+	kfree(flash_sector_buf);
+	return 0;
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_EP0
+
+#define USB_MAC_VENDOR_REQUEST 0x36
+#define USB_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define USB_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define SERIAL_NUMBER_LENGTH 14
+
+	int r = 0;
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(14, GFP_KERNEL);
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	send_vendor_request(udev, USB_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	memcpy(serial_number, dma_buffer, SERIAL_NUMBER_LENGTH);
+
+	memset(dma_buffer, 0x00, 14);
+
+	send_vendor_request(udev, USB_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, 8);
+
+	memcpy(&firmware_version, dma_buffer, sizeof(unsigned long long));
+
+	dev_info(&intf->dev, "Firmware Version: %llu, Revision: 16418\n",
+		 firmware_version);
+	kfree(dma_buffer);
+
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+error:
+	return r;
+#endif
+	return 0;
+}
+
diff --git a/drivers/net/wireless/purelifi/intf.h b/drivers/net/wireless/purelifi/intf.h
new file mode 100644
index 000000000000..20e96671e0a7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/intf.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	u16 rssi;
+	u8  rate_idx;
+	u8  pad;
+	u64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             buf[512];
+};
+
+#endif
diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
new file mode 100644
index 000000000000..5386ec1a7eb4
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.c
@@ -0,0 +1,876 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+struct plf_reg_alpha2_map {
+	u32 reg;
+	char alpha2[2];
+};
+
+static struct plf_reg_alpha2_map reg_alpha2_map[] = {
+	{ PLF_REGDOMAIN_FCC, "US" },
+	{ PLF_REGDOMAIN_IC, "CA" },
+	{ PLF_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
+	{ PLF_REGDOMAIN_JAPAN, "JP" },
+	{ PLF_REGDOMAIN_SPAIN, "ES" },
+	{ PLF_REGDOMAIN_FRANCE, "FR" },
+};
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 },
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr);
+
+static int plf_reg2alpha2(u8 regdomain, char *alpha2)
+{
+	unsigned int i;
+	struct plf_reg_alpha2_map *reg_map;
+		for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) {
+			reg_map = &reg_alpha2_map[i];
+			if (regdomain == reg_map->reg) {
+				alpha2[0] = reg_map->alpha2[0];
+				alpha2[1] = reg_map->alpha2[1];
+				return 0;
+			}
+	}
+	return 1;
+}
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
+{
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+	char alpha2[2];
+
+	mac->default_regdomain = PLF_REGDOMAIN_ETSI;
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	r = plf_reg2alpha2(mac->regdomain, alpha2);
+	r = regulatory_hint(hw->wiphy, alpha2);
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon, false);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status - reports tx status of a packet if required
+ * @hw - a &struct ieee80211_hw pointer
+ * @skb - a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success - True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1, retry = 1;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status) {
+		success = !tx_status->failure;
+		retry = tx_status->retry + success;
+	}
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	dev_info(purelifi_mac_dev(mac), "retry=%d\n", retry);
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		/* ieee80211_tx_status_irqsafe(hw, skb); */
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr)
+{
+	return usb_write_req((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (struct purelifi_ctrlset *)skb_push(skb,
+			sizeof(struct purelifi_ctrlset));
+	cs->id = USB_REQ_DATA_TX;
+	cs->payload_len_nw = frag_len;
+	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
+		- sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned and align data*/
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		cs->len +=  4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data*/
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen b/c
+				 * sufficient headroom was reserved
+				 */
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		cs->len +=  4;
+	}
+
+	cs->id = TO_NETWORK(cs->id);
+	cs->len = TO_NETWORK(cs->len);
+	cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
+
+	return 0;
+}
+
+/**
+ * purelifi_op_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+	info->rate_driver_data[0] = hw;
+
+	if (skb->data[24] == IEEE80211_FTYPE_DATA) {
+		u8 dst_mac[ETH_ALEN];
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		memcpy(dst_mac, &skb->data[28], ETH_ALEN);
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			block_queue(usb, tx->station[sidx].mac, true);
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			dev_kfree_skb(skb);
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
+					USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+		      struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	int found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_info(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++) {
+			skb = __skb_dequeue(q);
+			if (i == position) {
+				purelifi_mac_tx_status(hw, skb,
+						       mac->ack_pending ?
+						       mac->ack_signal : 0,
+						       NULL);
+				mac->ack_pending = 0;
+			}
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	u32 crc_error_cnt_low, crc_error_cnt_high;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_2GHZ;
+	mac->rssi      = -15 * ntohs(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	crc_error_cnt_low = status->crc_error_count;
+	crc_error_cnt_high = status->crc_error_count >> 32;
+	mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
+		ntohl(crc_error_cnt_high);
+
+	if (!bad_frame &&
+	    filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = TO_HOST(*(u32 *)buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (buffer[0] == IEEE80211_STYPE_PROBE_REQ)
+		dev_info(purelifi_mac_dev(mac), "Probe request\n");
+	else if (buffer[0] == IEEE80211_STYPE_ASSOC_REQ)
+		dev_info(purelifi_mac_dev(mac), "Association request\n");
+
+	if (buffer[0] == IEEE80211_STYPE_AUTH) {
+		dev_info(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+	} else if (buffer[0] == IEEE80211_FTYPE_DATA) {
+		unsigned short int seq_nmb = (buffer[23] << 4) |
+			((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000)) {
+			dev_info(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
+		} else {
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		}
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+	if (need_padding) {
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+	}
+
+	memcpy(skb_put(skb, payload_length), buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_info(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			 iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_info(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_info(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon, false);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;
+
+	_ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
+	_ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
+	_ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
+	_ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* 4 for 32 bit alignment if no tailroom */
+
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/mac.h b/drivers/net/wireless/purelifi/mac.h
new file mode 100644
index 000000000000..0c0a6e01a636
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             modulation;
+	u8             control;
+	u8             service;
+	u8             pad;
+	__le16         packet_length;
+	__le16         current_length;
+	__le16          next_frame_length;
+	__le16         tx_length;
+	u32            payload_len_nw;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	spinlock_t intr_lock; /* not used : interrupt lock for mac */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending:1;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
+		*chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block);
+
+#ifdef DEBUG
+void purelifi_dump_rx_status(const struct rx_status *status);
+#else
+#define purelifi_dump_rx_status(status)
+#endif
+
+#endif
diff --git a/drivers/net/wireless/purelifi/usb.c b/drivers/net/wireless/purelifi/usb.c
new file mode 100644
index 000000000000..918324fa6862
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.c
@@ -0,0 +1,1073 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+#define FCS_LEN 4
+
+#undef  LOAD_MAC_AND_SERIAL_FROM_FILE
+#undef  LOAD_MAC_AND_SERIAL_FROM_FLASH
+#define LOAD_MAC_AND_SERIAL_FROM_EP0
+
+#define PROC_FOLDER           "purelifi"
+#define MODULATION_FILENAME   "modulation"
+
+struct proc_dir_entry *proc_folder;
+struct proc_dir_entry *modulation_proc_entry;
+static atomic_t data_queue_flag;
+
+/*Tx retry backoff timer (in milliseconds).*/
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+
+/*Tx retry backoff timer (in jiffies).*/
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+const struct device_attribute purelifi_attr_frequency = {
+	  .attr = {.name = "frequency", .mode = (0666)},
+	  .show = purelifi_show_sysfs,
+	  .store = purelifi_store_frequency,
+};
+
+struct device_attribute purelifi_attr_modulation = {
+	.attr = {.name = "modulation", .mode = (0666)},
+	.show = purelifi_show_modulation,
+	.store = purelifi_store_modulation,
+};
+
+const struct proc_ops  modulation_fops = {
+	.proc_open  = modulation_open,
+	.proc_read  = modulation_read,
+	.proc_write = modulation_write
+};
+
+struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				    tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			block_queue(usb, tx->station[sidx].mac, false);
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int rx_usb_enabled;
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
+		return;
+	} else if (!urb->context) {
+		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
+
+	tx = &usb->tx;
+
+	if (urb->actual_length != 8) {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[7];
+
+	dev_info(&usb->intf->dev, "Recv status=%u\n", status);
+	dev_info(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+		 buffer[0], buffer[1], buffer[2], buffer[3],
+		 buffer[4], buffer[5]);
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_info(&usb->intf->dev,
+			 "FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_info(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= 0xFD;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		fpga_link_connection_f = 1;
+		dev_info(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		fpga_link_connection_f = 0;
+		dev_info(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_info(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit failed (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0; /*no error return*/
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	u32 payload_len_nw = TO_NETWORK((buffer_len + FCS_LEN));
+
+	usb_req->id = usb_req_id;
+	usb_req->len  = 0;
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == USB_REQ_BEACON_WR) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		usb_req->len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	usb_req->len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	usb_req->len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+		usb_req->len += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->id = TO_NETWORK(usb_req->id);
+	usb_req->len = TO_NETWORK(usb_req->len);
+}
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			usb_complete_t complete_fn,
+			void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(usb_req.id) + sizeof(usb_req.len) +
+			   TO_HOST(usb_req.len);
+
+	dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+	memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	static unsigned char hw_address[ETH_ALEN];
+	static unsigned char serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	create_config(intf);
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial_number(intf, hw_address, serial_number);
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg(&intf->dev, "register device failed (%d)\n", r);
+		goto error;
+	}
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(200);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_info(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = usb_write_req(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_info(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	device_remove_file(&intf->dev, &purelifi_attr_modulation);
+	device_remove_file(&intf->dev, &purelifi_attr_frequency);
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	mutex_lock(&mac->chip.mutex);
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	mutex_unlock(&mac->chip.mutex);
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+	.suspend        = suspend,
+	.resume         = resume,
+	.disable_hub_initiated_lpm = 1,
+};
+
+struct workqueue_struct *purelifi_workqueue;
+
+static int create_proc_entry_e(struct proc_dir_entry *entry,
+			       size_t size,
+			       mode_t mode,
+			       const struct proc_ops *pops,
+			       const char *entry_name,
+			       struct proc_dir_entry *parent
+			     )
+{
+	entry = proc_create(entry_name, mode, parent, pops);
+	if (!(entry))
+		return -ENOMEM;
+	return 0;
+}
+
+static int __init usb_init(void)
+{
+	int r;
+
+	proc_folder = proc_mkdir(PROC_FOLDER, NULL);
+	if (!proc_folder) {
+		pr_err("error creating proc folder\n");
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = create_proc_entry_e(modulation_proc_entry, sizeof(u32), 0666,
+				&modulation_fops, MODULATION_FILENAME,
+				proc_folder);
+	if (r)
+		goto remove_proc_dir;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto remove_modulation_proc_file;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+remove_modulation_proc_file:
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+remove_proc_dir:
+	remove_proc_entry(PROC_FOLDER, NULL);
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+	remove_proc_entry(PROC_FOLDER, NULL);
+	if (ez_usb_interface) {
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_modulation);
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_frequency);
+	}
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi Ltd");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("purelifi/li_fi_x/fpga.bit");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/usb.h b/drivers/net/wireless/purelifi/usb.h
new file mode 100644
index 000000000000..7bba5fef2ab2
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
+		      (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
+
+#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
+
+#define TO_NETWORK_32(X) TO_NETWORK(X)
+
+#define TO_HOST(X)    TO_NETWORK(X)
+#define TO_HOST_16(X) TO_NETWORK_16(X)
+#define TO_HOST_32(X) TO_NETWORK_32(X)
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2      |     1     |     0	    |
+   // Reserved  | Heartbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+extern const struct device_attribute purelifi_attr_frequency;
+extern struct device_attribute purelifi_attr_modulation;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+#define PURELIFI_SERIAL_LEN 256
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	u8 initialized:1, was_running:1;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+		usb_complete_t complete_fn, void *context);
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
+		*usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *purelifi_intf_to_hw(struct usb_interface
+		*intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
+		*usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/*Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+int upload_mac_and_serial_number(struct usb_interface *intf,
+				 unsigned char *hw_address,
+				 unsigned char *serial_number);
+void create_config(struct usb_interface *intf);
+
+/* Debugfs declarations */
+
+ssize_t purelifi_store_frequency(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t len);
+
+ssize_t purelifi_show_sysfs(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf);
+
+ssize_t purelifi_store_modulation(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t len);
+
+ssize_t purelifi_show_modulation(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf);
+
+int modulation_open(struct inode *inode_p, struct file *file_p);
+
+ssize_t modulation_read(struct file *file_p,
+			char __user *user_p,
+			size_t offset, loff_t *off_p);
+
+ssize_t modulation_write(struct file *file_p,
+			 const char __user *buffer,
+			 size_t count, loff_t *actual_data_written);
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v7] wireless: Initial driver submission for pureLiFi STA devices
  2020-11-26  5:01         ` Srinivasan Raju
@ 2020-12-03  4:43           ` Srinivasan Raju
  2020-12-03 15:58             ` Kalle Valo
  0 siblings, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2020-12-03  4:43 UTC (permalink / raw)
  To: Kalle Valo
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Hi Kalle,

we will be submitting to linux-firmware repository @ https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git
I will share the link once it is accpeted, we have sent another version of the patch v8 , please review and provide your comments

Thanks
Srini

________________________________________
From: Srinivasan Raju <srini.raju@purelifi.com>
Sent: Thursday, November 26, 2020 10:31 AM
To: Kalle Valo
Cc: Mostafa Afgani; David S. Miller; Jakub Kicinski; Mauro Carvalho Chehab; Rob Herring; Lukas Bulwahn; open list; open list:NETWORKING DRIVERS (WIRELESS); open list:NETWORKING DRIVERS
Subject: Re: [PATCH] [v7] wireless: Initial driver submission for pureLiFi STA devices



> I haven't had a chance to review this yet but we have some documentation for new drivers:

> https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches#new_driver

> Is the firmware publically available?

Thanks Kalle, We will make the firmware available in our website for public access and share the details.

Regards
Srini


^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v9] wireless: Initial driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (4 preceding siblings ...)
  2020-12-03  4:38   ` [PATCH] [v8] " Srinivasan Raju
@ 2020-12-03  5:09   ` Srinivasan Raju
  2020-12-03  7:53     ` Joe Perches
  2020-12-08  5:53   ` [PATCH] [v10] " Srinivasan Raju
                     ` (13 subsequent siblings)
  19 siblings, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2020-12-03  5:09 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Mauro Carvalho Chehab, Rob Herring,
	Lukas Bulwahn, open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

---
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- usb.c is split into two files firmware.c and dbgfs.c
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging
---
 MAINTAINERS                              |    5 +
 drivers/net/wireless/Kconfig             |    1 +
 drivers/net/wireless/Makefile            |    1 +
 drivers/net/wireless/purelifi/Kconfig    |   27 +
 drivers/net/wireless/purelifi/Makefile   |    3 +
 drivers/net/wireless/purelifi/chip.c     |   93 ++
 drivers/net/wireless/purelifi/chip.h     |   81 ++
 drivers/net/wireless/purelifi/dbgfs.c    |  161 ++++
 drivers/net/wireless/purelifi/firmware.c |  384 ++++++++
 drivers/net/wireless/purelifi/intf.h     |   38 +
 drivers/net/wireless/purelifi/mac.c      |  873 ++++++++++++++++++
 drivers/net/wireless/purelifi/mac.h      |  189 ++++
 drivers/net/wireless/purelifi/usb.c      | 1075 ++++++++++++++++++++++
 drivers/net/wireless/purelifi/usb.h      |  199 ++++
 14 files changed, 3130 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/chip.c
 create mode 100644 drivers/net/wireless/purelifi/chip.h
 create mode 100644 drivers/net/wireless/purelifi/dbgfs.c
 create mode 100644 drivers/net/wireless/purelifi/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/intf.h
 create mode 100644 drivers/net/wireless/purelifi/mac.c
 create mode 100644 drivers/net/wireless/purelifi/mac.h
 create mode 100644 drivers/net/wireless/purelifi/usb.c
 create mode 100644 drivers/net/wireless/purelifi/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index c80f87d7258c..17955b8497df 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14108,6 +14108,11 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PUREILIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+S:	Supported
+F:	drivers/net/wireless/purelifi
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 170a64e67709..b87da3139f94 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -48,6 +48,7 @@ source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
 source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 
 config PCMCIA_RAYCS
 	tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e9fc770026f0 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
 obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..f6630791df9d
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+config PURELIFI
+
+	tristate "pureLiFi device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This driver makes the adapter appear as a normal WLAN interface.
+
+	   The pureLiFi device requires external STA firmware to be loaded.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called purelifi.
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..cf9b931840ac
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PURELIFI)		:= purelifi.o
+purelifi-objs 		+= chip.o usb.o mac.o firmware.o dbgfs.o
diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
new file mode 100644
index 000000000000..9a7ccd0f98f2
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set && chip->beacon_interval == interval)) {
+		return 0;
+	}
+
+	chip->beacon_interval = interval;
+	chip->beacon_set = true;
+	return usb_write_req((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %02x:%02x v%02x  %02x-%02x-%02x %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr[0], addr[1], addr[2],
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
+{
+	int r;
+
+	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	static const u8 value;
+
+	usb_write_req(&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = usb_write_req(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/chip.h b/drivers/net/wireless/purelifi/chip.h
new file mode 100644
index 000000000000..710bf713b8c4
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	u16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1 << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/dbgfs.c b/drivers/net/wireless/purelifi/dbgfs.c
new file mode 100644
index 000000000000..2a72b57b180e
--- /dev/null
+++ b/drivers/net/wireless/purelifi/dbgfs.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include "mac.h"
+#include "usb.h"
+
+ssize_t purelifi_store_frequency(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t len)
+{
+	int i, r, row, col, predivider, feedback_divider, output_div_0;
+	int output_div_1, output_div_2, clkout3_phase;
+	char values[6][4] = {{0}, {0} };
+	char usr_input[8] = {0};
+	bool valid = false;
+	int rv = 0;
+
+	row = 0; col = 0;
+	for (i = 0; i < min(len, sizeof(values)); i++) {
+		values[row][col] = buf[i];
+		col++;
+		if (buf[i] == 0x20) {
+			row++;
+			col = 0;
+		} else if (buf[i] == 0x0A) {
+			if (row == 5 && col > 0) {
+				rv = sscanf((char *)&values[0][0],
+					    "%d", &predivider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[1][0],
+					    "%d", &feedback_divider);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[2][0],
+					    "%d", &output_div_0);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[3][0],
+					    "%d", &output_div_1);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[4][0],
+					    "%d", &output_div_2);
+				if (rv != 1)
+					valid = false;
+				rv = sscanf((char *)&values[5][0],
+					    "%d", &clkout3_phase);
+				if (rv != 1)
+					valid = false;
+				valid = true;
+
+				break;
+			}
+		}
+	}
+
+	if (valid) {
+		usr_input[0] = (char)predivider;
+		usr_input[1] = (char)feedback_divider;
+		usr_input[2] = (char)output_div_0;
+		usr_input[3] = (char)output_div_1;
+		usr_input[4] = (char)output_div_2;
+		usr_input[5] = (char)clkout3_phase;
+
+		dev_info(dev, "options IP_DIV: %d VCO_MULT: %d OP_DIV_PHY: %d",
+			 usr_input[0], usr_input[1], usr_input[2]);
+		dev_info(dev, "OP_DIV_CPU: %d OP_DIV_USB: %d CLK3_PH: %d\n",
+			 usr_input[3], usr_input[4], usr_input[5]);
+
+		r = usb_write_req((const u8 *)&usr_input[0],
+				  sizeof(usr_input), USB_REQ_SET_FREQ);
+		if (r < 0)
+			dev_err(dev, "SET_FREQ failed (%d)\n", r);
+	} else {
+		dev_err(dev, "Your options are invalid\n");
+	}
+
+	return len;
+}
+
+ssize_t purelifi_show_sysfs(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	return 0;
+}
+
+ssize_t purelifi_store_modulation(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t len)
+{
+	int rate, i;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	for (i = 0; i < min(len, sizeof(usr_input)); i++)
+		usr_input[i] = buf[i];
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return len;
+}
+
+ssize_t purelifi_show_modulation(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	return 0;
+}
+
+void create_config(struct usb_interface *intf)
+{
+	struct device *dev;
+
+	dev = &intf->dev;
+	if (device_create_file(dev, &purelifi_attr_modulation)) {
+		dev_err(&intf->dev,
+			"failed to create modulation sysfs entry\n");
+		return;
+	}
+	if (device_create_file(dev, &purelifi_attr_frequency)) {
+		device_remove_file(dev, &purelifi_attr_modulation);
+		dev_err(&intf->dev,
+			"failed to create frequency sysfs entry\n");
+	}
+}
+
+int modulation_open(struct inode *inode_p, struct file *file_p)
+{
+	return 0;
+}
+
+ssize_t modulation_read(struct file *file_p,
+			char __user *user_p,
+			size_t offset, loff_t *off_p)
+{
+	return 0;
+}
+
+ssize_t modulation_write(struct file *file_p,
+			 const char __user *buffer,
+			 size_t count, loff_t *actual_data_written)
+{
+	int rate;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	if (copy_from_user(usr_input, buffer, min(count, sizeof(usr_input))))
+		return -EFAULT;
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return count;
+}
diff --git a/drivers/net/wireless/purelifi/firmware.c b/drivers/net/wireless/purelifi/firmware.c
new file mode 100644
index 000000000000..36c4f6cac9a5
--- /dev/null
+++ b/drivers/net/wireless/purelifi/firmware.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/firmware.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+	int r, actual_length;
+	int fw_data_i, blk_tran_len = 16384;
+	const char *fw_name;
+	unsigned char fpga_setting[2];
+	unsigned char *fpga_dmabuff;
+	unsigned char *fw_data;
+	unsigned char fpga_state[9];
+	struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga.bit";
+		dev_info(&intf->dev, "bit file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/fpga_xc.bit";
+		dev_info(&intf->dev, "bit file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware((const struct firmware **)&fw, fw_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(2, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, 0x33, fpga_dmabuff, sizeof(fpga_setting));
+	memcpy(fpga_setting, fpga_dmabuff, 2);
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x34, NULL, 0);
+
+	if (fpga_setting[0] != 6) {
+		dev_err(&intf->dev, "fpga_setting[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			fw_data[tbuf_idx] =
+				((fw_data[tbuf_idx] & 128) >> 7) |
+				((fw_data[tbuf_idx] &  64) >> 5) |
+				((fw_data[tbuf_idx] &  32) >> 3) |
+				((fw_data[tbuf_idx] &  16) >> 1) |
+				((fw_data[tbuf_idx] &   8) << 1) |
+				((fw_data[tbuf_idx] &   4) << 3) |
+				((fw_data[tbuf_idx] &   2) << 5) |
+				((fw_data[tbuf_idx] &   1) << 7);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_setting[0] & 0xFF),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2000);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(9, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xFF, 9);
+
+	send_vendor_request(udev, 0x30, fpga_dmabuff, sizeof(fpga_state));
+
+	dev_info(&intf->dev, "fpga status: %x %x %x %x %x %x %x %x\n",
+		 fpga_dmabuff[0], fpga_dmabuff[1],
+		 fpga_dmabuff[2], fpga_dmabuff[3],
+		 fpga_dmabuff[4], fpga_dmabuff[5],
+		 fpga_dmabuff[6], fpga_dmabuff[7]);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		kfree(fpga_dmabuff);
+		goto error_free_fw;
+	}
+
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x35, NULL, 0);
+
+	msleep(200);
+
+error_free_fw:
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+	int r;
+
+	struct firmware *fw_packed = NULL;
+	int step;
+	u8 *buf;
+	u32 size, nmb_of_control_packets, i, no_of_files;
+	u32 tot_size, start_addr;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, 0x80, NULL, 0);
+	msleep(100);
+
+	/* Code for single pack file download */
+
+	fw_pack = "purelifi/li_fi_x/Li-Fi-XL.bin";
+
+	r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	no_of_files = *(u32 *)&fw_packed->data[0];
+	tot_size = fw_packed->size;
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n", no_of_files, tot_size);
+	buf = kzalloc(64, GFP_KERNEL);
+	if (!buf) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	for (step = 0; step < no_of_files; step++) {
+		buf[0] = step;
+		r = send_vendor_command(udev, 0x82, buf, 64);
+
+		if (step < no_of_files - 1)
+			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
+				- *(u32 *)&fw_packed->data[4 + (step) * 4];
+		else
+			size = tot_size -
+				*(u32 *)&fw_packed->data[4 + (step) * 4];
+
+		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
+
+		if ((size % 64) && step < 2)
+			size += 64 - size % 64;
+		nmb_of_control_packets = size / 64;
+
+		for (i = 0; i < nmb_of_control_packets; i++) {
+			memcpy(buf,
+			       &fw_packed->data[start_addr + (i * 64)], 64);
+			r = send_vendor_command(udev, 0x81, buf, 64);
+		}
+		dev_info(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", step, r,
+			 size);
+	}
+	release_firmware(fw_packed);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, 0x83, NULL, 0);
+	dev_info(&intf->dev, "download_fpga (4) (%d)\n", r);
+error:
+	return r;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FILE
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct firmware *fw = NULL;
+	char *mac_file_name = "purelifi/li_fi_x/mac.ascii";
+	char *serial_file_name = "purelifi/li_fi_x/serial.ascii";
+	int r;
+
+	r = request_firmware((const struct firmware **)&fw, mac_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	r = sscanf(fw->data,
+		   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+		    &hw_address[0], &hw_address[1],
+		    &hw_address[2], &hw_address[3],
+		    &hw_address[4], &hw_address[5]);
+	if (r != 6) {
+		dev_err(&intf->dev, "fw_dw - usage args fail (%d)\n", r);
+		goto ERROR;
+	}
+	release_firmware(fw);
+
+	r = request_firmware((const struct firmware **)&fw, serial_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	strlcpy(serial_number, fw->data, PURELIFI_SERIAL_LEN);
+	dev_info(&intf->dev, "serial_number=%s\n", serial_number);
+	release_firmware(fw);
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+ERROR:
+	return r;
+}
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FLASH
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+#define FLASH_EC_BUSY 3
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct flash_t flash;
+	unsigned char *flash_sector_buf;
+	unsigned int sector_index;
+
+	do {
+		unsigned char buf[8];
+
+		msleep(200);
+
+		send_vendor_request(udev, 0x40, buf, sizeof(buf));
+		flash.enabled = buf[0] & 0xFF;
+
+		if (flash.enabled) {
+			flash.sectors = ((buf[6] & 255) << 24) |
+				((buf[5] & 255) << 16) |
+				((buf[4] & 255) <<  8) |
+				(buf[3] & 255);
+
+			flash.sector_size =
+				((buf[2] & 255) << 8) | (buf[1] & 255);
+		} else {
+			flash.sectors = 0;
+			flash.sector_size = 0;
+		}
+
+		if (flash.sector_size & 0x8000)
+			flash.sector_size = 1 << (flash.sector_size & 0x7FFF);
+
+		flash.ec = buf[7] & 0xFF;
+	} while (flash.ec == FLASH_EC_BUSY);
+
+	flash_sector_buf = kmalloc(flash.sector_size,
+				   GFP_KERNEL);
+	if (!flash_sector_buf)
+		return -ENOMEM;
+
+	if (!flash.enabled) {
+		dev_err(&intf->dev, "Flash is not enabled\n");
+		return -EINVAL;
+	}
+
+	read_flash(intf, &flash, 0, flash_sector_buf);
+	sector_index = *((unsigned int *)flash_sector_buf);
+
+	read_flash(intf, &flash, sector_index, flash_sector_buf);
+	memcpy(hw_address, flash_sector_buf, ETH_ALEN);
+	strncpy(serial_number, &flash_sector_buf[ETH_ALEN], 256);
+
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+	kfree(flash_sector_buf);
+	return 0;
+}
+
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_EP0
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			unsigned char *serial_number)
+{
+#define USB_MAC_VENDOR_REQUEST 0x36
+#define USB_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define USB_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define SERIAL_NUMBER_LENGTH 14
+	struct usb_device *udev = interface_to_usbdev(intf);
+	int r = 0;
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(14, GFP_KERNEL);
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	send_vendor_request(udev, USB_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	memcpy(serial_number, dma_buffer, SERIAL_NUMBER_LENGTH);
+
+	memset(dma_buffer, 0x00, 14);
+
+	send_vendor_request(udev, USB_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, 8);
+
+	memcpy(&firmware_version, dma_buffer, sizeof(unsigned long long));
+
+	dev_info(&intf->dev, "Firmware Version: %llu, Revision: 16418\n",
+		 firmware_version);
+	kfree(dma_buffer);
+
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+error:
+	return r;
+}
+
+#endif
diff --git a/drivers/net/wireless/purelifi/intf.h b/drivers/net/wireless/purelifi/intf.h
new file mode 100644
index 000000000000..df0d87331ad7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/intf.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	u16 rssi;
+	u8  rate_idx;
+	u8  pad;
+	u64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             buf[512];
+};
+
+#endif
diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
new file mode 100644
index 000000000000..2966553bdc65
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.c
@@ -0,0 +1,873 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+struct plf_reg_alpha2_map {
+	u32 reg;
+	char alpha2[2];
+};
+
+static const struct plf_reg_alpha2_map reg_alpha2_map[] = {
+	{ PLF_REGDOMAIN_FCC, "US" },
+	{ PLF_REGDOMAIN_IC, "CA" },
+	{ PLF_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
+	{ PLF_REGDOMAIN_JAPAN, "JP" },
+	{ PLF_REGDOMAIN_SPAIN, "ES" },
+	{ PLF_REGDOMAIN_FRANCE, "FR" },
+};
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 },
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr);
+
+static int plf_reg2alpha2(u8 regdomain, char *alpha2)
+{
+	unsigned int i;
+	const struct plf_reg_alpha2_map *reg_map;
+		for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) {
+			reg_map = &reg_alpha2_map[i];
+			if (regdomain == reg_map->reg) {
+				alpha2[0] = reg_map->alpha2[0];
+				alpha2[1] = reg_map->alpha2[1];
+				return 0;
+			}
+	}
+	return 1;
+}
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
+{
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+	char alpha2[2];
+
+	mac->default_regdomain = PLF_REGDOMAIN_ETSI;
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	r = plf_reg2alpha2(mac->regdomain, alpha2);
+	r = regulatory_hint(hw->wiphy, alpha2);
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon, false);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status - reports tx status of a packet if required
+ * @hw - a &struct ieee80211_hw pointer
+ * @skb - a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success - True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1, retry = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status) {
+		success = !tx_status->failure;
+		retry = tx_status->retry + success;
+	}
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		/* ieee80211_tx_status_irqsafe(hw, skb); */
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr)
+{
+	return usb_write_req((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (struct purelifi_ctrlset *)skb_push(skb,
+			sizeof(struct purelifi_ctrlset));
+	cs->id = USB_REQ_DATA_TX;
+	cs->payload_len_nw = frag_len;
+	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
+		- sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned and align data*/
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		cs->len +=  4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data*/
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen b/c
+				 * sufficient headroom was reserved
+				 */
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		cs->len +=  4;
+	}
+
+	cs->id = TO_NETWORK(cs->id);
+	cs->len = TO_NETWORK(cs->len);
+	cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
+
+	return 0;
+}
+
+/**
+ * purelifi_op_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+	info->rate_driver_data[0] = hw;
+
+	if (skb->data[24] == IEEE80211_FTYPE_DATA) {
+		u8 dst_mac[ETH_ALEN];
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		memcpy(dst_mac, &skb->data[28], ETH_ALEN);
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			block_queue(usb, tx->station[sidx].mac, true);
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			dev_kfree_skb(skb);
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
+					USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+		      struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	bool found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_info(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	u32 crc_error_cnt_low, crc_error_cnt_high;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_2GHZ;
+	mac->rssi      = -15 * ntohs(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	crc_error_cnt_low = status->crc_error_count;
+	crc_error_cnt_high = status->crc_error_count >> 32;
+	mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
+		ntohl(crc_error_cnt_high);
+
+	if (!bad_frame &&
+	    filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = TO_HOST(*(u32 *)buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (buffer[0] == IEEE80211_STYPE_PROBE_REQ)
+		dev_info(purelifi_mac_dev(mac), "Probe request\n");
+	else if (buffer[0] == IEEE80211_STYPE_ASSOC_REQ)
+		dev_info(purelifi_mac_dev(mac), "Association request\n");
+
+	if (buffer[0] == IEEE80211_STYPE_AUTH) {
+		dev_info(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+	} else if (buffer[0] == IEEE80211_FTYPE_DATA) {
+		unsigned short int seq_nmb = (buffer[23] << 4) |
+			((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000)) {
+			dev_info(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
+		} else {
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		}
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+	if (need_padding) {
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+	}
+
+	memcpy(skb_put(skb, payload_length), buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_info(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			 iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_info(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_info(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon, false);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;
+
+	_ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
+	_ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
+	_ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
+	_ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* 4 for 32 bit alignment if no tailroom */
+
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/mac.h b/drivers/net/wireless/purelifi/mac.h
new file mode 100644
index 000000000000..f0ebe4018d7e
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             modulation;
+	u8             control;
+	u8             service;
+	u8             pad;
+	__le16         packet_length;
+	__le16         current_length;
+	__le16          next_frame_length;
+	__le16         tx_length;
+	u32            payload_len_nw;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	spinlock_t intr_lock; /* not used : interrupt lock for mac */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending:1;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
+		*chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block);
+
+#ifdef DEBUG
+void purelifi_dump_rx_status(const struct rx_status *status);
+#else
+#define purelifi_dump_rx_status(status)
+#endif
+
+#endif
diff --git a/drivers/net/wireless/purelifi/usb.c b/drivers/net/wireless/purelifi/usb.c
new file mode 100644
index 000000000000..5e95d336c6dc
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.c
@@ -0,0 +1,1075 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+#define FCS_LEN 4
+
+#define PROC_FOLDER           "purelifi"
+#define MODULATION_FILENAME   "modulation"
+
+#undef  LOAD_MAC_AND_SERIAL_FROM_FILE
+#undef  LOAD_MAC_AND_SERIAL_FROM_FLASH
+#define LOAD_MAC_AND_SERIAL_FROM_EP0
+
+struct proc_dir_entry *proc_folder;
+struct proc_dir_entry *modulation_proc_entry;
+static atomic_t data_queue_flag;
+
+/*Tx retry backoff timer (in milliseconds).*/
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+
+/*Tx retry backoff timer (in jiffies).*/
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+const struct device_attribute purelifi_attr_frequency = {
+	  .attr = {.name = "frequency", .mode = (0666)},
+	  .show = purelifi_show_sysfs,
+	  .store = purelifi_store_frequency,
+};
+
+struct device_attribute purelifi_attr_modulation = {
+	.attr = {.name = "modulation", .mode = (0666)},
+	.show = purelifi_show_modulation,
+	.store = purelifi_store_modulation,
+};
+
+const struct proc_ops  modulation_fops = {
+	.proc_open  = modulation_open,
+	.proc_read  = modulation_read,
+	.proc_write = modulation_write
+};
+
+struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				    tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			block_queue(usb, tx->station[sidx].mac, false);
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int rx_usb_enabled;
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
+		return;
+	} else if (!urb->context) {
+		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
+
+	tx = &usb->tx;
+
+	if (urb->actual_length != 8) {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[7];
+
+	dev_info(&usb->intf->dev, "Recv status=%u\n", status);
+	dev_info(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+		 buffer[0], buffer[1], buffer[2], buffer[3],
+		 buffer[4], buffer[5]);
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_info(&usb->intf->dev,
+			 "FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_info(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= 0xFD;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		fpga_link_connection_f = 1;
+		dev_info(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		fpga_link_connection_f = 0;
+		dev_info(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_info(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit failed (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0; /*no error return*/
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	u32 payload_len_nw = TO_NETWORK((buffer_len + FCS_LEN));
+
+	usb_req->id = usb_req_id;
+	usb_req->len  = 0;
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == USB_REQ_BEACON_WR) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		usb_req->len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	usb_req->len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	usb_req->len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+		usb_req->len += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->id = TO_NETWORK(usb_req->id);
+	usb_req->len = TO_NETWORK(usb_req->len);
+}
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			usb_complete_t complete_fn,
+			void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(usb_req.id) + sizeof(usb_req.len) +
+			   TO_HOST(usb_req.len);
+
+	dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+	memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	static unsigned char hw_address[ETH_ALEN];
+	static unsigned char serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	create_config(intf);
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+	dev_dbg(&intf->dev, "unit type is station");
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg(&intf->dev, "register device failed (%d)\n", r);
+		goto error;
+	}
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(200);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_info(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = usb_write_req(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_info(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	device_remove_file(&intf->dev, &purelifi_attr_modulation);
+	device_remove_file(&intf->dev, &purelifi_attr_frequency);
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	mutex_lock(&mac->chip.mutex);
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	mutex_unlock(&mac->chip.mutex);
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+	.suspend        = suspend,
+	.resume         = resume,
+	.disable_hub_initiated_lpm = 1,
+};
+
+struct workqueue_struct *purelifi_workqueue;
+
+static int create_proc_entry_e(struct proc_dir_entry *entry,
+			       size_t size,
+			       mode_t mode,
+			       const struct proc_ops *pops,
+			       const char *entry_name,
+			       struct proc_dir_entry *parent
+			     )
+{
+	entry = proc_create(entry_name, mode, parent, pops);
+	if (!(entry))
+		return -ENOMEM;
+	return 0;
+}
+
+static int __init usb_init(void)
+{
+	int r;
+
+	proc_folder = proc_mkdir(PROC_FOLDER, NULL);
+	if (!proc_folder) {
+		pr_err("error creating proc folder\n");
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = create_proc_entry_e(modulation_proc_entry, sizeof(u32), 0666,
+				&modulation_fops, MODULATION_FILENAME,
+				proc_folder);
+	if (r)
+		goto remove_proc_dir;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto remove_modulation_proc_file;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+remove_modulation_proc_file:
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+remove_proc_dir:
+	remove_proc_entry(PROC_FOLDER, NULL);
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+	remove_proc_entry(PROC_FOLDER, NULL);
+	if (ez_usb_interface) {
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_modulation);
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_frequency);
+	}
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi Ltd");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("purelifi/li_fi_x/fpga.bit");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/usb.h b/drivers/net/wireless/purelifi/usb.h
new file mode 100644
index 000000000000..ebc5e9dd2207
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.h
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
+		      (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
+
+#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
+
+#define TO_NETWORK_32(X) TO_NETWORK(X)
+
+#define TO_HOST(X)    TO_NETWORK(X)
+#define TO_HOST_16(X) TO_NETWORK_16(X)
+#define TO_HOST_32(X) TO_NETWORK_32(X)
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2      |     1     |     0	    |
+   // Reserved  | Heartbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+extern const struct device_attribute purelifi_attr_frequency;
+extern struct device_attribute purelifi_attr_modulation;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+#define PURELIFI_SERIAL_LEN 256
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	u8 initialized:1, was_running:1;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+		usb_complete_t complete_fn, void *context);
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
+		*usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *purelifi_intf_to_hw(struct usb_interface
+		*intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
+		*usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/*Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+void create_config(struct usb_interface *intf);
+
+/* Debugfs declarations */
+
+ssize_t purelifi_store_frequency(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t len);
+
+ssize_t purelifi_show_sysfs(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf);
+
+ssize_t purelifi_store_modulation(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t len);
+
+ssize_t purelifi_show_modulation(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf);
+
+int modulation_open(struct inode *inode_p, struct file *file_p);
+
+ssize_t modulation_read(struct file *file_p,
+			char __user *user_p,
+			size_t offset, loff_t *off_p);
+
+ssize_t modulation_write(struct file *file_p,
+			 const char __user *buffer,
+			 size_t count, loff_t *actual_data_written);
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v9] wireless: Initial driver submission for pureLiFi STA devices
  2020-12-03  5:09   ` [PATCH] [v9] " Srinivasan Raju
@ 2020-12-03  7:53     ` Joe Perches
  0 siblings, 0 replies; 108+ messages in thread
From: Joe Perches @ 2020-12-03  7:53 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

On Thu, 2020-12-03 at 10:39 +0530, Srinivasan Raju wrote:
> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.

The below is a trivial readability possible enhancement and should not
hinder this patch being applied.

> diff --git a/drivers/net/wireless/purelifi/dbgfs.c b/drivers/net/wireless/purelifi/dbgfs.c
[]
> +ssize_t purelifi_store_frequency(struct device *dev,
> +				 struct device_attribute *attr,
> +				 const char *buf,
> +				 size_t len)
> +{
> +	int i, r, row, col, predivider, feedback_divider, output_div_0;
> +	int output_div_1, output_div_2, clkout3_phase;
> +	char values[6][4] = {{0}, {0} };
> +	char usr_input[8] = {0};
> +	bool valid = false;
> +	int rv = 0;
> +
> +	row = 0; col = 0;
> +	for (i = 0; i < min(len, sizeof(values)); i++) {
> +		values[row][col] = buf[i];
> +		col++;
> +		if (buf[i] == 0x20) {
> +			row++;
> +			col = 0;
> +		} else if (buf[i] == 0x0A) {
> +			if (row == 5 && col > 0) {
> +				rv = sscanf((char *)&values[0][0],
> +					    "%d", &predivider);

Aren't the casts to (char *) redundant ?

> +				if (rv != 1)
> +					valid = false;
> +				rv = sscanf((char *)&values[1][0],
> +					    "%d", &feedback_divider);
> +				if (rv != 1)
> +					valid = false;
> +				rv = sscanf((char *)&values[2][0],
> +					    "%d", &output_div_0);
> +				if (rv != 1)
> +					valid = false;
> +				rv = sscanf((char *)&values[3][0],
> +					    "%d", &output_div_1);
> +				if (rv != 1)
> +					valid = false;
> +				rv = sscanf((char *)&values[4][0],
> +					    "%d", &output_div_2);
> +				if (rv != 1)
> +					valid = false;
> +				rv = sscanf((char *)&values[5][0],
> +					    "%d", &clkout3_phase);
> +				if (rv != 1)
> +					valid = false;
> +				valid = true;

trivia: this might be more intelligible with an array and a loop

	int *index[] = {
		&predivider,
		&feedback_divider,
		&output_div_0,
		&output_div_1,
		&output_div_2,
		&clkout3_phase
	};

	valid = true;
	i = 0;
	while (valid && i < ARRAY_SIZE(index)) {
		if (sscanf(&values[i][0], "%d", index[i]) != 1)
			valid = false;
		i++;
	}
		
> +
> +				break;
> +			}
> +		}
> +	}
> +
> +	if (valid) {
> +		usr_input[0] = (char)predivider;
> +		usr_input[1] = (char)feedback_divider;
> +		usr_input[2] = (char)output_div_0;
> +		usr_input[3] = (char)output_div_1;
> +		usr_input[4] = (char)output_div_2;
> +		usr_input[5] = (char)clkout3_phase;
> +
> +		dev_info(dev, "options IP_DIV: %d VCO_MULT: %d OP_DIV_PHY: %d",
> +			 usr_input[0], usr_input[1], usr_input[2]);
> +		dev_info(dev, "OP_DIV_CPU: %d OP_DIV_USB: %d CLK3_PH: %d\n",
> +			 usr_input[3], usr_input[4], usr_input[5]);
> +
> +		r = usb_write_req((const u8 *)&usr_input[0],
> +				  sizeof(usr_input), USB_REQ_SET_FREQ);
> +		if (r < 0)
> +			dev_err(dev, "SET_FREQ failed (%d)\n", r);
> +	} else {
> +		dev_err(dev, "Your options are invalid\n");
> +	}
> +
> +	return len;
> +}




^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v7] wireless: Initial driver submission for pureLiFi STA devices
  2020-12-03  4:43           ` Srinivasan Raju
@ 2020-12-03 15:58             ` Kalle Valo
  2020-12-03 16:50               ` Srinivasan Raju
  0 siblings, 1 reply; 108+ messages in thread
From: Kalle Valo @ 2020-12-03 15:58 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> we will be submitting to linux-firmware repository @
> https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git
> I will share the link once it is accpeted, we have sent another
> version of the patch v8 , please review and provide your comments

What will be the directory structure in linux-firmware? It should be
unique so that it's not possible to mix with other drivers.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v7] wireless: Initial driver submission for pureLiFi STA devices
  2020-12-03 15:58             ` Kalle Valo
@ 2020-12-03 16:50               ` Srinivasan Raju
  2020-12-19 13:15                 ` Kalle Valo
  0 siblings, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2020-12-03 16:50 UTC (permalink / raw)
  To: Kalle Valo
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS



> What will be the directory structure in linux-firmware? It should be
> unique so that it's not possible to mix with other drivers.

I have created the following directory structure, Please let me know if this is OK.

 LICENCE.purelifi_firmware |  29 +++++++++++++++++++++++++++++
 purelifi/Li-Fi-XL.bin     | Bin 0 -> 70228 bytes
 purelifi/fpga.bit         | Bin 0 -> 3825907 bytes
 purelifi/fpga_xc.bit      | Bin 0 -> 3825906 bytes
 4 files changed, 29 insertions(+)

Thanks 
Srini



^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v10] wireless: Initial driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (5 preceding siblings ...)
  2020-12-03  5:09   ` [PATCH] [v9] " Srinivasan Raju
@ 2020-12-08  5:53   ` Srinivasan Raju
  2020-12-08 11:57   ` [PATCH] [v11] " Srinivasan Raju
                     ` (12 subsequent siblings)
  19 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-12-08  5:53 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Mauro Carvalho Chehab, Rob Herring,
	Lukas Bulwahn, open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

---
v10:
- Addressed review comment on readability
- Changed firmware names to match products
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- usb.c is split into two files firmware.c and dbgfs.c
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging
---
 MAINTAINERS                              |    5 +
 drivers/net/wireless/Kconfig             |    1 +
 drivers/net/wireless/Makefile            |    1 +
 drivers/net/wireless/purelifi/Kconfig    |   27 +
 drivers/net/wireless/purelifi/Makefile   |    3 +
 drivers/net/wireless/purelifi/chip.c     |   93 ++
 drivers/net/wireless/purelifi/chip.h     |   81 ++
 drivers/net/wireless/purelifi/dbgfs.c    |  150 +++
 drivers/net/wireless/purelifi/firmware.c |  384 ++++++++
 drivers/net/wireless/purelifi/intf.h     |   38 +
 drivers/net/wireless/purelifi/mac.c      |  873 ++++++++++++++++++
 drivers/net/wireless/purelifi/mac.h      |  189 ++++
 drivers/net/wireless/purelifi/usb.c      | 1075 ++++++++++++++++++++++
 drivers/net/wireless/purelifi/usb.h      |  199 ++++
 14 files changed, 3119 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/chip.c
 create mode 100644 drivers/net/wireless/purelifi/chip.h
 create mode 100644 drivers/net/wireless/purelifi/dbgfs.c
 create mode 100644 drivers/net/wireless/purelifi/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/intf.h
 create mode 100644 drivers/net/wireless/purelifi/mac.c
 create mode 100644 drivers/net/wireless/purelifi/mac.h
 create mode 100644 drivers/net/wireless/purelifi/usb.c
 create mode 100644 drivers/net/wireless/purelifi/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index c80f87d7258c..17955b8497df 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14108,6 +14108,11 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PUREILIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+S:	Supported
+F:	drivers/net/wireless/purelifi
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 170a64e67709..b87da3139f94 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -48,6 +48,7 @@ source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
 source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 
 config PCMCIA_RAYCS
 	tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e9fc770026f0 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
 obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..f6630791df9d
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+config PURELIFI
+
+	tristate "pureLiFi device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This driver makes the adapter appear as a normal WLAN interface.
+
+	   The pureLiFi device requires external STA firmware to be loaded.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called purelifi.
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..cf9b931840ac
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PURELIFI)		:= purelifi.o
+purelifi-objs 		+= chip.o usb.o mac.o firmware.o dbgfs.o
diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
new file mode 100644
index 000000000000..9a7ccd0f98f2
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set && chip->beacon_interval == interval)) {
+		return 0;
+	}
+
+	chip->beacon_interval = interval;
+	chip->beacon_set = true;
+	return usb_write_req((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %02x:%02x v%02x  %02x-%02x-%02x %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr[0], addr[1], addr[2],
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
+{
+	int r;
+
+	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	static const u8 value;
+
+	usb_write_req(&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = usb_write_req(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/chip.h b/drivers/net/wireless/purelifi/chip.h
new file mode 100644
index 000000000000..710bf713b8c4
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	u16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1 << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/dbgfs.c b/drivers/net/wireless/purelifi/dbgfs.c
new file mode 100644
index 000000000000..d617e4b41c5e
--- /dev/null
+++ b/drivers/net/wireless/purelifi/dbgfs.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include "mac.h"
+#include "usb.h"
+
+ssize_t purelifi_store_frequency(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t len)
+{
+	int i, r, row, col, predivider, feedback_divider, output_div_0;
+	int output_div_1, output_div_2, clkout3_phase;
+	char values[6][4] = {{0}, {0} };
+	char usr_input[8] = {0};
+	bool valid = false;
+
+	row = 0; col = 0;
+	for (i = 0; i < min(len, sizeof(values)); i++) {
+		values[row][col] = buf[i];
+		col++;
+		if (buf[i] == 0x20) {
+			row++;
+			col = 0;
+		} else if (buf[i] == 0x0A) {
+			if (row == 5 && col > 0) {
+				int *index[] = {
+					&predivider,
+					&feedback_divider,
+					&output_div_0,
+					&output_div_1,
+					&output_div_2,
+					&clkout3_phase
+				};
+				valid = true;
+				i = 0;
+				while (valid && i < ARRAY_SIZE(index)) {
+					if (kstrtouint(&values[i][0], 0,
+						       index[i]))
+						valid = false;
+					i++;
+				}
+				break;
+			}
+		}
+	}
+
+	if (valid) {
+		usr_input[0] = (char)predivider;
+		usr_input[1] = (char)feedback_divider;
+		usr_input[2] = (char)output_div_0;
+		usr_input[3] = (char)output_div_1;
+		usr_input[4] = (char)output_div_2;
+		usr_input[5] = (char)clkout3_phase;
+
+		dev_info(dev, "options IP_DIV: %d VCO_MULT: %d OP_DIV_PHY: %d",
+			 usr_input[0], usr_input[1], usr_input[2]);
+		dev_info(dev, "OP_DIV_CPU: %d OP_DIV_USB: %d CLK3_PH: %d\n",
+			 usr_input[3], usr_input[4], usr_input[5]);
+
+		r = usb_write_req((const u8 *)&usr_input[0],
+				  sizeof(usr_input), USB_REQ_SET_FREQ);
+		if (r < 0)
+			dev_err(dev, "SET_FREQ failed (%d)\n", r);
+	} else {
+		dev_err(dev, "Your options are invalid\n");
+	}
+
+	return len;
+}
+
+ssize_t purelifi_show_sysfs(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	return 0;
+}
+
+ssize_t purelifi_store_modulation(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t len)
+{
+	int rate, i;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	for (i = 0; i < min(len, sizeof(usr_input)); i++)
+		usr_input[i] = buf[i];
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return len;
+}
+
+ssize_t purelifi_show_modulation(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	return 0;
+}
+
+void create_config(struct usb_interface *intf)
+{
+	struct device *dev;
+
+	dev = &intf->dev;
+	if (device_create_file(dev, &purelifi_attr_modulation)) {
+		dev_err(&intf->dev,
+			"failed to create modulation sysfs entry\n");
+		return;
+	}
+	if (device_create_file(dev, &purelifi_attr_frequency)) {
+		device_remove_file(dev, &purelifi_attr_modulation);
+		dev_err(&intf->dev,
+			"failed to create frequency sysfs entry\n");
+	}
+}
+
+int modulation_open(struct inode *inode_p, struct file *file_p)
+{
+	return 0;
+}
+
+ssize_t modulation_read(struct file *file_p,
+			char __user *user_p,
+			size_t offset, loff_t *off_p)
+{
+	return 0;
+}
+
+ssize_t modulation_write(struct file *file_p,
+			 const char __user *buffer,
+			 size_t count, loff_t *actual_data_written)
+{
+	int rate;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	if (copy_from_user(usr_input, buffer, min(count, sizeof(usr_input))))
+		return -EFAULT;
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return count;
+}
diff --git a/drivers/net/wireless/purelifi/firmware.c b/drivers/net/wireless/purelifi/firmware.c
new file mode 100644
index 000000000000..3a9a43b45f45
--- /dev/null
+++ b/drivers/net/wireless/purelifi/firmware.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/firmware.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+	int r, actual_length;
+	int fw_data_i, blk_tran_len = 16384;
+	const char *fw_name;
+	unsigned char fpga_setting[2];
+	unsigned char *fpga_dmabuff;
+	unsigned char *fw_data;
+	unsigned char fpga_state[9];
+	struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/LiFi-X.bit";
+		dev_info(&intf->dev, "bit file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/LiFi-XC.bit";
+		dev_info(&intf->dev, "bit file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware((const struct firmware **)&fw, fw_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(2, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, 0x33, fpga_dmabuff, sizeof(fpga_setting));
+	memcpy(fpga_setting, fpga_dmabuff, 2);
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x34, NULL, 0);
+
+	if (fpga_setting[0] != 6) {
+		dev_err(&intf->dev, "fpga_setting[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			fw_data[tbuf_idx] =
+				((fw_data[tbuf_idx] & 128) >> 7) |
+				((fw_data[tbuf_idx] &  64) >> 5) |
+				((fw_data[tbuf_idx] &  32) >> 3) |
+				((fw_data[tbuf_idx] &  16) >> 1) |
+				((fw_data[tbuf_idx] &   8) << 1) |
+				((fw_data[tbuf_idx] &   4) << 3) |
+				((fw_data[tbuf_idx] &   2) << 5) |
+				((fw_data[tbuf_idx] &   1) << 7);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_setting[0] & 0xFF),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2000);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(9, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xFF, 9);
+
+	send_vendor_request(udev, 0x30, fpga_dmabuff, sizeof(fpga_state));
+
+	dev_info(&intf->dev, "fpga status: %x %x %x %x %x %x %x %x\n",
+		 fpga_dmabuff[0], fpga_dmabuff[1],
+		 fpga_dmabuff[2], fpga_dmabuff[3],
+		 fpga_dmabuff[4], fpga_dmabuff[5],
+		 fpga_dmabuff[6], fpga_dmabuff[7]);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		kfree(fpga_dmabuff);
+		goto error_free_fw;
+	}
+
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x35, NULL, 0);
+
+	msleep(200);
+
+error_free_fw:
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+	int r;
+
+	struct firmware *fw_packed = NULL;
+	int step;
+	u8 *buf;
+	u32 size, nmb_of_control_packets, i, no_of_files;
+	u32 tot_size, start_addr;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, 0x80, NULL, 0);
+	msleep(100);
+
+	/* Code for single pack file download */
+
+	fw_pack = "purelifi/li_fi_x/LiFi-XL.bin";
+
+	r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	no_of_files = *(u32 *)&fw_packed->data[0];
+	tot_size = fw_packed->size;
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n", no_of_files, tot_size);
+	buf = kzalloc(64, GFP_KERNEL);
+	if (!buf) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	for (step = 0; step < no_of_files; step++) {
+		buf[0] = step;
+		r = send_vendor_command(udev, 0x82, buf, 64);
+
+		if (step < no_of_files - 1)
+			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
+				- *(u32 *)&fw_packed->data[4 + (step) * 4];
+		else
+			size = tot_size -
+				*(u32 *)&fw_packed->data[4 + (step) * 4];
+
+		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
+
+		if ((size % 64) && step < 2)
+			size += 64 - size % 64;
+		nmb_of_control_packets = size / 64;
+
+		for (i = 0; i < nmb_of_control_packets; i++) {
+			memcpy(buf,
+			       &fw_packed->data[start_addr + (i * 64)], 64);
+			r = send_vendor_command(udev, 0x81, buf, 64);
+		}
+		dev_info(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", step, r,
+			 size);
+	}
+	release_firmware(fw_packed);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, 0x83, NULL, 0);
+	dev_info(&intf->dev, "download_fpga (4) (%d)\n", r);
+error:
+	return r;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FILE
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct firmware *fw = NULL;
+	char *mac_file_name = "purelifi/li_fi_x/mac.ascii";
+	char *serial_file_name = "purelifi/li_fi_x/serial.ascii";
+	int r;
+
+	r = request_firmware((const struct firmware **)&fw, mac_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	r = sscanf(fw->data,
+		   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+		    &hw_address[0], &hw_address[1],
+		    &hw_address[2], &hw_address[3],
+		    &hw_address[4], &hw_address[5]);
+	if (r != 6) {
+		dev_err(&intf->dev, "fw_dw - usage args fail (%d)\n", r);
+		goto ERROR;
+	}
+	release_firmware(fw);
+
+	r = request_firmware((const struct firmware **)&fw, serial_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	strlcpy(serial_number, fw->data, PURELIFI_SERIAL_LEN);
+	dev_info(&intf->dev, "serial_number=%s\n", serial_number);
+	release_firmware(fw);
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+ERROR:
+	return r;
+}
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FLASH
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+#define FLASH_EC_BUSY 3
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct flash_t flash;
+	unsigned char *flash_sector_buf;
+	unsigned int sector_index;
+
+	do {
+		unsigned char buf[8];
+
+		msleep(200);
+
+		send_vendor_request(udev, 0x40, buf, sizeof(buf));
+		flash.enabled = buf[0] & 0xFF;
+
+		if (flash.enabled) {
+			flash.sectors = ((buf[6] & 255) << 24) |
+				((buf[5] & 255) << 16) |
+				((buf[4] & 255) <<  8) |
+				(buf[3] & 255);
+
+			flash.sector_size =
+				((buf[2] & 255) << 8) | (buf[1] & 255);
+		} else {
+			flash.sectors = 0;
+			flash.sector_size = 0;
+		}
+
+		if (flash.sector_size & 0x8000)
+			flash.sector_size = 1 << (flash.sector_size & 0x7FFF);
+
+		flash.ec = buf[7] & 0xFF;
+	} while (flash.ec == FLASH_EC_BUSY);
+
+	flash_sector_buf = kmalloc(flash.sector_size,
+				   GFP_KERNEL);
+	if (!flash_sector_buf)
+		return -ENOMEM;
+
+	if (!flash.enabled) {
+		dev_err(&intf->dev, "Flash is not enabled\n");
+		return -EINVAL;
+	}
+
+	read_flash(intf, &flash, 0, flash_sector_buf);
+	sector_index = *((unsigned int *)flash_sector_buf);
+
+	read_flash(intf, &flash, sector_index, flash_sector_buf);
+	memcpy(hw_address, flash_sector_buf, ETH_ALEN);
+	strncpy(serial_number, &flash_sector_buf[ETH_ALEN], 256);
+
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+	kfree(flash_sector_buf);
+	return 0;
+}
+
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_EP0
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			unsigned char *serial_number)
+{
+#define USB_MAC_VENDOR_REQUEST 0x36
+#define USB_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define USB_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define SERIAL_NUMBER_LENGTH 14
+	struct usb_device *udev = interface_to_usbdev(intf);
+	int r = 0;
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(14, GFP_KERNEL);
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	send_vendor_request(udev, USB_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	memcpy(serial_number, dma_buffer, SERIAL_NUMBER_LENGTH);
+
+	memset(dma_buffer, 0x00, 14);
+
+	send_vendor_request(udev, USB_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, 8);
+
+	memcpy(&firmware_version, dma_buffer, sizeof(unsigned long long));
+
+	dev_info(&intf->dev, "Firmware Version: %llu, Revision: 16418\n",
+		 firmware_version);
+	kfree(dma_buffer);
+
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+error:
+	return r;
+}
+
+#endif
diff --git a/drivers/net/wireless/purelifi/intf.h b/drivers/net/wireless/purelifi/intf.h
new file mode 100644
index 000000000000..df0d87331ad7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/intf.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	u16 rssi;
+	u8  rate_idx;
+	u8  pad;
+	u64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             buf[512];
+};
+
+#endif
diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
new file mode 100644
index 000000000000..2966553bdc65
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.c
@@ -0,0 +1,873 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+struct plf_reg_alpha2_map {
+	u32 reg;
+	char alpha2[2];
+};
+
+static const struct plf_reg_alpha2_map reg_alpha2_map[] = {
+	{ PLF_REGDOMAIN_FCC, "US" },
+	{ PLF_REGDOMAIN_IC, "CA" },
+	{ PLF_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
+	{ PLF_REGDOMAIN_JAPAN, "JP" },
+	{ PLF_REGDOMAIN_SPAIN, "ES" },
+	{ PLF_REGDOMAIN_FRANCE, "FR" },
+};
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 },
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr);
+
+static int plf_reg2alpha2(u8 regdomain, char *alpha2)
+{
+	unsigned int i;
+	const struct plf_reg_alpha2_map *reg_map;
+		for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) {
+			reg_map = &reg_alpha2_map[i];
+			if (regdomain == reg_map->reg) {
+				alpha2[0] = reg_map->alpha2[0];
+				alpha2[1] = reg_map->alpha2[1];
+				return 0;
+			}
+	}
+	return 1;
+}
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
+{
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+	char alpha2[2];
+
+	mac->default_regdomain = PLF_REGDOMAIN_ETSI;
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	r = plf_reg2alpha2(mac->regdomain, alpha2);
+	r = regulatory_hint(hw->wiphy, alpha2);
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon, false);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status - reports tx status of a packet if required
+ * @hw - a &struct ieee80211_hw pointer
+ * @skb - a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success - True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1, retry = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status) {
+		success = !tx_status->failure;
+		retry = tx_status->retry + success;
+	}
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		/* ieee80211_tx_status_irqsafe(hw, skb); */
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr)
+{
+	return usb_write_req((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (struct purelifi_ctrlset *)skb_push(skb,
+			sizeof(struct purelifi_ctrlset));
+	cs->id = USB_REQ_DATA_TX;
+	cs->payload_len_nw = frag_len;
+	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
+		- sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned and align data*/
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		cs->len +=  4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data*/
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen b/c
+				 * sufficient headroom was reserved
+				 */
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		cs->len +=  4;
+	}
+
+	cs->id = TO_NETWORK(cs->id);
+	cs->len = TO_NETWORK(cs->len);
+	cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
+
+	return 0;
+}
+
+/**
+ * purelifi_op_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+	info->rate_driver_data[0] = hw;
+
+	if (skb->data[24] == IEEE80211_FTYPE_DATA) {
+		u8 dst_mac[ETH_ALEN];
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		memcpy(dst_mac, &skb->data[28], ETH_ALEN);
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			block_queue(usb, tx->station[sidx].mac, true);
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			dev_kfree_skb(skb);
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
+					USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+		      struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	bool found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_info(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	u32 crc_error_cnt_low, crc_error_cnt_high;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_2GHZ;
+	mac->rssi      = -15 * ntohs(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	crc_error_cnt_low = status->crc_error_count;
+	crc_error_cnt_high = status->crc_error_count >> 32;
+	mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
+		ntohl(crc_error_cnt_high);
+
+	if (!bad_frame &&
+	    filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = TO_HOST(*(u32 *)buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (buffer[0] == IEEE80211_STYPE_PROBE_REQ)
+		dev_info(purelifi_mac_dev(mac), "Probe request\n");
+	else if (buffer[0] == IEEE80211_STYPE_ASSOC_REQ)
+		dev_info(purelifi_mac_dev(mac), "Association request\n");
+
+	if (buffer[0] == IEEE80211_STYPE_AUTH) {
+		dev_info(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+	} else if (buffer[0] == IEEE80211_FTYPE_DATA) {
+		unsigned short int seq_nmb = (buffer[23] << 4) |
+			((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000)) {
+			dev_info(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
+		} else {
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		}
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+	if (need_padding) {
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+	}
+
+	memcpy(skb_put(skb, payload_length), buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_info(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			 iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_info(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_info(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon, false);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;
+
+	_ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
+	_ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
+	_ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
+	_ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* 4 for 32 bit alignment if no tailroom */
+
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/mac.h b/drivers/net/wireless/purelifi/mac.h
new file mode 100644
index 000000000000..f0ebe4018d7e
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             modulation;
+	u8             control;
+	u8             service;
+	u8             pad;
+	__le16         packet_length;
+	__le16         current_length;
+	__le16          next_frame_length;
+	__le16         tx_length;
+	u32            payload_len_nw;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	spinlock_t intr_lock; /* not used : interrupt lock for mac */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending:1;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
+		*chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block);
+
+#ifdef DEBUG
+void purelifi_dump_rx_status(const struct rx_status *status);
+#else
+#define purelifi_dump_rx_status(status)
+#endif
+
+#endif
diff --git a/drivers/net/wireless/purelifi/usb.c b/drivers/net/wireless/purelifi/usb.c
new file mode 100644
index 000000000000..1e47b67533a1
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.c
@@ -0,0 +1,1075 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+#define FCS_LEN 4
+
+#define PROC_FOLDER           "purelifi"
+#define MODULATION_FILENAME   "modulation"
+
+#undef  LOAD_MAC_AND_SERIAL_FROM_FILE
+#undef  LOAD_MAC_AND_SERIAL_FROM_FLASH
+#define LOAD_MAC_AND_SERIAL_FROM_EP0
+
+struct proc_dir_entry *proc_folder;
+struct proc_dir_entry *modulation_proc_entry;
+static atomic_t data_queue_flag;
+
+/*Tx retry backoff timer (in milliseconds).*/
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+
+/*Tx retry backoff timer (in jiffies).*/
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+const struct device_attribute purelifi_attr_frequency = {
+	  .attr = {.name = "frequency", .mode = (0666)},
+	  .show = purelifi_show_sysfs,
+	  .store = purelifi_store_frequency,
+};
+
+struct device_attribute purelifi_attr_modulation = {
+	.attr = {.name = "modulation", .mode = (0666)},
+	.show = purelifi_show_modulation,
+	.store = purelifi_store_modulation,
+};
+
+const struct proc_ops  modulation_fops = {
+	.proc_open  = modulation_open,
+	.proc_read  = modulation_read,
+	.proc_write = modulation_write
+};
+
+struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				    tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			block_queue(usb, tx->station[sidx].mac, false);
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int rx_usb_enabled;
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
+		return;
+	} else if (!urb->context) {
+		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
+
+	tx = &usb->tx;
+
+	if (urb->actual_length != 8) {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[7];
+
+	dev_info(&usb->intf->dev, "Recv status=%u\n", status);
+	dev_info(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+		 buffer[0], buffer[1], buffer[2], buffer[3],
+		 buffer[4], buffer[5]);
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_info(&usb->intf->dev,
+			 "FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_info(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= 0xFD;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		fpga_link_connection_f = 1;
+		dev_info(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		fpga_link_connection_f = 0;
+		dev_info(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_info(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit failed (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0; /*no error return*/
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	u32 payload_len_nw = TO_NETWORK((buffer_len + FCS_LEN));
+
+	usb_req->id = usb_req_id;
+	usb_req->len  = 0;
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == USB_REQ_BEACON_WR) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		usb_req->len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	usb_req->len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	usb_req->len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+		usb_req->len += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->id = TO_NETWORK(usb_req->id);
+	usb_req->len = TO_NETWORK(usb_req->len);
+}
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			usb_complete_t complete_fn,
+			void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(usb_req.id) + sizeof(usb_req.len) +
+			   TO_HOST(usb_req.len);
+
+	dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+	memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	static unsigned char hw_address[ETH_ALEN];
+	static unsigned char serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	create_config(intf);
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+	dev_dbg(&intf->dev, "unit type is station");
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg(&intf->dev, "register device failed (%d)\n", r);
+		goto error;
+	}
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(200);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_info(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = usb_write_req(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_info(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	device_remove_file(&intf->dev, &purelifi_attr_modulation);
+	device_remove_file(&intf->dev, &purelifi_attr_frequency);
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	mutex_lock(&mac->chip.mutex);
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	mutex_unlock(&mac->chip.mutex);
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+	.suspend        = suspend,
+	.resume         = resume,
+	.disable_hub_initiated_lpm = 1,
+};
+
+struct workqueue_struct *purelifi_workqueue;
+
+static int create_proc_entry_e(struct proc_dir_entry *entry,
+			       size_t size,
+			       mode_t mode,
+			       const struct proc_ops *pops,
+			       const char *entry_name,
+			       struct proc_dir_entry *parent
+			     )
+{
+	entry = proc_create(entry_name, mode, parent, pops);
+	if (!(entry))
+		return -ENOMEM;
+	return 0;
+}
+
+static int __init usb_init(void)
+{
+	int r;
+
+	proc_folder = proc_mkdir(PROC_FOLDER, NULL);
+	if (!proc_folder) {
+		pr_err("error creating proc folder\n");
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = create_proc_entry_e(modulation_proc_entry, sizeof(u32), 0666,
+				&modulation_fops, MODULATION_FILENAME,
+				proc_folder);
+	if (r)
+		goto remove_proc_dir;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto remove_modulation_proc_file;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+remove_modulation_proc_file:
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+remove_proc_dir:
+	remove_proc_entry(PROC_FOLDER, NULL);
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+	remove_proc_entry(PROC_FOLDER, NULL);
+	if (ez_usb_interface) {
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_modulation);
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_frequency);
+	}
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi Ltd");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("purelifi/li_fi_x/LiFi-X.bit");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/usb.h b/drivers/net/wireless/purelifi/usb.h
new file mode 100644
index 000000000000..ebc5e9dd2207
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.h
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
+		      (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
+
+#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
+
+#define TO_NETWORK_32(X) TO_NETWORK(X)
+
+#define TO_HOST(X)    TO_NETWORK(X)
+#define TO_HOST_16(X) TO_NETWORK_16(X)
+#define TO_HOST_32(X) TO_NETWORK_32(X)
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2      |     1     |     0	    |
+   // Reserved  | Heartbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+extern const struct device_attribute purelifi_attr_frequency;
+extern struct device_attribute purelifi_attr_modulation;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+#define PURELIFI_SERIAL_LEN 256
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	u8 initialized:1, was_running:1;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+		usb_complete_t complete_fn, void *context);
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
+		*usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *purelifi_intf_to_hw(struct usb_interface
+		*intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
+		*usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/*Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+void create_config(struct usb_interface *intf);
+
+/* Debugfs declarations */
+
+ssize_t purelifi_store_frequency(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t len);
+
+ssize_t purelifi_show_sysfs(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf);
+
+ssize_t purelifi_store_modulation(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t len);
+
+ssize_t purelifi_show_modulation(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf);
+
+int modulation_open(struct inode *inode_p, struct file *file_p);
+
+ssize_t modulation_read(struct file *file_p,
+			char __user *user_p,
+			size_t offset, loff_t *off_p);
+
+ssize_t modulation_write(struct file *file_p,
+			 const char __user *buffer,
+			 size_t count, loff_t *actual_data_written);
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH] [v11] wireless: Initial driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (6 preceding siblings ...)
  2020-12-08  5:53   ` [PATCH] [v10] " Srinivasan Raju
@ 2020-12-08 11:57   ` Srinivasan Raju
  2020-12-08 14:37     ` Kalle Valo
                       ` (3 more replies)
  2021-01-05 13:19   ` [PATCH] [PATCH] [v12] " Srinivasan Raju
                     ` (11 subsequent siblings)
  19 siblings, 4 replies; 108+ messages in thread
From: Srinivasan Raju @ 2020-12-08 11:57 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Mauro Carvalho Chehab, Rob Herring,
	Lukas Bulwahn, open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

---
v11, v10:
- Addressed review comment on readability
- Changed firmware names to match products and latest firmware
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- usb.c is split into two files firmware.c and dbgfs.c
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging
---
 MAINTAINERS                              |    5 +
 drivers/net/wireless/Kconfig             |    1 +
 drivers/net/wireless/Makefile            |    1 +
 drivers/net/wireless/purelifi/Kconfig    |   27 +
 drivers/net/wireless/purelifi/Makefile   |    3 +
 drivers/net/wireless/purelifi/chip.c     |   93 ++
 drivers/net/wireless/purelifi/chip.h     |   81 ++
 drivers/net/wireless/purelifi/dbgfs.c    |  150 +++
 drivers/net/wireless/purelifi/firmware.c |  384 ++++++++
 drivers/net/wireless/purelifi/intf.h     |   38 +
 drivers/net/wireless/purelifi/mac.c      |  873 ++++++++++++++++++
 drivers/net/wireless/purelifi/mac.h      |  189 ++++
 drivers/net/wireless/purelifi/usb.c      | 1075 ++++++++++++++++++++++
 drivers/net/wireless/purelifi/usb.h      |  199 ++++
 14 files changed, 3119 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/chip.c
 create mode 100644 drivers/net/wireless/purelifi/chip.h
 create mode 100644 drivers/net/wireless/purelifi/dbgfs.c
 create mode 100644 drivers/net/wireless/purelifi/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/intf.h
 create mode 100644 drivers/net/wireless/purelifi/mac.c
 create mode 100644 drivers/net/wireless/purelifi/mac.h
 create mode 100644 drivers/net/wireless/purelifi/usb.c
 create mode 100644 drivers/net/wireless/purelifi/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index c80f87d7258c..17955b8497df 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14108,6 +14108,11 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PUREILIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+S:	Supported
+F:	drivers/net/wireless/purelifi
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 170a64e67709..b87da3139f94 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -48,6 +48,7 @@ source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
 source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 
 config PCMCIA_RAYCS
 	tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e9fc770026f0 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
 obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..f6630791df9d
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+config PURELIFI
+
+	tristate "pureLiFi device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This driver makes the adapter appear as a normal WLAN interface.
+
+	   The pureLiFi device requires external STA firmware to be loaded.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called purelifi.
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..cf9b931840ac
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PURELIFI)		:= purelifi.o
+purelifi-objs 		+= chip.o usb.o mac.o firmware.o dbgfs.o
diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
new file mode 100644
index 000000000000..9a7ccd0f98f2
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set && chip->beacon_interval == interval)) {
+		return 0;
+	}
+
+	chip->beacon_interval = interval;
+	chip->beacon_set = true;
+	return usb_write_req((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %02x:%02x v%02x  %02x-%02x-%02x %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr[0], addr[1], addr[2],
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
+{
+	int r;
+
+	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	static const u8 value;
+
+	usb_write_req(&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = usb_write_req(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/chip.h b/drivers/net/wireless/purelifi/chip.h
new file mode 100644
index 000000000000..710bf713b8c4
--- /dev/null
+++ b/drivers/net/wireless/purelifi/chip.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	u16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1 << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/dbgfs.c b/drivers/net/wireless/purelifi/dbgfs.c
new file mode 100644
index 000000000000..d617e4b41c5e
--- /dev/null
+++ b/drivers/net/wireless/purelifi/dbgfs.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include "mac.h"
+#include "usb.h"
+
+ssize_t purelifi_store_frequency(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t len)
+{
+	int i, r, row, col, predivider, feedback_divider, output_div_0;
+	int output_div_1, output_div_2, clkout3_phase;
+	char values[6][4] = {{0}, {0} };
+	char usr_input[8] = {0};
+	bool valid = false;
+
+	row = 0; col = 0;
+	for (i = 0; i < min(len, sizeof(values)); i++) {
+		values[row][col] = buf[i];
+		col++;
+		if (buf[i] == 0x20) {
+			row++;
+			col = 0;
+		} else if (buf[i] == 0x0A) {
+			if (row == 5 && col > 0) {
+				int *index[] = {
+					&predivider,
+					&feedback_divider,
+					&output_div_0,
+					&output_div_1,
+					&output_div_2,
+					&clkout3_phase
+				};
+				valid = true;
+				i = 0;
+				while (valid && i < ARRAY_SIZE(index)) {
+					if (kstrtouint(&values[i][0], 0,
+						       index[i]))
+						valid = false;
+					i++;
+				}
+				break;
+			}
+		}
+	}
+
+	if (valid) {
+		usr_input[0] = (char)predivider;
+		usr_input[1] = (char)feedback_divider;
+		usr_input[2] = (char)output_div_0;
+		usr_input[3] = (char)output_div_1;
+		usr_input[4] = (char)output_div_2;
+		usr_input[5] = (char)clkout3_phase;
+
+		dev_info(dev, "options IP_DIV: %d VCO_MULT: %d OP_DIV_PHY: %d",
+			 usr_input[0], usr_input[1], usr_input[2]);
+		dev_info(dev, "OP_DIV_CPU: %d OP_DIV_USB: %d CLK3_PH: %d\n",
+			 usr_input[3], usr_input[4], usr_input[5]);
+
+		r = usb_write_req((const u8 *)&usr_input[0],
+				  sizeof(usr_input), USB_REQ_SET_FREQ);
+		if (r < 0)
+			dev_err(dev, "SET_FREQ failed (%d)\n", r);
+	} else {
+		dev_err(dev, "Your options are invalid\n");
+	}
+
+	return len;
+}
+
+ssize_t purelifi_show_sysfs(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	return 0;
+}
+
+ssize_t purelifi_store_modulation(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t len)
+{
+	int rate, i;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	for (i = 0; i < min(len, sizeof(usr_input)); i++)
+		usr_input[i] = buf[i];
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return len;
+}
+
+ssize_t purelifi_show_modulation(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	return 0;
+}
+
+void create_config(struct usb_interface *intf)
+{
+	struct device *dev;
+
+	dev = &intf->dev;
+	if (device_create_file(dev, &purelifi_attr_modulation)) {
+		dev_err(&intf->dev,
+			"failed to create modulation sysfs entry\n");
+		return;
+	}
+	if (device_create_file(dev, &purelifi_attr_frequency)) {
+		device_remove_file(dev, &purelifi_attr_modulation);
+		dev_err(&intf->dev,
+			"failed to create frequency sysfs entry\n");
+	}
+}
+
+int modulation_open(struct inode *inode_p, struct file *file_p)
+{
+	return 0;
+}
+
+ssize_t modulation_read(struct file *file_p,
+			char __user *user_p,
+			size_t offset, loff_t *off_p)
+{
+	return 0;
+}
+
+ssize_t modulation_write(struct file *file_p,
+			 const char __user *buffer,
+			 size_t count, loff_t *actual_data_written)
+{
+	int rate;
+	char usr_input[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+	int rv = 0;
+
+	if (copy_from_user(usr_input, buffer, min(count, sizeof(usr_input))))
+		return -EFAULT;
+	usr_input[7] = 0;
+	rv = sscanf((char *)&usr_input[0], "%d", &rate);
+	if (rv != 1)
+		return 0;
+	purelifi_chip_set_rate(NULL, rate);
+	return count;
+}
diff --git a/drivers/net/wireless/purelifi/firmware.c b/drivers/net/wireless/purelifi/firmware.c
new file mode 100644
index 000000000000..8981dea90eb5
--- /dev/null
+++ b/drivers/net/wireless/purelifi/firmware.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/firmware.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, 1000);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+	int r, actual_length;
+	int fw_data_i, blk_tran_len = 16384;
+	const char *fw_name;
+	unsigned char fpga_setting[2];
+	unsigned char *fpga_dmabuff;
+	unsigned char *fw_data;
+	unsigned char fpga_state[9];
+	struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/LiFi-X.bin";
+		dev_info(&intf->dev, "bin file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "purelifi/li_fi_x/LiFi-XC.bin";
+		dev_info(&intf->dev, "bin file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware((const struct firmware **)&fw, fw_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(2, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, 0x33, fpga_dmabuff, sizeof(fpga_setting));
+	memcpy(fpga_setting, fpga_dmabuff, 2);
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x34, NULL, 0);
+
+	if (fpga_setting[0] != 6) {
+		dev_err(&intf->dev, "fpga_setting[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			fw_data[tbuf_idx] =
+				((fw_data[tbuf_idx] & 128) >> 7) |
+				((fw_data[tbuf_idx] &  64) >> 5) |
+				((fw_data[tbuf_idx] &  32) >> 3) |
+				((fw_data[tbuf_idx] &  16) >> 1) |
+				((fw_data[tbuf_idx] &   8) << 1) |
+				((fw_data[tbuf_idx] &   4) << 3) |
+				((fw_data[tbuf_idx] &   2) << 5) |
+				((fw_data[tbuf_idx] &   1) << 7);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_setting[0] & 0xFF),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2000);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(9, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xFF, 9);
+
+	send_vendor_request(udev, 0x30, fpga_dmabuff, sizeof(fpga_state));
+
+	dev_info(&intf->dev, "fpga status: %x %x %x %x %x %x %x %x\n",
+		 fpga_dmabuff[0], fpga_dmabuff[1],
+		 fpga_dmabuff[2], fpga_dmabuff[3],
+		 fpga_dmabuff[4], fpga_dmabuff[5],
+		 fpga_dmabuff[6], fpga_dmabuff[7]);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		kfree(fpga_dmabuff);
+		goto error_free_fw;
+	}
+
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, 0x35, NULL, 0);
+
+	msleep(200);
+
+error_free_fw:
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+	int r;
+
+	struct firmware *fw_packed = NULL;
+	int step;
+	u8 *buf;
+	u32 size, nmb_of_control_packets, i, no_of_files;
+	u32 tot_size, start_addr;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, 0x80, NULL, 0);
+	msleep(100);
+
+	/* Code for single pack file download */
+
+	fw_pack = "purelifi/li_fi_x/LiFi-XL.bin";
+
+	r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	no_of_files = *(u32 *)&fw_packed->data[0];
+	tot_size = fw_packed->size;
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n", no_of_files, tot_size);
+	buf = kzalloc(64, GFP_KERNEL);
+	if (!buf) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	for (step = 0; step < no_of_files; step++) {
+		buf[0] = step;
+		r = send_vendor_command(udev, 0x82, buf, 64);
+
+		if (step < no_of_files - 1)
+			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
+				- *(u32 *)&fw_packed->data[4 + (step) * 4];
+		else
+			size = tot_size -
+				*(u32 *)&fw_packed->data[4 + (step) * 4];
+
+		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
+
+		if ((size % 64) && step < 2)
+			size += 64 - size % 64;
+		nmb_of_control_packets = size / 64;
+
+		for (i = 0; i < nmb_of_control_packets; i++) {
+			memcpy(buf,
+			       &fw_packed->data[start_addr + (i * 64)], 64);
+			r = send_vendor_command(udev, 0x81, buf, 64);
+		}
+		dev_info(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", step, r,
+			 size);
+	}
+	release_firmware(fw_packed);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, 0x83, NULL, 0);
+	dev_info(&intf->dev, "download_fpga (4) (%d)\n", r);
+error:
+	return r;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FILE
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct firmware *fw = NULL;
+	char *mac_file_name = "purelifi/li_fi_x/mac.ascii";
+	char *serial_file_name = "purelifi/li_fi_x/serial.ascii";
+	int r;
+
+	r = request_firmware((const struct firmware **)&fw, mac_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	r = sscanf(fw->data,
+		   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+		    &hw_address[0], &hw_address[1],
+		    &hw_address[2], &hw_address[3],
+		    &hw_address[4], &hw_address[5]);
+	if (r != 6) {
+		dev_err(&intf->dev, "fw_dw - usage args fail (%d)\n", r);
+		goto ERROR;
+	}
+	release_firmware(fw);
+
+	r = request_firmware((const struct firmware **)&fw, serial_file_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware fail (%d)\n", r);
+		goto ERROR;
+	}
+	dev_info(&intf->dev, "fw->data=%s\n", fw->data);
+	strlcpy(serial_number, fw->data, PURELIFI_SERIAL_LEN);
+	dev_info(&intf->dev, "serial_number=%s\n", serial_number);
+	release_firmware(fw);
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+ERROR:
+	return r;
+}
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_FLASH
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+#define FLASH_EC_BUSY 3
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct flash_t flash;
+	unsigned char *flash_sector_buf;
+	unsigned int sector_index;
+
+	do {
+		unsigned char buf[8];
+
+		msleep(200);
+
+		send_vendor_request(udev, 0x40, buf, sizeof(buf));
+		flash.enabled = buf[0] & 0xFF;
+
+		if (flash.enabled) {
+			flash.sectors = ((buf[6] & 255) << 24) |
+				((buf[5] & 255) << 16) |
+				((buf[4] & 255) <<  8) |
+				(buf[3] & 255);
+
+			flash.sector_size =
+				((buf[2] & 255) << 8) | (buf[1] & 255);
+		} else {
+			flash.sectors = 0;
+			flash.sector_size = 0;
+		}
+
+		if (flash.sector_size & 0x8000)
+			flash.sector_size = 1 << (flash.sector_size & 0x7FFF);
+
+		flash.ec = buf[7] & 0xFF;
+	} while (flash.ec == FLASH_EC_BUSY);
+
+	flash_sector_buf = kmalloc(flash.sector_size,
+				   GFP_KERNEL);
+	if (!flash_sector_buf)
+		return -ENOMEM;
+
+	if (!flash.enabled) {
+		dev_err(&intf->dev, "Flash is not enabled\n");
+		return -EINVAL;
+	}
+
+	read_flash(intf, &flash, 0, flash_sector_buf);
+	sector_index = *((unsigned int *)flash_sector_buf);
+
+	read_flash(intf, &flash, sector_index, flash_sector_buf);
+	memcpy(hw_address, flash_sector_buf, ETH_ALEN);
+	strncpy(serial_number, &flash_sector_buf[ETH_ALEN], 256);
+
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+	kfree(flash_sector_buf);
+	return 0;
+}
+
+#endif
+
+#ifdef LOAD_MAC_AND_SERIAL_FROM_EP0
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			unsigned char *serial_number)
+{
+#define USB_MAC_VENDOR_REQUEST 0x36
+#define USB_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define USB_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define SERIAL_NUMBER_LENGTH 14
+	struct usb_device *udev = interface_to_usbdev(intf);
+	int r = 0;
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(14, GFP_KERNEL);
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	send_vendor_request(udev, USB_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	send_vendor_request(udev, USB_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, 14);
+
+	memcpy(serial_number, dma_buffer, SERIAL_NUMBER_LENGTH);
+
+	memset(dma_buffer, 0x00, 14);
+
+	send_vendor_request(udev, USB_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, 8);
+
+	memcpy(&firmware_version, dma_buffer, sizeof(unsigned long long));
+
+	dev_info(&intf->dev, "Firmware Version: %llu, Revision: 16418\n",
+		 firmware_version);
+	kfree(dma_buffer);
+
+	dev_info(&intf->dev, "hw_address=%pM\n", hw_address);
+error:
+	return r;
+}
+
+#endif
diff --git a/drivers/net/wireless/purelifi/intf.h b/drivers/net/wireless/purelifi/intf.h
new file mode 100644
index 000000000000..df0d87331ad7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/intf.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	u16 rssi;
+	u8  rate_idx;
+	u8  pad;
+	u64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             buf[512];
+};
+
+#endif
diff --git a/drivers/net/wireless/purelifi/mac.c b/drivers/net/wireless/purelifi/mac.c
new file mode 100644
index 000000000000..2966553bdc65
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.c
@@ -0,0 +1,873 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+struct plf_reg_alpha2_map {
+	u32 reg;
+	char alpha2[2];
+};
+
+static const struct plf_reg_alpha2_map reg_alpha2_map[] = {
+	{ PLF_REGDOMAIN_FCC, "US" },
+	{ PLF_REGDOMAIN_IC, "CA" },
+	{ PLF_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
+	{ PLF_REGDOMAIN_JAPAN, "JP" },
+	{ PLF_REGDOMAIN_SPAIN, "ES" },
+	{ PLF_REGDOMAIN_FRANCE, "FR" },
+};
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 },
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr);
+
+static int plf_reg2alpha2(u8 regdomain, char *alpha2)
+{
+	unsigned int i;
+	const struct plf_reg_alpha2_map *reg_map;
+		for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) {
+			reg_map = &reg_alpha2_map[i];
+			if (regdomain == reg_map->reg) {
+				alpha2[0] = reg_map->alpha2[0];
+				alpha2[1] = reg_map->alpha2[1];
+				return 0;
+			}
+	}
+	return 1;
+}
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
+{
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+	char alpha2[2];
+
+	mac->default_regdomain = PLF_REGDOMAIN_ETSI;
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	r = plf_reg2alpha2(mac->regdomain, alpha2);
+	r = regulatory_hint(hw->wiphy, alpha2);
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon, false);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status - reports tx status of a packet if required
+ * @hw - a &struct ieee80211_hw pointer
+ * @skb - a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success - True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1, retry = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status) {
+		success = !tx_status->failure;
+		retry = tx_status->retry + success;
+	}
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		/* ieee80211_tx_status_irqsafe(hw, skb); */
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr)
+{
+	return usb_write_req((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_info(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (struct purelifi_ctrlset *)skb_push(skb,
+			sizeof(struct purelifi_ctrlset));
+	cs->id = USB_REQ_DATA_TX;
+	cs->payload_len_nw = frag_len;
+	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
+		- sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned and align data*/
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		cs->len +=  4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data*/
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen b/c
+				 * sufficient headroom was reserved
+				 */
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		cs->len +=  4;
+	}
+
+	cs->id = TO_NETWORK(cs->id);
+	cs->len = TO_NETWORK(cs->len);
+	cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
+
+	return 0;
+}
+
+/**
+ * purelifi_op_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+	info->rate_driver_data[0] = hw;
+
+	if (skb->data[24] == IEEE80211_FTYPE_DATA) {
+		u8 dst_mac[ETH_ALEN];
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		memcpy(dst_mac, &skb->data[28], ETH_ALEN);
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			block_queue(usb, tx->station[sidx].mac, true);
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			dev_kfree_skb(skb);
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
+					USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+		      struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	bool found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_info(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	u32 crc_error_cnt_low, crc_error_cnt_high;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_2GHZ;
+	mac->rssi      = -15 * ntohs(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	crc_error_cnt_low = status->crc_error_count;
+	crc_error_cnt_high = status->crc_error_count >> 32;
+	mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
+		ntohl(crc_error_cnt_high);
+
+	if (!bad_frame &&
+	    filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = TO_HOST(*(u32 *)buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (buffer[0] == IEEE80211_STYPE_PROBE_REQ)
+		dev_info(purelifi_mac_dev(mac), "Probe request\n");
+	else if (buffer[0] == IEEE80211_STYPE_ASSOC_REQ)
+		dev_info(purelifi_mac_dev(mac), "Association request\n");
+
+	if (buffer[0] == IEEE80211_STYPE_AUTH) {
+		dev_info(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+	} else if (buffer[0] == IEEE80211_FTYPE_DATA) {
+		unsigned short int seq_nmb = (buffer[23] << 4) |
+			((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000)) {
+			dev_info(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
+		} else {
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		}
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+	if (need_padding) {
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+	}
+
+	memcpy(skb_put(skb, payload_length), buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_info(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			 iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_info(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_info(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon, false);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;
+
+	_ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
+	_ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
+	_ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
+	_ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* 4 for 32 bit alignment if no tailroom */
+
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/mac.h b/drivers/net/wireless/purelifi/mac.h
new file mode 100644
index 000000000000..f0ebe4018d7e
--- /dev/null
+++ b/drivers/net/wireless/purelifi/mac.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             modulation;
+	u8             control;
+	u8             service;
+	u8             pad;
+	__le16         packet_length;
+	__le16         current_length;
+	__le16          next_frame_length;
+	__le16         tx_length;
+	u32            payload_len_nw;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	spinlock_t intr_lock; /* not used : interrupt lock for mac */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending:1;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
+		*chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block);
+
+#ifdef DEBUG
+void purelifi_dump_rx_status(const struct rx_status *status);
+#else
+#define purelifi_dump_rx_status(status)
+#endif
+
+#endif
diff --git a/drivers/net/wireless/purelifi/usb.c b/drivers/net/wireless/purelifi/usb.c
new file mode 100644
index 000000000000..94a258827165
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.c
@@ -0,0 +1,1075 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+#define FCS_LEN 4
+
+#define PROC_FOLDER           "purelifi"
+#define MODULATION_FILENAME   "modulation"
+
+#undef  LOAD_MAC_AND_SERIAL_FROM_FILE
+#undef  LOAD_MAC_AND_SERIAL_FROM_FLASH
+#define LOAD_MAC_AND_SERIAL_FROM_EP0
+
+struct proc_dir_entry *proc_folder;
+struct proc_dir_entry *modulation_proc_entry;
+static atomic_t data_queue_flag;
+
+/*Tx retry backoff timer (in milliseconds).*/
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+
+/*Tx retry backoff timer (in jiffies).*/
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+const struct device_attribute purelifi_attr_frequency = {
+	  .attr = {.name = "frequency", .mode = (0666)},
+	  .show = purelifi_show_sysfs,
+	  .store = purelifi_store_frequency,
+};
+
+struct device_attribute purelifi_attr_modulation = {
+	.attr = {.name = "modulation", .mode = (0666)},
+	.show = purelifi_show_modulation,
+	.store = purelifi_store_modulation,
+};
+
+const struct proc_ops  modulation_fops = {
+	.proc_open  = modulation_open,
+	.proc_read  = modulation_read,
+	.proc_write = modulation_write
+};
+
+struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				    tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			block_queue(usb, tx->station[sidx].mac, false);
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int rx_usb_enabled;
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
+		return;
+	} else if (!urb->context) {
+		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
+
+	tx = &usb->tx;
+
+	if (urb->actual_length != 8) {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[7];
+
+	dev_info(&usb->intf->dev, "Recv status=%u\n", status);
+	dev_info(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+		 buffer[0], buffer[1], buffer[2], buffer[3],
+		 buffer[4], buffer[5]);
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_info(&usb->intf->dev,
+			 "FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_info(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= 0xFD;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		fpga_link_connection_f = 1;
+		dev_info(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		fpga_link_connection_f = 0;
+		dev_info(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_info(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit failed (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0; /*no error return*/
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	u32 payload_len_nw = TO_NETWORK((buffer_len + FCS_LEN));
+
+	usb_req->id = usb_req_id;
+	usb_req->len  = 0;
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == USB_REQ_BEACON_WR) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		usb_req->len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	usb_req->len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	usb_req->len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+		usb_req->len += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->id = TO_NETWORK(usb_req->id);
+	usb_req->len = TO_NETWORK(usb_req->len);
+}
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			usb_complete_t complete_fn,
+			void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(usb_req.id) + sizeof(usb_req.len) +
+			   TO_HOST(usb_req.len);
+
+	dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+	memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	static unsigned char hw_address[ETH_ALEN];
+	static unsigned char serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	create_config(intf);
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+	dev_dbg(&intf->dev, "unit type is station");
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg(&intf->dev, "register device failed (%d)\n", r);
+		goto error;
+	}
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(200);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_info(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_info(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = usb_write_req(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_info(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	device_remove_file(&intf->dev, &purelifi_attr_modulation);
+	device_remove_file(&intf->dev, &purelifi_attr_frequency);
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	mutex_lock(&mac->chip.mutex);
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	mutex_unlock(&mac->chip.mutex);
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+	.suspend        = suspend,
+	.resume         = resume,
+	.disable_hub_initiated_lpm = 1,
+};
+
+struct workqueue_struct *purelifi_workqueue;
+
+static int create_proc_entry_e(struct proc_dir_entry *entry,
+			       size_t size,
+			       mode_t mode,
+			       const struct proc_ops *pops,
+			       const char *entry_name,
+			       struct proc_dir_entry *parent
+			     )
+{
+	entry = proc_create(entry_name, mode, parent, pops);
+	if (!(entry))
+		return -ENOMEM;
+	return 0;
+}
+
+static int __init usb_init(void)
+{
+	int r;
+
+	proc_folder = proc_mkdir(PROC_FOLDER, NULL);
+	if (!proc_folder) {
+		pr_err("error creating proc folder\n");
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = create_proc_entry_e(modulation_proc_entry, sizeof(u32), 0666,
+				&modulation_fops, MODULATION_FILENAME,
+				proc_folder);
+	if (r)
+		goto remove_proc_dir;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto remove_modulation_proc_file;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+remove_modulation_proc_file:
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+remove_proc_dir:
+	remove_proc_entry(PROC_FOLDER, NULL);
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	remove_proc_entry(MODULATION_FILENAME, proc_folder);
+	remove_proc_entry(PROC_FOLDER, NULL);
+	if (ez_usb_interface) {
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_modulation);
+		device_remove_file(&ez_usb_interface->dev,
+				   &purelifi_attr_frequency);
+	}
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi Ltd");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("purelifi/li_fi_x/LiFi-X.bin");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/usb.h b/drivers/net/wireless/purelifi/usb.h
new file mode 100644
index 000000000000..ebc5e9dd2207
--- /dev/null
+++ b/drivers/net/wireless/purelifi/usb.h
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
+		      (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
+
+#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
+
+#define TO_NETWORK_32(X) TO_NETWORK(X)
+
+#define TO_HOST(X)    TO_NETWORK(X)
+#define TO_HOST_16(X) TO_NETWORK_16(X)
+#define TO_HOST_32(X) TO_NETWORK_32(X)
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2      |     1     |     0	    |
+   // Reserved  | Heartbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+extern const struct device_attribute purelifi_attr_frequency;
+extern struct device_attribute purelifi_attr_modulation;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+#define PURELIFI_SERIAL_LEN 256
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	u8 initialized:1, was_running:1;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+		usb_complete_t complete_fn, void *context);
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
+		*usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *purelifi_intf_to_hw(struct usb_interface
+		*intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
+		*usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/*Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+void create_config(struct usb_interface *intf);
+
+/* Debugfs declarations */
+
+ssize_t purelifi_store_frequency(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t len);
+
+ssize_t purelifi_show_sysfs(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf);
+
+ssize_t purelifi_store_modulation(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t len);
+
+ssize_t purelifi_show_modulation(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf);
+
+int modulation_open(struct inode *inode_p, struct file *file_p);
+
+ssize_t modulation_read(struct file *file_p,
+			char __user *user_p,
+			size_t offset, loff_t *off_p);
+
+ssize_t modulation_write(struct file *file_p,
+			 const char __user *buffer,
+			 size_t count, loff_t *actual_data_written);
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v11] wireless: Initial driver submission for pureLiFi STA devices
  2020-12-08 11:57   ` [PATCH] [v11] " Srinivasan Raju
@ 2020-12-08 14:37     ` Kalle Valo
  2020-12-19 12:51     ` Kalle Valo
                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2020-12-08 14:37 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
>
> This driver implementation has been based on the zd1211rw driver.
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
>
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

Please limit how often you send a new version of this driver. It does
not speed up the review, quite the opposite actually as you just flood
the patchwork (and my inbox). Especially if there are only cosmetic
changes there's no point of sending a new version immediately, only send
a new version when there are major changes.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v11] wireless: Initial driver submission for pureLiFi STA devices
  2020-12-08 11:57   ` [PATCH] [v11] " Srinivasan Raju
  2020-12-08 14:37     ` Kalle Valo
@ 2020-12-19 12:51     ` Kalle Valo
  2020-12-19 13:06     ` Kalle Valo
  2020-12-19 13:14     ` Kalle Valo
  3 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2020-12-19 12:51 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
>
> This driver implementation has been based on the zd1211rw driver.
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
>
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

My first quick comments after 10 minutes of looking at this driver, so
not complete in any way:

Does not compile:

ERROR: modpost: "upload_mac_and_serial" [drivers/net/wireless/purelifi/purelifi.ko] undefined!

>  MAINTAINERS                              |    5 +
>  drivers/net/wireless/Kconfig             |    1 +
>  drivers/net/wireless/Makefile            |    1 +
>  drivers/net/wireless/purelifi/Kconfig    |   27 +
>  drivers/net/wireless/purelifi/Makefile   |    3 +
>  drivers/net/wireless/purelifi/chip.c     |   93 ++
>  drivers/net/wireless/purelifi/chip.h     |   81 ++
>  drivers/net/wireless/purelifi/dbgfs.c    |  150 +++
>  drivers/net/wireless/purelifi/firmware.c |  384 ++++++++
>  drivers/net/wireless/purelifi/intf.h     |   38 +
>  drivers/net/wireless/purelifi/mac.c      |  873 ++++++++++++++++++
>  drivers/net/wireless/purelifi/mac.h      |  189 ++++
>  drivers/net/wireless/purelifi/usb.c      | 1075 ++++++++++++++++++++++
>  drivers/net/wireless/purelifi/usb.h      |  199 ++++

The directory structure should be:

drivers/net/wireless/<vendor>/<drivername>/<file>

So please come up with a unique name for the driver which describes the
supported hardware somehow. Calling the driver "purelifi" is imho too
generic, what happens if/when there's a second generation hardware and
that needs a completely new driver? Just to give examples I like names
like rtw88 and mt76.

And I would prefer that the driver name is also used as the directory
name for firmware files, easier to find that way.

> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_PURELIFI)		:= purelifi.o
> +purelifi-objs 		+= chip.o usb.o mac.o firmware.o dbgfs.o
> diff --git a/drivers/net/wireless/purelifi/chip.c b/drivers/net/wireless/purelifi/chip.c
> new file mode 100644
> index 000000000000..9a7ccd0f98f2
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/chip.c
> @@ -0,0 +1,93 @@
> +// SPDX-License-Identifier: GPL-2.0-only

Copyright missing in all files.

> +#undef  LOAD_MAC_AND_SERIAL_FROM_FILE
> +#undef  LOAD_MAC_AND_SERIAL_FROM_FLASH
> +#define LOAD_MAC_AND_SERIAL_FROM_EP0

This should be dynamic and not compile time configurable. For example
try file first, next flash and EP0 last, or something like that.

> +const struct device_attribute purelifi_attr_frequency = {
> +	  .attr = {.name = "frequency", .mode = (0666)},
> +	  .show = purelifi_show_sysfs,
> +	  .store = purelifi_store_frequency,
> +};
> +
> +struct device_attribute purelifi_attr_modulation = {
> +	.attr = {.name = "modulation", .mode = (0666)},
> +	.show = purelifi_show_modulation,
> +	.store = purelifi_store_modulation,
> +};
> +
> +const struct proc_ops  modulation_fops = {
> +	.proc_open  = modulation_open,
> +	.proc_read  = modulation_read,
> +	.proc_write = modulation_write
> +};

No procfs or sysfs files in wireless drivers, please. Needs a strong
reason to have an exception for that rule.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v11] wireless: Initial driver submission for pureLiFi STA devices
  2020-12-08 11:57   ` [PATCH] [v11] " Srinivasan Raju
  2020-12-08 14:37     ` Kalle Valo
  2020-12-19 12:51     ` Kalle Valo
@ 2020-12-19 13:06     ` Kalle Valo
  2020-12-19 13:14     ` Kalle Valo
  3 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2020-12-19 13:06 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
>
> This driver implementation has been based on the zd1211rw driver.
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
>
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.

Is endianess support is properly implemented?

> +			fw_data[tbuf_idx] =
> +				((fw_data[tbuf_idx] & 128) >> 7) |
> +				((fw_data[tbuf_idx] &  64) >> 5) |
> +				((fw_data[tbuf_idx] &  32) >> 3) |
> +				((fw_data[tbuf_idx] &  16) >> 1) |
> +				((fw_data[tbuf_idx] &   8) << 1) |
> +				((fw_data[tbuf_idx] &   4) << 3) |
> +				((fw_data[tbuf_idx] &   2) << 5) |
> +				((fw_data[tbuf_idx] &   1) << 7);

Is this cpu_to_le16() or what? Try avoid reinventing the wheel and use
what kernel provides you.

Also noticed lots of dev_info() spamming, please convert those to debug
messages.

And rx_usb_enabled is racy and it will not work if there are multiple
devices. Maybe move it to struct purelifi_usb or similar?

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v11] wireless: Initial driver submission for pureLiFi STA devices
  2020-12-08 11:57   ` [PATCH] [v11] " Srinivasan Raju
                       ` (2 preceding siblings ...)
  2020-12-19 13:06     ` Kalle Valo
@ 2020-12-19 13:14     ` Kalle Valo
  2020-12-21  5:52       ` Srinivasan Raju
  3 siblings, 1 reply; 108+ messages in thread
From: Kalle Valo @ 2020-12-19 13:14 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
>
> This driver implementation has been based on the zd1211rw driver.
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
>
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

[...]

> +int download_fpga(struct usb_interface *intf)
> +{
> +	int r, actual_length;
> +	int fw_data_i, blk_tran_len = 16384;
> +	const char *fw_name;
> +	unsigned char fpga_setting[2];
> +	unsigned char *fpga_dmabuff;
> +	unsigned char *fw_data;
> +	unsigned char fpga_state[9];
> +	struct firmware *fw = NULL;
> +	struct usb_device *udev = interface_to_usbdev(intf);
> +
> +	if ((le16_to_cpu(udev->descriptor.idVendor) ==
> +				PURELIFI_X_VENDOR_ID_0) &&
> +	    (le16_to_cpu(udev->descriptor.idProduct) ==
> +				PURELIFI_X_PRODUCT_ID_0)) {
> +		fw_name = "purelifi/li_fi_x/LiFi-X.bin";
> +		dev_info(&intf->dev, "bin file for X selected\n");
> +
> +	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
> +					PURELIFI_XC_VENDOR_ID_0 &&
> +		   (le16_to_cpu(udev->descriptor.idProduct) ==
> +					PURELIFI_XC_PRODUCT_ID_0)) {
> +		fw_name = "purelifi/li_fi_x/LiFi-XC.bin";
> +		dev_info(&intf->dev, "bin file for XC selected\n");
> +
> +	} else {
> +		r = -EINVAL;
> +		goto error;
> +	}
> +
> +	r = request_firmware((const struct firmware **)&fw, fw_name,
> +			     &intf->dev);
> +	if (r) {
> +		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
> +		goto error;
> +	}
> +	fpga_dmabuff = NULL;
> +	fpga_dmabuff = kmalloc(2, GFP_KERNEL);
> +
> +	if (!fpga_dmabuff) {
> +		r = -ENOMEM;
> +		goto error_free_fw;
> +	}
> +	send_vendor_request(udev, 0x33, fpga_dmabuff, sizeof(fpga_setting));
> +	memcpy(fpga_setting, fpga_dmabuff, 2);
> +	kfree(fpga_dmabuff);
> +
> +	send_vendor_command(udev, 0x34, NULL, 0);

I see lots of magic numbers in the driver like 2, 0x33 and 0x34 here.
Please convert the magic numbers to proper defines explaining the
meaning. And for vendor commands you could even use enum to group them
better in .h file somewhere.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v7] wireless: Initial driver submission for pureLiFi STA devices
  2020-12-03 16:50               ` Srinivasan Raju
@ 2020-12-19 13:15                 ` Kalle Valo
  0 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2020-12-19 13:15 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

>> What will be the directory structure in linux-firmware? It should be
>> unique so that it's not possible to mix with other drivers.
>
> I have created the following directory structure, Please let me know if this is OK.
>
>  LICENCE.purelifi_firmware |  29 +++++++++++++++++++++++++++++
>  purelifi/Li-Fi-XL.bin     | Bin 0 -> 70228 bytes
>  purelifi/fpga.bit         | Bin 0 -> 3825907 bytes
>  purelifi/fpga_xc.bit      | Bin 0 -> 3825906 bytes
>  4 files changed, 29 insertions(+)

I would prefer to change purelifi to the name of the driver, I'll
explain more in my review in v11.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* RE: [PATCH] [v11] wireless: Initial driver submission for pureLiFi STA devices
  2020-12-19 13:14     ` Kalle Valo
@ 2020-12-21  5:52       ` Srinivasan Raju
  2020-12-21  5:57         ` Kalle Valo
  0 siblings, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2020-12-21  5:52 UTC (permalink / raw)
  To: Kalle Valo
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS


> I see lots of magic numbers in the driver like 2, 0x33 and 0x34 here.
> Please convert the magic numbers to proper defines explaining the meaning. And for vendor commands you could even use enum to group them better in .h file somewhere.

Hi Kalle,

Thanks for reviewing the driver, We will work on the comments.

Regards,
Srini


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v11] wireless: Initial driver submission for pureLiFi STA devices
  2020-12-21  5:52       ` Srinivasan Raju
@ 2020-12-21  5:57         ` Kalle Valo
  2021-01-15 12:13           ` Srinivasan Raju
  0 siblings, 1 reply; 108+ messages in thread
From: Kalle Valo @ 2020-12-21  5:57 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

>> I see lots of magic numbers in the driver like 2, 0x33 and 0x34 here.
>> Please convert the magic numbers to proper defines explaining the
>> meaning. And for vendor commands you could even use enum to group
>> them better in .h file somewhere.
>
> Hi Kalle,
>
> Thanks for reviewing the driver, We will work on the comments.

I haven't had time to do a throrough review yet, but I suggest fixing
the stuff I commented and submitting v12. I'll then do a new review with
v12.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [PATCH] [v12] wireless: Initial driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (7 preceding siblings ...)
  2020-12-08 11:57   ` [PATCH] [v11] " Srinivasan Raju
@ 2021-01-05 13:19   ` Srinivasan Raju
  2021-02-12 11:49   ` [PATCH] [v13] " Srinivasan Raju
                     ` (10 subsequent siblings)
  19 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-01-05 13:19 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

---
v12:
- Removed sysfs, procfs related code
- Addressed race condition bug
- Used macros instead of magic numbers in firmware.c
- Added copyright in all files
v11, v10:
- Addressed review comment on readability
- Changed firmware names to match products
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging
---
 MAINTAINERS                                   |    5 +
 drivers/net/wireless/Kconfig                  |    1 +
 drivers/net/wireless/Makefile                 |    1 +
 drivers/net/wireless/purelifi/Kconfig         |   17 +
 drivers/net/wireless/purelifi/Makefile        |    2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |   13 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |    3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |   96 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |   84 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   |  290 +++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |   41 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    |  876 ++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    |  192 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 1016 +++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    |  177 +++
 15 files changed, 2814 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 5c1a6ba5ef26..3178ded0a91e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14199,6 +14199,11 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PUREILIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+S:	Supported
+F:	drivers/net/wireless/purelifi
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7add2002ff4c..3a1f5ef3aa43 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -35,6 +35,7 @@ source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
 source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 
 config PCMCIA_RAYCS
 	tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e9fc770026f0 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
 obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..e39afec3dcae
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+source "drivers/net/wireless/purelifi/plfxlc/Kconfig"
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..2ed07f279329
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI)		:= plfxlc/
diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
new file mode 100644
index 000000000000..07a65c0fce68
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config PLFXLC
+
+	tristate "pureLiFi X, XL, XC device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This driver makes the adapter appear as a normal WLAN interface.
+
+	   The pureLiFi device requires external STA firmware to be loaded.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called purelifi.
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/Makefile b/drivers/net/wireless/purelifi/plfxlc/Makefile
new file mode 100644
index 000000000000..81fe9e010353
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PLFXLC)		:= plfxlc.o
+plfxlc-objs 		+= chip.o firmware.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.c b/drivers/net/wireless/purelifi/plfxlc/chip.c
new file mode 100644
index 000000000000..a5fda26241e8
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set && chip->beacon_interval == interval)) {
+		return 0;
+	}
+
+	chip->beacon_interval = interval;
+	chip->beacon_set = true;
+	return usb_write_req((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %02x:%02x v%02x  %02x-%02x-%02x %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr[0], addr[1], addr[2],
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
+{
+	int r;
+
+	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	static const u8 value;
+
+	usb_write_req(&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = usb_write_req(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.h b/drivers/net/wireless/purelifi/plfxlc/chip.h
new file mode 100644
index 000000000000..ce406000769c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	u16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1 << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/plfxlc/firmware.c b/drivers/net/wireless/purelifi/plfxlc/firmware.c
new file mode 100644
index 000000000000..5c3948b4f668
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/firmware.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/firmware.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+#define PLF_VNDR_FPGA_STATE_REQ 0x30
+#define PLF_VNDR_FPGA_SET_REQ 0x33
+#define PLF_VNDR_FPGA_SET_CMD 0x34
+#define PLF_VNDR_FPGA_STATE_CMD 0x35
+	int r, actual_length;
+	int fw_data_i, blk_tran_len = PLF_BULK_TLEN;
+	const char *fw_name;
+	unsigned char *fpga_dmabuff;
+	unsigned char *fw_data;
+	unsigned char fpga_setting[PLF_FPGA_SLEN];
+	unsigned char fpga_state[PLF_FPGA_ST_LEN];
+	struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/LiFi-X.bin";
+		dev_dbg(&intf->dev, "bin file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/LiFi-XC.bin";
+		dev_dbg(&intf->dev, "bin file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware((const struct firmware **)&fw, fw_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(PLF_FPGA_SLEN, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ, fpga_dmabuff, sizeof(fpga_setting));
+	memcpy(fpga_setting, fpga_dmabuff, PLF_FPGA_SLEN);
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_SET_CMD, NULL, 0);
+
+	if (fpga_setting[0] != PLF_FPGA_MG) {
+		dev_err(&intf->dev, "fpga_setting[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			/* u8 bit reverse */
+			fw_data[tbuf_idx] =
+				((fw_data[tbuf_idx] & 128) >> 7) |
+				((fw_data[tbuf_idx] &  64) >> 5) |
+				((fw_data[tbuf_idx] &  32) >> 3) |
+				((fw_data[tbuf_idx] &  16) >> 1) |
+				((fw_data[tbuf_idx] &   8) << 1) |
+				((fw_data[tbuf_idx] &   4) << 3) |
+				((fw_data[tbuf_idx] &   2) << 5) |
+				((fw_data[tbuf_idx] &   1) << 7);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_setting[0] & 0xFF),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2 * PLF_USB_TIMEOUT);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(PLF_FPGA_ST_LEN, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xFF, PLF_FPGA_ST_LEN);
+
+	send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
+			    sizeof(fpga_state));
+
+	dev_dbg(&intf->dev, "fpga status: %x %x %x %x %x %x %x %x\n",
+		fpga_dmabuff[0], fpga_dmabuff[1],
+		fpga_dmabuff[2], fpga_dmabuff[3],
+		fpga_dmabuff[4], fpga_dmabuff[5],
+		fpga_dmabuff[6], fpga_dmabuff[7]);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		kfree(fpga_dmabuff);
+		goto error_free_fw;
+	}
+
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
+
+	msleep(200);
+
+error_free_fw:
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+#define PLF_VNDR_XL_FW_CMD 0x80
+#define PLF_VNDR_XL_DATA_CMD 0x81
+#define PLF_VNDR_XL_FILE_CMD 0x82
+#define PLF_VNDR_XL_EX_CMD 0x83
+	int r;
+	struct firmware *fw_packed = NULL;
+	int step;
+	u8 *buf;
+	u32 size, nmb_of_control_packets, i, no_of_files;
+	u32 tot_size, start_addr;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
+	msleep(100);
+
+	/* Code for single pack file download */
+
+	fw_pack = "plfxlc/LiFi-XL.bin";
+
+	r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	no_of_files = *(u32 *)&fw_packed->data[0];
+	tot_size = fw_packed->size;
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n", no_of_files, tot_size);
+	buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
+	if (!buf) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	for (step = 0; step < no_of_files; step++) {
+		buf[0] = step;
+		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
+					PLF_XL_BUF_LEN);
+
+		if (step < no_of_files - 1)
+			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
+				- *(u32 *)&fw_packed->data[4 + (step) * 4];
+		else
+			size = tot_size -
+				*(u32 *)&fw_packed->data[4 + (step) * 4];
+
+		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
+
+		if ((size % PLF_XL_BUF_LEN) && step < 2)
+			size += PLF_XL_BUF_LEN - size % PLF_XL_BUF_LEN;
+		nmb_of_control_packets = size / PLF_XL_BUF_LEN;
+
+		for (i = 0; i < nmb_of_control_packets; i++) {
+			memcpy(buf,
+			       &fw_packed->data[start_addr + (i * PLF_XL_BUF_LEN)],
+			       PLF_XL_BUF_LEN);
+			r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
+						PLF_XL_BUF_LEN);
+		}
+		dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", step, r,
+			size);
+	}
+	release_firmware(fw_packed);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
+	dev_dbg(&intf->dev, "download_fpga (4) (%d)\n", r);
+error:
+	return r;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+#define PLF_MAC_VENDOR_REQUEST 0x36
+#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define PLF_SERIAL_LEN 14
+#define PLF_FW_VER_LEN 8
+	struct usb_device *udev = interface_to_usbdev(intf);
+	int r = 0;
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	send_vendor_request(udev, PLF_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	memcpy(serial_number, dma_buffer, PLF_SERIAL_LEN);
+
+	memset(dma_buffer, 0x00, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, PLF_FW_VER_LEN);
+
+	memcpy(&firmware_version, dma_buffer, PLF_FW_VER_LEN);
+
+	dev_info(&intf->dev, "Firmware Version: %llu\n", firmware_version);
+	kfree(dma_buffer);
+
+	dev_dbg(&intf->dev, "hw_address=%pM\n", hw_address);
+error:
+	return r;
+}
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/intf.h b/drivers/net/wireless/purelifi/plfxlc/intf.h
new file mode 100644
index 000000000000..4877882cc9e2
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/intf.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	u16 rssi;
+	u8  rate_idx;
+	u8  pad;
+	u64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             buf[512];
+};
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
new file mode 100644
index 000000000000..848225c08a73
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.c
@@ -0,0 +1,876 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+struct plf_reg_alpha2_map {
+	u32 reg;
+	char alpha2[2];
+};
+
+static const struct plf_reg_alpha2_map reg_alpha2_map[] = {
+	{ PLF_REGDOMAIN_FCC, "US" },
+	{ PLF_REGDOMAIN_IC, "CA" },
+	{ PLF_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
+	{ PLF_REGDOMAIN_JAPAN, "JP" },
+	{ PLF_REGDOMAIN_SPAIN, "ES" },
+	{ PLF_REGDOMAIN_FRANCE, "FR" },
+};
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 },
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr);
+
+static int plf_reg2alpha2(u8 regdomain, char *alpha2)
+{
+	unsigned int i;
+	const struct plf_reg_alpha2_map *reg_map;
+		for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) {
+			reg_map = &reg_alpha2_map[i];
+			if (regdomain == reg_map->reg) {
+				alpha2[0] = reg_map->alpha2[0];
+				alpha2[1] = reg_map->alpha2[1];
+				return 0;
+			}
+	}
+	return 1;
+}
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
+{
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+	char alpha2[2];
+
+	mac->default_regdomain = PLF_REGDOMAIN_ETSI;
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	r = plf_reg2alpha2(mac->regdomain, alpha2);
+	r = regulatory_hint(hw->wiphy, alpha2);
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon, false);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status - reports tx status of a packet if required
+ * @hw - a &struct ieee80211_hw pointer
+ * @skb - a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success - True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1, retry = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status) {
+		success = !tx_status->failure;
+		retry = tx_status->retry + success;
+	}
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		/* ieee80211_tx_status_irqsafe(hw, skb); */
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr)
+{
+	return usb_write_req((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_dbg(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (struct purelifi_ctrlset *)skb_push(skb,
+			sizeof(struct purelifi_ctrlset));
+	cs->id = USB_REQ_DATA_TX;
+	cs->payload_len_nw = frag_len;
+	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
+		- sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned and align data*/
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		cs->len +=  4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data*/
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen b/c
+				 * sufficient headroom was reserved
+				 */
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		cs->len +=  4;
+	}
+
+	cs->id = TO_NETWORK(cs->id);
+	cs->len = TO_NETWORK(cs->len);
+	cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
+
+	return 0;
+}
+
+/**
+ * purelifi_op_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+	info->rate_driver_data[0] = hw;
+
+	if (skb->data[24] == IEEE80211_FTYPE_DATA) {
+		u8 dst_mac[ETH_ALEN];
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		memcpy(dst_mac, &skb->data[28], ETH_ALEN);
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			block_queue(usb, tx->station[sidx].mac, true);
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			dev_kfree_skb(skb);
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
+					USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+		      struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	bool found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_dbg(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	u32 crc_error_cnt_low, crc_error_cnt_high;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_2GHZ;
+	mac->rssi      = -15 * ntohs(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	crc_error_cnt_low = status->crc_error_count;
+	crc_error_cnt_high = status->crc_error_count >> 32;
+	mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
+		ntohl(crc_error_cnt_high);
+
+	if (!bad_frame &&
+	    filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = TO_HOST(*(u32 *)buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (buffer[0] == IEEE80211_STYPE_PROBE_REQ)
+		dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
+	else if (buffer[0] == IEEE80211_STYPE_ASSOC_REQ)
+		dev_dbg(purelifi_mac_dev(mac), "Association request\n");
+
+	if (buffer[0] == IEEE80211_STYPE_AUTH) {
+		dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+	} else if (buffer[0] == IEEE80211_FTYPE_DATA) {
+		unsigned short int seq_nmb = (buffer[23] << 4) |
+			((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000)) {
+			dev_dbg(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
+		} else {
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		}
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+	if (need_padding) {
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+	}
+
+	memcpy(skb_put(skb, payload_length), buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_dbg(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_dbg(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_dbg(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon, false);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;
+
+	_ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
+	_ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
+	_ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
+	_ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* 4 for 32 bit alignment if no tailroom */
+
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.h b/drivers/net/wireless/purelifi/plfxlc/mac.h
new file mode 100644
index 000000000000..03b945e70dfb
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.h
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             modulation;
+	u8             control;
+	u8             service;
+	u8             pad;
+	__le16         packet_length;
+	__le16         current_length;
+	__le16          next_frame_length;
+	__le16         tx_length;
+	u32            payload_len_nw;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	spinlock_t intr_lock; /* not used : interrupt lock for mac */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending:1;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
+		*chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block);
+
+#ifdef DEBUG
+void purelifi_dump_rx_status(const struct rx_status *status);
+#else
+#define purelifi_dump_rx_status(status)
+#endif
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
new file mode 100644
index 000000000000..4a4c7565023f
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -0,0 +1,1016 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+#define FCS_LEN 4
+
+#define PROC_FOLDER           "purelifi"
+#define MODULATION_FILENAME   "modulation"
+
+#undef  LOAD_MAC_AND_SERIAL_FROM_FILE
+#undef  LOAD_MAC_AND_SERIAL_FROM_FLASH
+#define LOAD_MAC_AND_SERIAL_FROM_EP0
+
+struct proc_dir_entry *proc_folder;
+struct proc_dir_entry *modulation_proc_entry;
+static atomic_t data_queue_flag;
+
+/*Tx retry backoff timer (in milliseconds).*/
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+
+/*Tx retry backoff timer (in jiffies).*/
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				    tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			block_queue(usb, tx->station[sidx].mac, false);
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
+		return;
+	} else if (!urb->context) {
+		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
+
+	tx = &usb->tx;
+
+	if (urb->actual_length != 8) {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[7];
+
+	dev_dbg(&usb->intf->dev, "Recv status=%u\n", status);
+	dev_dbg(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+		buffer[0], buffer[1], buffer[2], buffer[3],
+		buffer[4], buffer[5]);
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_dbg(&usb->intf->dev,
+			"FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_dbg(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= 0xFD;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		fpga_link_connection_f = 1;
+		dev_dbg(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		fpga_link_connection_f = 0;
+		dev_dbg(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_dbg(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit failed (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0; /*no error return*/
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		usb->rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	usb->rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	u32 payload_len_nw = TO_NETWORK((buffer_len + FCS_LEN));
+
+	usb_req->id = usb_req_id;
+	usb_req->len  = 0;
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == USB_REQ_BEACON_WR) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		usb_req->len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	usb_req->len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	usb_req->len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+		usb_req->len += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->id = TO_NETWORK(usb_req->id);
+	usb_req->len = TO_NETWORK(usb_req->len);
+}
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			usb_complete_t complete_fn,
+			void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(usb_req.id) + sizeof(usb_req.len) +
+			   TO_HOST(usb_req.len);
+
+	dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+	memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	static unsigned char hw_address[ETH_ALEN];
+	static unsigned char serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+	dev_dbg(&intf->dev, "unit type is station");
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg(&intf->dev, "register device failed (%d)\n", r);
+		goto error;
+	}
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(200);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = usb_write_req(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	mutex_lock(&mac->chip.mutex);
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	mutex_unlock(&mac->chip.mutex);
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+	.suspend        = suspend,
+	.resume         = resume,
+	.disable_hub_initiated_lpm = 1,
+};
+
+struct workqueue_struct *purelifi_workqueue;
+
+static int __init usb_init(void)
+{
+	int r;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("plfxlc/LiFi-X.bin");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.h b/drivers/net/wireless/purelifi/plfxlc/usb.h
new file mode 100644
index 000000000000..6ce5ec664746
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.h
@@ -0,0 +1,177 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
+		      (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
+
+#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
+
+#define TO_NETWORK_32(X) TO_NETWORK(X)
+
+#define TO_HOST(X)    TO_NETWORK(X)
+#define TO_HOST_16(X) TO_NETWORK_16(X)
+#define TO_HOST_32(X) TO_NETWORK_32(X)
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#define PLF_FPGA_SLEN 2
+#define PLF_FPGA_ST_LEN 9
+#define PLF_BULK_TLEN 16384
+#define PLF_FPGA_MG 6 /* Magic check */
+#define PLF_XL_BUF_LEN 64
+#define PLF_USB_TIMEOUT 1000
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2      |     1     |     0	    |
+   // Reserved  | Heartbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+#define PURELIFI_SERIAL_LEN 256
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	bool rx_usb_enabled;
+	bool initialized;
+	bool was_running;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+		usb_complete_t complete_fn, void *context);
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
+		*usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *purelifi_intf_to_hw(struct usb_interface
+		*intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
+		*usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/*Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v11] wireless: Initial driver submission for pureLiFi STA devices
  2020-12-21  5:57         ` Kalle Valo
@ 2021-01-15 12:13           ` Srinivasan Raju
  0 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-01-15 12:13 UTC (permalink / raw)
  To: Kalle Valo
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski,
	Mauro Carvalho Chehab, Rob Herring, Lukas Bulwahn, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS


> I haven't had time to do a throrough review yet, but I suggest fixing
> the stuff I commented and submitting v12. I'll then do a new review with v12.

Hi Kalle,

We have submitted v12 of the driver for review.

Thanks
Srini

^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (8 preceding siblings ...)
  2021-01-05 13:19   ` [PATCH] [PATCH] [v12] " Srinivasan Raju
@ 2021-02-12 11:49   ` Srinivasan Raju
  2021-02-12 13:44     ` Johannes Berg
                       ` (5 more replies)
  2021-02-26 13:07   ` [PATCH] [v14] " Srinivasan Raju
                     ` (9 subsequent siblings)
  19 siblings, 6 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-02-12 11:49 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

---
v13
- Removed unused #defines
v12:
- Removed sysfs, procfs related code
- Addressed race condition bug
- Used macros instead of magic numbers in firmware.c
- Added copyright in all files
v11, v10:
- Addressed review comment on readability
- Changed firmware names to match products
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging
---
 MAINTAINERS                                   |    5 +
 drivers/net/wireless/Kconfig                  |    1 +
 drivers/net/wireless/Makefile                 |    1 +
 drivers/net/wireless/purelifi/Kconfig         |   17 +
 drivers/net/wireless/purelifi/Makefile        |    2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |   13 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |    3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |   96 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |   84 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   |  290 +++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |   41 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    |  876 ++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    |  192 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 1009 +++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    |  177 +++
 15 files changed, 2807 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 5c1a6ba5ef26..3178ded0a91e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14199,6 +14199,11 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PUREILIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+S:	Supported
+F:	drivers/net/wireless/purelifi
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7add2002ff4c..3a1f5ef3aa43 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -35,6 +35,7 @@ source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
 source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 
 config PCMCIA_RAYCS
 	tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e9fc770026f0 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
 obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..e39afec3dcae
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+source "drivers/net/wireless/purelifi/plfxlc/Kconfig"
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..2ed07f279329
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI)		:= plfxlc/
diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
new file mode 100644
index 000000000000..07a65c0fce68
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config PLFXLC
+
+	tristate "pureLiFi X, XL, XC device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This driver makes the adapter appear as a normal WLAN interface.
+
+	   The pureLiFi device requires external STA firmware to be loaded.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called purelifi.
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/Makefile b/drivers/net/wireless/purelifi/plfxlc/Makefile
new file mode 100644
index 000000000000..81fe9e010353
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PLFXLC)		:= plfxlc.o
+plfxlc-objs 		+= chip.o firmware.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.c b/drivers/net/wireless/purelifi/plfxlc/chip.c
new file mode 100644
index 000000000000..a5fda26241e8
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set && chip->beacon_interval == interval)) {
+		return 0;
+	}
+
+	chip->beacon_interval = interval;
+	chip->beacon_set = true;
+	return usb_write_req((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %02x:%02x v%02x  %02x-%02x-%02x %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr[0], addr[1], addr[2],
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
+{
+	int r;
+
+	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	static const u8 value;
+
+	usb_write_req(&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = usb_write_req(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.h b/drivers/net/wireless/purelifi/plfxlc/chip.h
new file mode 100644
index 000000000000..ce406000769c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	u16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1 << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/plfxlc/firmware.c b/drivers/net/wireless/purelifi/plfxlc/firmware.c
new file mode 100644
index 000000000000..5c3948b4f668
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/firmware.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/firmware.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+#define PLF_VNDR_FPGA_STATE_REQ 0x30
+#define PLF_VNDR_FPGA_SET_REQ 0x33
+#define PLF_VNDR_FPGA_SET_CMD 0x34
+#define PLF_VNDR_FPGA_STATE_CMD 0x35
+	int r, actual_length;
+	int fw_data_i, blk_tran_len = PLF_BULK_TLEN;
+	const char *fw_name;
+	unsigned char *fpga_dmabuff;
+	unsigned char *fw_data;
+	unsigned char fpga_setting[PLF_FPGA_SLEN];
+	unsigned char fpga_state[PLF_FPGA_ST_LEN];
+	struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/LiFi-X.bin";
+		dev_dbg(&intf->dev, "bin file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/LiFi-XC.bin";
+		dev_dbg(&intf->dev, "bin file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware((const struct firmware **)&fw, fw_name,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(PLF_FPGA_SLEN, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ, fpga_dmabuff, sizeof(fpga_setting));
+	memcpy(fpga_setting, fpga_dmabuff, PLF_FPGA_SLEN);
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_SET_CMD, NULL, 0);
+
+	if (fpga_setting[0] != PLF_FPGA_MG) {
+		dev_err(&intf->dev, "fpga_setting[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			/* u8 bit reverse */
+			fw_data[tbuf_idx] =
+				((fw_data[tbuf_idx] & 128) >> 7) |
+				((fw_data[tbuf_idx] &  64) >> 5) |
+				((fw_data[tbuf_idx] &  32) >> 3) |
+				((fw_data[tbuf_idx] &  16) >> 1) |
+				((fw_data[tbuf_idx] &   8) << 1) |
+				((fw_data[tbuf_idx] &   4) << 3) |
+				((fw_data[tbuf_idx] &   2) << 5) |
+				((fw_data[tbuf_idx] &   1) << 7);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_setting[0] & 0xFF),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2 * PLF_USB_TIMEOUT);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(PLF_FPGA_ST_LEN, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xFF, PLF_FPGA_ST_LEN);
+
+	send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
+			    sizeof(fpga_state));
+
+	dev_dbg(&intf->dev, "fpga status: %x %x %x %x %x %x %x %x\n",
+		fpga_dmabuff[0], fpga_dmabuff[1],
+		fpga_dmabuff[2], fpga_dmabuff[3],
+		fpga_dmabuff[4], fpga_dmabuff[5],
+		fpga_dmabuff[6], fpga_dmabuff[7]);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		kfree(fpga_dmabuff);
+		goto error_free_fw;
+	}
+
+	kfree(fpga_dmabuff);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
+
+	msleep(200);
+
+error_free_fw:
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+#define PLF_VNDR_XL_FW_CMD 0x80
+#define PLF_VNDR_XL_DATA_CMD 0x81
+#define PLF_VNDR_XL_FILE_CMD 0x82
+#define PLF_VNDR_XL_EX_CMD 0x83
+	int r;
+	struct firmware *fw_packed = NULL;
+	int step;
+	u8 *buf;
+	u32 size, nmb_of_control_packets, i, no_of_files;
+	u32 tot_size, start_addr;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
+	msleep(100);
+
+	/* Code for single pack file download */
+
+	fw_pack = "plfxlc/LiFi-XL.bin";
+
+	r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
+			     &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	no_of_files = *(u32 *)&fw_packed->data[0];
+	tot_size = fw_packed->size;
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n", no_of_files, tot_size);
+	buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
+	if (!buf) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	for (step = 0; step < no_of_files; step++) {
+		buf[0] = step;
+		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
+					PLF_XL_BUF_LEN);
+
+		if (step < no_of_files - 1)
+			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
+				- *(u32 *)&fw_packed->data[4 + (step) * 4];
+		else
+			size = tot_size -
+				*(u32 *)&fw_packed->data[4 + (step) * 4];
+
+		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];
+
+		if ((size % PLF_XL_BUF_LEN) && step < 2)
+			size += PLF_XL_BUF_LEN - size % PLF_XL_BUF_LEN;
+		nmb_of_control_packets = size / PLF_XL_BUF_LEN;
+
+		for (i = 0; i < nmb_of_control_packets; i++) {
+			memcpy(buf,
+			       &fw_packed->data[start_addr + (i * PLF_XL_BUF_LEN)],
+			       PLF_XL_BUF_LEN);
+			r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
+						PLF_XL_BUF_LEN);
+		}
+		dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", step, r,
+			size);
+	}
+	release_firmware(fw_packed);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
+	dev_dbg(&intf->dev, "download_fpga (4) (%d)\n", r);
+error:
+	return r;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+#define PLF_MAC_VENDOR_REQUEST 0x36
+#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define PLF_SERIAL_LEN 14
+#define PLF_FW_VER_LEN 8
+	struct usb_device *udev = interface_to_usbdev(intf);
+	int r = 0;
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	send_vendor_request(udev, PLF_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	memcpy(serial_number, dma_buffer, PLF_SERIAL_LEN);
+
+	memset(dma_buffer, 0x00, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, PLF_FW_VER_LEN);
+
+	memcpy(&firmware_version, dma_buffer, PLF_FW_VER_LEN);
+
+	dev_info(&intf->dev, "Firmware Version: %llu\n", firmware_version);
+	kfree(dma_buffer);
+
+	dev_dbg(&intf->dev, "hw_address=%pM\n", hw_address);
+error:
+	return r;
+}
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/intf.h b/drivers/net/wireless/purelifi/plfxlc/intf.h
new file mode 100644
index 000000000000..4877882cc9e2
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/intf.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define _PURELIFI_MAC_DRIVER_USB_INTERFACE
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	u16 rssi;
+	u8  rate_idx;
+	u8  pad;
+	u64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             buf[512];
+};
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
new file mode 100644
index 000000000000..848225c08a73
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.c
@@ -0,0 +1,876 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+struct plf_reg_alpha2_map {
+	u32 reg;
+	char alpha2[2];
+};
+
+static const struct plf_reg_alpha2_map reg_alpha2_map[] = {
+	{ PLF_REGDOMAIN_FCC, "US" },
+	{ PLF_REGDOMAIN_IC, "CA" },
+	{ PLF_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
+	{ PLF_REGDOMAIN_JAPAN, "JP" },
+	{ PLF_REGDOMAIN_SPAIN, "ES" },
+	{ PLF_REGDOMAIN_FRANCE, "FR" },
+};
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 },
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr);
+
+static int plf_reg2alpha2(u8 regdomain, char *alpha2)
+{
+	unsigned int i;
+	const struct plf_reg_alpha2_map *reg_map;
+		for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) {
+			reg_map = &reg_alpha2_map[i];
+			if (regdomain == reg_map->reg) {
+				alpha2[0] = reg_map->alpha2[0];
+				alpha2[1] = reg_map->alpha2[1];
+				return 0;
+			}
+	}
+	return 1;
+}
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)
+{
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+	char alpha2[2];
+
+	mac->default_regdomain = PLF_REGDOMAIN_ETSI;
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	r = plf_reg2alpha2(mac->regdomain, alpha2);
+	r = regulatory_hint(hw->wiphy, alpha2);
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon, false);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status - reports tx status of a packet if required
+ * @hw - a &struct ieee80211_hw pointer
+ * @skb - a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success - True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1, retry = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status) {
+		success = !tx_status->failure;
+		retry = tx_status->retry + success;
+	}
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		/* ieee80211_tx_status_irqsafe(hw, skb); */
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon, bool in_intr)
+{
+	return usb_write_req((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_dbg(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (struct purelifi_ctrlset *)skb_push(skb,
+			sizeof(struct purelifi_ctrlset));
+	cs->id = USB_REQ_DATA_TX;
+	cs->payload_len_nw = frag_len;
+	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
+		- sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned and align data*/
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		cs->len +=  4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data*/
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen b/c
+				 * sufficient headroom was reserved
+				 */
+				return 1;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		cs->len +=  4;
+	}
+
+	cs->id = TO_NETWORK(cs->id);
+	cs->len = TO_NETWORK(cs->len);
+	cs->payload_len_nw = TO_NETWORK(cs->payload_len_nw);
+
+	return 0;
+}
+
+/**
+ * purelifi_op_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+	info->rate_driver_data[0] = hw;
+
+	if (skb->data[24] == IEEE80211_FTYPE_DATA) {
+		u8 dst_mac[ETH_ALEN];
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		memcpy(dst_mac, &skb->data[28], ETH_ALEN);
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			block_queue(usb, tx->station[sidx].mac, true);
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			dev_kfree_skb(skb);
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
+					USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+		      struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	bool found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_dbg(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	u32 crc_error_cnt_low, crc_error_cnt_high;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_2GHZ;
+	mac->rssi      = -15 * ntohs(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	crc_error_cnt_low = status->crc_error_count;
+	crc_error_cnt_high = status->crc_error_count >> 32;
+	mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
+		ntohl(crc_error_cnt_high);
+
+	if (!bad_frame &&
+	    filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = TO_HOST(*(u32 *)buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (buffer[0] == IEEE80211_STYPE_PROBE_REQ)
+		dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
+	else if (buffer[0] == IEEE80211_STYPE_ASSOC_REQ)
+		dev_dbg(purelifi_mac_dev(mac), "Association request\n");
+
+	if (buffer[0] == IEEE80211_STYPE_AUTH) {
+		dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+	} else if (buffer[0] == IEEE80211_FTYPE_DATA) {
+		unsigned short int seq_nmb = (buffer[23] << 4) |
+			((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000)) {
+			dev_dbg(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
+		} else {
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		}
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+	if (need_padding) {
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+	}
+
+	memcpy(skb_put(skb, payload_length), buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_dbg(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_dbg(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_dbg(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon, false);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;
+
+	_ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
+	_ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
+	_ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
+	_ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* 4 for 32 bit alignment if no tailroom */
+
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.h b/drivers/net/wireless/purelifi/plfxlc/mac.h
new file mode 100644
index 000000000000..03b945e70dfb
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.h
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	enum usb_req_enum_t id;
+	u32            len;
+	u8             modulation;
+	u8             control;
+	u8             service;
+	u8             pad;
+	__le16         packet_length;
+	__le16         current_length;
+	__le16          next_frame_length;
+	__le16         tx_length;
+	u32            payload_len_nw;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	spinlock_t intr_lock; /* not used : interrupt lock for mac */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending:1;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
+		*chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block);
+
+#ifdef DEBUG
+void purelifi_dump_rx_status(const struct rx_status *status);
+#else
+#define purelifi_dump_rx_status(status)
+#endif
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
new file mode 100644
index 000000000000..8140120181b0
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -0,0 +1,1009 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+#define FCS_LEN 4
+
+struct proc_dir_entry *proc_folder;
+struct proc_dir_entry *modulation_proc_entry;
+static atomic_t data_queue_flag;
+
+/*Tx retry backoff timer (in milliseconds).*/
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+
+/*Tx retry backoff timer (in jiffies).*/
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		usb_write_req_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				    tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			block_queue(usb, tx->station[sidx].mac, false);
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
+		return;
+	} else if (!urb->context) {
+		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);
+
+	tx = &usb->tx;
+
+	if (urb->actual_length != 8) {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[7];
+
+	dev_dbg(&usb->intf->dev, "Recv status=%u\n", status);
+	dev_dbg(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+		buffer[0], buffer[1], buffer[2], buffer[3],
+		buffer[4], buffer[5]);
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_dbg(&usb->intf->dev,
+			"FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_dbg(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= 0xFD;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		fpga_link_connection_f = 1;
+		dev_dbg(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		fpga_link_connection_f = 0;
+		dev_dbg(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_dbg(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit failed (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0; /*no error return*/
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		usb->rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	usb->rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	u32 payload_len_nw = TO_NETWORK((buffer_len + FCS_LEN));
+
+	usb_req->id = usb_req_id;
+	usb_req->len  = 0;
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == USB_REQ_BEACON_WR) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		usb_req->len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	usb_req->len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	usb_req->len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+		usb_req->len += PURELIFI_BYTE_NUM_ALIGNMENT -
+			(usb_req->len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->id = TO_NETWORK(usb_req->id);
+	usb_req->len = TO_NETWORK(usb_req->len);
+}
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+			usb_complete_t complete_fn,
+			void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(usb_req.id) + sizeof(usb_req.len) +
+			   TO_HOST(usb_req.len);
+
+	dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+	memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	static unsigned char hw_address[ETH_ALEN];
+	static unsigned char serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+	dev_dbg(&intf->dev, "unit type is station");
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg(&intf->dev, "register device failed (%d)\n", r);
+		goto error;
+	}
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(200);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(200);
+	r = usb_write_req(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	mutex_lock(&mac->chip.mutex);
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	mutex_unlock(&mac->chip.mutex);
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+	.suspend        = suspend,
+	.resume         = resume,
+	.disable_hub_initiated_lpm = 1,
+};
+
+struct workqueue_struct *purelifi_workqueue;
+
+static int __init usb_init(void)
+{
+	int r;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("plfxlc/LiFi-X.bin");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.h b/drivers/net/wireless/purelifi/plfxlc/usb.h
new file mode 100644
index 000000000000..6ce5ec664746
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.h
@@ -0,0 +1,177 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
+		      (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
+
+#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
+
+#define TO_NETWORK_32(X) TO_NETWORK(X)
+
+#define TO_HOST(X)    TO_NETWORK(X)
+#define TO_HOST_16(X) TO_NETWORK_16(X)
+#define TO_HOST_32(X) TO_NETWORK_32(X)
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#define PLF_FPGA_SLEN 2
+#define PLF_FPGA_ST_LEN 9
+#define PLF_BULK_TLEN 16384
+#define PLF_FPGA_MG 6 /* Magic check */
+#define PLF_XL_BUF_LEN 64
+#define PLF_USB_TIMEOUT 1000
+
+int usb_write_req(const u8 *buffer, int buffer_len,
+		  enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2      |     1     |     0	    |
+   // Reserved  | Heartbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+#define PURELIFI_SERIAL_LEN 256
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	bool rx_usb_enabled;
+	bool initialized;
+	bool was_running;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int usb_write_req_async(struct purelifi_usb *usb, const u8 *buffer,
+			int buffer_len, enum usb_req_enum_t usb_req_id,
+		usb_complete_t complete_fn, void *context);
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
+		*usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *purelifi_intf_to_hw(struct usb_interface
+		*intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *purelifi_usb_to_hw(struct purelifi_usb
+		*usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/*Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-12 11:49   ` [PATCH] [v13] " Srinivasan Raju
@ 2021-02-12 13:44     ` Johannes Berg
  2021-02-17 10:05       ` Kalle Valo
  2021-02-19  5:15       ` Srinivasan Raju
  2021-02-12 15:06     ` kernel test robot
                       ` (4 subsequent siblings)
  5 siblings, 2 replies; 108+ messages in thread
From: Johannes Berg @ 2021-02-12 13:44 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Hi,

Thanks for your patience, and thanks for sticking around!

I'm sorry I haven't reviewed this again in a long time, but I was able
to today.


> +PUREILIFI USB DRIVER

Did you mistype "PURELIFI" here, or was that intentional?

> +PUREILIFI USB DRIVER
> +M:	Srinivasan Raju <srini.raju@purelifi.com>

Probably would be good to have an "L" entry with the linux-wireless list
here.

> +if WLAN_VENDOR_PURELIFI
> +
> +source "drivers/net/wireless/purelifi/plfxlc/Kconfig"

Seems odd to have the Makefile under purelifi/ but the Kconfig is yet
another directory deeper?

> +++ b/drivers/net/wireless/purelifi/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_WLAN_VENDOR_PURELIFI)		:= plfxlc/

Although this one doesn't do anything, so all you did was save a level
of Kconfig inclusion I guess ... no real objection to that.

> diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
> new file mode 100644
> index 000000000000..07a65c0fce68
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
> @@ -0,0 +1,13 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config PLFXLC
> +
> +	tristate "pureLiFi X, XL, XC device support"

extra blank line.

Also, maybe that should be a bit more verbose? PURELIFI_XLC or so? I
don't think it shows up in many places, but if you see "PLFXLC"
somewhere that's not very helpful.

> +	depends on CFG80211 && MAC80211 && USB
> +	help
> +	   This driver makes the adapter appear as a normal WLAN interface.
> +
> +	   The pureLiFi device requires external STA firmware to be loaded.
> +
> +	   To compile this driver as a module, choose M here: the module will
> +	   be called purelifi.

But will it? Seems like it would be called "plfxlc"?

See here:

> +++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_PLFXLC)		:= plfxlc.o
> +plfxlc-objs 		+= chip.o firmware.o usb.o mac.o


> +int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
> +				 u8 dtim_period, int type)
> +{
> +	if (!interval ||
> +	    (chip->beacon_set && chip->beacon_interval == interval)) {
> +		return 0;
> +	}

Don't need braces here

> +	chip->beacon_interval = interval;
> +	chip->beacon_set = true;
> +	return usb_write_req((const u8 *)&chip->beacon_interval,
> +			     sizeof(chip->beacon_interval),
> +			     USB_REQ_BEACON_INTERVAL_WR);

There's clearly an endian problem hiding here somewhere. You should have
"chip->beacon_interval" be (probably) __le16 since you send it to the
device as a buffer, yet the parameter to this function is just "u16".
Therefore, a conversion is missing somewhere - likely you need it to be
__le16 in the chip struct since you can't send USB requests from stack.


You should add some annotations for things getting sent to the device
like this, and then you can run sparse to validate them all over the
code.


> +}
> +
> +int purelifi_chip_init_hw(struct purelifi_chip *chip)
> +{
> +	u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
> +	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
> +
> +	pr_info("purelifi chip %02x:%02x v%02x  %02x-%02x-%02x %s\n",

"%04x:%04x" for the USB ID?

And you should probably use %pM for the MAC address - the format you
have there looks ... strange? Why only print the vendor OUI?

> +int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
> +{
> +	int r;
> +
> +	r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
> +	if (r)
> +		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);

Same endian problem here.

> 
> +static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
> +					u8 *addr)
> +{
> +	unsigned int i = addr[5] >> 2;
> +
> +	if (i < 32)
> +		hash->low |= 1 << i;
> +	else
> +		hash->high |= 1 << (i - 32);

That's UB if i == 31, you need 1U << i, or just use the BIT() macro.

> +	r = request_firmware((const struct firmware **)&fw, fw_name,

Not sure why that cast should be required?

> +	fpga_dmabuff = NULL;
> +	fpga_dmabuff = kmalloc(PLF_FPGA_SLEN, GFP_KERNEL);

that NULL assignment is pointless :)

> +
> +	if (!fpga_dmabuff) {
> +		r = -ENOMEM;
> +		goto error_free_fw;
> +	}
> +	send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ, fpga_dmabuff, sizeof(fpga_setting));
> +	memcpy(fpga_setting, fpga_dmabuff, PLF_FPGA_SLEN);
> +	kfree(fpga_dmabuff);

I'd say you should remove fpga_setting from the stack since you anyway
allocated it, save the memcpy() to the stack, and just use fpga_dmabuff
below - and then free it later?

> +		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
> +			/* u8 bit reverse */
> +			fw_data[tbuf_idx] =
> +				((fw_data[tbuf_idx] & 128) >> 7) |
> +				((fw_data[tbuf_idx] &  64) >> 5) |
> +				((fw_data[tbuf_idx] &  32) >> 3) |
> +				((fw_data[tbuf_idx] &  16) >> 1) |
> +				((fw_data[tbuf_idx] &   8) << 1) |
> +				((fw_data[tbuf_idx] &   4) << 3) |
> +				((fw_data[tbuf_idx] &   2) << 5) |
> +				((fw_data[tbuf_idx] &   1) << 7);
> +		}

check out include/linux/bitrev.h and bitrev8().

> +	fpga_dmabuff = NULL;
> +	fpga_dmabuff = kmalloc(PLF_FPGA_ST_LEN, GFP_KERNEL);

again useless NULL assignment

> +	if (!fpga_dmabuff) {
> +		r = -ENOMEM;
> +		goto error_free_fw;
> +	}
> +	memset(fpga_dmabuff, 0xFF, PLF_FPGA_ST_LEN);

does it have to be 0xff? Maybe kzalloc() would be shorter?

> +	send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
> +			    sizeof(fpga_state));
> +
> +	dev_dbg(&intf->dev, "fpga status: %x %x %x %x %x %x %x %x\n",
> +		fpga_dmabuff[0], fpga_dmabuff[1],
> +		fpga_dmabuff[2], fpga_dmabuff[3],
> +		fpga_dmabuff[4], fpga_dmabuff[5],
> +		fpga_dmabuff[6], fpga_dmabuff[7]);

consider something like
	dev_dbg(..., "%*ph\n", 8, fpga_dmabuff);

to simplify this.

> +	if (fpga_dmabuff[0] != 0) {
> +		r = -EINVAL;
> +		kfree(fpga_dmabuff);
> +		goto error_free_fw;

you have an out label anyway, make it free fpga_dmabuff too? kfree(NULL)
is OK, so you can just always do that there.

> +	no_of_files = *(u32 *)&fw_packed->data[0];

All of this is completely endianness broken, and quite possibly also has
alignment problems. Need get_unaligned_le32() I guess.

> +	for (step = 0; step < no_of_files; step++) {
> +		buf[0] = step;
> +		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
> +					PLF_XL_BUF_LEN);
> +
> +		if (step < no_of_files - 1)
> +			size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
> +				- *(u32 *)&fw_packed->data[4 + (step) * 4];
> +		else
> +			size = tot_size -
> +				*(u32 *)&fw_packed->data[4 + (step) * 4];
> +
> +		start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];

Lots of those here too - you might want to define a struct with __le32
entries for some of this stuff so sparse can validate your assumptions
("make C=1").

Also, you *really* need some validation here, rather than blindly
trusting that the file is well-formed, otherwise you immediately have a
security issue here.

> +#define PLF_MAC_VENDOR_REQUEST 0x36
> +#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
> +#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
> +#define PLF_SERIAL_LEN 14
> +#define PLF_FW_VER_LEN 8
> +	struct usb_device *udev = interface_to_usbdev(intf);
> +	int r = 0;
> +	unsigned char *dma_buffer = NULL;
> +	unsigned long long firmware_version;
> +
> +	dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);

I'd probably throw in a few build assertions such as

	BUILD_BUG_ON(ETH_ALEN > PLF_SERIAL_LEN);
	BUILD_BUG_ON(PLF_FW_VER_LEN > PLF_SERIAL_LEN);

> +	if (!dma_buffer) {
> +		r = -ENOMEM;
> +		goto error;

you do nothing at the error label, might as well remove it

and then you can remove the 'r' variable

> +struct rx_status {
> +	u16 rssi;
> +	u8  rate_idx;
> +	u8  pad;
> +	u64 crc_error_count;
> +} __packed;

endian issues, I assume

> +struct usb_req_t {
> +	enum usb_req_enum_t id;
> +	u32            len;
> +	u8             buf[512];
> +};

quite possibly here too, but dunno. Not sure I'd rely on 'enum' to have
a specific size here, and anyway the enum here will basically make the
sparse endian annotations impossible - better use __le32 with a comment
pointing to the enum.

> +static const struct plf_reg_alpha2_map reg_alpha2_map[] = {
> +	{ PLF_REGDOMAIN_FCC, "US" },
> +	{ PLF_REGDOMAIN_IC, "CA" },
> +	{ PLF_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
> +	{ PLF_REGDOMAIN_JAPAN, "JP" },
> +	{ PLF_REGDOMAIN_SPAIN, "ES" },
> +	{ PLF_REGDOMAIN_FRANCE, "FR" },
> +};

You actually have regulatory restrictions on this stuff?

> +static const struct ieee80211_rate purelifi_rates[] = {
> +	{ .bitrate = 10,
> +		.hw_value = PURELIFI_CCK_RATE_1M,
> +		.flags = 0 },
> +	{ .bitrate = 20,
> +		.hw_value = PURELIFI_CCK_RATE_2M,
> +		.hw_value_short = PURELIFI_CCK_RATE_2M
> +			| PURELIFI_CCK_PREA_SHORT,
> +		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
> +	{ .bitrate = 55,
> +		.hw_value = PURELIFI_CCK_RATE_5_5M,
> +		.hw_value_short = PURELIFI_CCK_RATE_5_5M
> +			| PURELIFI_CCK_PREA_SHORT,
> +		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
> +	{ .bitrate = 110,
> +		.hw_value = PURELIFI_CCK_RATE_11M,
> +		.hw_value_short = PURELIFI_CCK_RATE_11M
> +			| PURELIFI_CCK_PREA_SHORT,
> +		.flags = IEEE80211_RATE_SHORT_PREAMBLE },

So ... how much of that is completely fake? Are you _actually_ doing 1,
2, 5.5 etc. Mbps over the air, and you even have short and long
preamble? Why do all of that legacy mess, when you're essentially
greenfield??

> +
> +static const struct ieee80211_channel purelifi_channels[] = {
> +	{ .center_freq = 2412, .hw_value = 1 },
> +	{ .center_freq = 2417, .hw_value = 2 },
> +	{ .center_freq = 2422, .hw_value = 3 },
> +	{ .center_freq = 2427, .hw_value = 4 },
> +	{ .center_freq = 2432, .hw_value = 5 },
> +	{ .center_freq = 2437, .hw_value = 6 },
> +	{ .center_freq = 2442, .hw_value = 7 },
> +	{ .center_freq = 2447, .hw_value = 8 },
> +	{ .center_freq = 2452, .hw_value = 9 },
> +	{ .center_freq = 2457, .hw_value = 10 },
> +	{ .center_freq = 2462, .hw_value = 11 },
> +	{ .center_freq = 2467, .hw_value = 12 },
> +	{ .center_freq = 2472, .hw_value = 13 },
> +	{ .center_freq = 2484, .hw_value = 14 },
> +};
> +
> +static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
> +				      struct sk_buff *beacon, bool in_intr);
> +
> +static int plf_reg2alpha2(u8 regdomain, char *alpha2)
> +{
> +	unsigned int i;
> +	const struct plf_reg_alpha2_map *reg_map;
> +		for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) {

indentation is off here, and a blank line after the variables would be
nice

> +int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)

const u8 *?

> +void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)

Why is there a mac argument to this?

> +	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
> +	r = plf_reg2alpha2(mac->regdomain, alpha2);
> +	r = regulatory_hint(hw->wiphy, alpha2);

that first assignment to r is unused?

> +/**
> + * purelifi_mac_tx_status - reports tx status of a packet if required
> + * @hw - a &struct ieee80211_hw pointer
> + * @skb - a sk-buffer

bad kernel doc format I believe, and you do use : below:

> + * @flags: extra flags to set in the TX status info
> + * @ackssi: ACK signal strength
> + * @success - True for successful transmission of the frame

just not always :)

> + * This information calls ieee80211_tx_status_irqsafe() if required by the
> + * control information. It copies the control information into the status
> + * information.
> + *
> + * If no status information has been requested, the skb is freed.

That last line doesn't seem true?

> +	} else {
> +		/* ieee80211_tx_status_irqsafe(hw, skb); */

?

> +static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
> +				      struct sk_buff *beacon, bool in_intr)

unused in_intr arg?

> +	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
> +		dev_dbg(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
> +		return 1;
> +	}
> +
> +	cs = (struct purelifi_ctrlset *)skb_push(skb,
> +			sizeof(struct purelifi_ctrlset));

FWIW, (void *) cast is sufficient

> +	cs->id = USB_REQ_DATA_TX;
> +	cs->payload_len_nw = frag_len;
> +	cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
> +		- sizeof(cs->id) - sizeof(cs->len);

the - at the end of line is generally preferred, but not really that
important I guess

> +	/*check if 32 bit aligned and align data*/
> +	tmp = skb->len & 3;
> +	if (tmp) {
> +		if (skb_tailroom(skb) < (3 - tmp)) {
> +			if (skb_headroom(skb) >= 4 - tmp) {
> +				u8 len;
> +				u8 *src_pt;
> +				u8 *dest_pt;
> +
> +				len = skb->len;
> +				src_pt = skb->data;
> +				dest_pt = skb_push(skb, 4 - tmp);
> +				memcpy(dest_pt, src_pt, len);

that can overlap, so you really need memmove(), not memcpy().

> +			} else {
> +				return 1;

return 1 is "unidiomatic" in the kernel, maybe just return -ENOBUFS?

> +				/* should never happen b/c

wouldn't hurt to spell that out ;)

> +				 * sufficient headroom was reserved
> +				 */
> +				return 1;

same here

> +static void purelifi_op_tx(struct ieee80211_hw *hw,
> +			   struct ieee80211_tx_control *control,
> +			   struct sk_buff *skb)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct purelifi_usb *usb = &mac->chip.usb;
> +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +	unsigned long flags;
> +	int r;
> +
> +	r = fill_ctrlset(mac, skb);
> +	if (r)
> +		goto fail;
> +	info->rate_driver_data[0] = hw;
> +
> +	if (skb->data[24] == IEEE80211_FTYPE_DATA) {

it would be much clearer if you had a struct that you overlay on top of
the struct and then you have something like

	struct purelifi_header *plhdr = (void *)skb->data;

	if (plhdr->frametype == IEEE80211_FTYPE_DATA)

here.

Assuming that's what it does? Otherwise it doesn't make much sense?

It's not obvious what the SKB contains at this point, having a struct
there would make it obvious.

> +		u8 dst_mac[ETH_ALEN];
> +		u8 sidx;
> +		bool found = false;
> +		struct purelifi_usb_tx *tx = &usb->tx;
> +
> +		memcpy(dst_mac, &skb->data[28], ETH_ALEN);

Why copy it to the stack first?

> +		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
> +			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
> +				continue;
> +			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {

you haven't changed the skb?

And if you wanted it for line length or something, then just use a
pointer? Again though, the data[28] probably should be some struct
dereference.

> +				found = true;
> +				break;
> +			}
> +		}
> +
> +		/* Default to broadcast address for unknown MACs */
> +		if (!found)
> +			sidx = STA_BROADCAST_INDEX;
> +
> +		/* Stop OS from sending packets, if the queue is half full */
> +		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
> +			block_queue(usb, tx->station[sidx].mac, true);
> +
> +		/* Schedule packet for transmission if queue is not full */
> +		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {

60/256 isn't really half, but hey :)

> +			skb_queue_tail(&tx->station[sidx].data_list, skb);
> +			purelifi_send_packet_from_data_queue(usb);
> +		} else {
> +			dev_kfree_skb(skb);

It's kind of strangely inconsistent that you have here a free and then
fall through to the return, but other times you a 'goto fail':

> +		}
> +	} else {
> +		spin_lock_irqsave(&usb->tx.lock, flags);
> +		r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
> +					USB_REQ_DATA_TX, tx_urb_complete, skb);
> +		spin_unlock_irqrestore(&usb->tx.lock, flags);
> +		if (r)
> +			goto fail;
> +	}
> +	return;
> +
> +fail:
> +	dev_kfree_skb(skb);

where you free it

> +static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
> +		      struct ieee80211_rx_status *stats)

Generally, it'd be nice if more of your functions came with some prefix,
like say plf_filter_ack() in this case.

> +int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
> +		    unsigned int length)

or, well, purelifi_ :)

> +	crc_error_cnt_low = status->crc_error_count;
> +	crc_error_cnt_high = status->crc_error_count >> 32;
> +	mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
> +		ntohl(crc_error_cnt_high);

Oh hey, I found one place where you do endian conversion ;-)

but ... what's wrong with be64_to_cpu()?

> +	if (buffer[0] == IEEE80211_STYPE_PROBE_REQ)
> +		dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
> +	else if (buffer[0] == IEEE80211_STYPE_ASSOC_REQ)
> +		dev_dbg(purelifi_mac_dev(mac), "Association request\n");
> +
> +	if (buffer[0] == IEEE80211_STYPE_AUTH) {
> +		dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
> +		min_exp_seq_nmb = 0;
> +	} else if (buffer[0] == IEEE80211_FTYPE_DATA) {

maybe a switch statement would be better anyway, but this "interrupted
else-if chain" looks really weird.

Not sure I'd add any of this in the first place

> +		unsigned short int seq_nmb = (buffer[23] << 4) |
> +			((buffer[22] & 0xF0) >> 4);
> +
> +		if (seq_nmb < min_exp_seq_nmb &&
> +		    ((min_exp_seq_nmb - seq_nmb) < 3000)) {
> +			dev_dbg(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
> +		} else {
> +			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
> +		}

don't need braces

> +	memcpy(skb_put(skb, payload_length), buffer, payload_length);

skb_put_data()

> +	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
> +	if (!hw) {
> +		dev_dbg(&intf->dev, "out of memory\n");
> +		return NULL;
> +	}
> +	set_wiphy_dev(hw->wiphy, &intf->dev);
> +
> +	mac = purelifi_hw_mac(hw);
> +
> +	memset(mac, 0, sizeof(*mac));

no need, we use kzalloc() internally.

> +	hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;

This is kind of the only fundamental problem I have with this ... are we
really sure we want this to pretend to be 2.4 GHz? You only have a
single channel anyway, and you pretend that's a fixed one (somewhere
above, I skipped it)...

But it really has absolutely no relation to 2.4 GHz!

OTOH, I can sort of see why/how you're reusing wifi functionality here,
very old versions of wifi even had an infrared PHY I think.

Conceptually, it seems odd. Perhaps we should add a new band definition?

And what I also asked above - how much of the rate stuff is completely
fake? Are you really doing CCK/OFDM in some (strange?) way?

> +	_ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
> +	_ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
> +	_ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
> +	_ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);

Why not use the macro?

> +	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;

Ah, so you _do_ have a struct for all this header stuff! Please use it
where I pointed it out above.

> +struct purelifi_ctrlset {
> +	enum usb_req_enum_t id;
> +	u32            len;
> +	u8             modulation;
> +	u8             control;
> +	u8             service;
> +	u8             pad;
> +	__le16         packet_length;
> +	__le16         current_length;
> +	__le16          next_frame_length;
> +	__le16         tx_length;
> +	u32            payload_len_nw;

having a mix of __le32 and u32 seems weird :)

but not sure you ever even converted things, did you run sparse with
"make C=1"?

> +struct purelifi_mac {
> +	struct purelifi_chip chip;
> +	spinlock_t lock; /* lock for mac data */
> +	spinlock_t intr_lock; /* not used : interrupt lock for mac */

well, remove it if it's not used?

> +	/* whether to pass frames with CRC errors to stack */
> +	unsigned int pass_failed_fcs:1;
> +
> +	/* whether to pass control frames to stack */
> +	unsigned int pass_ctrl:1;
> +
> +	/* whether we have received a 802.11 ACK that is pending */
> +	bool ack_pending:1;

bool bitfield looks really odd ... what does that even do? I think
better use unsigned int like above.

> +static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
> +		*chip)

I'd recommend writing that as

static inline struct ... *
purelifi_chip_to_mac(...)


> +#ifdef DEBUG
> +void purelifi_dump_rx_status(const struct rx_status *status);
> +#else
> +#define purelifi_dump_rx_status(status)

do {} while (0)

> +#define FCS_LEN 4

might want to reuse FCS_LEN from ieee80211.h, i.e. just remove this?

> +struct proc_dir_entry *proc_folder;
> +struct proc_dir_entry *modulation_proc_entry;

You really need to remove procfs stuff, even these traces ;-)

> +	switch (urb->status) {
> +	case 0:
> +		break;
> +	case -ESHUTDOWN:
> +	case -EINVAL:
> +	case -ENODEV:
> +	case -ENOENT:
> +	case -ECONNRESET:
> +	case -EPIPE:
> +		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
> +		return;
> +	default:
> +		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
> +		goto resubmit;

you might want to limit that resubmit loop here to a few attempts

> +	buffer = urb->transfer_buffer;
> +	length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);

endian issues

> +	kfree(urbs);
> +
> +	spin_lock_irqsave(&rx->lock, flags);
> +	rx->urbs = NULL;
> +	rx->urbs_count = 0;
> +	spin_unlock_irqrestore(&rx->lock, flags);

That looks ... really weird, first you free and then you assign the
pointer to NULL, but under the lock?

I guess the lock is completely pointless here.

> +void purelifi_usb_release(struct purelifi_usb *usb)
> +{
> +	usb_set_intfdata(usb->intf, NULL);
> +	usb_put_intf(usb->intf);
> +	/* FIXME: usb_interrupt, usb_tx, usb_rx? */

what about them?

> +	dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
> +
> +	if (!dma_buffer) {
> +		r = -ENOMEM;
> +		goto error;
> +	}
> +	memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);

kmemdup()


> +static int pre_reset(struct usb_interface *intf)
> +{
> +	struct ieee80211_hw *hw = usb_get_intfdata(intf);
> +	struct purelifi_mac *mac;
> +	struct purelifi_usb *usb;
> +
> +	if (!hw || intf->condition != USB_INTERFACE_BOUND)
> +		return 0;
> +
> +	mac = purelifi_hw_mac(hw);
> +	usb = &mac->chip.usb;
> +
> +	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
> +
> +	purelifi_usb_stop(usb);
> +
> +	mutex_lock(&mac->chip.mutex);

this looks kind of problematic locking-wise

> +static int post_reset(struct usb_interface *intf)
> +{
> +	struct ieee80211_hw *hw = usb_get_intfdata(intf);
> +	struct purelifi_mac *mac;
> +	struct purelifi_usb *usb;
> +
> +	if (!hw || intf->condition != USB_INTERFACE_BOUND)
> +		return 0;

especially since this doesn't always unlock?

Not sure intf->condition can change inbetween, but it looks very
fragile.

> +#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
> +		      (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
> +
> +#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
> +
> +#define TO_NETWORK_32(X) TO_NETWORK(X)
> +
> +#define TO_HOST(X)    TO_NETWORK(X)
> +#define TO_HOST_16(X) TO_NETWORK_16(X)
> +#define TO_HOST_32(X) TO_NETWORK_32(X)

Remove all of that.

> +static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
> +		*usb)

indentation per above comment

johannes


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-12 11:49   ` [PATCH] [v13] " Srinivasan Raju
  2021-02-12 13:44     ` Johannes Berg
@ 2021-02-12 15:06     ` kernel test robot
  2021-02-12 17:57     ` kernel test robot
                       ` (3 subsequent siblings)
  5 siblings, 0 replies; 108+ messages in thread
From: kernel test robot @ 2021-02-12 15:06 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: kbuild-all, mostafa.afgani, Srinivasan Raju, Kalle Valo,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS)

[-- Attachment #1: Type: text/plain, Size: 3387 bytes --]

Hi Srinivasan,

I love your patch! Perhaps something to improve:

[auto build test WARNING on wireless-drivers-next/master]
[also build test WARNING on wireless-drivers/master net-next/master net/master linus/master sparc-next/master v5.11-rc7 next-20210211]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Srinivasan-Raju/wireless-Initial-driver-submission-for-pureLiFi-STA-devices/20210212-195451
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next.git master
config: arm64-allyesconfig (attached as .config)
compiler: aarch64-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/5d8fb5ce1f136940c10fd16bc96856d2f6ad2741
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Srinivasan-Raju/wireless-Initial-driver-submission-for-pureLiFi-STA-devices/20210212-195451
        git checkout 5d8fb5ce1f136940c10fd16bc96856d2f6ad2741
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=arm64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/net/wireless/purelifi/plfxlc/mac.c: In function 'purelifi_mac_tx_status':
>> drivers/net/wireless/purelifi/plfxlc/mac.c:219:19: warning: variable 'retry' set but not used [-Wunused-but-set-variable]
     219 |  int success = 1, retry = 1;
         |                   ^~~~~


vim +/retry +219 drivers/net/wireless/purelifi/plfxlc/mac.c

   198	
   199	/**
   200	 * purelifi_mac_tx_status - reports tx status of a packet if required
   201	 * @hw - a &struct ieee80211_hw pointer
   202	 * @skb - a sk-buffer
   203	 * @flags: extra flags to set in the TX status info
   204	 * @ackssi: ACK signal strength
   205	 * @success - True for successful transmission of the frame
   206	 *
   207	 * This information calls ieee80211_tx_status_irqsafe() if required by the
   208	 * control information. It copies the control information into the status
   209	 * information.
   210	 *
   211	 * If no status information has been requested, the skb is freed.
   212	 */
   213	static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
   214					   struct sk_buff *skb,
   215					   int ackssi,
   216					   struct tx_status *tx_status)
   217	{
   218		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 > 219		int success = 1, retry = 1;
   220	
   221		ieee80211_tx_info_clear_status(info);
   222	
   223		if (tx_status) {
   224			success = !tx_status->failure;
   225			retry = tx_status->retry + success;
   226		}
   227	
   228		if (success)
   229			info->flags |= IEEE80211_TX_STAT_ACK;
   230		else
   231			info->flags &= ~IEEE80211_TX_STAT_ACK;
   232	
   233		info->status.ack_signal = 50;
   234		ieee80211_tx_status_irqsafe(hw, skb);
   235	}
   236	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 76491 bytes --]

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-12 11:49   ` [PATCH] [v13] " Srinivasan Raju
  2021-02-12 13:44     ` Johannes Berg
  2021-02-12 15:06     ` kernel test robot
@ 2021-02-12 17:57     ` kernel test robot
  2021-02-17 10:02     ` Kalle Valo
                       ` (2 subsequent siblings)
  5 siblings, 0 replies; 108+ messages in thread
From: kernel test robot @ 2021-02-12 17:57 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: kbuild-all, mostafa.afgani, Srinivasan Raju, Kalle Valo,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS)

[-- Attachment #1: Type: text/plain, Size: 3855 bytes --]

Hi Srinivasan,

I love your patch! Yet something to improve:

[auto build test ERROR on wireless-drivers-next/master]
[also build test ERROR on wireless-drivers/master net-next/master net/master linus/master sparc-next/master v5.11-rc7 next-20210211]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Srinivasan-Raju/wireless-Initial-driver-submission-for-pureLiFi-STA-devices/20210212-195451
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next.git master
config: m68k-allmodconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/5d8fb5ce1f136940c10fd16bc96856d2f6ad2741
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Srinivasan-Raju/wireless-Initial-driver-submission-for-pureLiFi-STA-devices/20210212-195451
        git checkout 5d8fb5ce1f136940c10fd16bc96856d2f6ad2741
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=m68k 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   In file included from include/linux/kernel.h:10,
                    from drivers/net/wireless/purelifi/plfxlc/usb.c:6:
   include/linux/scatterlist.h: In function 'sg_set_buf':
   arch/m68k/include/asm/page_mm.h:174:49: warning: ordered comparison of pointer with null pointer [-Wextra]
     174 | #define virt_addr_valid(kaddr) ((void *)(kaddr) >= (void *)PAGE_OFFSET && (void *)(kaddr) < high_memory)
         |                                                 ^~
   include/linux/compiler.h:78:42: note: in definition of macro 'unlikely'
      78 | # define unlikely(x) __builtin_expect(!!(x), 0)
         |                                          ^
   include/linux/scatterlist.h:137:2: note: in expansion of macro 'BUG_ON'
     137 |  BUG_ON(!virt_addr_valid(buf));
         |  ^~~~~~
   include/linux/scatterlist.h:137:10: note: in expansion of macro 'virt_addr_valid'
     137 |  BUG_ON(!virt_addr_valid(buf));
         |          ^~~~~~~~~~~~~~~
   drivers/net/wireless/purelifi/plfxlc/usb.c: At top level:
>> drivers/net/wireless/purelifi/plfxlc/usb.c:962:20: error: 'suspend' undeclared here (not in a function)
     962 |  .suspend        = suspend,
         |                    ^~~~~~~
>> drivers/net/wireless/purelifi/plfxlc/usb.c:963:20: error: 'resume' undeclared here (not in a function); did you mean 'resource'?
     963 |  .resume         = resume,
         |                    ^~~~~~
         |                    resource
   drivers/net/wireless/purelifi/plfxlc/usb.c:909:29: warning: 'get_purelifi_usb' defined but not used [-Wunused-function]
     909 | static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
         |                             ^~~~~~~~~~~~~~~~


vim +/suspend +962 drivers/net/wireless/purelifi/plfxlc/usb.c

   954	
   955	static struct usb_driver driver = {
   956		.name        = KBUILD_MODNAME,
   957		.id_table    = usb_ids,
   958		.probe        = probe,
   959		.disconnect    = disconnect,
   960		.pre_reset    = pre_reset,
   961		.post_reset    = post_reset,
 > 962		.suspend        = suspend,
 > 963		.resume         = resume,
   964		.disable_hub_initiated_lpm = 1,
   965	};
   966	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 59765 bytes --]

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-12 11:49   ` [PATCH] [v13] " Srinivasan Raju
                       ` (2 preceding siblings ...)
  2021-02-12 17:57     ` kernel test robot
@ 2021-02-17 10:02     ` Kalle Valo
  2021-02-17 10:13       ` Kalle Valo
  2021-02-17 10:09     ` Kalle Valo
  2021-02-17 10:19     ` Kalle Valo
  5 siblings, 1 reply; 108+ messages in thread
From: Kalle Valo @ 2021-02-17 10:02 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
>
> This driver implementation has been based on the zd1211rw driver.
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
>
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

I pushed this now to the pending branch of wireless-drivers-next for
build testing, let's see what kind of results we get. I didn't manage to
do a thorough review yet, but few quick comments:

> --- a/drivers/net/wireless/Kconfig
> +++ b/drivers/net/wireless/Kconfig
> @@ -35,6 +35,7 @@ source "drivers/net/wireless/st/Kconfig"
>  source "drivers/net/wireless/ti/Kconfig"
>  source "drivers/net/wireless/zydas/Kconfig"
>  source "drivers/net/wireless/quantenna/Kconfig"
> +source "drivers/net/wireless/purelifi/Kconfig"

This is supposed to be in alphabetical order.

> --- a/drivers/net/wireless/Makefile
> +++ b/drivers/net/wireless/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/
>  obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
>  obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
>  obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
> +obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/

And this one as well. Clearly I missed these with quantenna, but no need
to redo the same mistake with purelife. Also patches welcome to fix
quantenna sorting.

> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
> @@ -0,0 +1,13 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config PLFXLC
> +
> +	tristate "pureLiFi X, XL, XC device support"
> +	depends on CFG80211 && MAC80211 && USB
> +	help
> +	   This driver makes the adapter appear as a normal WLAN interface.
> +
> +	   The pureLiFi device requires external STA firmware to be loaded.
> +

The documentation here is not telling much. I think it goes without
saying that a firmware is needed, so no need to mention it. And also
"normal WLAN interface" is a standard upstream requirement so drop that
as well. Instead describe here in detail what hardware this driver
supports, for example exact product names, bus types, wifi/ieee
generation etc.

> +	   To compile this driver as a module, choose M here: the module will
> +	   be called purelifi.

Didn't you rename it to plfxlc?

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-12 13:44     ` Johannes Berg
@ 2021-02-17 10:05       ` Kalle Valo
  2021-02-19  5:15       ` Srinivasan Raju
  1 sibling, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2021-02-17 10:05 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Srinivasan Raju, mostafa.afgani, David S. Miller, Jakub Kicinski,
	open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Johannes Berg <johannes@sipsolutions.net> writes:

>> +++ b/drivers/net/wireless/purelifi/Makefile
>> @@ -0,0 +1,2 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +obj-$(CONFIG_WLAN_VENDOR_PURELIFI)		:= plfxlc/
>
> Although this one doesn't do anything, so all you did was save a level
> of Kconfig inclusion I guess ... no real objection to that.

I think this should use obj-$(CONFIG_PLFXLC).

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-12 11:49   ` [PATCH] [v13] " Srinivasan Raju
                       ` (3 preceding siblings ...)
  2021-02-17 10:02     ` Kalle Valo
@ 2021-02-17 10:09     ` Kalle Valo
  2021-02-17 10:19     ` Kalle Valo
  5 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2021-02-17 10:09 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
>
> This driver implementation has been based on the zd1211rw driver.
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
>
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

[...]

> +	send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
> +
> +	msleep(200);

There are multiple msleeps like this, please add a descriptive define
for value 200.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-17 10:02     ` Kalle Valo
@ 2021-02-17 10:13       ` Kalle Valo
  2021-02-17 10:16         ` Srinivasan Raju
  0 siblings, 1 reply; 108+ messages in thread
From: Kalle Valo @ 2021-02-17 10:13 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Kalle Valo <kvalo@codeaurora.org> writes:

> Srinivasan Raju <srini.raju@purelifi.com> writes:
>
>> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
>> and LiFi-XL USB devices.
>>
>> This driver implementation has been based on the zd1211rw driver.
>>
>> Driver is based on 802.11 softMAC Architecture and uses
>> native 802.11 for configuration and management.
>>
>> The driver is compiled and tested in ARM, x86 architectures and
>> compiled in powerpc architecture.
>>
>> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
>
> I pushed this now to the pending branch of wireless-drivers-next for
> build testing, let's see what kind of results we get.

Ah, kbuild bot had already reported few issues:

https://patchwork.kernel.org/project/linux-wireless/patch/20210212115030.124490-1-srini.raju@purelifi.com/

Please fix those and I recommend waiting few days in case the bot finds
more issues. After that you can submitt v14 fixing the comments you got
in this review round.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-17 10:13       ` Kalle Valo
@ 2021-02-17 10:16         ` Srinivasan Raju
  0 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-02-17 10:16 UTC (permalink / raw)
  To: Kalle Valo
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS



> Ah, kbuild bot had already reported few issues:

> https://patchwork.kernel.org/project/linux-wireless/patch/20210212115030.124490-1-srini.raju@purelifi.com/

> Please fix those and I recommend waiting few days in case the bot finds
> more issues. After that you can submitt v14 fixing the comments you got
> in this review round.

Hi Kalle,

Thanks for reviewing the patch. 
We will fix / address the comments and resubmit v14

Thanks
Srini
--
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-12 11:49   ` [PATCH] [v13] " Srinivasan Raju
                       ` (4 preceding siblings ...)
  2021-02-17 10:09     ` Kalle Valo
@ 2021-02-17 10:19     ` Kalle Valo
  2021-02-24 10:44       ` Srinivasan Raju
  5 siblings, 1 reply; 108+ messages in thread
From: Kalle Valo @ 2021-02-17 10:19 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
>
> This driver implementation has been based on the zd1211rw driver.
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
>
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

[...]

> +	if ((le16_to_cpu(udev->descriptor.idVendor) ==
> +				PURELIFI_X_VENDOR_ID_0) &&
> +	    (le16_to_cpu(udev->descriptor.idProduct) ==
> +				PURELIFI_X_PRODUCT_ID_0)) {
> +		fw_name = "plfxlc/LiFi-X.bin";
> +		dev_dbg(&intf->dev, "bin file for X selected\n");
> +
> +	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
> +					PURELIFI_XC_VENDOR_ID_0 &&
> +		   (le16_to_cpu(udev->descriptor.idProduct) ==
> +					PURELIFI_XC_PRODUCT_ID_0)) {
> +		fw_name = "plfxlc/LiFi-XC.bin";
> +		dev_dbg(&intf->dev, "bin file for XC selected\n");
> +
> +	} else {
> +		r = -EINVAL;
> +		goto error;
> +	}
> +
> +	r = request_firmware((const struct firmware **)&fw, fw_name,
> +			     &intf->dev);
> +	if (r) {
> +		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
> +		goto error;
> +	}

[...]

> +	/* Code for single pack file download */
> +
> +	fw_pack = "plfxlc/LiFi-XL.bin";
> +
> +	r = request_firmware((const struct firmware **)&fw_packed, fw_pack,
> +			     &intf->dev);
> +	if (r) {
> +		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
> +		goto error;
> +	}

Having the firmware files under plfxlc/ directory looks good to me. But
I'm not really a fan of upper case filenames, is there a reason for
that? I would prefer have all lowercase filenames.

Also the cast (const struct firmware **) looks very wrong, but I didn't
investigate why you do it. I'm sure there is a proper way to implement
whatever you need here, without the cast.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-12 13:44     ` Johannes Berg
  2021-02-17 10:05       ` Kalle Valo
@ 2021-02-19  5:15       ` Srinivasan Raju
  2021-02-19  8:25         ` Johannes Berg
  1 sibling, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2021-02-19  5:15 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Hi,

Please find a few responses to the comments , We will fix rest of the comments and submit v14 of the patch.

> Also, you *really* need some validation here, rather than blindly
> trusting that the file is well-formed, otherwise you immediately have a
> security issue here.

The firmware is signed and the harware validates the signature , so we are not validating in the software.

>> +static const struct plf_reg_alpha2_map reg_alpha2_map[] = {
>> +     { PLF_REGDOMAIN_FCC, "US" },
>> +     { PLF_REGDOMAIN_IC, "CA" },
>> +     { PLF_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
>> +     { PLF_REGDOMAIN_JAPAN, "JP" },
>> +     { PLF_REGDOMAIN_SPAIN, "ES" },
>> +     { PLF_REGDOMAIN_FRANCE, "FR" },
>> +};

> You actually have regulatory restrictions on this stuff?

Currently, There are no regulatory restrictions applicable for LiFi , regulatory_hint setting is only for wifi core 

>> +static const struct ieee80211_rate purelifi_rates[] = {
>> +     { .bitrate = 10,
>> +             .hw_value = PURELIFI_CCK_RATE_1M,
>> +             .flags = 0 },
>> +     { .bitrate = 20,
>> +             .hw_value = PURELIFI_CCK_RATE_2M,
>> +             .hw_value_short = PURELIFI_CCK_RATE_2M
>> +                     | PURELIFI_CCK_PREA_SHORT,
>> +             .flags = IEEE80211_RATE_SHORT_PREAMBLE },
>> +     { .bitrate = 55,
>> +             .hw_value = PURELIFI_CCK_RATE_5_5M,
>> +             .hw_value_short = PURELIFI_CCK_RATE_5_5M
>> +                     | PURELIFI_CCK_PREA_SHORT,
>> +             .flags = IEEE80211_RATE_SHORT_PREAMBLE },
>> +     { .bitrate = 110,
>> +             .hw_value = PURELIFI_CCK_RATE_11M,
>> +             .hw_value_short = PURELIFI_CCK_RATE_11M
>> +                     | PURELIFI_CCK_PREA_SHORT,
>> +             .flags = IEEE80211_RATE_SHORT_PREAMBLE },


> So ... how much of that is completely fake? Are you _actually_ doing 1,
> 2, 5.5 etc. Mbps over the air, and you even have short and long
> preamble? Why do all of that legacy mess, when you're essentially
> greenfield??

Yes, this not used. We will test and remove the unused legacy settings

> OTOH, I can sort of see why/how you're reusing wifi functionality here,
> very old versions of wifi even had an infrared PHY I think.
>
> Conceptually, it seems odd. Perhaps we should add a new band definition?
>
> And what I also asked above - how much of the rate stuff is completely
> fake? Are you really doing CCK/OFDM in some (strange?) way?

Yes, your understanding is correct, and we use OFDM.
For now we will use the existing band definition.

Thanks
Srini
________________________________________
From: Johannes Berg <johannes@sipsolutions.net>
Sent: Friday, February 12, 2021 7:14 PM
To: Srinivasan Raju
Cc: Mostafa Afgani; Kalle Valo; David S. Miller; Jakub Kicinski; open list; open list:NETWORKING DRIVERS (WIRELESS); open list:NETWORKING DRIVERS
Subject: Re: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices

Hi,

Thanks for your patience, and thanks for sticking around!

I'm sorry I haven't reviewed this again in a long time, but I was able
to today.


> +PUREILIFI USB DRIVER

Did you mistype "PURELIFI" here, or was that intentional?

> +PUREILIFI USB DRIVER
> +M:   Srinivasan Raju <srini.raju@purelifi.com>

Probably would be good to have an "L" entry with the linux-wireless list
here.

> +if WLAN_VENDOR_PURELIFI
> +
> +source "drivers/net/wireless/purelifi/plfxlc/Kconfig"

Seems odd to have the Makefile under purelifi/ but the Kconfig is yet
another directory deeper?

> +++ b/drivers/net/wireless/purelifi/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_WLAN_VENDOR_PURELIFI)           := plfxlc/

Although this one doesn't do anything, so all you did was save a level
of Kconfig inclusion I guess ... no real objection to that.

> diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
> new file mode 100644
> index 000000000000..07a65c0fce68
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
> @@ -0,0 +1,13 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config PLFXLC
> +
> +     tristate "pureLiFi X, XL, XC device support"

extra blank line.

Also, maybe that should be a bit more verbose? PURELIFI_XLC or so? I
don't think it shows up in many places, but if you see "PLFXLC"
somewhere that's not very helpful.

> +     depends on CFG80211 && MAC80211 && USB
> +     help
> +        This driver makes the adapter appear as a normal WLAN interface.
> +
> +        The pureLiFi device requires external STA firmware to be loaded.
> +
> +        To compile this driver as a module, choose M here: the module will
> +        be called purelifi.

But will it? Seems like it would be called "plfxlc"?

See here:

> +++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_PLFXLC)         := plfxlc.o
> +plfxlc-objs          += chip.o firmware.o usb.o mac.o


> +int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
> +                              u8 dtim_period, int type)
> +{
> +     if (!interval ||
> +         (chip->beacon_set && chip->beacon_interval == interval)) {
> +             return 0;
> +     }

Don't need braces here

> +     chip->beacon_interval = interval;
> +     chip->beacon_set = true;
> +     return usb_write_req((const u8 *)&chip->beacon_interval,
> +                          sizeof(chip->beacon_interval),
> +                          USB_REQ_BEACON_INTERVAL_WR);

There's clearly an endian problem hiding here somewhere. You should have
"chip->beacon_interval" be (probably) __le16 since you send it to the
device as a buffer, yet the parameter to this function is just "u16".
Therefore, a conversion is missing somewhere - likely you need it to be
__le16 in the chip struct since you can't send USB requests from stack.


You should add some annotations for things getting sent to the device
like this, and then you can run sparse to validate them all over the
code.


> +}
> +
> +int purelifi_chip_init_hw(struct purelifi_chip *chip)
> +{
> +     u8 *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
> +     struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
> +
> +     pr_info("purelifi chip %02x:%02x v%02x  %02x-%02x-%02x %s\n",

"%04x:%04x" for the USB ID?

And you should probably use %pM for the MAC address - the format you
have there looks ... strange? Why only print the vendor OUI?

> +int purelifi_chip_switch_radio(struct purelifi_chip *chip, u32 value)
> +{
> +     int r;
> +
> +     r = usb_write_req((const u8 *)&value, sizeof(value), USB_REQ_POWER_WR);
> +     if (r)
> +             dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);

Same endian problem here.

>
> +static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
> +                                     u8 *addr)
> +{
> +     unsigned int i = addr[5] >> 2;
> +
> +     if (i < 32)
> +             hash->low |= 1 << i;
> +     else
> +             hash->high |= 1 << (i - 32);

That's UB if i == 31, you need 1U << i, or just use the BIT() macro.

> +     r = request_firmware((const struct firmware **)&fw, fw_name,

Not sure why that cast should be required?

> +     fpga_dmabuff = NULL;
> +     fpga_dmabuff = kmalloc(PLF_FPGA_SLEN, GFP_KERNEL);

that NULL assignment is pointless :)

> +
> +     if (!fpga_dmabuff) {
> +             r = -ENOMEM;
> +             goto error_free_fw;
> +     }
> +     send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ, fpga_dmabuff, sizeof(fpga_setting));
> +     memcpy(fpga_setting, fpga_dmabuff, PLF_FPGA_SLEN);
> +     kfree(fpga_dmabuff);

I'd say you should remove fpga_setting from the stack since you anyway
allocated it, save the memcpy() to the stack, and just use fpga_dmabuff
below - and then free it later?

> +             for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
> +                     /* u8 bit reverse */
> +                     fw_data[tbuf_idx] =
> +                             ((fw_data[tbuf_idx] & 128) >> 7) |
> +                             ((fw_data[tbuf_idx] &  64) >> 5) |
> +                             ((fw_data[tbuf_idx] &  32) >> 3) |
> +                             ((fw_data[tbuf_idx] &  16) >> 1) |
> +                             ((fw_data[tbuf_idx] &   8) << 1) |
> +                             ((fw_data[tbuf_idx] &   4) << 3) |
> +                             ((fw_data[tbuf_idx] &   2) << 5) |
> +                             ((fw_data[tbuf_idx] &   1) << 7);
> +             }

check out include/linux/bitrev.h and bitrev8().

> +     fpga_dmabuff = NULL;
> +     fpga_dmabuff = kmalloc(PLF_FPGA_ST_LEN, GFP_KERNEL);

again useless NULL assignment

> +     if (!fpga_dmabuff) {
> +             r = -ENOMEM;
> +             goto error_free_fw;
> +     }
> +     memset(fpga_dmabuff, 0xFF, PLF_FPGA_ST_LEN);

does it have to be 0xff? Maybe kzalloc() would be shorter?

> +     send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
> +                         sizeof(fpga_state));
> +
> +     dev_dbg(&intf->dev, "fpga status: %x %x %x %x %x %x %x %x\n",
> +             fpga_dmabuff[0], fpga_dmabuff[1],
> +             fpga_dmabuff[2], fpga_dmabuff[3],
> +             fpga_dmabuff[4], fpga_dmabuff[5],
> +             fpga_dmabuff[6], fpga_dmabuff[7]);

consider something like
        dev_dbg(..., "%*ph\n", 8, fpga_dmabuff);

to simplify this.

> +     if (fpga_dmabuff[0] != 0) {
> +             r = -EINVAL;
> +             kfree(fpga_dmabuff);
> +             goto error_free_fw;

you have an out label anyway, make it free fpga_dmabuff too? kfree(NULL)
is OK, so you can just always do that there.

> +     no_of_files = *(u32 *)&fw_packed->data[0];

All of this is completely endianness broken, and quite possibly also has
alignment problems. Need get_unaligned_le32() I guess.

> +     for (step = 0; step < no_of_files; step++) {
> +             buf[0] = step;
> +             r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
> +                                     PLF_XL_BUF_LEN);
> +
> +             if (step < no_of_files - 1)
> +                     size = *(u32 *)&fw_packed->data[4 + ((step + 1) * 4)]
> +                             - *(u32 *)&fw_packed->data[4 + (step) * 4];
> +             else
> +                     size = tot_size -
> +                             *(u32 *)&fw_packed->data[4 + (step) * 4];
> +
> +             start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];

Lots of those here too - you might want to define a struct with __le32
entries for some of this stuff so sparse can validate your assumptions
("make C=1").

Also, you *really* need some validation here, rather than blindly
trusting that the file is well-formed, otherwise you immediately have a
security issue here.

> +#define PLF_MAC_VENDOR_REQUEST 0x36
> +#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
> +#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
> +#define PLF_SERIAL_LEN 14
> +#define PLF_FW_VER_LEN 8
> +     struct usb_device *udev = interface_to_usbdev(intf);
> +     int r = 0;
> +     unsigned char *dma_buffer = NULL;
> +     unsigned long long firmware_version;
> +
> +     dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);

I'd probably throw in a few build assertions such as

        BUILD_BUG_ON(ETH_ALEN > PLF_SERIAL_LEN);
        BUILD_BUG_ON(PLF_FW_VER_LEN > PLF_SERIAL_LEN);

> +     if (!dma_buffer) {
> +             r = -ENOMEM;
> +             goto error;

you do nothing at the error label, might as well remove it

and then you can remove the 'r' variable

> +struct rx_status {
> +     u16 rssi;
> +     u8  rate_idx;
> +     u8  pad;
> +     u64 crc_error_count;
> +} __packed;

endian issues, I assume

> +struct usb_req_t {
> +     enum usb_req_enum_t id;
> +     u32            len;
> +     u8             buf[512];
> +};

quite possibly here too, but dunno. Not sure I'd rely on 'enum' to have
a specific size here, and anyway the enum here will basically make the
sparse endian annotations impossible - better use __le32 with a comment
pointing to the enum.

> +static const struct plf_reg_alpha2_map reg_alpha2_map[] = {
> +     { PLF_REGDOMAIN_FCC, "US" },
> +     { PLF_REGDOMAIN_IC, "CA" },
> +     { PLF_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
> +     { PLF_REGDOMAIN_JAPAN, "JP" },
> +     { PLF_REGDOMAIN_SPAIN, "ES" },
> +     { PLF_REGDOMAIN_FRANCE, "FR" },
> +};

You actually have regulatory restrictions on this stuff?

> +static const struct ieee80211_rate purelifi_rates[] = {
> +     { .bitrate = 10,
> +             .hw_value = PURELIFI_CCK_RATE_1M,
> +             .flags = 0 },
> +     { .bitrate = 20,
> +             .hw_value = PURELIFI_CCK_RATE_2M,
> +             .hw_value_short = PURELIFI_CCK_RATE_2M
> +                     | PURELIFI_CCK_PREA_SHORT,
> +             .flags = IEEE80211_RATE_SHORT_PREAMBLE },
> +     { .bitrate = 55,
> +             .hw_value = PURELIFI_CCK_RATE_5_5M,
> +             .hw_value_short = PURELIFI_CCK_RATE_5_5M
> +                     | PURELIFI_CCK_PREA_SHORT,
> +             .flags = IEEE80211_RATE_SHORT_PREAMBLE },
> +     { .bitrate = 110,
> +             .hw_value = PURELIFI_CCK_RATE_11M,
> +             .hw_value_short = PURELIFI_CCK_RATE_11M
> +                     | PURELIFI_CCK_PREA_SHORT,
> +             .flags = IEEE80211_RATE_SHORT_PREAMBLE },

So ... how much of that is completely fake? Are you _actually_ doing 1,
2, 5.5 etc. Mbps over the air, and you even have short and long
preamble? Why do all of that legacy mess, when you're essentially
greenfield??

> +
> +static const struct ieee80211_channel purelifi_channels[] = {
> +     { .center_freq = 2412, .hw_value = 1 },
> +     { .center_freq = 2417, .hw_value = 2 },
> +     { .center_freq = 2422, .hw_value = 3 },
> +     { .center_freq = 2427, .hw_value = 4 },
> +     { .center_freq = 2432, .hw_value = 5 },
> +     { .center_freq = 2437, .hw_value = 6 },
> +     { .center_freq = 2442, .hw_value = 7 },
> +     { .center_freq = 2447, .hw_value = 8 },
> +     { .center_freq = 2452, .hw_value = 9 },
> +     { .center_freq = 2457, .hw_value = 10 },
> +     { .center_freq = 2462, .hw_value = 11 },
> +     { .center_freq = 2467, .hw_value = 12 },
> +     { .center_freq = 2472, .hw_value = 13 },
> +     { .center_freq = 2484, .hw_value = 14 },
> +};
> +
> +static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
> +                                   struct sk_buff *beacon, bool in_intr);
> +
> +static int plf_reg2alpha2(u8 regdomain, char *alpha2)
> +{
> +     unsigned int i;
> +     const struct plf_reg_alpha2_map *reg_map;
> +             for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) {

indentation is off here, and a blank line after the variables would be
nice

> +int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, unsigned char *hw_address)

const u8 *?

> +void block_queue(struct purelifi_usb *usb, const u8 *mac, bool block)

Why is there a mac argument to this?

> +     dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
> +     r = plf_reg2alpha2(mac->regdomain, alpha2);
> +     r = regulatory_hint(hw->wiphy, alpha2);

that first assignment to r is unused?

> +/**
> + * purelifi_mac_tx_status - reports tx status of a packet if required
> + * @hw - a &struct ieee80211_hw pointer
> + * @skb - a sk-buffer

bad kernel doc format I believe, and you do use : below:

> + * @flags: extra flags to set in the TX status info
> + * @ackssi: ACK signal strength
> + * @success - True for successful transmission of the frame

just not always :)

> + * This information calls ieee80211_tx_status_irqsafe() if required by the
> + * control information. It copies the control information into the status
> + * information.
> + *
> + * If no status information has been requested, the skb is freed.

That last line doesn't seem true?

> +     } else {
> +             /* ieee80211_tx_status_irqsafe(hw, skb); */

?

> +static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
> +                                   struct sk_buff *beacon, bool in_intr)

unused in_intr arg?

> +     if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
> +             dev_dbg(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
> +             return 1;
> +     }
> +
> +     cs = (struct purelifi_ctrlset *)skb_push(skb,
> +                     sizeof(struct purelifi_ctrlset));

FWIW, (void *) cast is sufficient

> +     cs->id = USB_REQ_DATA_TX;
> +     cs->payload_len_nw = frag_len;
> +     cs->len = cs->payload_len_nw + sizeof(struct purelifi_ctrlset)
> +             - sizeof(cs->id) - sizeof(cs->len);

the - at the end of line is generally preferred, but not really that
important I guess

> +     /*check if 32 bit aligned and align data*/
> +     tmp = skb->len & 3;
> +     if (tmp) {
> +             if (skb_tailroom(skb) < (3 - tmp)) {
> +                     if (skb_headroom(skb) >= 4 - tmp) {
> +                             u8 len;
> +                             u8 *src_pt;
> +                             u8 *dest_pt;
> +
> +                             len = skb->len;
> +                             src_pt = skb->data;
> +                             dest_pt = skb_push(skb, 4 - tmp);
> +                             memcpy(dest_pt, src_pt, len);

that can overlap, so you really need memmove(), not memcpy().

> +                     } else {
> +                             return 1;

return 1 is "unidiomatic" in the kernel, maybe just return -ENOBUFS?

> +                             /* should never happen b/c

wouldn't hurt to spell that out ;)

> +                              * sufficient headroom was reserved
> +                              */
> +                             return 1;

same here

> +static void purelifi_op_tx(struct ieee80211_hw *hw,
> +                        struct ieee80211_tx_control *control,
> +                        struct sk_buff *skb)
> +{
> +     struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +     struct purelifi_usb *usb = &mac->chip.usb;
> +     struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +     unsigned long flags;
> +     int r;
> +
> +     r = fill_ctrlset(mac, skb);
> +     if (r)
> +             goto fail;
> +     info->rate_driver_data[0] = hw;
> +
> +     if (skb->data[24] == IEEE80211_FTYPE_DATA) {

it would be much clearer if you had a struct that you overlay on top of
the struct and then you have something like

        struct purelifi_header *plhdr = (void *)skb->data;

        if (plhdr->frametype == IEEE80211_FTYPE_DATA)

here.

Assuming that's what it does? Otherwise it doesn't make much sense?

It's not obvious what the SKB contains at this point, having a struct
there would make it obvious.

> +             u8 dst_mac[ETH_ALEN];
> +             u8 sidx;
> +             bool found = false;
> +             struct purelifi_usb_tx *tx = &usb->tx;
> +
> +             memcpy(dst_mac, &skb->data[28], ETH_ALEN);

Why copy it to the stack first?

> +             for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
> +                     if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
> +                             continue;
> +                     if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {

you haven't changed the skb?

And if you wanted it for line length or something, then just use a
pointer? Again though, the data[28] probably should be some struct
dereference.

> +                             found = true;
> +                             break;
> +                     }
> +             }
> +
> +             /* Default to broadcast address for unknown MACs */
> +             if (!found)
> +                     sidx = STA_BROADCAST_INDEX;
> +
> +             /* Stop OS from sending packets, if the queue is half full */
> +             if (skb_queue_len(&tx->station[sidx].data_list) > 60)
> +                     block_queue(usb, tx->station[sidx].mac, true);
> +
> +             /* Schedule packet for transmission if queue is not full */
> +             if (skb_queue_len(&tx->station[sidx].data_list) < 256) {

60/256 isn't really half, but hey :)

> +                     skb_queue_tail(&tx->station[sidx].data_list, skb);
> +                     purelifi_send_packet_from_data_queue(usb);
> +             } else {
> +                     dev_kfree_skb(skb);

It's kind of strangely inconsistent that you have here a free and then
fall through to the return, but other times you a 'goto fail':

> +             }
> +     } else {
> +             spin_lock_irqsave(&usb->tx.lock, flags);
> +             r = usb_write_req_async(&mac->chip.usb, skb->data, skb->len,
> +                                     USB_REQ_DATA_TX, tx_urb_complete, skb);
> +             spin_unlock_irqrestore(&usb->tx.lock, flags);
> +             if (r)
> +                     goto fail;
> +     }
> +     return;
> +
> +fail:
> +     dev_kfree_skb(skb);

where you free it

> +static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
> +                   struct ieee80211_rx_status *stats)

Generally, it'd be nice if more of your functions came with some prefix,
like say plf_filter_ack() in this case.

> +int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
> +                 unsigned int length)

or, well, purelifi_ :)

> +     crc_error_cnt_low = status->crc_error_count;
> +     crc_error_cnt_high = status->crc_error_count >> 32;
> +     mac->crc_errors = ((u64)ntohl(crc_error_cnt_low) << 32) |
> +             ntohl(crc_error_cnt_high);

Oh hey, I found one place where you do endian conversion ;-)

but ... what's wrong with be64_to_cpu()?

> +     if (buffer[0] == IEEE80211_STYPE_PROBE_REQ)
> +             dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
> +     else if (buffer[0] == IEEE80211_STYPE_ASSOC_REQ)
> +             dev_dbg(purelifi_mac_dev(mac), "Association request\n");
> +
> +     if (buffer[0] == IEEE80211_STYPE_AUTH) {
> +             dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
> +             min_exp_seq_nmb = 0;
> +     } else if (buffer[0] == IEEE80211_FTYPE_DATA) {

maybe a switch statement would be better anyway, but this "interrupted
else-if chain" looks really weird.

Not sure I'd add any of this in the first place

> +             unsigned short int seq_nmb = (buffer[23] << 4) |
> +                     ((buffer[22] & 0xF0) >> 4);
> +
> +             if (seq_nmb < min_exp_seq_nmb &&
> +                 ((min_exp_seq_nmb - seq_nmb) < 3000)) {
> +                     dev_dbg(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
> +             } else {
> +                     min_exp_seq_nmb = (seq_nmb + 1) % 4096;
> +             }

don't need braces

> +     memcpy(skb_put(skb, payload_length), buffer, payload_length);

skb_put_data()

> +     hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
> +     if (!hw) {
> +             dev_dbg(&intf->dev, "out of memory\n");
> +             return NULL;
> +     }
> +     set_wiphy_dev(hw->wiphy, &intf->dev);
> +
> +     mac = purelifi_hw_mac(hw);
> +
> +     memset(mac, 0, sizeof(*mac));

no need, we use kzalloc() internally.

> +     hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;

This is kind of the only fundamental problem I have with this ... are we
really sure we want this to pretend to be 2.4 GHz? You only have a
single channel anyway, and you pretend that's a fixed one (somewhere
above, I skipped it)...

But it really has absolutely no relation to 2.4 GHz!

OTOH, I can sort of see why/how you're reusing wifi functionality here,
very old versions of wifi even had an infrared PHY I think.

Conceptually, it seems odd. Perhaps we should add a new band definition?

And what I also asked above - how much of the rate stuff is completely
fake? Are you really doing CCK/OFDM in some (strange?) way?

> +     _ieee80211_hw_set(hw, IEEE80211_HW_RX_INCLUDES_FCS);
> +     _ieee80211_hw_set(hw, IEEE80211_HW_SIGNAL_DBM);
> +     _ieee80211_hw_set(hw, IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING);
> +     _ieee80211_hw_set(hw, IEEE80211_HW_MFP_CAPABLE);

Why not use the macro?

> +     hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;

Ah, so you _do_ have a struct for all this header stuff! Please use it
where I pointed it out above.

> +struct purelifi_ctrlset {
> +     enum usb_req_enum_t id;
> +     u32            len;
> +     u8             modulation;
> +     u8             control;
> +     u8             service;
> +     u8             pad;
> +     __le16         packet_length;
> +     __le16         current_length;
> +     __le16          next_frame_length;
> +     __le16         tx_length;
> +     u32            payload_len_nw;

having a mix of __le32 and u32 seems weird :)

but not sure you ever even converted things, did you run sparse with
"make C=1"?

> +struct purelifi_mac {
> +     struct purelifi_chip chip;
> +     spinlock_t lock; /* lock for mac data */
> +     spinlock_t intr_lock; /* not used : interrupt lock for mac */

well, remove it if it's not used?

> +     /* whether to pass frames with CRC errors to stack */
> +     unsigned int pass_failed_fcs:1;
> +
> +     /* whether to pass control frames to stack */
> +     unsigned int pass_ctrl:1;
> +
> +     /* whether we have received a 802.11 ACK that is pending */
> +     bool ack_pending:1;

bool bitfield looks really odd ... what does that even do? I think
better use unsigned int like above.

> +static inline struct purelifi_mac *purelifi_chip_to_mac(struct purelifi_chip
> +             *chip)

I'd recommend writing that as

static inline struct ... *
purelifi_chip_to_mac(...)


> +#ifdef DEBUG
> +void purelifi_dump_rx_status(const struct rx_status *status);
> +#else
> +#define purelifi_dump_rx_status(status)

do {} while (0)

> +#define FCS_LEN 4

might want to reuse FCS_LEN from ieee80211.h, i.e. just remove this?

> +struct proc_dir_entry *proc_folder;
> +struct proc_dir_entry *modulation_proc_entry;

You really need to remove procfs stuff, even these traces ;-)

> +     switch (urb->status) {
> +     case 0:
> +             break;
> +     case -ESHUTDOWN:
> +     case -EINVAL:
> +     case -ENODEV:
> +     case -ENOENT:
> +     case -ECONNRESET:
> +     case -EPIPE:
> +             dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
> +             return;
> +     default:
> +             dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
> +             goto resubmit;

you might want to limit that resubmit loop here to a few attempts

> +     buffer = urb->transfer_buffer;
> +     length = (*(u32 *)(buffer + sizeof(struct rx_status))) + sizeof(u32);

endian issues

> +     kfree(urbs);
> +
> +     spin_lock_irqsave(&rx->lock, flags);
> +     rx->urbs = NULL;
> +     rx->urbs_count = 0;
> +     spin_unlock_irqrestore(&rx->lock, flags);

That looks ... really weird, first you free and then you assign the
pointer to NULL, but under the lock?

I guess the lock is completely pointless here.

> +void purelifi_usb_release(struct purelifi_usb *usb)
> +{
> +     usb_set_intfdata(usb->intf, NULL);
> +     usb_put_intf(usb->intf);
> +     /* FIXME: usb_interrupt, usb_tx, usb_rx? */

what about them?

> +     dma_buffer = kzalloc(usb_bulk_msg_len, GFP_KERNEL);
> +
> +     if (!dma_buffer) {
> +             r = -ENOMEM;
> +             goto error;
> +     }
> +     memcpy(dma_buffer, &usb_req, usb_bulk_msg_len);

kmemdup()


> +static int pre_reset(struct usb_interface *intf)
> +{
> +     struct ieee80211_hw *hw = usb_get_intfdata(intf);
> +     struct purelifi_mac *mac;
> +     struct purelifi_usb *usb;
> +
> +     if (!hw || intf->condition != USB_INTERFACE_BOUND)
> +             return 0;
> +
> +     mac = purelifi_hw_mac(hw);
> +     usb = &mac->chip.usb;
> +
> +     usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
> +
> +     purelifi_usb_stop(usb);
> +
> +     mutex_lock(&mac->chip.mutex);

this looks kind of problematic locking-wise

> +static int post_reset(struct usb_interface *intf)
> +{
> +     struct ieee80211_hw *hw = usb_get_intfdata(intf);
> +     struct purelifi_mac *mac;
> +     struct purelifi_usb *usb;
> +
> +     if (!hw || intf->condition != USB_INTERFACE_BOUND)
> +             return 0;

especially since this doesn't always unlock?

Not sure intf->condition can change inbetween, but it looks very
fragile.

> +#define TO_NETWORK(X) ((((X) & 0xFF000000) >> 24) | (((X) & 0xFF0000) >> 8) |\
> +                   (((X) & 0xFF00) << 8) | (((X) & 0xFF) << 24))
> +
> +#define TO_NETWORK_16(X) ((((X) & 0xFF00) >> 8) | (((X) & 0x00FF) << 8))
> +
> +#define TO_NETWORK_32(X) TO_NETWORK(X)
> +
> +#define TO_HOST(X)    TO_NETWORK(X)
> +#define TO_HOST_16(X) TO_NETWORK_16(X)
> +#define TO_HOST_32(X) TO_NETWORK_32(X)

Remove all of that.

> +static inline struct usb_device *purelifi_usb_to_usbdev(struct purelifi_usb
> +             *usb)

indentation per above comment

johannes


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-19  5:15       ` Srinivasan Raju
@ 2021-02-19  8:25         ` Johannes Berg
  2021-02-24 10:41           ` Srinivasan Raju
  0 siblings, 1 reply; 108+ messages in thread
From: Johannes Berg @ 2021-02-19  8:25 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

On Fri, 2021-02-19 at 05:15 +0000, Srinivasan Raju wrote:
> Hi,
> 
> Please find a few responses to the comments , We will fix rest of the comments and submit v14 of the patch.
> 
> > Also, you *really* need some validation here, rather than blindly
> > trusting that the file is well-formed, otherwise you immediately have a
> > security issue here.
> 
> The firmware is signed and the harware validates the signature , so we are not validating in the software.

That wasn't my point. My point was that the kernel code trusts the
validity of the firmware image, in the sense of e.g. this piece:

> +     no_of_files = *(u32 *)&fw_packed->data[0];

If the firmware file was corrupted (intentionally/maliciously or not), this could now be say 0xffffffff.

> +     for (step = 0; step < no_of_files; step++) {

> +             start_addr = *(u32 *)&fw_packed->data[4 + (step * 4)];

But you trust it completely and don't even check that "4 + (step * 4)"
fits into the length of the data!

That's what I meant. Don't allow this to crash the kernel.

> > > +static const struct plf_reg_alpha2_map reg_alpha2_map[] = {
> > > +     { PLF_REGDOMAIN_FCC, "US" },
> > > +     { PLF_REGDOMAIN_IC, "CA" },
> > > +     { PLF_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
> > > +     { PLF_REGDOMAIN_JAPAN, "JP" },
> > > +     { PLF_REGDOMAIN_SPAIN, "ES" },
> > > +     { PLF_REGDOMAIN_FRANCE, "FR" },
> > > +};
> > You actually have regulatory restrictions on this stuff?
> 
> Currently, There are no regulatory restrictions applicable for LiFi ,
> regulatory_hint setting is only for wifi core 

So why do you have PLF_REGDOMAIN_* things? What does that even mean
then?

> > OTOH, I can sort of see why/how you're reusing wifi functionality here,
> > very old versions of wifi even had an infrared PHY I think.
> > 
> > Conceptually, it seems odd. Perhaps we should add a new band definition?
> > 
> > And what I also asked above - how much of the rate stuff is completely
> > fake? Are you really doing CCK/OFDM in some (strange?) way?
> 
> Yes, your understanding is correct, and we use OFDM.
> For now we will use the existing band definition.

OFDM over infrared, nice.

Still, I'm not convinced we should piggy-back this on 2.4 GHz.

NL80211_BAND_1THZ? ;-)

Ok, I don't know what wavelength you're using, of course...

But at least thinking about this, wouldn't we be better off if we define
NL80211_BAND_INFRARED or something? That way, at least we can tell that
it's not going to interoperate with real 2.4 GHz, etc.

OTOH, this is a bit of a slippery slow - what if somebody else designs
an *incompatible* infrared PHY? I guess this is not really standardised
at this point.

Not really sure about all this, but I guess we need to think about it
more.

What are your reasons for piggy-backing on 2.4 GHz? Just practical "it's
there and we don't care"?

johannes


^ permalink raw reply	[flat|nested] 108+ messages in thread

* RE: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-19  8:25         ` Johannes Berg
@ 2021-02-24 10:41           ` Srinivasan Raju
  0 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-02-24 10:41 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

> That wasn't my point. My point was that the kernel code trusts the validity of the firmware image, in the sense of e.g. this piece:

>>  +     no_of_files = *(u32 *)&fw_packed->data[0];

> If the firmware file was corrupted (intentionally/maliciously or not), this could now be say 0xffffffff.

Thanks for the clarification, We will submit next patch with additional validations to this

> What are your reasons for piggy-backing on 2.4 GHz? Just practical "it's there and we don't care"?

As the LiFi is not standardised yet we are using the existing wireless frameworks. For now piggy-backing with 2.4GHz is seamless for users. We will undertake band and other wider change once IEEE 802.11bb is standardised.

Thanks
Srini

^ permalink raw reply	[flat|nested] 108+ messages in thread

* RE: [PATCH] [v13] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-17 10:19     ` Kalle Valo
@ 2021-02-24 10:44       ` Srinivasan Raju
  0 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-02-24 10:44 UTC (permalink / raw)
  To: Kalle Valo
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS


> Having the firmware files under plfxlc/ directory looks good to me. But I'm not really a fan of upper case filenames, is there a reason for that? I would prefer have all lowercase filenames.

Thanks for your suggestions. We have renamed the firmware names to lower-case and will submit v14

Thanks
Srini


^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v14] wireless: Initial driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (9 preceding siblings ...)
  2021-02-12 11:49   ` [PATCH] [v13] " Srinivasan Raju
@ 2021-02-26 13:07   ` Srinivasan Raju
  2021-04-19 11:52     ` Srinivasan Raju
  2021-08-18 14:13     ` [PATCH] [v15] " Srinivasan Raju
  2021-09-24 13:26   ` [PATCH] [v16] wireless: Initial driver submission for pureLiFi LiFi Station Srinivasan Raju
                     ` (8 subsequent siblings)
  19 siblings, 2 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-02-26 13:07 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

---
v14:
 - Endianess comments addressed
 - Sparse checked and fixed warnings
 - Firmware files renamed to lowercase
 - All other review comments in v13 addressed
v13:
- Removed unused #defines
v12:
- Removed sysfs, procfs related code
- Addressed race condition bug
- Used macros instead of magic numbers in firmware.c
- Added copyright in all files
v11, v10:
- Addressed review comment on readability
- Changed firmware names to match products
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging
---
 MAINTAINERS                                   |    6 +
 drivers/net/wireless/Kconfig                  |    1 +
 drivers/net/wireless/Makefile                 |    1 +
 drivers/net/wireless/purelifi/Kconfig         |   17 +
 drivers/net/wireless/purelifi/Makefile        |    2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |   14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |    3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |   97 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |   84 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   |  292 +++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |   36 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    |  841 ++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    |  194 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 1014 +++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    |  179 +++
 15 files changed, 2781 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 5c1a6ba5ef26..c64888b19476 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14199,6 +14199,12 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PURELIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+L:	linux-wireless@vger.kernel.org
+S:	Supported
+F:	drivers/net/wireless/purelifi/
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7add2002ff4c..404afe574920 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -28,6 +28,7 @@ source "drivers/net/wireless/intersil/Kconfig"
 source "drivers/net/wireless/marvell/Kconfig"
 source "drivers/net/wireless/mediatek/Kconfig"
 source "drivers/net/wireless/microchip/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 source "drivers/net/wireless/ralink/Kconfig"
 source "drivers/net/wireless/realtek/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e3345893c9c5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
 obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
 obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
 obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
 obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
 obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..e39afec3dcae
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+source "drivers/net/wireless/purelifi/plfxlc/Kconfig"
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..56ebf96bd298
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)		:= plfxlc/
diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
new file mode 100644
index 000000000000..400ab2ee660c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config PURELIFI_XLC
+	tristate "pureLiFi X, XL, XC device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This option adds support for pureLiFi LiFi wireless USB adapters.
+
+	   The pureLiFi X, XL, XC USB devices are based on 802.11 OFDM PHY.
+
+	   Supports common 802.11 encryption/authentication methods including
+	   Open, WPA, WPA2-Personal and WPA2-Enterprise (802.1X).
+
+	   To compile this driver as a module, choose m here. The module will
+	   be called purelifi_xlc.
diff --git a/drivers/net/wireless/purelifi/plfxlc/Makefile b/drivers/net/wireless/purelifi/plfxlc/Makefile
new file mode 100644
index 000000000000..3d66f485c024
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)	:= purelifi_xlc.o
+purelifi_xlc-objs 		+= chip.o firmware.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.c b/drivers/net/wireless/purelifi/plfxlc/chip.c
new file mode 100644
index 000000000000..53ad5ebaa47c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set &&
+	     le16_to_cpu(chip->beacon_interval) == interval))
+		return 0;
+
+	chip->beacon_interval = cpu_to_le16(interval);
+	chip->beacon_set = true;
+	return plf_usb_wreq((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	unsigned char *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %04x:%04x v%02x %pM %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr,
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value)
+{
+	int r;
+	__le16 radio_on = cpu_to_le16(value);
+
+	r = plf_usb_wreq((const u8 *)&radio_on, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	static const u8 value;
+
+	plf_usb_wreq(&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = plf_usb_wreq(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.h b/drivers/net/wireless/purelifi/plfxlc/chip.h
new file mode 100644
index 000000000000..6ba1875730b7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	__le16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low  = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1U << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/plfxlc/firmware.c b/drivers/net/wireless/purelifi/plfxlc/firmware.c
new file mode 100644
index 000000000000..d177fe7f4e76
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/firmware.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/firmware.h>
+#include <linux/bitrev.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+#define PLF_VNDR_FPGA_STATE_REQ 0x30
+#define PLF_VNDR_FPGA_SET_REQ 0x33
+#define PLF_VNDR_FPGA_SET_CMD 0x34
+#define PLF_VNDR_FPGA_STATE_CMD 0x35
+	int r, actual_length;
+	int fw_data_i, blk_tran_len = PLF_BULK_TLEN;
+	const char *fw_name;
+	unsigned char *fpga_dmabuff = NULL;
+	unsigned char *fw_data;
+	const struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-x.bin";
+		dev_dbg(&intf->dev, "bin file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-xc.bin";
+		dev_dbg(&intf->dev, "bin file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware(&fw, fw_name, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATUS_LEN, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ,
+			    fpga_dmabuff, PLF_FPGA_STATUS_LEN);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_SET_CMD, NULL, 0);
+
+	if (fpga_dmabuff[0] != PLF_FPGA_MG) {
+		dev_err(&intf->dev, "fpga_dmabuff[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			/* u8 bit reverse */
+			fw_data[tbuf_idx] = bitrev8(fw_data[tbuf_idx]);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_dmabuff[0] & 0xff),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2 * PLF_USB_TIMEOUT);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	kfree(fpga_dmabuff);/* free PLF_FPGA_STATUS_LEN*/
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATE_LEN, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xff, PLF_FPGA_STATE_LEN);
+
+	send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
+			    PLF_FPGA_STATE_LEN);
+
+	dev_dbg(&intf->dev, "%*ph\n", 8, fpga_dmabuff);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
+
+	msleep(PLF_MSLEEP_TIME);
+
+error_free_fw:
+	kfree(fpga_dmabuff);
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+#define PLF_VNDR_XL_FW_CMD 0x80
+#define PLF_VNDR_XL_DATA_CMD 0x81
+#define PLF_VNDR_XL_FILE_CMD 0x82
+#define PLF_VNDR_XL_EX_CMD 0x83
+	const struct firmware *fwp = NULL;
+	struct firmware_file file = {0};
+	int s, r;
+	u8 *buf;
+	u32 i;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
+	msleep(PLF_MSLEEP_TIME);
+
+	if (r) {
+		dev_err(&intf->dev, "vendor command failed (%d)\n", r);
+		return -EINVAL;
+	}
+	/* Code for single pack file download */
+
+	fw_pack = "plfxlc/lifi-xl.bin";
+
+	r = request_firmware(&fwp, fw_pack, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		return -EINVAL;
+	}
+	file.total_files = get_unaligned_le32(&fwp->data[0]);
+	file.total_size = get_unaligned_le32(&fwp->size);
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n",
+		file.total_files, file.total_size);
+
+	buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
+	if (!buf) {
+		release_firmware(fwp);
+		return -ENOMEM;
+	}
+
+	if (file.total_files > 10) {
+		dev_err(&intf->dev, "Too many files (%d)\n", file.total_files);
+		release_firmware(fwp);
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	/* Download firmware files in multiple steps */
+	for (s = 0; s < file.total_files; s++) {
+		buf[0] = s;
+		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
+					PLF_XL_BUF_LEN);
+
+		if (s < file.total_files - 1)
+			file.size = get_unaligned_le32(&fwp->data[4 + ((s + 1) * 4)])
+				    - get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+		else
+			file.size = file.total_size -
+				    get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+
+		if (file.size > file.total_size || file.size > 60000) {
+			dev_err(&intf->dev, "file size is too large (%d)\n", file.size);
+			break;
+		}
+
+		file.start_addr = get_unaligned_le32(&fwp->data[4 + (s * 4)]);
+
+		if (file.size % PLF_XL_BUF_LEN && s < 2)
+			file.size += PLF_XL_BUF_LEN - file.size % PLF_XL_BUF_LEN;
+
+		file.control_packets = file.size / PLF_XL_BUF_LEN;
+
+		for (i = 0; i < file.control_packets; i++) {
+			memcpy(buf,
+			       &fwp->data[file.start_addr + (i * PLF_XL_BUF_LEN)],
+			       PLF_XL_BUF_LEN);
+			r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
+						PLF_XL_BUF_LEN);
+		}
+		dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", s, r,
+			file.size);
+	}
+	release_firmware(fwp);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
+	dev_dbg(&intf->dev, "download_fpga (4) (%d)\n", r);
+
+	return 0;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+#define PLF_MAC_VENDOR_REQUEST 0x36
+#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define PLF_SERIAL_LEN 14
+#define PLF_FW_VER_LEN 8
+	struct usb_device *udev = interface_to_usbdev(intf);
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);
+	if (!dma_buffer)
+		return -ENOMEM;
+
+	BUILD_BUG_ON(ETH_ALEN > PLF_SERIAL_LEN);
+	BUILD_BUG_ON(PLF_FW_VER_LEN > PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	memcpy(serial_number, dma_buffer, PLF_SERIAL_LEN);
+
+	memset(dma_buffer, 0x00, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, PLF_FW_VER_LEN);
+
+	memcpy(&firmware_version, dma_buffer, PLF_FW_VER_LEN);
+
+	dev_info(&intf->dev, "Firmware Version: %llu\n", firmware_version);
+	kfree(dma_buffer);
+
+	dev_dbg(&intf->dev, "Mac: %pM\n", hw_address);
+
+	return 0;
+}
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/intf.h b/drivers/net/wireless/purelifi/plfxlc/intf.h
new file mode 100644
index 000000000000..93b77b7666b6
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/intf.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#define ETH_ALEN 6
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	__be16 rssi;
+	u8     rate_idx;
+	u8     pad;
+	__be64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	__be32         id; /* should be usb_req_enum_t */
+	__be32	       len;
+	u8             buf[512];
+};
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
new file mode 100644
index 000000000000..35d80985ea9f
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.c
@@ -0,0 +1,841 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 }
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void purelifi_block_queue(struct purelifi_usb *usb, bool block)
+{
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	regulatory_hint(hw->wiphy, "00");
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status: reports tx status of a packet if required
+ * @hw: a &struct ieee80211_hw pointer
+ * @skb: a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success: True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status)
+		success = !tx_status->failure;
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev: callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon)
+{
+	return plf_usb_wreq((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	u32 temp_len = 0;
+	u32 temp_payload_len = 0;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_dbg(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (void *)skb_push(skb, sizeof(struct purelifi_ctrlset));
+	temp_payload_len = frag_len;
+	temp_len = temp_payload_len +
+		  sizeof(struct purelifi_ctrlset) -
+		  sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned and align data*/
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memmove(dest_pt, src_pt, len);
+			} else {
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		temp_len += 4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data*/
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen because
+				 * sufficient headroom was reserved
+				 */
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		temp_len += 4;
+	}
+
+	cs->id = cpu_to_be32(USB_REQ_DATA_TX);
+	cs->len = cpu_to_be32(temp_len);
+	cs->payload_len_nw = cpu_to_be32(temp_payload_len);
+
+	return 0;
+}
+
+/**
+ * purelifi_op_tx: transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct purelifi_header *plhdr = (void *)skb->data;
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+
+	info->rate_driver_data[0] = hw;
+
+	if (plhdr->frametype  == IEEE80211_FTYPE_DATA) {
+		u8 *dst_mac = plhdr->dmac;
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			purelifi_block_queue(usb, true);
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			goto fail;
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = plf_usb_wreq_async(&mac->chip.usb, skb->data, skb->len,
+				       USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * purelifi_filter_ack: filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int purelifi_filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+			       struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	bool found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_dbg(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_2GHZ;
+	mac->rssi      = -15 * be16_to_cpu(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	mac->crc_errors = be64_to_cpu(status->crc_error_count);
+
+	if (!bad_frame &&
+	    purelifi_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = get_unaligned_be32(buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	switch (buffer[0]) {
+	unsigned short int seq_nmb;
+
+	case IEEE80211_STYPE_PROBE_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
+		break;
+	case IEEE80211_STYPE_ASSOC_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Association request\n");
+		break;
+	case IEEE80211_STYPE_AUTH:
+		dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+		break;
+	case IEEE80211_FTYPE_DATA:
+		seq_nmb = (buffer[23] << 4) | ((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000))
+			dev_dbg(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
+		else
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		break;
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+
+	if (need_padding)
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+
+	skb_put_data(skb, buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_dbg(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_dbg(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_dbg(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+static const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;
+
+	ieee80211_hw_set(hw, RX_INCLUDES_FCS);
+	ieee80211_hw_set(hw, SIGNAL_DBM);
+	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+	ieee80211_hw_set(hw, MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* 4 for 32 bit alignment if no tailroom */
+
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.h b/drivers/net/wireless/purelifi/plfxlc/mac.h
new file mode 100644
index 000000000000..49c08ba07260
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.h
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	__be32		id;/*should be usb_req_enum_t*/
+	__be32		len;
+	u8              modulation;
+	u8              control;
+	u8              service;
+	u8		pad;
+	__le16		packet_length;
+	__le16		current_length;
+	__le16		next_frame_length;
+	__le16		tx_length;
+	__be32		payload_len_nw;
+} __packed;
+
+/*overlay*/
+struct purelifi_header {
+	struct purelifi_ctrlset plf_ctrl;
+	u32    frametype;
+	u8    *dmac;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *
+purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *
+purelifi_chip_to_mac(struct purelifi_chip *chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *
+purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void purelifi_block_queue(struct purelifi_usb *usb, bool block);
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
new file mode 100644
index 000000000000..b48d69902a76
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -0,0 +1,1014 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static atomic_t data_queue_flag;
+
+/*Tx retry backoff timer (in milliseconds).*/
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+
+/*Tx retry backoff timer (in jiffies).*/
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+static struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		plf_usb_wreq_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				   tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			purelifi_block_queue(usb, false);
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	static u8 retry;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
+		return;
+	} else if (!urb->context) {
+		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		if (retry++ < PURELIFI_URB_RETRY_MAX) {
+			dev_dbg(urb_dev(urb), "urb %p resubmit %d", urb, retry);
+			goto resubmit;
+		} else {
+			dev_dbg(urb_dev(urb), "urb %p  max resubmits reached", urb);
+			retry = 0;
+			return;
+		}
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status)))
+		 + sizeof(u32);
+
+	tx = &usb->tx;
+
+	if (urb->actual_length != 8) {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[7];
+
+	dev_dbg(&usb->intf->dev, "Recv status=%u\n", status);
+	dev_dbg(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+		buffer[0], buffer[1], buffer[2], buffer[3],
+		buffer[4], buffer[5]);
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_dbg(&usb->intf->dev,
+			"FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_dbg(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= 0xFD;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		fpga_link_connection_f = 1;
+		dev_dbg(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		fpga_link_connection_f = 0;
+		dev_dbg(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_dbg(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit fail (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0; /*no error return*/
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		usb->rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	usb->rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			u32 buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	__be32 payload_len_nw = cpu_to_be32(buffer_len + FCS_LEN);
+	u32 temp_usb_len = 0;
+
+	usb_req->id = cpu_to_be32(usb_req_id);
+	usb_req->len  = cpu_to_be32(0);
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == cpu_to_be32(USB_REQ_BEACON_WR)) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		temp_usb_len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	temp_usb_len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	temp_usb_len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (temp_usb_len %
+			PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len %
+				PURELIFI_BYTE_NUM_ALIGNMENT);
+		temp_usb_len += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->len = cpu_to_be32(temp_usb_len);
+}
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum usb_req_enum_t usb_req_id,
+		       usb_complete_t complete_fn,
+		       void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int plf_usb_wreq(const u8 *buffer, int buffer_len,
+		 enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(__le32) + sizeof(__le32) +
+			   be32_to_cpu(usb_req.len);
+
+	dma_buffer = kmemdup(&usb_req, usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	static u8 hw_address[ETH_ALEN];
+	static u8 serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+	dev_dbg(&intf->dev, "unit type is station");
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg(&intf->dev, "register device failed (%d)\n", r);
+		goto error;
+	}
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plf_usb_wreq(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+#ifdef CONFIG_PM
+	.suspend        = suspend,
+	.resume         = resume,
+#endif
+	.disable_hub_initiated_lpm = 1,
+};
+
+static struct workqueue_struct *purelifi_workqueue;
+
+static int __init usb_init(void)
+{
+	int r;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("plfxlc/lifi-x.bin");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.h b/drivers/net/wireless/purelifi/plfxlc/usb.h
new file mode 100644
index 000000000000..28c1ac7d9062
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.h
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#define PLF_FPGA_STATUS_LEN 2
+#define PLF_FPGA_STATE_LEN 9
+#define PLF_BULK_TLEN 16384
+#define PLF_FPGA_MG 6 /* Magic check */
+#define PLF_XL_BUF_LEN 64
+
+#define PLF_USB_TIMEOUT 1000
+#define PLF_MSLEEP_TIME 200
+
+#define PURELIFI_URB_RETRY_MAX 5
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+
+int plf_usb_wreq(const u8 *buffer, int buffer_len,
+		 enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2      |     1     |     0	    |
+   // Reserved  | Heartbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+struct firmware_file {
+	u32 total_files;
+	u32 total_size;
+	u32 size;
+	u32 start_addr;
+	u32 control_packets;
+} __packed;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+#define PURELIFI_SERIAL_LEN 256
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	bool rx_usb_enabled;
+	bool initialized;
+	bool was_running;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum usb_req_enum_t usb_req_id,
+		       usb_complete_t complete_fn, void *context);
+
+static inline struct usb_device *
+purelifi_usb_to_usbdev(struct purelifi_usb *usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_intf_to_hw(struct usb_interface *intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_usb_to_hw(struct purelifi_usb *usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/*Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* RE: [PATCH] [v14] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-26 13:07   ` [PATCH] [v14] " Srinivasan Raju
@ 2021-04-19 11:52     ` Srinivasan Raju
  2021-08-10 13:02       ` Srinivasan Raju
  2021-08-18 14:13     ` [PATCH] [v15] " Srinivasan Raju
  1 sibling, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2021-04-19 11:52 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

> From: Srinivasan Raju <srini.raju@purelifi.com> 
> Sent: 26 February 2021 13:09
> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
> 
> This driver implementation has been based on the zd1211rw driver.
> 
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
> 
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
>
> ---
> v14:
 > - Endianess comments addressed
 > - Sparse checked and fixed warnings
 > - Firmware files renamed to lowercase
 > - All other review comments in v13 addressed

Hi,

Could you please review this patch and let us know if there are any comments.
And please let us know if any changes has to be made to the driver for getting into wireless-next. 

Thanks
Srini

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v14] wireless: Initial driver submission for pureLiFi STA devices
  2021-04-19 11:52     ` Srinivasan Raju
@ 2021-08-10 13:02       ` Srinivasan Raju
  2021-08-21 13:42         ` Kalle Valo
  0 siblings, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2021-08-10 13:02 UTC (permalink / raw)
  Cc: Mostafa Afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Hi,

> From: Srinivasan Raju <srini.raju@purelifi.com> 
> Sent: 26 February 2021 13:09
> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
> 
> This driver implementation has been based on the zd1211rw driver.
> 
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
> 
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
>
> ---
> v14:
 > - Endianess comments addressed
 > - Sparse checked and fixed warnings
 > - Firmware files renamed to lowercase
 > - All other review comments in v13 addressed

Could you please review this patch and let us know if there are any comments.
And please let us know if any changes has to be made to the driver for getting into wireless-next. 
We have already submitted the firmware for review as well. The patch is in "Awaiting Upstream" state for a long time. 
Please let us know.

https://patchwork.kernel.org/project/netdevbpf/patch/20210226130810.119216-1-srini.raju@purelifi.com/

Thanks
Srini

^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v15] wireless: Initial driver submission for pureLiFi STA devices
  2021-02-26 13:07   ` [PATCH] [v14] " Srinivasan Raju
  2021-04-19 11:52     ` Srinivasan Raju
@ 2021-08-18 14:13     ` Srinivasan Raju
  2021-09-20 13:05       ` Kalle Valo
                         ` (2 more replies)
  1 sibling, 3 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-08-18 14:13 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

---
v15:
 - resubmit v14 of the patch
v14:
 - Endianess comments addressed
 - Sparse checked and fixed warnings
 - Firmware files renamed to lowercase
 - All other review comments in v13 addressed
v13:
- Removed unused #defines
v12:
- Removed sysfs, procfs related code
- Addressed race condition bug
- Used macros instead of magic numbers in firmware.c
- Added copyright in all files
v11, v10:
- Addressed review comment on readability
- Changed firmware names to match products
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging
---
---
 MAINTAINERS                                   |    6 +
 drivers/net/wireless/Kconfig                  |    1 +
 drivers/net/wireless/Makefile                 |    1 +
 drivers/net/wireless/purelifi/Kconfig         |   17 +
 drivers/net/wireless/purelifi/Makefile        |    2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |   14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |    3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |   97 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |   84 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   |  292 +++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |   36 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    |  841 ++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    |  194 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 1014 +++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    |  179 +++
 15 files changed, 2781 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 492bc169c3bd..1fd6f64c614c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15040,6 +15040,12 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PURELIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+L:	linux-wireless@vger.kernel.org
+S:	Supported
+F:	drivers/net/wireless/purelifi/
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7add2002ff4c..404afe574920 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -28,6 +28,7 @@ source "drivers/net/wireless/intersil/Kconfig"
 source "drivers/net/wireless/marvell/Kconfig"
 source "drivers/net/wireless/mediatek/Kconfig"
 source "drivers/net/wireless/microchip/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 source "drivers/net/wireless/ralink/Kconfig"
 source "drivers/net/wireless/realtek/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e3345893c9c5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
 obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
 obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
 obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
 obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
 obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..e39afec3dcae
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+source "drivers/net/wireless/purelifi/plfxlc/Kconfig"
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..56ebf96bd298
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)		:= plfxlc/
diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
new file mode 100644
index 000000000000..400ab2ee660c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config PURELIFI_XLC
+	tristate "pureLiFi X, XL, XC device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This option adds support for pureLiFi LiFi wireless USB adapters.
+
+	   The pureLiFi X, XL, XC USB devices are based on 802.11 OFDM PHY.
+
+	   Supports common 802.11 encryption/authentication methods including
+	   Open, WPA, WPA2-Personal and WPA2-Enterprise (802.1X).
+
+	   To compile this driver as a module, choose m here. The module will
+	   be called purelifi_xlc.
diff --git a/drivers/net/wireless/purelifi/plfxlc/Makefile b/drivers/net/wireless/purelifi/plfxlc/Makefile
new file mode 100644
index 000000000000..3d66f485c024
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)	:= purelifi_xlc.o
+purelifi_xlc-objs 		+= chip.o firmware.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.c b/drivers/net/wireless/purelifi/plfxlc/chip.c
new file mode 100644
index 000000000000..53ad5ebaa47c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set &&
+	     le16_to_cpu(chip->beacon_interval) == interval))
+		return 0;
+
+	chip->beacon_interval = cpu_to_le16(interval);
+	chip->beacon_set = true;
+	return plf_usb_wreq((const u8 *)&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	unsigned char *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %04x:%04x v%02x %pM %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr,
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value)
+{
+	int r;
+	__le16 radio_on = cpu_to_le16(value);
+
+	r = plf_usb_wreq((const u8 *)&radio_on, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	static const u8 value;
+
+	plf_usb_wreq(&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = plf_usb_wreq(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.h b/drivers/net/wireless/purelifi/plfxlc/chip.h
new file mode 100644
index 000000000000..6ba1875730b7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type_t {
+	STA = 0,
+	AP = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type_t unit_type;
+	u16 link_led;
+	u8  beacon_set;
+	__le16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low  = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1U << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/plfxlc/firmware.c b/drivers/net/wireless/purelifi/plfxlc/firmware.c
new file mode 100644
index 000000000000..d177fe7f4e76
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/firmware.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/firmware.h>
+#include <linux/bitrev.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+#define PLF_VNDR_FPGA_STATE_REQ 0x30
+#define PLF_VNDR_FPGA_SET_REQ 0x33
+#define PLF_VNDR_FPGA_SET_CMD 0x34
+#define PLF_VNDR_FPGA_STATE_CMD 0x35
+	int r, actual_length;
+	int fw_data_i, blk_tran_len = PLF_BULK_TLEN;
+	const char *fw_name;
+	unsigned char *fpga_dmabuff = NULL;
+	unsigned char *fw_data;
+	const struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-x.bin";
+		dev_dbg(&intf->dev, "bin file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-xc.bin";
+		dev_dbg(&intf->dev, "bin file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware(&fw, fw_name, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATUS_LEN, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ,
+			    fpga_dmabuff, PLF_FPGA_STATUS_LEN);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_SET_CMD, NULL, 0);
+
+	if (fpga_dmabuff[0] != PLF_FPGA_MG) {
+		dev_err(&intf->dev, "fpga_dmabuff[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			/* u8 bit reverse */
+			fw_data[tbuf_idx] = bitrev8(fw_data[tbuf_idx]);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_dmabuff[0] & 0xff),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2 * PLF_USB_TIMEOUT);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	kfree(fpga_dmabuff);/* free PLF_FPGA_STATUS_LEN*/
+	fpga_dmabuff = NULL;
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATE_LEN, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xff, PLF_FPGA_STATE_LEN);
+
+	send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
+			    PLF_FPGA_STATE_LEN);
+
+	dev_dbg(&intf->dev, "%*ph\n", 8, fpga_dmabuff);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
+
+	msleep(PLF_MSLEEP_TIME);
+
+error_free_fw:
+	kfree(fpga_dmabuff);
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+#define PLF_VNDR_XL_FW_CMD 0x80
+#define PLF_VNDR_XL_DATA_CMD 0x81
+#define PLF_VNDR_XL_FILE_CMD 0x82
+#define PLF_VNDR_XL_EX_CMD 0x83
+	const struct firmware *fwp = NULL;
+	struct firmware_file file = {0};
+	int s, r;
+	u8 *buf;
+	u32 i;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
+	msleep(PLF_MSLEEP_TIME);
+
+	if (r) {
+		dev_err(&intf->dev, "vendor command failed (%d)\n", r);
+		return -EINVAL;
+	}
+	/* Code for single pack file download */
+
+	fw_pack = "plfxlc/lifi-xl.bin";
+
+	r = request_firmware(&fwp, fw_pack, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		return -EINVAL;
+	}
+	file.total_files = get_unaligned_le32(&fwp->data[0]);
+	file.total_size = get_unaligned_le32(&fwp->size);
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n",
+		file.total_files, file.total_size);
+
+	buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
+	if (!buf) {
+		release_firmware(fwp);
+		return -ENOMEM;
+	}
+
+	if (file.total_files > 10) {
+		dev_err(&intf->dev, "Too many files (%d)\n", file.total_files);
+		release_firmware(fwp);
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	/* Download firmware files in multiple steps */
+	for (s = 0; s < file.total_files; s++) {
+		buf[0] = s;
+		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
+					PLF_XL_BUF_LEN);
+
+		if (s < file.total_files - 1)
+			file.size = get_unaligned_le32(&fwp->data[4 + ((s + 1) * 4)])
+				    - get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+		else
+			file.size = file.total_size -
+				    get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+
+		if (file.size > file.total_size || file.size > 60000) {
+			dev_err(&intf->dev, "file size is too large (%d)\n", file.size);
+			break;
+		}
+
+		file.start_addr = get_unaligned_le32(&fwp->data[4 + (s * 4)]);
+
+		if (file.size % PLF_XL_BUF_LEN && s < 2)
+			file.size += PLF_XL_BUF_LEN - file.size % PLF_XL_BUF_LEN;
+
+		file.control_packets = file.size / PLF_XL_BUF_LEN;
+
+		for (i = 0; i < file.control_packets; i++) {
+			memcpy(buf,
+			       &fwp->data[file.start_addr + (i * PLF_XL_BUF_LEN)],
+			       PLF_XL_BUF_LEN);
+			r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
+						PLF_XL_BUF_LEN);
+		}
+		dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", s, r,
+			file.size);
+	}
+	release_firmware(fwp);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
+	dev_dbg(&intf->dev, "download_fpga (4) (%d)\n", r);
+
+	return 0;
+}
+
+struct flash_t {
+	unsigned char enabled;
+	unsigned int sector_size;
+	unsigned int sectors;
+	unsigned char ec;
+};
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+#define PLF_MAC_VENDOR_REQUEST 0x36
+#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define PLF_SERIAL_LEN 14
+#define PLF_FW_VER_LEN 8
+	struct usb_device *udev = interface_to_usbdev(intf);
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);
+	if (!dma_buffer)
+		return -ENOMEM;
+
+	BUILD_BUG_ON(ETH_ALEN > PLF_SERIAL_LEN);
+	BUILD_BUG_ON(PLF_FW_VER_LEN > PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	memcpy(serial_number, dma_buffer, PLF_SERIAL_LEN);
+
+	memset(dma_buffer, 0x00, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, PLF_FW_VER_LEN);
+
+	memcpy(&firmware_version, dma_buffer, PLF_FW_VER_LEN);
+
+	dev_info(&intf->dev, "Firmware Version: %llu\n", firmware_version);
+	kfree(dma_buffer);
+
+	dev_dbg(&intf->dev, "Mac: %pM\n", hw_address);
+
+	return 0;
+}
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/intf.h b/drivers/net/wireless/purelifi/plfxlc/intf.h
new file mode 100644
index 000000000000..93b77b7666b6
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/intf.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#define ETH_ALEN 6
+#define AP_USER_LIMIT 8
+
+struct rx_status {
+	__be16 rssi;
+	u8     rate_idx;
+	u8     pad;
+	__be64 crc_error_count;
+} __packed;
+
+enum usb_req_enum_t {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct usb_req_t {
+	__be32         id; /* should be usb_req_enum_t */
+	__be32	       len;
+	u8             buf[512];
+};
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
new file mode 100644
index 000000000000..35d80985ea9f
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.c
@@ -0,0 +1,841 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 }
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+void purelifi_block_queue(struct purelifi_usb *usb, bool block)
+{
+	if (block)
+		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+	else
+		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	regulatory_hint(hw->wiphy, "00");
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int purelifi_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void purelifi_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+/**
+ * purelifi_mac_tx_status: reports tx status of a packet if required
+ * @hw: a &struct ieee80211_hw pointer
+ * @skb: a sk-buffer
+ * @flags: extra flags to set in the TX status info
+ * @ackssi: ACK signal strength
+ * @success: True for successful transmission of the frame
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ */
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status)
+		success = !tx_status->failure;
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * purelifi_mac_tx_to_dev: callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon)
+{
+	return plf_usb_wreq((const u8 *)beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	u32 temp_len = 0;
+	u32 temp_payload_len = 0;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_dbg(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (void *)skb_push(skb, sizeof(struct purelifi_ctrlset));
+	temp_payload_len = frag_len;
+	temp_len = temp_payload_len +
+		  sizeof(struct purelifi_ctrlset) -
+		  sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/*check if 32 bit aligned and align data*/
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memmove(dest_pt, src_pt, len);
+			} else {
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		temp_len += 4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data*/
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen because
+				 * sufficient headroom was reserved
+				 */
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		temp_len += 4;
+	}
+
+	cs->id = cpu_to_be32(USB_REQ_DATA_TX);
+	cs->len = cpu_to_be32(temp_len);
+	cs->payload_len_nw = cpu_to_be32(temp_payload_len);
+
+	return 0;
+}
+
+/**
+ * purelifi_op_tx: transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void purelifi_op_tx(struct ieee80211_hw *hw,
+			   struct ieee80211_tx_control *control,
+			   struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct purelifi_header *plhdr = (void *)skb->data;
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+
+	info->rate_driver_data[0] = hw;
+
+	if (plhdr->frametype  == IEEE80211_FTYPE_DATA) {
+		u8 *dst_mac = plhdr->dmac;
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			purelifi_block_queue(usb, true);
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			goto fail;
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = plf_usb_wreq_async(&mac->chip.usb, skb->data, skb->len,
+				       USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+/**
+ * purelifi_filter_ack: filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int purelifi_filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+			       struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	bool found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_dbg(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_2GHZ;
+	mac->rssi      = -15 * be16_to_cpu(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	mac->crc_errors = be64_to_cpu(status->crc_error_count);
+
+	if (!bad_frame &&
+	    purelifi_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = get_unaligned_be32(buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	switch (buffer[0]) {
+	unsigned short int seq_nmb;
+
+	case IEEE80211_STYPE_PROBE_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
+		break;
+	case IEEE80211_STYPE_ASSOC_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Association request\n");
+		break;
+	case IEEE80211_STYPE_AUTH:
+		dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+		break;
+	case IEEE80211_FTYPE_DATA:
+		seq_nmb = (buffer[23] << 4) | ((buffer[22] & 0xF0) >> 4);
+
+		if (seq_nmb < min_exp_seq_nmb &&
+		    ((min_exp_seq_nmb - seq_nmb) < 3000))
+			dev_dbg(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
+		else
+			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
+		break;
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+
+	if (need_padding)
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+
+	skb_put_data(skb, buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int purelifi_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_dbg(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_dbg(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void purelifi_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int purelifi_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void purelifi_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by purelifi_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void purelifi_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_dbg(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+static const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops purelifi_ops = {
+	.tx                 = purelifi_op_tx,
+	.start              = purelifi_op_start,
+	.stop               = purelifi_op_stop,
+	.add_interface      = purelifi_op_add_interface,
+	.remove_interface   = purelifi_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = purelifi_op_config,
+	.configure_filter   = purelifi_op_configure_filter,
+	.bss_info_changed   = purelifi_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &purelifi_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+
+	hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;
+
+	ieee80211_hw_set(hw, RX_INCLUDES_FCS);
+	ieee80211_hw_set(hw, SIGNAL_DBM);
+	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+	ieee80211_hw_set(hw, MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->max_signal = 100;
+	hw->queues = 1;
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* 4 for 32 bit alignment if no tailroom */
+
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.h b/drivers/net/wireless/purelifi/plfxlc/mac.h
new file mode 100644
index 000000000000..49c08ba07260
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.h
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	__be32		id;/*should be usb_req_enum_t*/
+	__be32		len;
+	u8              modulation;
+	u8              control;
+	u8              service;
+	u8		pad;
+	__le16		packet_length;
+	__le16		current_length;
+	__le16		next_frame_length;
+	__le16		tx_length;
+	__be32		payload_len_nw;
+} __packed;
+
+/*overlay*/
+struct purelifi_header {
+	struct purelifi_ctrlset plf_ctrl;
+	u32    frametype;
+	u8    *dmac;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	unsigned int pass_failed_fcs:1;
+
+	/* whether to pass control frames to stack */
+	unsigned int pass_ctrl:1;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[256];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *
+purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *
+purelifi_chip_to_mac(struct purelifi_chip *chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *
+purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int purelifi_op_start(struct ieee80211_hw *hw);
+void purelifi_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+void purelifi_block_queue(struct purelifi_usb *usb, bool block);
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
new file mode 100644
index 000000000000..b48d69902a76
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -0,0 +1,1014 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static atomic_t data_queue_flag;
+
+/*Tx retry backoff timer (in milliseconds).*/
+# define TX_RETRY_BACKOFF_MS 10
+# define STA_QUEUE_CLEANUP_MS 5000
+
+/*Tx retry backoff timer (in jiffies).*/
+# define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+# define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+static struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+static struct usb_interface *ez_usb_interface;
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		plf_usb_wreq_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				   tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			purelifi_block_queue(usb, false);
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	static u8 retry;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	static u8 fpga_link_connection_f;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
+		return;
+	} else if (!urb->context) {
+		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		if (retry++ < PURELIFI_URB_RETRY_MAX) {
+			dev_dbg(urb_dev(urb), "urb %p resubmit %d", urb, retry);
+			goto resubmit;
+		} else {
+			dev_dbg(urb_dev(urb), "urb %p  max resubmits reached", urb);
+			retry = 0;
+			return;
+		}
+	}
+
+	buffer = urb->transfer_buffer;
+	length = (*(u32 *)(buffer + sizeof(struct rx_status)))
+		 + sizeof(u32);
+
+	tx = &usb->tx;
+
+	if (urb->actual_length != 8) {
+		if (usb->initialized && fpga_link_connection_f)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[7];
+
+	dev_dbg(&usb->intf->dev, "Recv status=%u\n", status);
+	dev_dbg(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
+		buffer[0], buffer[1], buffer[2], buffer[3],
+		buffer[4], buffer[5]);
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_dbg(&usb->intf->dev,
+			"FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_dbg(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= 0xFD;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		fpga_link_connection_f = 1;
+		dev_dbg(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		fpga_link_connection_f = 0;
+		dev_dbg(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_dbg(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit fail (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0; /*no error return*/
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		usb->rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	usb->rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	atomic_set(&tx->enabled, 1);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+/**
+ * purelifi_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the driver USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	atomic_set(&tx->enabled, 0);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, const u8 *buffer,
+			u32 buffer_len, enum usb_req_enum_t usb_req_id,
+			struct usb_req_t *usb_req)
+{
+	u8 *buffer_dst_p = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	__be32 payload_len_nw = cpu_to_be32(buffer_len + FCS_LEN);
+	u32 temp_usb_len = 0;
+
+	usb_req->id = cpu_to_be32(usb_req_id);
+	usb_req->len  = cpu_to_be32(0);
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == cpu_to_be32(USB_REQ_BEACON_WR)) {
+		memcpy(buffer_dst_p, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst_p += sizeof(payload_len_nw);
+		temp_usb_len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst_p, buffer_src_p, buffer_len);
+	buffer_dst_p += buffer_len;
+	buffer_src_p += buffer_len;
+	temp_usb_len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst_p, 0, FCS_LEN);
+	buffer_dst_p += FCS_LEN;
+	temp_usb_len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst_p, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (temp_usb_len %
+			PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst_p += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len %
+				PURELIFI_BYTE_NUM_ALIGNMENT);
+		temp_usb_len += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->len = cpu_to_be32(temp_usb_len);
+}
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum usb_req_enum_t usb_req_id,
+		       usb_complete_t complete_fn,
+		       void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int plf_usb_wreq(const u8 *buffer, int buffer_len,
+		 enum usb_req_enum_t usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct usb_req_t usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(__le32) + sizeof(__le32) +
+			   be32_to_cpu(usb_req.len);
+
+	dma_buffer = kmemdup(&usb_req, usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	static u8 hw_address[ETH_ALEN];
+	static u8 serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+	dev_dbg(&intf->dev, "unit type is station");
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_dbg(&intf->dev, "register device failed (%d)\n", r);
+		goto error;
+	}
+	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plf_usb_wreq(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	atomic_set(&data_queue_flag, 1);
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+	atomic_set(&data_queue_flag, 0);
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = purelifi_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	purelifi_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+
+	return 0;
+}
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+#ifdef CONFIG_PM
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+#ifdef CONFIG_PM
+	.suspend        = suspend,
+	.resume         = resume,
+#endif
+	.disable_hub_initiated_lpm = 1,
+};
+
+static struct workqueue_struct *purelifi_workqueue;
+
+static int __init usb_init(void)
+{
+	int r;
+
+	purelifi_workqueue = create_singlethread_workqueue(driver.name);
+	if (!purelifi_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(purelifi_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	usb_deregister(&driver);
+	destroy_workqueue(purelifi_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("plfxlc/lifi-x.bin");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.h b/drivers/net/wireless/purelifi/plfxlc/usb.h
new file mode 100644
index 000000000000..28c1ac7d9062
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.h
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#define PLF_FPGA_STATUS_LEN 2
+#define PLF_FPGA_STATE_LEN 9
+#define PLF_BULK_TLEN 16384
+#define PLF_FPGA_MG 6 /* Magic check */
+#define PLF_XL_BUF_LEN 64
+
+#define PLF_USB_TIMEOUT 1000
+#define PLF_MSLEEP_TIME 200
+
+#define PURELIFI_URB_RETRY_MAX 5
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+
+int plf_usb_wreq(const u8 *buffer, int buffer_len,
+		 enum usb_req_enum_t usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+/* USB interrupt */
+struct purelifi_usb_interrupt {
+	spinlock_t lock;/* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock;/* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct station_t {
+   //  7...3    |    2      |     1     |     0	    |
+   // Reserved  | Heartbeat | FIFO full | Connected |
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+struct firmware_file {
+	u32 total_files;
+	u32 total_size;
+	u32 size;
+	u32 start_addr;
+	u32 control_packets;
+} __packed;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+
+#define PURELIFI_SERIAL_LEN 256
+/**
+ * struct purelifi_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *   device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+struct purelifi_usb_tx {
+	atomic_t enabled;
+	spinlock_t lock; /*spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	u8 stopped:1;
+	struct timer_list tx_retry_timer;
+	struct station_t station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	bool rx_usb_enabled;
+	bool initialized;
+	bool was_running;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum usb_req_enum_t usb_req_id,
+		       usb_complete_t complete_fn, void *context);
+
+static inline struct usb_device *
+purelifi_usb_to_usbdev(struct purelifi_usb *usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_intf_to_hw(struct usb_interface *intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_usb_to_hw(struct purelifi_usb *usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/*Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v14] wireless: Initial driver submission for pureLiFi STA devices
  2021-08-10 13:02       ` Srinivasan Raju
@ 2021-08-21 13:42         ` Kalle Valo
  0 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2021-08-21 13:42 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> Could you please review this patch and let us know if there are any comments.
> And please let us know if any changes has to be made to the driver for getting into wireless-next. 
> We have already submitted the firmware for review as well. The patch is in "Awaiting Upstream" state for a long time. 
> Please let us know.

Reviewing new drivers is time consuming and it's not always easy to find
time for that. It's unfortunate that the review takes so long, but
please take into account that we are volunteers.

> https://patchwork.kernel.org/project/netdevbpf/patch/20210226130810.119216-1-srini.raju@purelifi.com/

You are looking at the wrong patchwork project, you should follow
linux-wireless project. Direct link below.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v15] wireless: Initial driver submission for pureLiFi STA devices
  2021-08-18 14:13     ` [PATCH] [v15] " Srinivasan Raju
@ 2021-09-20 13:05       ` Kalle Valo
       [not found]         ` <CWLP265MB3217BB5AA5F102629A3AD204E0A19@CWLP265MB3217.GBRP265.PROD.OUTLOOK.COM>
  2021-09-20 14:11       ` Kalle Valo
  2021-09-24 11:11       ` Kalle Valo
  2 siblings, 1 reply; 108+ messages in thread
From: Kalle Valo @ 2021-09-20 13:05 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

(big picture comments first)

Srinivasan Raju <srini.raju@purelifi.com> writes:

> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
>
> This driver implementation has been based on the zd1211rw driver.
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
>
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.

You should describe in the commit log what LiFi means, not everyone know
the term and might mistake this as a regular Wi-Fi device (which it's
not).

> +	hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;

Johannes comment about piggy-backing NL80211_BAND_2GHZ is not yet addressed:

https://patchwork.kernel.org/project/linux-wireless/patch/20210212115030.124490-1-srini.raju@purelifi.com/

I agree with Johannes, a Li-Fi driver should not claim to be a regular
2.4 GHz Wi-Fi device.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v15] wireless: Initial driver submission for pureLiFi STA devices
  2021-08-18 14:13     ` [PATCH] [v15] " Srinivasan Raju
  2021-09-20 13:05       ` Kalle Valo
@ 2021-09-20 14:11       ` Kalle Valo
  2021-09-24 11:11       ` Kalle Valo
  2 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2021-09-20 14:11 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
>
> This driver implementation has been based on the zd1211rw driver.
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
>
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

[...]

> +int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
> +				 u8 dtim_period, int type)
> +{
> +	if (!interval ||
> +	    (chip->beacon_set &&
> +	     le16_to_cpu(chip->beacon_interval) == interval))
> +		return 0;
> +
> +	chip->beacon_interval = cpu_to_le16(interval);
> +	chip->beacon_set = true;
> +	return plf_usb_wreq((const u8 *)&chip->beacon_interval,
> +			     sizeof(chip->beacon_interval),
> +			     USB_REQ_BEACON_INTERVAL_WR);

Can't you make plf_usb_wreq() to a void pointer? Then that ugly "const
u8 *" cast is not needed.

> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
> @@ -0,0 +1,84 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2021 pureLiFi
> + */
> +
> +#ifndef _LF_X_CHIP_H
> +#define _LF_X_CHIP_H
> +
> +#include <net/mac80211.h>
> +
> +#include "usb.h"
> +
> +enum unit_type_t {
> +	STA = 0,
> +	AP = 1,
> +};

unit_type_t -> unit_type

> +struct purelifi_chip {
> +	struct purelifi_usb usb;
> +	struct mutex mutex; /* lock to protect chip data */
> +	enum unit_type_t unit_type;
> +	u16 link_led;
> +	u8  beacon_set;

Extra space after u8.

> +int download_fpga(struct usb_interface *intf)
> +{
> +#define PLF_VNDR_FPGA_STATE_REQ 0x30
> +#define PLF_VNDR_FPGA_SET_REQ 0x33
> +#define PLF_VNDR_FPGA_SET_CMD 0x34
> +#define PLF_VNDR_FPGA_STATE_CMD 0x35

Please move these to a .h file.

> +	kfree(fpga_dmabuff);/* free PLF_FPGA_STATUS_LEN*/

Pointless comment.

> +	fpga_dmabuff = NULL;
> +	fpga_dmabuff = kmalloc(PLF_FPGA_STATE_LEN, GFP_KERNEL);

No need to assign fpga_dmabuff to NULL.

> +int download_xl_firmware(struct usb_interface *intf)
> +{
> +#define PLF_VNDR_XL_FW_CMD 0x80
> +#define PLF_VNDR_XL_DATA_CMD 0x81
> +#define PLF_VNDR_XL_FILE_CMD 0x82
> +#define PLF_VNDR_XL_EX_CMD 0x83

Please move these to a .h file.

> +struct flash_t {
> +	unsigned char enabled;
> +	unsigned int sector_size;
> +	unsigned int sectors;
> +	unsigned char ec;
> +};

Remove all _t suffixes in names.

> +int upload_mac_and_serial(struct usb_interface *intf,
> +			  unsigned char *hw_address,
> +			  unsigned char *serial_number)
> +{
> +#define PLF_MAC_VENDOR_REQUEST 0x36
> +#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
> +#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
> +#define PLF_SERIAL_LEN 14
> +#define PLF_FW_VER_LEN 8

Move to .h file.

> +void purelifi_block_queue(struct purelifi_usb *usb, bool block)
> +{
> +	if (block)
> +		ieee80211_stop_queues(purelifi_usb_to_hw(usb));
> +	else
> +		ieee80211_wake_queues(purelifi_usb_to_hw(usb));
> +}

Looks like a useless abstraction to me, just use ieee80211_stop_queues()
and ieee80211_wake_queues() directly.

> +/**
> + * purelifi_mac_tx_status: reports tx status of a packet if required
> + * @hw: a &struct ieee80211_hw pointer
> + * @skb: a sk-buffer
> + * @flags: extra flags to set in the TX status info
> + * @ackssi: ACK signal strength
> + * @success: True for successful transmission of the frame
> + *
> + * This information calls ieee80211_tx_status_irqsafe() if required by the
> + * control information. It copies the control information into the status
> + * information.
> + *
> + */

I'm against this kind of documentation in wireless drivers, they get out
of date so easily.

> +	/*check if 32 bit aligned and align data*/

Correct format is:

/* check if 32 bit aligned and align data */

> +	/* check if not multiple of 512 and align data*/

/* check if not multiple of 512 and align data */

> +int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
> +		    unsigned int length)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct ieee80211_rx_status stats;
> +	const struct rx_status *status;
> +	struct sk_buff *skb;
> +	int bad_frame = 0;
> +	__le16 fc;
> +	int need_padding;
> +	unsigned int payload_length;
> +	static unsigned short int min_exp_seq_nmb;

No static variables, "static const" are only exceptions. Otherwise two
devices on the same host won't work correctly.

> +	switch (buffer[0]) {
> +	unsigned short int seq_nmb;

Please move the variable declaration to the beginning of the function.

> +	case IEEE80211_STYPE_PROBE_REQ:
> +		dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
> +		break;
> +	case IEEE80211_STYPE_ASSOC_REQ:
> +		dev_dbg(purelifi_mac_dev(mac), "Association request\n");
> +		break;
> +	case IEEE80211_STYPE_AUTH:
> +		dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
> +		min_exp_seq_nmb = 0;
> +		break;
> +	case IEEE80211_FTYPE_DATA:
> +		seq_nmb = (buffer[23] << 4) | ((buffer[22] & 0xF0) >> 4);

No magic numbers, please.

> +		if (seq_nmb < min_exp_seq_nmb &&
> +		    ((min_exp_seq_nmb - seq_nmb) < 3000))
> +			dev_dbg(purelifi_mac_dev(mac), "seq_nmb < min_exp\n");
> +		else
> +			min_exp_seq_nmb = (seq_nmb + 1) % 4096;
> +		break;

Here are also 3000 and 4096.

> +static const struct ieee80211_ops purelifi_ops = {
> +	.tx                 = purelifi_op_tx,
> +	.start              = purelifi_op_start,
> +	.stop               = purelifi_op_stop,
> +	.add_interface      = purelifi_op_add_interface,
> +	.remove_interface   = purelifi_op_remove_interface,
> +	.set_rts_threshold  = purelifi_set_rts_threshold,
> +	.config             = purelifi_op_config,
> +	.configure_filter   = purelifi_op_configure_filter,
> +	.bss_info_changed   = purelifi_op_bss_info_changed,
> +	.get_stats          = purelifi_get_stats,
> +	.get_et_sset_count  = purelifi_get_et_sset_count,
> +	.get_et_stats       = purelifi_get_et_stats,
> +	.get_et_strings     = purelifi_get_et_strings,

AFAICS, purelife is the vendor and plfxlc is the driver, so you should
"plfxlc_" as the prefix here.

> +	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
> +	/* 4 for 32 bit alignment if no tailroom */

Move the comment before the actual code.

> +#define purelifi_mac_dev(mac) (purelifi_chip_dev(&(mac)->chip))

Isn't the parenthesis unnecessary?

> +
> +#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
> +#define PURELIFI_MAC_MAX_ACK_WAITERS 50
> +
> +struct purelifi_ctrlset {
> +	__be32		id;/*should be usb_req_enum_t*/

Move the comment to a separate line, above this line. And fix the
comment format.

> +	__be32		len;
> +	u8              modulation;
> +	u8              control;
> +	u8              service;
> +	u8		pad;
> +	__le16		packet_length;
> +	__le16		current_length;
> +	__le16		next_frame_length;
> +	__le16		tx_length;
> +	__be32		payload_len_nw;
> +} __packed;
> +
> +/*overlay*/

Fix comment format.

> +	/* whether to pass frames with CRC errors to stack */
> +	unsigned int pass_failed_fcs:1;
> +
> +	/* whether to pass control frames to stack */
> +	unsigned int pass_ctrl:1;
> +
> +	/* whether we have received a 802.11 ACK that is pending */
> +	bool ack_pending;

Inconsistent use of flags/booleans, either use unsigned int or bool.

> +static atomic_t data_queue_flag;

No static variables.

> +/*Tx retry backoff timer (in milliseconds).*/

I'm going to stop commenting about comment format now, you should know
by now.

> +# define TX_RETRY_BACKOFF_MS 10
> +# define STA_QUEUE_CLEANUP_MS 5000

No space after #. Applies to all usage in the driver.


> +static struct usb_device_id usb_ids[] = {

static const

> +	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
> +	  .driver_info = DEVICE_LIFI_X },
> +	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
> +	  .driver_info = DEVICE_LIFI_XC },
> +	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
> +	  .driver_info = DEVICE_LIFI_XL },
> +	{}
> +};
> +
> +static struct usb_interface *ez_usb_interface;

No static variables.

> +#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
> +#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
> +#define STATION_CONNECT_MESSAGE              2
> +#define STATION_DISCONNECT_MESSAGE           3

Move to a .h file.

> +static void rx_urb_complete(struct urb *urb)
> +{
> +	int r;
> +	static u8 retry;

No static variables.

> +	struct purelifi_usb *usb;
> +	struct purelifi_usb_tx *tx;
> +	const u8 *buffer;
> +	static u8 fpga_link_connection_f;

Ditto.

> +	unsigned int length;
> +	u16 status;
> +	u8 sidx;
> +
> +	if (!urb) {
> +		dev_err(purelifi_usb_dev(usb), "urb is NULL.\n");
> +		return;
> +	} else if (!urb->context) {
> +		dev_err(purelifi_usb_dev(usb), "urb ctx is NULL.\n");
> +		return;
> +	}
> +	usb = urb->context;
> +
> +	if (usb->initialized != 1)
> +		return;
> +
> +	switch (urb->status) {
> +	case 0:
> +		break;
> +	case -ESHUTDOWN:
> +	case -EINVAL:
> +	case -ENODEV:
> +	case -ENOENT:
> +	case -ECONNRESET:
> +	case -EPIPE:
> +		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
> +		return;
> +	default:
> +		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
> +		if (retry++ < PURELIFI_URB_RETRY_MAX) {
> +			dev_dbg(urb_dev(urb), "urb %p resubmit %d", urb, retry);
> +			goto resubmit;
> +		} else {
> +			dev_dbg(urb_dev(urb), "urb %p  max resubmits reached", urb);
> +			retry = 0;
> +			return;
> +		}
> +	}
> +
> +	buffer = urb->transfer_buffer;
> +	length = (*(u32 *)(buffer + sizeof(struct rx_status)))

This does not look endian safe.

> +		 + sizeof(u32);
> +
> +	tx = &usb->tx;
> +
> +	if (urb->actual_length != 8) {

Magic value.

> +		if (usb->initialized && fpga_link_connection_f)
> +			handle_rx_packet(usb, buffer, length);
> +		goto resubmit;
> +	}
> +
> +	status = buffer[7];

Magic value. A proper struct for these 8 bytes would be the best way to
document and access it.

> +
> +	dev_dbg(&usb->intf->dev, "Recv status=%u\n", status);
> +	dev_dbg(&usb->intf->dev, "Tx packet MAC=%x:%x:%x:%x:%x:%x\n",
> +		buffer[0], buffer[1], buffer[2], buffer[3],
> +		buffer[4], buffer[5]);

Ok, so that struct should start with a mac address.

> +
> +	switch (status) {
> +	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
> +		dev_dbg(&usb->intf->dev,
> +			"FIFO full not packet receipt\n");
> +		tx->mac_fifo_full = 1;
> +		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
> +			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
> +		break;
> +	case STATION_FIFO_ALMOST_FULL_MESSAGE:
> +		dev_dbg(&usb->intf->dev, "FIFO full packet receipt\n");
> +
> +		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
> +			tx->station[sidx].flag &= 0xFD;

Magic value.

> +
> +		purelifi_send_packet_from_data_queue(usb);
> +		break;
> +	case STATION_CONNECT_MESSAGE:
> +		fpga_link_connection_f = 1;

I remember if I said this already, but no "_f" style of naming. What
does it even mean, a flag?

> +	for (i = 0; i < RX_URBS_COUNT; i++) {
> +		r = usb_submit_urb(urbs[i], GFP_KERNEL);
> +		if (r)
> +			goto error_submit;
> +	}
> +
> +	return 0; /*no error return*/

A useless comment.

> +void purelifi_usb_disable_tx(struct purelifi_usb *usb)
> +{
> +	struct purelifi_usb_tx *tx = &usb->tx;
> +	unsigned long flags;
> +
> +	atomic_set(&tx->enabled, 0);

I didn't check, but I suspect you are misusing atomic variables here.
Just use SET_BIT() & co.

> +void tx_urb_complete(struct urb *urb)
> +{
> +	struct sk_buff *skb;
> +	struct ieee80211_tx_info *info;
> +	struct purelifi_usb *usb;
> +
> +	skb = (struct sk_buff *)urb->context;

urb->context is a void pointer, no need to cast it.

> +int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
> +{
> +	int r;
> +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
> +	struct urb *urb;
> +	struct purelifi_usb_tx *tx = &usb->tx;
> +
> +	if (!atomic_read(&tx->enabled)) {

Yeah, just use TEST_BIT().

> +void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
> +		       struct usb_interface *intf)
> +{
> +	memset(usb, 0, sizeof(*usb));
> +	usb->intf = usb_get_intf(intf);
> +	usb_set_intfdata(usb->intf, hw);
> +	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;

I was expecting to set the width in mac.c.

> +static void get_usb_req(struct usb_device *udev, const u8 *buffer,
> +			u32 buffer_len, enum usb_req_enum_t usb_req_id,
> +			struct usb_req_t *usb_req)
> +{
> +	u8 *buffer_dst_p = usb_req->buf;

No "_p" naming either. Is that hungarian notation or what? Anyway, we
don't use that in upstream.

> +static int probe(struct usb_interface *intf,
> +		 const struct usb_device_id *id)
> +{
> +	int r = 0;
> +	struct purelifi_chip *chip;
> +	struct purelifi_usb *usb;
> +	struct purelifi_usb_tx *tx;
> +	struct ieee80211_hw *hw = NULL;
> +	static u8 hw_address[ETH_ALEN];
> +	static u8 serial_number[PURELIFI_SERIAL_LEN];

No static variables.

> +	unsigned int i;
> +
> +	ez_usb_interface = intf;
> +
> +	hw = purelifi_mac_alloc_hw(intf);
> +
> +	if (!hw) {
> +		r = -ENOMEM;
> +		goto error;
> +	}
> +
> +	chip = &purelifi_hw_mac(hw)->chip;
> +	usb = &chip->usb;
> +	tx = &usb->tx;
> +
> +	r = upload_mac_and_serial(intf, hw_address, serial_number);
> +
> +	if (r) {
> +		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
> +		goto error;
> +	}
> +	chip->unit_type = STA;
> +	dev_dbg(&intf->dev, "unit type is station");
> +
> +	r = purelifi_mac_preinit_hw(hw, hw_address);
> +	if (r) {
> +		dev_dbg(&intf->dev, "init mac failed (%d)\n", r);
> +		goto error;
> +	}
> +
> +	r = ieee80211_register_hw(hw);
> +	if (r) {
> +		dev_dbg(&intf->dev, "register device failed (%d)\n", r);
> +		goto error;
> +	}
> +	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));

Looks like a pointless info message to me, please remove.

> +	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
> +				PURELIFI_XL_VENDOR_ID_0) &&
> +	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
> +				PURELIFI_XL_PRODUCT_ID_0)) {
> +		r = download_xl_firmware(intf);
> +	} else {
> +		r = download_fpga(intf);
> +	}
> +	if (r != 0) {
> +		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
> +		goto error;
> +	}
> +
> +	tx->mac_fifo_full = 0;
> +	spin_lock_init(&tx->lock);
> +
> +	msleep(PLF_MSLEEP_TIME);
> +	r = purelifi_usb_init_hw(usb);
> +	if (r < 0) {
> +		dev_dbg(&intf->dev, "usb_init_hw failed (%d)\n", r);
> +		goto error;
> +	}
> +
> +	msleep(PLF_MSLEEP_TIME);
> +	r = purelifi_chip_switch_radio(chip, 1); /* Switch ON Radio */

A pointless comment. You can change the integer to an enum
(PLFXLC_RADIO_ON) if you want to document clearly when it's turned on.

> +	if (r < 0) {
> +		dev_dbg(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
> +		goto error;
> +	}
> +
> +	msleep(PLF_MSLEEP_TIME);
> +	r = purelifi_chip_set_rate(chip, 8);
> +	if (r < 0) {
> +		dev_dbg(&intf->dev, "chip_set_rate failed (%d)\n", r);
> +		goto error;
> +	}
> +
> +	msleep(PLF_MSLEEP_TIME);
> +	r = plf_usb_wreq(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
> +	if (r < 0) {
> +		dev_dbg(&intf->dev, "MAC_WR failure (%d)\n", r);
> +		goto error;
> +	}
> +
> +	purelifi_chip_enable_rxtx(chip);
> +
> +	/* Initialise the data plane Tx queue */
> +	atomic_set(&data_queue_flag, 1);

Misuse of atomic variables again, SET_BIT().

> +static struct workqueue_struct *purelifi_workqueue;

This static variable actually might be ok as it's called only from
usb_init() and usb_exit(). Just rename it to plfxlc_workqueue.

> +static int __init usb_init(void)
> +{
> +	int r;
> +
> +	purelifi_workqueue = create_singlethread_workqueue(driver.name);
> +	if (!purelifi_workqueue) {
> +		pr_err("%s couldn't create workqueue\n", driver.name);
> +		r = -ENOMEM;
> +		goto error;
> +	}
> +
> +	r = usb_register(&driver);
> +	if (r) {
> +		destroy_workqueue(purelifi_workqueue);
> +		pr_err("%s usb_register() failed %d\n", driver.name, r);
> +		return r;
> +	}
> +
> +	pr_debug("Driver initialized :%s\n", driver.name);
> +	return 0;
> +
> +error:
> +	return r;
> +}
> +
> +static void __exit usb_exit(void)
> +{
> +	usb_deregister(&driver);
> +	destroy_workqueue(purelifi_workqueue);
> +	pr_debug("%s %s\n", driver.name, __func__);
> +}
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("USB driver for pureLiFi devices");
> +MODULE_AUTHOR("pureLiFi");
> +MODULE_VERSION("1.0");
> +MODULE_FIRMWARE("plfxlc/lifi-x.bin");
> +MODULE_DEVICE_TABLE(usb, usb_ids);
> +module_init(usb_init);
> +module_exit(usb_exit);

I see that you have MODULE_LICENSE() three times:

chip.c.97:MODULE_LICENSE("GPL");
mac.c.841:MODULE_LICENSE("GPL");
usb.c.1006:MODULE_LICENSE("GPL");

But you only have one module. So remove MODULE_LICENSE() from chip.c and
mac.c, and leave it only here.

> +/* USB interrupt */
> +struct purelifi_usb_interrupt {

A useless comment.

> +	spinlock_t lock;/* spin lock for usb interrupt buffer */

Add space before the comment.

> +	struct urb *urb;
> +	void *buffer;
> +	int interval;
> +};
> +
> +#define RX_URBS_COUNT 5
> +
> +struct purelifi_usb_rx {
> +	spinlock_t lock;/* spin lock for rx urb */

Here too.

> +	struct mutex setup_mutex; /* mutex lockt for rx urb */
> +	u8 fragment[2 * USB_MAX_RX_SIZE];
> +	unsigned int fragment_length;
> +	unsigned int usb_packet_size;
> +	struct urb **urbs;
> +	int urbs_count;
> +};
> +
> +struct station_t {
> +   //  7...3    |    2      |     1     |     0	    |
> +   // Reserved  | Heartbeat | FIFO full | Connected |

No C++ comments, please.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [EXTERNAL] Re: [PATCH] [v15] wireless: Initial driver submission for pureLiFi STA devices
       [not found]         ` <CWLP265MB3217BB5AA5F102629A3AD204E0A19@CWLP265MB3217.GBRP265.PROD.OUTLOOK.COM>
@ 2021-09-21 12:30           ` Kalle Valo
  2021-09-22  7:33             ` Johannes Berg
  0 siblings, 1 reply; 108+ messages in thread
From: Kalle Valo @ 2021-09-21 12:30 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> Thanks for reviewing the patch.
>
>>> +     hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;
>
>> Johannes comment about piggy-backing NL80211_BAND_2GHZ is not yet addressed:
>
>> I agree with Johannes, a Li-Fi driver should not claim to be a regular
>> 2.4 GHz Wi-Fi device.
>
> Yes, I agree, As LiFi is not standardized yet we are using the
> existing wireless frameworks. For now, piggy backing with 2.4GHz is
> seamless for users. We will undertake band and other wider change once
> IEEE 802.11bb is standardized.

I don't see why the IEEE standard needs to be final before adding the
band. Much better to add a band which is in draft stage compared to
giving false information to the user space.

BTW do not use HTML mail, our lists drop those.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [EXTERNAL] Re: [PATCH] [v15] wireless: Initial driver submission for pureLiFi STA devices
  2021-09-21 12:30           ` [EXTERNAL] " Kalle Valo
@ 2021-09-22  7:33             ` Johannes Berg
  2021-09-24 13:27               ` [EXTERNAL] " Srinivasan Raju
  0 siblings, 1 reply; 108+ messages in thread
From: Johannes Berg @ 2021-09-22  7:33 UTC (permalink / raw)
  To: Kalle Valo, Srinivasan Raju
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

On Tue, 2021-09-21 at 15:30 +0300, Kalle Valo wrote:
> > 
> > Yes, I agree, As LiFi is not standardized yet we are using the
> > existing wireless frameworks. For now, piggy backing with 2.4GHz is
> > seamless for users. We will undertake band and other wider change once
> > IEEE 802.11bb is standardized.
> 
> I don't see why the IEEE standard needs to be final before adding the
> band. Much better to add a band which is in draft stage compared to
> giving false information to the user space.

I tend to agree, but looking at the current draft (D0.6), that's ...
vague? Maybe it's obvious to somebody familiar with the technology, but
I really don't understand how 800-1000nm infrared band maps to 21 MHz +
channel offset? Isn't the frequency there a couple hundred THz?

Regardless, if the channelisation plan says 21 MHz + n_ch * 5 MHz, then
I think we can just define NL80211_BAND_LC and the driver advertises
those channels - that even gets you easy access to all the defined
channels (apparently today all the odd channels from 1-61, split into
20/40/80/160 MHz bandwidth).

I guess I'm really not sure how that maps to the actual infrared, but
reusing all the 20/40/80/160 machinery from VHT means we can actually do
a lot of things in mac80211/etc. without much changes, which isn't bad.

Anyway, I'd feel more comfortable defining an LC band here, even if it
potentially changes later. Or maybe especially if the actual channels
there change later.

johannes


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v15] wireless: Initial driver submission for pureLiFi STA devices
  2021-08-18 14:13     ` [PATCH] [v15] " Srinivasan Raju
  2021-09-20 13:05       ` Kalle Valo
  2021-09-20 14:11       ` Kalle Valo
@ 2021-09-24 11:11       ` Kalle Valo
  2 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2021-09-24 11:11 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
>
> This driver implementation has been based on the zd1211rw driver.
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
>
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

I applied this to the pending branch for build testing and so far one
issue was found:

https://lkml.kernel.org/r/202109210010.Ph7Wxg7N-lkp@intel.com

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v16] wireless: Initial driver submission for pureLiFi LiFi Station
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (10 preceding siblings ...)
  2021-02-26 13:07   ` [PATCH] [v14] " Srinivasan Raju
@ 2021-09-24 13:26   ` Srinivasan Raju
  2021-09-24 13:40     ` Kalle Valo
  2021-10-05 11:22   ` [PATCH] [v17] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
                     ` (7 subsequent siblings)
  19 siblings, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2021-09-24 13:26 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

LiFi is a mobile wireless technology that uses light
rather than radio frequencies to transmit data.

802.11 bb is focused on introducing necessary changes to
IEEE 802.11 Stds to enable communications in the light medium

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

---
v16:
 - Fixed atomic variable misuses
 - Fixed comments spacing
 - Removed static variables used
 - Moved #defines to header file
 - Removed doxygen style comments
 - Removed magic numbers and cleanup code
 - Fixed warning reported by kernel test robot
v15:
 - resubmit v14 of the patch
v14:
 - Endianess comments addressed
 - Sparse checked and fixed warnings
 - Firmware files renamed to lowercase
 - All other review comments in v13 addressed
v13:
- Removed unused #defines
v12:
- Removed sysfs, procfs related code
- Addressed race condition bug
- Used macros instead of magic numbers in firmware.c
- Added copyright in all files
v11, v10:
- Addressed review comment on readability
- Changed firmware names to match products
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging
---
 MAINTAINERS                                   |   6 +
 drivers/net/wireless/Kconfig                  |   1 +
 drivers/net/wireless/Makefile                 |   1 +
 drivers/net/wireless/purelifi/Kconfig         |  17 +
 drivers/net/wireless/purelifi/Makefile        |   2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |  14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |   3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |  95 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |  89 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   | 271 +++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |  52 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    | 773 ++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    | 190 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 975 ++++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    | 196 ++++
 15 files changed, 2685 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 492bc169c3bd..1fd6f64c614c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15040,6 +15040,12 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PURELIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+L:	linux-wireless@vger.kernel.org
+S:	Supported
+F:	drivers/net/wireless/purelifi/
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7add2002ff4c..404afe574920 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -28,6 +28,7 @@ source "drivers/net/wireless/intersil/Kconfig"
 source "drivers/net/wireless/marvell/Kconfig"
 source "drivers/net/wireless/mediatek/Kconfig"
 source "drivers/net/wireless/microchip/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 source "drivers/net/wireless/ralink/Kconfig"
 source "drivers/net/wireless/realtek/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e3345893c9c5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
 obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
 obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
 obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
 obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
 obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..e39afec3dcae
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+source "drivers/net/wireless/purelifi/plfxlc/Kconfig"
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..56ebf96bd298
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)		:= plfxlc/
diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
new file mode 100644
index 000000000000..400ab2ee660c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config PURELIFI_XLC
+	tristate "pureLiFi X, XL, XC device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This option adds support for pureLiFi LiFi wireless USB adapters.
+
+	   The pureLiFi X, XL, XC USB devices are based on 802.11 OFDM PHY.
+
+	   Supports common 802.11 encryption/authentication methods including
+	   Open, WPA, WPA2-Personal and WPA2-Enterprise (802.1X).
+
+	   To compile this driver as a module, choose m here. The module will
+	   be called purelifi_xlc.
diff --git a/drivers/net/wireless/purelifi/plfxlc/Makefile b/drivers/net/wireless/purelifi/plfxlc/Makefile
new file mode 100644
index 000000000000..3d66f485c024
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)	:= purelifi_xlc.o
+purelifi_xlc-objs 		+= chip.o firmware.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.c b/drivers/net/wireless/purelifi/plfxlc/chip.c
new file mode 100644
index 000000000000..ece74682d413
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set &&
+	     le16_to_cpu(chip->beacon_interval) == interval))
+		return 0;
+
+	chip->beacon_interval = cpu_to_le16(interval);
+	chip->beacon_set = true;
+	return plf_usb_wreq(&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	unsigned char *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %04x:%04x v%02x %pM %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr,
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value)
+{
+	int r;
+	__le16 radio_on = cpu_to_le16(value);
+
+	r = plf_usb_wreq(&radio_on, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	u8 value = 0;
+
+	plf_usb_wreq(&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = plf_usb_wreq(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.h b/drivers/net/wireless/purelifi/plfxlc/chip.h
new file mode 100644
index 000000000000..167e9b9bd7a7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type {
+	STA = 0,
+	AP = 1,
+};
+
+enum {
+	PLFXLC_RADIO_OFF = 0,
+	PLFXLC_RADIO_ON = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type unit_type;
+	u16 link_led;
+	u8 beacon_set;
+	__le16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low  = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1U << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/plfxlc/firmware.c b/drivers/net/wireless/purelifi/plfxlc/firmware.c
new file mode 100644
index 000000000000..884751bd5a1a
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/firmware.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/firmware.h>
+#include <linux/bitrev.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+	int r, actual_length;
+	int fw_data_i, blk_tran_len = PLF_BULK_TLEN;
+	const char *fw_name;
+	unsigned char *fpga_dmabuff = NULL;
+	unsigned char *fw_data;
+	const struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-x.bin";
+		dev_dbg(&intf->dev, "bin file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-xc.bin";
+		dev_dbg(&intf->dev, "bin file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware(&fw, fw_name, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATUS_LEN, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ,
+			    fpga_dmabuff, PLF_FPGA_STATUS_LEN);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_SET_CMD, NULL, 0);
+
+	if (fpga_dmabuff[0] != PLF_FPGA_MG) {
+		dev_err(&intf->dev, "fpga_dmabuff[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			/* u8 bit reverse */
+			fw_data[tbuf_idx] = bitrev8(fw_data[tbuf_idx]);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_dmabuff[0] & 0xff),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2 * PLF_USB_TIMEOUT);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	kfree(fpga_dmabuff);
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATE_LEN, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xff, PLF_FPGA_STATE_LEN);
+
+	send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
+			    PLF_FPGA_STATE_LEN);
+
+	dev_dbg(&intf->dev, "%*ph\n", 8, fpga_dmabuff);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
+
+	msleep(PLF_MSLEEP_TIME);
+
+error_free_fw:
+	kfree(fpga_dmabuff);
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+	const struct firmware *fwp = NULL;
+	struct firmware_file file = {0};
+	int s, r;
+	u8 *buf;
+	u32 i;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
+	msleep(PLF_MSLEEP_TIME);
+
+	if (r) {
+		dev_err(&intf->dev, "vendor command failed (%d)\n", r);
+		return -EINVAL;
+	}
+	/* Code for single pack file download */
+
+	fw_pack = "plfxlc/lifi-xl.bin";
+
+	r = request_firmware(&fwp, fw_pack, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "Request_firmware failed (%d)\n", r);
+		return -EINVAL;
+	}
+	file.total_files = get_unaligned_le32(&fwp->data[0]);
+	file.total_size = get_unaligned_le32(&fwp->size);
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n",
+		file.total_files, file.total_size);
+
+	buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
+	if (!buf) {
+		release_firmware(fwp);
+		return -ENOMEM;
+	}
+
+	if (file.total_files > 10) {
+		dev_err(&intf->dev, "Too many files (%d)\n", file.total_files);
+		release_firmware(fwp);
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	/* Download firmware files in multiple steps */
+	for (s = 0; s < file.total_files; s++) {
+		buf[0] = s;
+		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
+					PLF_XL_BUF_LEN);
+
+		if (s < file.total_files - 1)
+			file.size = get_unaligned_le32(&fwp->data[4 + ((s + 1) * 4)])
+				    - get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+		else
+			file.size = file.total_size -
+				    get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+
+		if (file.size > file.total_size || file.size > 60000) {
+			dev_err(&intf->dev, "File size is too large (%d)\n", file.size);
+			break;
+		}
+
+		file.start_addr = get_unaligned_le32(&fwp->data[4 + (s * 4)]);
+
+		if (file.size % PLF_XL_BUF_LEN && s < 2)
+			file.size += PLF_XL_BUF_LEN - file.size % PLF_XL_BUF_LEN;
+
+		file.control_packets = file.size / PLF_XL_BUF_LEN;
+
+		for (i = 0; i < file.control_packets; i++) {
+			memcpy(buf,
+			       &fwp->data[file.start_addr + (i * PLF_XL_BUF_LEN)],
+			       PLF_XL_BUF_LEN);
+			r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
+						PLF_XL_BUF_LEN);
+		}
+		dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", s, r,
+			file.size);
+	}
+	release_firmware(fwp);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
+	dev_dbg(&intf->dev, "Download fpga (4) (%d)\n", r);
+
+	return 0;
+}
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);
+	if (!dma_buffer)
+		return -ENOMEM;
+
+	BUILD_BUG_ON(ETH_ALEN > PLF_SERIAL_LEN);
+	BUILD_BUG_ON(PLF_FW_VER_LEN > PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	memcpy(serial_number, dma_buffer, PLF_SERIAL_LEN);
+
+	memset(dma_buffer, 0x00, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, PLF_FW_VER_LEN);
+
+	memcpy(&firmware_version, dma_buffer, PLF_FW_VER_LEN);
+
+	dev_info(&intf->dev, "Firmware Version: %llu\n", firmware_version);
+	kfree(dma_buffer);
+
+	dev_dbg(&intf->dev, "Mac: %pM\n", hw_address);
+
+	return 0;
+}
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/intf.h b/drivers/net/wireless/purelifi/plfxlc/intf.h
new file mode 100644
index 000000000000..5ae89343b579
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/intf.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#define ETH_ALEN 6
+#define AP_USER_LIMIT 8
+
+#define PLF_VNDR_FPGA_STATE_REQ 0x30
+#define PLF_VNDR_FPGA_SET_REQ 0x33
+#define PLF_VNDR_FPGA_SET_CMD 0x34
+#define PLF_VNDR_FPGA_STATE_CMD 0x35
+
+#define PLF_VNDR_XL_FW_CMD 0x80
+#define PLF_VNDR_XL_DATA_CMD 0x81
+#define PLF_VNDR_XL_FILE_CMD 0x82
+#define PLF_VNDR_XL_EX_CMD 0x83
+
+#define PLF_MAC_VENDOR_REQUEST 0x36
+#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define PLF_SERIAL_LEN 14
+#define PLF_FW_VER_LEN 8
+
+struct rx_status {
+	__be16 rssi;
+	u8     rate_idx;
+	u8     pad;
+	__be64 crc_error_count;
+} __packed;
+
+enum plf_usb_req_enum {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct plf_usb_req {
+	__be32         id; /* should be plf_usb_req_enum */
+	__be32	       len;
+	u8             buf[512];
+};
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
new file mode 100644
index 000000000000..4d322f38e5cb
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.c
@@ -0,0 +1,773 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 }
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	regulatory_hint(hw->wiphy, "00");
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int plfxlc_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void plfxlc_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status)
+		success = !tx_status->failure;
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon)
+{
+	return plf_usb_wreq(beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	u32 temp_len = 0;
+	u32 temp_payload_len = 0;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_dbg(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (void *)skb_push(skb, sizeof(struct purelifi_ctrlset));
+	temp_payload_len = frag_len;
+	temp_len = temp_payload_len +
+		  sizeof(struct purelifi_ctrlset) -
+		  sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/* check if 32 bit aligned and align data */
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memmove(dest_pt, src_pt, len);
+			} else {
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		temp_len += 4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data */
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen because
+				 * sufficient headroom was reserved
+				 */
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		temp_len += 4;
+	}
+
+	cs->id = cpu_to_be32(USB_REQ_DATA_TX);
+	cs->len = cpu_to_be32(temp_len);
+	cs->payload_len_nw = cpu_to_be32(temp_payload_len);
+
+	return 0;
+}
+
+static void plfxlc_op_tx(struct ieee80211_hw *hw,
+			 struct ieee80211_tx_control *control,
+			 struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct purelifi_header *plhdr = (void *)skb->data;
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+
+	info->rate_driver_data[0] = hw;
+
+	if (plhdr->frametype  == IEEE80211_FTYPE_DATA) {
+		u8 *dst_mac = plhdr->dmac;
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			goto fail;
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = plf_usb_wreq_async(&mac->chip.usb, skb->data, skb->len,
+				       USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+static int purelifi_filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+			       struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	bool found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_dbg(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_2GHZ;
+	mac->rssi      = -15 * be16_to_cpu(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	mac->crc_errors = be64_to_cpu(status->crc_error_count);
+
+	if (!bad_frame &&
+	    purelifi_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = get_unaligned_be32(buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	switch (buffer[0]) {
+	case IEEE80211_STYPE_PROBE_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
+		break;
+	case IEEE80211_STYPE_ASSOC_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Association request\n");
+		break;
+	case IEEE80211_STYPE_AUTH:
+		dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+		break;
+	case IEEE80211_FTYPE_DATA:
+		dev_dbg(purelifi_mac_dev(mac), "802.11 data frame\n");
+		break;
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+
+	if (need_padding)
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+
+	skb_put_data(skb, buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int plfxlc_op_add_interface(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_dbg(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_dbg(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void plfxlc_op_remove_interface(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int plfxlc_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void plfxlc_op_configure_filter(struct ieee80211_hw *hw,
+				       unsigned int changed_flags,
+				       unsigned int *new_flags,
+				       u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by plfxlc_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void plfxlc_op_bss_info_changed(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *bss_conf,
+				       u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_dbg(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+static const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops plfxlc_ops = {
+	.tx                 = plfxlc_op_tx,
+	.start              = plfxlc_op_start,
+	.stop               = plfxlc_op_stop,
+	.add_interface      = plfxlc_op_add_interface,
+	.remove_interface   = plfxlc_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = plfxlc_op_config,
+	.configure_filter   = plfxlc_op_configure_filter,
+	.bss_info_changed   = plfxlc_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &plfxlc_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+	/* TODO should use NL80211_BAND_LC */
+	hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+
+	ieee80211_hw_set(hw, RX_INCLUDES_FCS);
+	ieee80211_hw_set(hw, SIGNAL_DBM);
+	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+	ieee80211_hw_set(hw, MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+	hw->max_signal = 100;
+	hw->queues = 1;
+	/* 4 for 32 bit alignment if no tailroom */
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.h b/drivers/net/wireless/purelifi/plfxlc/mac.h
new file mode 100644
index 000000000000..3852d7343a40
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.h
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) purelifi_chip_dev(&(mac)->chip)
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	/* id should be plf_usb_req_enum */
+	__be32		id;
+	__be32		len;
+	u8              modulation;
+	u8              control;
+	u8              service;
+	u8		pad;
+	__le16		packet_length;
+	__le16		current_length;
+	__le16		next_frame_length;
+	__le16		tx_length;
+	__be32		payload_len_nw;
+} __packed;
+
+/* overlay */
+struct purelifi_header {
+	struct purelifi_ctrlset plf_ctrl;
+	u32    frametype;
+	u8    *dmac;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	bool pass_failed_fcs;
+
+	/* whether to pass control frames to stack */
+	bool pass_ctrl;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[PURELIFI_SERIAL_LEN];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *
+purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *
+purelifi_chip_to_mac(struct purelifi_chip *chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *
+purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int plfxlc_op_start(struct ieee80211_hw *hw);
+void plfxlc_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
new file mode 100644
index 000000000000..8cd9f223025e
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -0,0 +1,975 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+struct usb_interface *ez_usb_interface;
+
+static const struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		plf_usb_wreq_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				   tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		pr_err("urb is NULL\n");
+		return;
+	} else if (!urb->context) {
+		pr_err("urb ctx is NULL\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1) {
+		pr_err("usb is not initialized\n");
+		return;
+	}
+
+	tx = &usb->tx;
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		if (tx->submitted_urbs++ < PURELIFI_URB_RETRY_MAX) {
+			dev_dbg(urb_dev(urb), "urb %p resubmit %d", urb,
+				tx->submitted_urbs++);
+			goto resubmit;
+		} else {
+			dev_dbg(urb_dev(urb), "urb %p  max resubmits reached", urb);
+			tx->submitted_urbs = 0;
+			return;
+		}
+	}
+
+	buffer = urb->transfer_buffer;
+	length = le32_to_cpu(*(__le32 *)(buffer + sizeof(struct rx_status)))
+		 + sizeof(u32);
+
+	if (urb->actual_length != (PLF_MSG_STATUS_OFFSET + 1)) {
+		if (usb->initialized && usb->link_up)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[PLF_MSG_STATUS_OFFSET];
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_dbg(&usb->intf->dev,
+			"FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_dbg(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= STATION_ACTIVE_FLAG;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		usb->link_up = 1;
+		dev_dbg(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		usb->link_up = 0;
+		dev_dbg(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_dbg(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit fail (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0;
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		usb->rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	usb->rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	set_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!test_bit(PLF_BIT_ENABLED, &tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	plfxlc_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, void *buffer,
+			u32 buffer_len, enum plf_usb_req_enum usb_req_id,
+			struct plf_usb_req *usb_req)
+{
+	u8 *buffer_dst = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	__be32 payload_len_nw = cpu_to_be32(buffer_len + FCS_LEN);
+	u32 temp_usb_len = 0;
+
+	usb_req->id = cpu_to_be32(usb_req_id);
+	usb_req->len  = cpu_to_be32(0);
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == cpu_to_be32(USB_REQ_BEACON_WR)) {
+		memcpy(buffer_dst, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst += sizeof(payload_len_nw);
+		temp_usb_len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst, buffer_src_p, buffer_len);
+	buffer_dst += buffer_len;
+	buffer_src_p += buffer_len;
+	temp_usb_len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst, 0, FCS_LEN);
+	buffer_dst += FCS_LEN;
+	temp_usb_len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (temp_usb_len %
+			PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len %
+				PURELIFI_BYTE_NUM_ALIGNMENT);
+		temp_usb_len += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->len = cpu_to_be32(temp_usb_len);
+}
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn,
+		       void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "Async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct plf_usb_req usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(__le32) + sizeof(__le32) +
+			   be32_to_cpu(usb_req.len);
+
+	dma_buffer = kmemdup(&usb_req, usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	u8 hw_address[ETH_ALEN];
+	u8 serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+	dev_err(&intf->dev, "Unit type is station");
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_err(&intf->dev, "Init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_err(&intf->dev, "Register device failed (%d)\n", r);
+		goto error;
+	}
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_err(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_switch_radio(chip, PLFXLC_RADIO_ON);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plf_usb_wreq(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	dev_err(&intf->dev, "pureLifi:Device error");
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = plfxlc_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"Restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	plfxlc_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+#ifdef CONFIG_PM
+	.suspend        = suspend,
+	.resume         = resume,
+#endif
+	.disable_hub_initiated_lpm = 1,
+};
+
+static struct workqueue_struct *plfxlc_workqueue;
+
+static int __init usb_init(void)
+{
+	int r;
+
+	plfxlc_workqueue = create_singlethread_workqueue(driver.name);
+	if (!plfxlc_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(plfxlc_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	usb_deregister(&driver);
+	destroy_workqueue(plfxlc_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("plfxlc/lifi-x.bin");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.h b/drivers/net/wireless/purelifi/plfxlc/usb.h
new file mode 100644
index 000000000000..b44d446d87ee
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#define PLF_FPGA_STATUS_LEN 2
+#define PLF_FPGA_STATE_LEN 9
+#define PLF_BULK_TLEN 16384
+#define PLF_FPGA_MG 6 /* Magic check */
+#define PLF_XL_BUF_LEN 64
+#define PLF_MSG_STATUS_OFFSET 7
+
+#define PLF_USB_TIMEOUT 1000
+#define PLF_MSLEEP_TIME 200
+
+#define PURELIFI_URB_RETRY_MAX 5
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+
+/* Tx retry backoff timer (in milliseconds) */
+#define TX_RETRY_BACKOFF_MS 10
+#define STA_QUEUE_CLEANUP_MS 5000
+
+/* Tx retry backoff timer (in jiffies) */
+#define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+#define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+struct purelifi_usb_interrupt {
+	spinlock_t lock; /* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock; /* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct plf_station {
+   /*  7...3    |    2      |     1     |     0	    |
+    * Reserved  | Heartbeat | FIFO full | Connected |
+    */
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+struct firmware_file {
+	u32 total_files;
+	u32 total_size;
+	u32 size;
+	u32 start_addr;
+	u32 control_packets;
+} __packed;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+#define STATION_ACTIVE_FLAG    0xFD
+
+#define PURELIFI_SERIAL_LEN 256
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+
+struct purelifi_usb_tx {
+	unsigned long enabled;
+	spinlock_t lock; /* spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	bool stopped;
+	struct timer_list tx_retry_timer;
+	struct plf_station station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	bool rx_usb_enabled;
+	bool initialized;
+	bool was_running;
+	bool link_up;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+enum {
+	PLF_BIT_ENABLED = 1,
+	PLF_BIT_MAX = 2,
+};
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn, void *context);
+
+static inline struct usb_device *
+purelifi_usb_to_usbdev(struct purelifi_usb *usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_intf_to_hw(struct usb_interface *intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_usb_to_hw(struct purelifi_usb *usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/* Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [EXTERNAL] Re: [EXTERNAL] Re: [PATCH] [v15] wireless: Initial driver submission for pureLiFi STA devices
  2021-09-22  7:33             ` Johannes Berg
@ 2021-09-24 13:27               ` Srinivasan Raju
  0 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-09-24 13:27 UTC (permalink / raw)
  To: Johannes Berg, Kalle Valo
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

> >
> > Yes, I agree, As LiFi is not standardized yet we are using the
> > existing wireless frameworks. For now, piggy backing with 2.4GHz is
> > seamless for users. We will undertake band and other wider change once
> > IEEE 802.11bb is standardized.
>
> I don't see why the IEEE standard needs to be final before adding the
> band. Much better to add a band which is in draft stage compared to
> giving false information to the user space.

> I tend to agree, but looking at the current draft (D0.6), that's ...
> vague? Maybe it's obvious to somebody familiar with the technology, but
> I really don't understand how 800-1000nm infrared band maps to 21 MHz +
> channel offset? Isn't the frequency there a couple hundred THz?

> Regardless, if the channelisation plan says 21 MHz + n_ch * 5 MHz, then
> I think we can just define NL80211_BAND_LC and the driver advertises
> those channels - that even gets you easy access to all the defined
> channels (apparently today all the odd channels from 1-61, split into
> 20/40/80/160 MHz bandwidth).

> I guess I'm really not sure how that maps to the actual infrared, but
> reusing all the 20/40/80/160 machinery from VHT means we can actually do
> a lot of things in mac80211/etc. without much changes, which isn't bad.

> Anyway, I'd feel more comfortable defining an LC band here, even if it
> potentially changes later. Or maybe especially if the actual channels
> there change later.

Thanks, I have submitted next version of the patch. I will study how to define NL80211_BAND_LC and other changes/tests required.
I will also consider other points mentioned and will reply / update the patch (or send addional pathces).

--Srini

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v16] wireless: Initial driver submission for pureLiFi LiFi Station
  2021-09-24 13:26   ` [PATCH] [v16] wireless: Initial driver submission for pureLiFi LiFi Station Srinivasan Raju
@ 2021-09-24 13:40     ` Kalle Valo
  0 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2021-09-24 13:40 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
>
> LiFi is a mobile wireless technology that uses light
> rather than radio frequencies to transmit data.
>
> 802.11 bb is focused on introducing necessary changes to
> IEEE 802.11 Stds to enable communications in the light medium
>
> This driver implementation has been based on the zd1211rw driver.
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
>
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
>
> ---
> v16:
>  - Fixed atomic variable misuses
>  - Fixed comments spacing
>  - Removed static variables used
>  - Moved #defines to header file
>  - Removed doxygen style comments
>  - Removed magic numbers and cleanup code
>  - Fixed warning reported by kernel test robot

Just FYI I'm not going to review this version, I'll wait for the version
with the new band.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v17] wireless: Initial driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (11 preceding siblings ...)
  2021-09-24 13:26   ` [PATCH] [v16] wireless: Initial driver submission for pureLiFi LiFi Station Srinivasan Raju
@ 2021-10-05 11:22   ` Srinivasan Raju
  2021-10-05 11:26     ` Johannes Berg
  2021-10-05 12:30   ` [PATCH] [v18 1/2] nl80211: Add LC placeholder band definition to enum nl80211_band Srinivasan Raju
                     ` (6 subsequent siblings)
  19 siblings, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2021-10-05 11:22 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Johannes Berg, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

LiFi is a mobile wireless technology that uses light
rather than radio frequencies to transmit data.

802.11 bb is focused on introducing necessary changes to
IEEE 802.11 Stds to enable communications in the light medium

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

---
v17:
 - Add Light communication band
v16:
 - Fixed atomic variable misuses
 - Fixed comments spacing
 - Removed static variables used
 - Moved #defines to header file
 - Removed doxygen style comments
 - Removed magic numbers and cleanup code
v15:
 - resubmit v14 of the patch
v14:
 - Endianess comments addressed
 - Sparse checked and fixed warnings
 - Firmware files renamed to lowercase
 - All other review comments in v13 addressed
v13:
- Removed unused #defines
v12:
- Removed sysfs, procfs related code
- Addressed race condition bug
- Used macros instead of magic numbers in firmware.c
- Added copyright in all files
v11, v10:
- Addressed review comment on readability
- Changed firmware names to match products
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging
---
 MAINTAINERS                                   |   6 +
 drivers/net/wireless/Kconfig                  |   1 +
 drivers/net/wireless/Makefile                 |   1 +
 drivers/net/wireless/purelifi/Kconfig         |  17 +
 drivers/net/wireless/purelifi/Makefile        |   2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |  14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |   3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |  95 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |  89 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   | 271 +++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |  52 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    | 772 ++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    | 190 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 975 ++++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    | 196 ++++
 include/uapi/linux/nl80211.h                  |   1 +
 net/mac80211/mlme.c                           |   1 +
 net/mac80211/sta_info.c                       |   1 +
 net/mac80211/tx.c                             |   3 +-
 net/wireless/nl80211.c                        |   1 +
 net/wireless/util.c                           |   2 +
 21 files changed, 2692 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index eeb4c70b3d5b..1d271e9e9223 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15193,6 +15193,12 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PURELIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+L:	linux-wireless@vger.kernel.org
+S:	Supported
+F:	drivers/net/wireless/purelifi/
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7add2002ff4c..404afe574920 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -28,6 +28,7 @@ source "drivers/net/wireless/intersil/Kconfig"
 source "drivers/net/wireless/marvell/Kconfig"
 source "drivers/net/wireless/mediatek/Kconfig"
 source "drivers/net/wireless/microchip/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 source "drivers/net/wireless/ralink/Kconfig"
 source "drivers/net/wireless/realtek/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e3345893c9c5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
 obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
 obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
 obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
 obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
 obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..e39afec3dcae
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+source "drivers/net/wireless/purelifi/plfxlc/Kconfig"
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..56ebf96bd298
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)		:= plfxlc/
diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
new file mode 100644
index 000000000000..400ab2ee660c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config PURELIFI_XLC
+	tristate "pureLiFi X, XL, XC device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This option adds support for pureLiFi LiFi wireless USB adapters.
+
+	   The pureLiFi X, XL, XC USB devices are based on 802.11 OFDM PHY.
+
+	   Supports common 802.11 encryption/authentication methods including
+	   Open, WPA, WPA2-Personal and WPA2-Enterprise (802.1X).
+
+	   To compile this driver as a module, choose m here. The module will
+	   be called purelifi_xlc.
diff --git a/drivers/net/wireless/purelifi/plfxlc/Makefile b/drivers/net/wireless/purelifi/plfxlc/Makefile
new file mode 100644
index 000000000000..3d66f485c024
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)	:= purelifi_xlc.o
+purelifi_xlc-objs 		+= chip.o firmware.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.c b/drivers/net/wireless/purelifi/plfxlc/chip.c
new file mode 100644
index 000000000000..ece74682d413
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set &&
+	     le16_to_cpu(chip->beacon_interval) == interval))
+		return 0;
+
+	chip->beacon_interval = cpu_to_le16(interval);
+	chip->beacon_set = true;
+	return plf_usb_wreq(&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	unsigned char *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %04x:%04x v%02x %pM %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr,
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value)
+{
+	int r;
+	__le16 radio_on = cpu_to_le16(value);
+
+	r = plf_usb_wreq(&radio_on, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	u8 value = 0;
+
+	plf_usb_wreq(&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = plf_usb_wreq(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.h b/drivers/net/wireless/purelifi/plfxlc/chip.h
new file mode 100644
index 000000000000..167e9b9bd7a7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type {
+	STA = 0,
+	AP = 1,
+};
+
+enum {
+	PLFXLC_RADIO_OFF = 0,
+	PLFXLC_RADIO_ON = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type unit_type;
+	u16 link_led;
+	u8 beacon_set;
+	__le16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low  = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1U << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/plfxlc/firmware.c b/drivers/net/wireless/purelifi/plfxlc/firmware.c
new file mode 100644
index 000000000000..884751bd5a1a
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/firmware.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/firmware.h>
+#include <linux/bitrev.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+	int r, actual_length;
+	int fw_data_i, blk_tran_len = PLF_BULK_TLEN;
+	const char *fw_name;
+	unsigned char *fpga_dmabuff = NULL;
+	unsigned char *fw_data;
+	const struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-x.bin";
+		dev_dbg(&intf->dev, "bin file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-xc.bin";
+		dev_dbg(&intf->dev, "bin file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware(&fw, fw_name, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATUS_LEN, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ,
+			    fpga_dmabuff, PLF_FPGA_STATUS_LEN);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_SET_CMD, NULL, 0);
+
+	if (fpga_dmabuff[0] != PLF_FPGA_MG) {
+		dev_err(&intf->dev, "fpga_dmabuff[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			/* u8 bit reverse */
+			fw_data[tbuf_idx] = bitrev8(fw_data[tbuf_idx]);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_dmabuff[0] & 0xff),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2 * PLF_USB_TIMEOUT);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	kfree(fpga_dmabuff);
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATE_LEN, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xff, PLF_FPGA_STATE_LEN);
+
+	send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
+			    PLF_FPGA_STATE_LEN);
+
+	dev_dbg(&intf->dev, "%*ph\n", 8, fpga_dmabuff);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
+
+	msleep(PLF_MSLEEP_TIME);
+
+error_free_fw:
+	kfree(fpga_dmabuff);
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+	const struct firmware *fwp = NULL;
+	struct firmware_file file = {0};
+	int s, r;
+	u8 *buf;
+	u32 i;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
+	msleep(PLF_MSLEEP_TIME);
+
+	if (r) {
+		dev_err(&intf->dev, "vendor command failed (%d)\n", r);
+		return -EINVAL;
+	}
+	/* Code for single pack file download */
+
+	fw_pack = "plfxlc/lifi-xl.bin";
+
+	r = request_firmware(&fwp, fw_pack, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "Request_firmware failed (%d)\n", r);
+		return -EINVAL;
+	}
+	file.total_files = get_unaligned_le32(&fwp->data[0]);
+	file.total_size = get_unaligned_le32(&fwp->size);
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n",
+		file.total_files, file.total_size);
+
+	buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
+	if (!buf) {
+		release_firmware(fwp);
+		return -ENOMEM;
+	}
+
+	if (file.total_files > 10) {
+		dev_err(&intf->dev, "Too many files (%d)\n", file.total_files);
+		release_firmware(fwp);
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	/* Download firmware files in multiple steps */
+	for (s = 0; s < file.total_files; s++) {
+		buf[0] = s;
+		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
+					PLF_XL_BUF_LEN);
+
+		if (s < file.total_files - 1)
+			file.size = get_unaligned_le32(&fwp->data[4 + ((s + 1) * 4)])
+				    - get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+		else
+			file.size = file.total_size -
+				    get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+
+		if (file.size > file.total_size || file.size > 60000) {
+			dev_err(&intf->dev, "File size is too large (%d)\n", file.size);
+			break;
+		}
+
+		file.start_addr = get_unaligned_le32(&fwp->data[4 + (s * 4)]);
+
+		if (file.size % PLF_XL_BUF_LEN && s < 2)
+			file.size += PLF_XL_BUF_LEN - file.size % PLF_XL_BUF_LEN;
+
+		file.control_packets = file.size / PLF_XL_BUF_LEN;
+
+		for (i = 0; i < file.control_packets; i++) {
+			memcpy(buf,
+			       &fwp->data[file.start_addr + (i * PLF_XL_BUF_LEN)],
+			       PLF_XL_BUF_LEN);
+			r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
+						PLF_XL_BUF_LEN);
+		}
+		dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", s, r,
+			file.size);
+	}
+	release_firmware(fwp);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
+	dev_dbg(&intf->dev, "Download fpga (4) (%d)\n", r);
+
+	return 0;
+}
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);
+	if (!dma_buffer)
+		return -ENOMEM;
+
+	BUILD_BUG_ON(ETH_ALEN > PLF_SERIAL_LEN);
+	BUILD_BUG_ON(PLF_FW_VER_LEN > PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	memcpy(serial_number, dma_buffer, PLF_SERIAL_LEN);
+
+	memset(dma_buffer, 0x00, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, PLF_FW_VER_LEN);
+
+	memcpy(&firmware_version, dma_buffer, PLF_FW_VER_LEN);
+
+	dev_info(&intf->dev, "Firmware Version: %llu\n", firmware_version);
+	kfree(dma_buffer);
+
+	dev_dbg(&intf->dev, "Mac: %pM\n", hw_address);
+
+	return 0;
+}
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/intf.h b/drivers/net/wireless/purelifi/plfxlc/intf.h
new file mode 100644
index 000000000000..5ae89343b579
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/intf.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#define ETH_ALEN 6
+#define AP_USER_LIMIT 8
+
+#define PLF_VNDR_FPGA_STATE_REQ 0x30
+#define PLF_VNDR_FPGA_SET_REQ 0x33
+#define PLF_VNDR_FPGA_SET_CMD 0x34
+#define PLF_VNDR_FPGA_STATE_CMD 0x35
+
+#define PLF_VNDR_XL_FW_CMD 0x80
+#define PLF_VNDR_XL_DATA_CMD 0x81
+#define PLF_VNDR_XL_FILE_CMD 0x82
+#define PLF_VNDR_XL_EX_CMD 0x83
+
+#define PLF_MAC_VENDOR_REQUEST 0x36
+#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define PLF_SERIAL_LEN 14
+#define PLF_FW_VER_LEN 8
+
+struct rx_status {
+	__be16 rssi;
+	u8     rate_idx;
+	u8     pad;
+	__be64 crc_error_count;
+} __packed;
+
+enum plf_usb_req_enum {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct plf_usb_req {
+	__be32         id; /* should be plf_usb_req_enum */
+	__be32	       len;
+	u8             buf[512];
+};
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
new file mode 100644
index 000000000000..ba16687803ce
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.c
@@ -0,0 +1,772 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 }
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	regulatory_hint(hw->wiphy, "00");
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int plfxlc_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void plfxlc_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status)
+		success = !tx_status->failure;
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon)
+{
+	return plf_usb_wreq(beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	u32 temp_len = 0;
+	u32 temp_payload_len = 0;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_dbg(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (void *)skb_push(skb, sizeof(struct purelifi_ctrlset));
+	temp_payload_len = frag_len;
+	temp_len = temp_payload_len +
+		  sizeof(struct purelifi_ctrlset) -
+		  sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/* check if 32 bit aligned and align data */
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memmove(dest_pt, src_pt, len);
+			} else {
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		temp_len += 4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data */
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen because
+				 * sufficient headroom was reserved
+				 */
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		temp_len += 4;
+	}
+
+	cs->id = cpu_to_be32(USB_REQ_DATA_TX);
+	cs->len = cpu_to_be32(temp_len);
+	cs->payload_len_nw = cpu_to_be32(temp_payload_len);
+
+	return 0;
+}
+
+static void plfxlc_op_tx(struct ieee80211_hw *hw,
+			 struct ieee80211_tx_control *control,
+			 struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct purelifi_header *plhdr = (void *)skb->data;
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+
+	info->rate_driver_data[0] = hw;
+
+	if (plhdr->frametype  == IEEE80211_FTYPE_DATA) {
+		u8 *dst_mac = plhdr->dmac;
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			goto fail;
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = plf_usb_wreq_async(&mac->chip.usb, skb->data, skb->len,
+				       USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+static int purelifi_filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+			       struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	bool found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_dbg(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_LC;
+	mac->rssi      = -15 * be16_to_cpu(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	mac->crc_errors = be64_to_cpu(status->crc_error_count);
+
+	if (!bad_frame &&
+	    purelifi_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = get_unaligned_be32(buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	switch (buffer[0]) {
+	case IEEE80211_STYPE_PROBE_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
+		break;
+	case IEEE80211_STYPE_ASSOC_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Association request\n");
+		break;
+	case IEEE80211_STYPE_AUTH:
+		dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+		break;
+	case IEEE80211_FTYPE_DATA:
+		dev_dbg(purelifi_mac_dev(mac), "802.11 data frame\n");
+		break;
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+
+	if (need_padding)
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+
+	skb_put_data(skb, buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int plfxlc_op_add_interface(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_dbg(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_dbg(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void plfxlc_op_remove_interface(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int plfxlc_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void plfxlc_op_configure_filter(struct ieee80211_hw *hw,
+				       unsigned int changed_flags,
+				       unsigned int *new_flags,
+				       u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by plfxlc_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void plfxlc_op_bss_info_changed(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *bss_conf,
+				       u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_dbg(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+static const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops plfxlc_ops = {
+	.tx                 = plfxlc_op_tx,
+	.start              = plfxlc_op_start,
+	.stop               = plfxlc_op_stop,
+	.add_interface      = plfxlc_op_add_interface,
+	.remove_interface   = plfxlc_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = plfxlc_op_config,
+	.configure_filter   = plfxlc_op_configure_filter,
+	.bss_info_changed   = plfxlc_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &plfxlc_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+	hw->wiphy->bands[NL80211_BAND_LC] = &mac->band;
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+
+	ieee80211_hw_set(hw, RX_INCLUDES_FCS);
+	ieee80211_hw_set(hw, SIGNAL_DBM);
+	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+	ieee80211_hw_set(hw, MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+	hw->max_signal = 100;
+	hw->queues = 1;
+	/* 4 for 32 bit alignment if no tailroom */
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.h b/drivers/net/wireless/purelifi/plfxlc/mac.h
new file mode 100644
index 000000000000..3852d7343a40
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.h
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) purelifi_chip_dev(&(mac)->chip)
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	/* id should be plf_usb_req_enum */
+	__be32		id;
+	__be32		len;
+	u8              modulation;
+	u8              control;
+	u8              service;
+	u8		pad;
+	__le16		packet_length;
+	__le16		current_length;
+	__le16		next_frame_length;
+	__le16		tx_length;
+	__be32		payload_len_nw;
+} __packed;
+
+/* overlay */
+struct purelifi_header {
+	struct purelifi_ctrlset plf_ctrl;
+	u32    frametype;
+	u8    *dmac;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	bool pass_failed_fcs;
+
+	/* whether to pass control frames to stack */
+	bool pass_ctrl;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[PURELIFI_SERIAL_LEN];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *
+purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *
+purelifi_chip_to_mac(struct purelifi_chip *chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *
+purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int plfxlc_op_start(struct ieee80211_hw *hw);
+void plfxlc_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
new file mode 100644
index 000000000000..8cd9f223025e
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -0,0 +1,975 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+struct usb_interface *ez_usb_interface;
+
+static const struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		plf_usb_wreq_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				   tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		pr_err("urb is NULL\n");
+		return;
+	} else if (!urb->context) {
+		pr_err("urb ctx is NULL\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1) {
+		pr_err("usb is not initialized\n");
+		return;
+	}
+
+	tx = &usb->tx;
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		if (tx->submitted_urbs++ < PURELIFI_URB_RETRY_MAX) {
+			dev_dbg(urb_dev(urb), "urb %p resubmit %d", urb,
+				tx->submitted_urbs++);
+			goto resubmit;
+		} else {
+			dev_dbg(urb_dev(urb), "urb %p  max resubmits reached", urb);
+			tx->submitted_urbs = 0;
+			return;
+		}
+	}
+
+	buffer = urb->transfer_buffer;
+	length = le32_to_cpu(*(__le32 *)(buffer + sizeof(struct rx_status)))
+		 + sizeof(u32);
+
+	if (urb->actual_length != (PLF_MSG_STATUS_OFFSET + 1)) {
+		if (usb->initialized && usb->link_up)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[PLF_MSG_STATUS_OFFSET];
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_dbg(&usb->intf->dev,
+			"FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_dbg(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= STATION_ACTIVE_FLAG;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		usb->link_up = 1;
+		dev_dbg(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		usb->link_up = 0;
+		dev_dbg(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_dbg(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit fail (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0;
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		usb->rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	usb->rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	set_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!test_bit(PLF_BIT_ENABLED, &tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	plfxlc_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, void *buffer,
+			u32 buffer_len, enum plf_usb_req_enum usb_req_id,
+			struct plf_usb_req *usb_req)
+{
+	u8 *buffer_dst = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	__be32 payload_len_nw = cpu_to_be32(buffer_len + FCS_LEN);
+	u32 temp_usb_len = 0;
+
+	usb_req->id = cpu_to_be32(usb_req_id);
+	usb_req->len  = cpu_to_be32(0);
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == cpu_to_be32(USB_REQ_BEACON_WR)) {
+		memcpy(buffer_dst, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst += sizeof(payload_len_nw);
+		temp_usb_len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst, buffer_src_p, buffer_len);
+	buffer_dst += buffer_len;
+	buffer_src_p += buffer_len;
+	temp_usb_len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst, 0, FCS_LEN);
+	buffer_dst += FCS_LEN;
+	temp_usb_len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (temp_usb_len %
+			PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len %
+				PURELIFI_BYTE_NUM_ALIGNMENT);
+		temp_usb_len += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->len = cpu_to_be32(temp_usb_len);
+}
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn,
+		       void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "Async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct plf_usb_req usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(__le32) + sizeof(__le32) +
+			   be32_to_cpu(usb_req.len);
+
+	dma_buffer = kmemdup(&usb_req, usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	u8 hw_address[ETH_ALEN];
+	u8 serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+	dev_err(&intf->dev, "Unit type is station");
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_err(&intf->dev, "Init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_err(&intf->dev, "Register device failed (%d)\n", r);
+		goto error;
+	}
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_err(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_switch_radio(chip, PLFXLC_RADIO_ON);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plf_usb_wreq(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	dev_err(&intf->dev, "pureLifi:Device error");
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = plfxlc_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"Restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	plfxlc_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+#ifdef CONFIG_PM
+	.suspend        = suspend,
+	.resume         = resume,
+#endif
+	.disable_hub_initiated_lpm = 1,
+};
+
+static struct workqueue_struct *plfxlc_workqueue;
+
+static int __init usb_init(void)
+{
+	int r;
+
+	plfxlc_workqueue = create_singlethread_workqueue(driver.name);
+	if (!plfxlc_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(plfxlc_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	usb_deregister(&driver);
+	destroy_workqueue(plfxlc_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("plfxlc/lifi-x.bin");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.h b/drivers/net/wireless/purelifi/plfxlc/usb.h
new file mode 100644
index 000000000000..b44d446d87ee
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#define PLF_FPGA_STATUS_LEN 2
+#define PLF_FPGA_STATE_LEN 9
+#define PLF_BULK_TLEN 16384
+#define PLF_FPGA_MG 6 /* Magic check */
+#define PLF_XL_BUF_LEN 64
+#define PLF_MSG_STATUS_OFFSET 7
+
+#define PLF_USB_TIMEOUT 1000
+#define PLF_MSLEEP_TIME 200
+
+#define PURELIFI_URB_RETRY_MAX 5
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+
+/* Tx retry backoff timer (in milliseconds) */
+#define TX_RETRY_BACKOFF_MS 10
+#define STA_QUEUE_CLEANUP_MS 5000
+
+/* Tx retry backoff timer (in jiffies) */
+#define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+#define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+struct purelifi_usb_interrupt {
+	spinlock_t lock; /* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock; /* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct plf_station {
+   /*  7...3    |    2      |     1     |     0	    |
+    * Reserved  | Heartbeat | FIFO full | Connected |
+    */
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+struct firmware_file {
+	u32 total_files;
+	u32 total_size;
+	u32 size;
+	u32 start_addr;
+	u32 control_packets;
+} __packed;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+#define STATION_ACTIVE_FLAG    0xFD
+
+#define PURELIFI_SERIAL_LEN 256
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+
+struct purelifi_usb_tx {
+	unsigned long enabled;
+	spinlock_t lock; /* spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	bool stopped;
+	struct timer_list tx_retry_timer;
+	struct plf_station station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	bool rx_usb_enabled;
+	bool initialized;
+	bool was_running;
+	bool link_up;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+enum {
+	PLF_BIT_ENABLED = 1,
+	PLF_BIT_MAX = 2,
+};
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn, void *context);
+
+static inline struct usb_device *
+purelifi_usb_to_usbdev(struct purelifi_usb *usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_intf_to_hw(struct usb_interface *intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_usb_to_hw(struct purelifi_usb *usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/* Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+#endif
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index c2efea98e060..fe7fc49a96db 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -4938,6 +4938,7 @@ enum nl80211_band {
 	NL80211_BAND_60GHZ,
 	NL80211_BAND_6GHZ,
 	NL80211_BAND_S1GHZ,
+	NL80211_BAND_LC,
 
 	NUM_NL80211_BANDS,
 };
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index c0ea3b1aa9e1..c577d03ab128 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1490,6 +1490,7 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
 		fallthrough;
 	case NL80211_BAND_2GHZ:
 	case NL80211_BAND_60GHZ:
+	case NL80211_BAND_LC:
 		chan_increment = 1;
 		break;
 	case NL80211_BAND_5GHZ:
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 2b5acb37587f..36524101d11f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -444,6 +444,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
 		switch (i) {
 		case NL80211_BAND_2GHZ:
+		case NL80211_BAND_LC:
 			/*
 			 * We use both here, even if we cannot really know for
 			 * sure the station will support both, but the only use
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 2d1193ed3eb5..d311937f2add 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -146,7 +146,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
 			rate = DIV_ROUND_UP(r->bitrate, 1 << shift);
 
 		switch (sband->band) {
-		case NL80211_BAND_2GHZ: {
+		case NL80211_BAND_2GHZ:
+		case NL80211_BAND_LC: {
 			u32 flag;
 			if (tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
 				flag = IEEE80211_RATE_MANDATORY_G;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index bf7cd4752547..cf1434049abb 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -853,6 +853,7 @@ nl80211_match_band_rssi_policy[NUM_NL80211_BANDS] = {
 	[NL80211_BAND_5GHZ] = { .type = NLA_S32 },
 	[NL80211_BAND_6GHZ] = { .type = NLA_S32 },
 	[NL80211_BAND_60GHZ] = { .type = NLA_S32 },
+	[NL80211_BAND_LC]    = { .type = NLA_S32 },
 };
 
 static const struct nla_policy
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 18dba3d7c638..2991f711491a 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -80,6 +80,7 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
 		return 0; /* not supported */
 	switch (band) {
 	case NL80211_BAND_2GHZ:
+	case NL80211_BAND_LC:
 		if (chan == 14)
 			return MHZ_TO_KHZ(2484);
 		else if (chan < 14)
@@ -209,6 +210,7 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband)
 		WARN_ON(want);
 		break;
 	case NL80211_BAND_2GHZ:
+	case NL80211_BAND_LC:
 		want = 7;
 		for (i = 0; i < sband->n_bitrates; i++) {
 			switch (sband->bitrates[i].bitrate) {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v17] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-05 11:22   ` [PATCH] [v17] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
@ 2021-10-05 11:26     ` Johannes Berg
  0 siblings, 0 replies; 108+ messages in thread
From: Johannes Berg @ 2021-10-05 11:26 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

> 
> ---
> v17:
>  - Add Light communication band

Please do this in a separate patch.

johannes


^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v18 1/2] nl80211: Add LC placeholder band definition to enum nl80211_band
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (12 preceding siblings ...)
  2021-10-05 11:22   ` [PATCH] [v17] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
@ 2021-10-05 12:30   ` Srinivasan Raju
  2021-10-05 12:31   ` [PATCH] [v18 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
                     ` (5 subsequent siblings)
  19 siblings, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-10-05 12:30 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Johannes Berg, David S. Miller,
	Jakub Kicinski, open list:802.11 (including CFG80211/NL80211),
	open list, open list:NETWORKING [GENERAL]

Define LC band which is a draft under IEEE 802.11 bb
Current NL80211_BAND_LC is a placeholder band
The band will be redefined as IEEE 802.11 bb progresses

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
---
 include/uapi/linux/nl80211.h | 2 ++
 net/mac80211/mlme.c          | 1 +
 net/mac80211/sta_info.c      | 1 +
 net/mac80211/tx.c            | 3 ++-
 net/wireless/nl80211.c       | 1 +
 net/wireless/util.c          | 2 ++
 6 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index c2efea98e060..cb06fb604a60 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -4929,6 +4929,7 @@ enum nl80211_txrate_gi {
  * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz)
  * @NL80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.2 GHz)
  * @NL80211_BAND_S1GHZ: around 900MHz, supported by S1G PHYs
+ * @NL80211_BAND_LC: light communication band (placeholder)
  * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
  *	since newer kernel versions may support more bands
  */
@@ -4938,6 +4939,7 @@ enum nl80211_band {
 	NL80211_BAND_60GHZ,
 	NL80211_BAND_6GHZ,
 	NL80211_BAND_S1GHZ,
+	NL80211_BAND_LC,
 
 	NUM_NL80211_BANDS,
 };
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index c0ea3b1aa9e1..c577d03ab128 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1490,6 +1490,7 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
 		fallthrough;
 	case NL80211_BAND_2GHZ:
 	case NL80211_BAND_60GHZ:
+	case NL80211_BAND_LC:
 		chan_increment = 1;
 		break;
 	case NL80211_BAND_5GHZ:
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 2b5acb37587f..36524101d11f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -444,6 +444,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
 		switch (i) {
 		case NL80211_BAND_2GHZ:
+		case NL80211_BAND_LC:
 			/*
 			 * We use both here, even if we cannot really know for
 			 * sure the station will support both, but the only use
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 2d1193ed3eb5..d311937f2add 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -146,7 +146,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
 			rate = DIV_ROUND_UP(r->bitrate, 1 << shift);
 
 		switch (sband->band) {
-		case NL80211_BAND_2GHZ: {
+		case NL80211_BAND_2GHZ:
+		case NL80211_BAND_LC: {
 			u32 flag;
 			if (tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
 				flag = IEEE80211_RATE_MANDATORY_G;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index bf7cd4752547..cf1434049abb 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -853,6 +853,7 @@ nl80211_match_band_rssi_policy[NUM_NL80211_BANDS] = {
 	[NL80211_BAND_5GHZ] = { .type = NLA_S32 },
 	[NL80211_BAND_6GHZ] = { .type = NLA_S32 },
 	[NL80211_BAND_60GHZ] = { .type = NLA_S32 },
+	[NL80211_BAND_LC]    = { .type = NLA_S32 },
 };
 
 static const struct nla_policy
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 18dba3d7c638..2991f711491a 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -80,6 +80,7 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
 		return 0; /* not supported */
 	switch (band) {
 	case NL80211_BAND_2GHZ:
+	case NL80211_BAND_LC:
 		if (chan == 14)
 			return MHZ_TO_KHZ(2484);
 		else if (chan < 14)
@@ -209,6 +210,7 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband)
 		WARN_ON(want);
 		break;
 	case NL80211_BAND_2GHZ:
+	case NL80211_BAND_LC:
 		want = 7;
 		for (i = 0; i < sband->n_bitrates; i++) {
 			switch (sband->bitrates[i].bitrate) {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH] [v18 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (13 preceding siblings ...)
  2021-10-05 12:30   ` [PATCH] [v18 1/2] nl80211: Add LC placeholder band definition to enum nl80211_band Srinivasan Raju
@ 2021-10-05 12:31   ` Srinivasan Raju
  2021-10-05 22:09     ` Jeff Johnson
  2021-10-06 10:04   ` [PATCH] [v19 " Srinivasan Raju
                     ` (4 subsequent siblings)
  19 siblings, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2021-10-05 12:31 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

LiFi is a mobile wireless technology that uses light
rather than radio frequencies to transmit data.

802.11 bb is focused on introducing necessary changes to
IEEE 802.11 to enable communications in the light medium

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

---
v18:
 - Use light communication band
v16:
 - Fixed atomic variable misuses
 - Fixed comments spacing
 - Removed static variables used
 - Moved #defines to header file
 - Removed doxygen style comments
 - Removed magic numbers and cleanup code
v15:
 - resubmit v14 of the patch
v14:
 - Endianess comments addressed
 - Sparse checked and fixed warnings
 - Firmware files renamed to lowercase
 - All other review comments in v13 addressed
v13:
- Removed unused #defines
v12:
- Removed sysfs, procfs related code
- Addressed race condition bug
- Used macros instead of magic numbers in firmware.c
- Added copyright in all files
v11, v10:
- Addressed review comment on readability
- Changed firmware names to match products
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging
---
 MAINTAINERS                                   |   6 +
 drivers/net/wireless/Kconfig                  |   1 +
 drivers/net/wireless/Makefile                 |   1 +
 drivers/net/wireless/purelifi/Kconfig         |  17 +
 drivers/net/wireless/purelifi/Makefile        |   2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |  14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |   3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |  95 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |  89 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   | 271 +++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |  52 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    | 772 ++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    | 190 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 975 ++++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    | 196 ++++
 15 files changed, 2684 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index eeb4c70b3d5b..1d271e9e9223 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15193,6 +15193,12 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PURELIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+L:	linux-wireless@vger.kernel.org
+S:	Supported
+F:	drivers/net/wireless/purelifi/
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7add2002ff4c..404afe574920 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -28,6 +28,7 @@ source "drivers/net/wireless/intersil/Kconfig"
 source "drivers/net/wireless/marvell/Kconfig"
 source "drivers/net/wireless/mediatek/Kconfig"
 source "drivers/net/wireless/microchip/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 source "drivers/net/wireless/ralink/Kconfig"
 source "drivers/net/wireless/realtek/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e3345893c9c5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
 obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
 obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
 obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
 obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
 obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..e39afec3dcae
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+source "drivers/net/wireless/purelifi/plfxlc/Kconfig"
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..56ebf96bd298
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)		:= plfxlc/
diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
new file mode 100644
index 000000000000..400ab2ee660c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config PURELIFI_XLC
+	tristate "pureLiFi X, XL, XC device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This option adds support for pureLiFi LiFi wireless USB adapters.
+
+	   The pureLiFi X, XL, XC USB devices are based on 802.11 OFDM PHY.
+
+	   Supports common 802.11 encryption/authentication methods including
+	   Open, WPA, WPA2-Personal and WPA2-Enterprise (802.1X).
+
+	   To compile this driver as a module, choose m here. The module will
+	   be called purelifi_xlc.
diff --git a/drivers/net/wireless/purelifi/plfxlc/Makefile b/drivers/net/wireless/purelifi/plfxlc/Makefile
new file mode 100644
index 000000000000..3d66f485c024
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)	:= purelifi_xlc.o
+purelifi_xlc-objs 		+= chip.o firmware.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.c b/drivers/net/wireless/purelifi/plfxlc/chip.c
new file mode 100644
index 000000000000..ece74682d413
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set &&
+	     le16_to_cpu(chip->beacon_interval) == interval))
+		return 0;
+
+	chip->beacon_interval = cpu_to_le16(interval);
+	chip->beacon_set = true;
+	return plf_usb_wreq(&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	unsigned char *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %04x:%04x v%02x %pM %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr,
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value)
+{
+	int r;
+	__le16 radio_on = cpu_to_le16(value);
+
+	r = plf_usb_wreq(&radio_on, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	u8 value = 0;
+
+	plf_usb_wreq(&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = plf_usb_wreq(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.h b/drivers/net/wireless/purelifi/plfxlc/chip.h
new file mode 100644
index 000000000000..167e9b9bd7a7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type {
+	STA = 0,
+	AP = 1,
+};
+
+enum {
+	PLFXLC_RADIO_OFF = 0,
+	PLFXLC_RADIO_ON = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type unit_type;
+	u16 link_led;
+	u8 beacon_set;
+	__le16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low  = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1U << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/plfxlc/firmware.c b/drivers/net/wireless/purelifi/plfxlc/firmware.c
new file mode 100644
index 000000000000..884751bd5a1a
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/firmware.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/firmware.h>
+#include <linux/bitrev.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+	int r, actual_length;
+	int fw_data_i, blk_tran_len = PLF_BULK_TLEN;
+	const char *fw_name;
+	unsigned char *fpga_dmabuff = NULL;
+	unsigned char *fw_data;
+	const struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-x.bin";
+		dev_dbg(&intf->dev, "bin file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-xc.bin";
+		dev_dbg(&intf->dev, "bin file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware(&fw, fw_name, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATUS_LEN, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ,
+			    fpga_dmabuff, PLF_FPGA_STATUS_LEN);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_SET_CMD, NULL, 0);
+
+	if (fpga_dmabuff[0] != PLF_FPGA_MG) {
+		dev_err(&intf->dev, "fpga_dmabuff[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			/* u8 bit reverse */
+			fw_data[tbuf_idx] = bitrev8(fw_data[tbuf_idx]);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_dmabuff[0] & 0xff),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2 * PLF_USB_TIMEOUT);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	kfree(fpga_dmabuff);
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATE_LEN, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xff, PLF_FPGA_STATE_LEN);
+
+	send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
+			    PLF_FPGA_STATE_LEN);
+
+	dev_dbg(&intf->dev, "%*ph\n", 8, fpga_dmabuff);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
+
+	msleep(PLF_MSLEEP_TIME);
+
+error_free_fw:
+	kfree(fpga_dmabuff);
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+	const struct firmware *fwp = NULL;
+	struct firmware_file file = {0};
+	int s, r;
+	u8 *buf;
+	u32 i;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
+	msleep(PLF_MSLEEP_TIME);
+
+	if (r) {
+		dev_err(&intf->dev, "vendor command failed (%d)\n", r);
+		return -EINVAL;
+	}
+	/* Code for single pack file download */
+
+	fw_pack = "plfxlc/lifi-xl.bin";
+
+	r = request_firmware(&fwp, fw_pack, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "Request_firmware failed (%d)\n", r);
+		return -EINVAL;
+	}
+	file.total_files = get_unaligned_le32(&fwp->data[0]);
+	file.total_size = get_unaligned_le32(&fwp->size);
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n",
+		file.total_files, file.total_size);
+
+	buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
+	if (!buf) {
+		release_firmware(fwp);
+		return -ENOMEM;
+	}
+
+	if (file.total_files > 10) {
+		dev_err(&intf->dev, "Too many files (%d)\n", file.total_files);
+		release_firmware(fwp);
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	/* Download firmware files in multiple steps */
+	for (s = 0; s < file.total_files; s++) {
+		buf[0] = s;
+		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
+					PLF_XL_BUF_LEN);
+
+		if (s < file.total_files - 1)
+			file.size = get_unaligned_le32(&fwp->data[4 + ((s + 1) * 4)])
+				    - get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+		else
+			file.size = file.total_size -
+				    get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+
+		if (file.size > file.total_size || file.size > 60000) {
+			dev_err(&intf->dev, "File size is too large (%d)\n", file.size);
+			break;
+		}
+
+		file.start_addr = get_unaligned_le32(&fwp->data[4 + (s * 4)]);
+
+		if (file.size % PLF_XL_BUF_LEN && s < 2)
+			file.size += PLF_XL_BUF_LEN - file.size % PLF_XL_BUF_LEN;
+
+		file.control_packets = file.size / PLF_XL_BUF_LEN;
+
+		for (i = 0; i < file.control_packets; i++) {
+			memcpy(buf,
+			       &fwp->data[file.start_addr + (i * PLF_XL_BUF_LEN)],
+			       PLF_XL_BUF_LEN);
+			r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
+						PLF_XL_BUF_LEN);
+		}
+		dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", s, r,
+			file.size);
+	}
+	release_firmware(fwp);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
+	dev_dbg(&intf->dev, "Download fpga (4) (%d)\n", r);
+
+	return 0;
+}
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);
+	if (!dma_buffer)
+		return -ENOMEM;
+
+	BUILD_BUG_ON(ETH_ALEN > PLF_SERIAL_LEN);
+	BUILD_BUG_ON(PLF_FW_VER_LEN > PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	memcpy(serial_number, dma_buffer, PLF_SERIAL_LEN);
+
+	memset(dma_buffer, 0x00, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, PLF_FW_VER_LEN);
+
+	memcpy(&firmware_version, dma_buffer, PLF_FW_VER_LEN);
+
+	dev_info(&intf->dev, "Firmware Version: %llu\n", firmware_version);
+	kfree(dma_buffer);
+
+	dev_dbg(&intf->dev, "Mac: %pM\n", hw_address);
+
+	return 0;
+}
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/intf.h b/drivers/net/wireless/purelifi/plfxlc/intf.h
new file mode 100644
index 000000000000..5ae89343b579
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/intf.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#define ETH_ALEN 6
+#define AP_USER_LIMIT 8
+
+#define PLF_VNDR_FPGA_STATE_REQ 0x30
+#define PLF_VNDR_FPGA_SET_REQ 0x33
+#define PLF_VNDR_FPGA_SET_CMD 0x34
+#define PLF_VNDR_FPGA_STATE_CMD 0x35
+
+#define PLF_VNDR_XL_FW_CMD 0x80
+#define PLF_VNDR_XL_DATA_CMD 0x81
+#define PLF_VNDR_XL_FILE_CMD 0x82
+#define PLF_VNDR_XL_EX_CMD 0x83
+
+#define PLF_MAC_VENDOR_REQUEST 0x36
+#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define PLF_SERIAL_LEN 14
+#define PLF_FW_VER_LEN 8
+
+struct rx_status {
+	__be16 rssi;
+	u8     rate_idx;
+	u8     pad;
+	__be64 crc_error_count;
+} __packed;
+
+enum plf_usb_req_enum {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct plf_usb_req {
+	__be32         id; /* should be plf_usb_req_enum */
+	__be32	       len;
+	u8             buf[512];
+};
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
new file mode 100644
index 000000000000..ba16687803ce
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.c
@@ -0,0 +1,772 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 }
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	regulatory_hint(hw->wiphy, "00");
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int plfxlc_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void plfxlc_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status)
+		success = !tx_status->failure;
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon)
+{
+	return plf_usb_wreq(beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	u32 temp_len = 0;
+	u32 temp_payload_len = 0;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_dbg(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (void *)skb_push(skb, sizeof(struct purelifi_ctrlset));
+	temp_payload_len = frag_len;
+	temp_len = temp_payload_len +
+		  sizeof(struct purelifi_ctrlset) -
+		  sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/* check if 32 bit aligned and align data */
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memmove(dest_pt, src_pt, len);
+			} else {
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		temp_len += 4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data */
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen because
+				 * sufficient headroom was reserved
+				 */
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		temp_len += 4;
+	}
+
+	cs->id = cpu_to_be32(USB_REQ_DATA_TX);
+	cs->len = cpu_to_be32(temp_len);
+	cs->payload_len_nw = cpu_to_be32(temp_payload_len);
+
+	return 0;
+}
+
+static void plfxlc_op_tx(struct ieee80211_hw *hw,
+			 struct ieee80211_tx_control *control,
+			 struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct purelifi_header *plhdr = (void *)skb->data;
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+
+	info->rate_driver_data[0] = hw;
+
+	if (plhdr->frametype  == IEEE80211_FTYPE_DATA) {
+		u8 *dst_mac = plhdr->dmac;
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			goto fail;
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = plf_usb_wreq_async(&mac->chip.usb, skb->data, skb->len,
+				       USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+static int purelifi_filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+			       struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	bool found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_dbg(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_LC;
+	mac->rssi      = -15 * be16_to_cpu(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	mac->crc_errors = be64_to_cpu(status->crc_error_count);
+
+	if (!bad_frame &&
+	    purelifi_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = get_unaligned_be32(buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	switch (buffer[0]) {
+	case IEEE80211_STYPE_PROBE_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
+		break;
+	case IEEE80211_STYPE_ASSOC_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Association request\n");
+		break;
+	case IEEE80211_STYPE_AUTH:
+		dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+		break;
+	case IEEE80211_FTYPE_DATA:
+		dev_dbg(purelifi_mac_dev(mac), "802.11 data frame\n");
+		break;
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+
+	if (need_padding)
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+
+	skb_put_data(skb, buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int plfxlc_op_add_interface(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_dbg(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_dbg(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void plfxlc_op_remove_interface(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int plfxlc_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void plfxlc_op_configure_filter(struct ieee80211_hw *hw,
+				       unsigned int changed_flags,
+				       unsigned int *new_flags,
+				       u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by plfxlc_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void plfxlc_op_bss_info_changed(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *bss_conf,
+				       u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_dbg(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+static const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops plfxlc_ops = {
+	.tx                 = plfxlc_op_tx,
+	.start              = plfxlc_op_start,
+	.stop               = plfxlc_op_stop,
+	.add_interface      = plfxlc_op_add_interface,
+	.remove_interface   = plfxlc_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = plfxlc_op_config,
+	.configure_filter   = plfxlc_op_configure_filter,
+	.bss_info_changed   = plfxlc_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &plfxlc_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+	hw->wiphy->bands[NL80211_BAND_LC] = &mac->band;
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+
+	ieee80211_hw_set(hw, RX_INCLUDES_FCS);
+	ieee80211_hw_set(hw, SIGNAL_DBM);
+	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+	ieee80211_hw_set(hw, MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+	hw->max_signal = 100;
+	hw->queues = 1;
+	/* 4 for 32 bit alignment if no tailroom */
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.h b/drivers/net/wireless/purelifi/plfxlc/mac.h
new file mode 100644
index 000000000000..3852d7343a40
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.h
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) purelifi_chip_dev(&(mac)->chip)
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	/* id should be plf_usb_req_enum */
+	__be32		id;
+	__be32		len;
+	u8              modulation;
+	u8              control;
+	u8              service;
+	u8		pad;
+	__le16		packet_length;
+	__le16		current_length;
+	__le16		next_frame_length;
+	__le16		tx_length;
+	__be32		payload_len_nw;
+} __packed;
+
+/* overlay */
+struct purelifi_header {
+	struct purelifi_ctrlset plf_ctrl;
+	u32    frametype;
+	u8    *dmac;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	bool pass_failed_fcs;
+
+	/* whether to pass control frames to stack */
+	bool pass_ctrl;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[PURELIFI_SERIAL_LEN];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *
+purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *
+purelifi_chip_to_mac(struct purelifi_chip *chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *
+purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int plfxlc_op_start(struct ieee80211_hw *hw);
+void plfxlc_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
new file mode 100644
index 000000000000..8cd9f223025e
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -0,0 +1,975 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+struct usb_interface *ez_usb_interface;
+
+static const struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		plf_usb_wreq_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				   tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		pr_err("urb is NULL\n");
+		return;
+	} else if (!urb->context) {
+		pr_err("urb ctx is NULL\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1) {
+		pr_err("usb is not initialized\n");
+		return;
+	}
+
+	tx = &usb->tx;
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		if (tx->submitted_urbs++ < PURELIFI_URB_RETRY_MAX) {
+			dev_dbg(urb_dev(urb), "urb %p resubmit %d", urb,
+				tx->submitted_urbs++);
+			goto resubmit;
+		} else {
+			dev_dbg(urb_dev(urb), "urb %p  max resubmits reached", urb);
+			tx->submitted_urbs = 0;
+			return;
+		}
+	}
+
+	buffer = urb->transfer_buffer;
+	length = le32_to_cpu(*(__le32 *)(buffer + sizeof(struct rx_status)))
+		 + sizeof(u32);
+
+	if (urb->actual_length != (PLF_MSG_STATUS_OFFSET + 1)) {
+		if (usb->initialized && usb->link_up)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[PLF_MSG_STATUS_OFFSET];
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_dbg(&usb->intf->dev,
+			"FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_dbg(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= STATION_ACTIVE_FLAG;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		usb->link_up = 1;
+		dev_dbg(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		usb->link_up = 0;
+		dev_dbg(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_dbg(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit fail (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0;
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		usb->rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	usb->rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	set_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!test_bit(PLF_BIT_ENABLED, &tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	plfxlc_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, void *buffer,
+			u32 buffer_len, enum plf_usb_req_enum usb_req_id,
+			struct plf_usb_req *usb_req)
+{
+	u8 *buffer_dst = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	__be32 payload_len_nw = cpu_to_be32(buffer_len + FCS_LEN);
+	u32 temp_usb_len = 0;
+
+	usb_req->id = cpu_to_be32(usb_req_id);
+	usb_req->len  = cpu_to_be32(0);
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == cpu_to_be32(USB_REQ_BEACON_WR)) {
+		memcpy(buffer_dst, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst += sizeof(payload_len_nw);
+		temp_usb_len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst, buffer_src_p, buffer_len);
+	buffer_dst += buffer_len;
+	buffer_src_p += buffer_len;
+	temp_usb_len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst, 0, FCS_LEN);
+	buffer_dst += FCS_LEN;
+	temp_usb_len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (temp_usb_len %
+			PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len %
+				PURELIFI_BYTE_NUM_ALIGNMENT);
+		temp_usb_len += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->len = cpu_to_be32(temp_usb_len);
+}
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn,
+		       void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "Async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct plf_usb_req usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(__le32) + sizeof(__le32) +
+			   be32_to_cpu(usb_req.len);
+
+	dma_buffer = kmemdup(&usb_req, usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	u8 hw_address[ETH_ALEN];
+	u8 serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+	dev_err(&intf->dev, "Unit type is station");
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_err(&intf->dev, "Init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_err(&intf->dev, "Register device failed (%d)\n", r);
+		goto error;
+	}
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_err(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_switch_radio(chip, PLFXLC_RADIO_ON);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plf_usb_wreq(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	dev_err(&intf->dev, "pureLifi:Device error");
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = plfxlc_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"Restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	plfxlc_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+#ifdef CONFIG_PM
+	.suspend        = suspend,
+	.resume         = resume,
+#endif
+	.disable_hub_initiated_lpm = 1,
+};
+
+static struct workqueue_struct *plfxlc_workqueue;
+
+static int __init usb_init(void)
+{
+	int r;
+
+	plfxlc_workqueue = create_singlethread_workqueue(driver.name);
+	if (!plfxlc_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(plfxlc_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	usb_deregister(&driver);
+	destroy_workqueue(plfxlc_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("plfxlc/lifi-x.bin");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.h b/drivers/net/wireless/purelifi/plfxlc/usb.h
new file mode 100644
index 000000000000..b44d446d87ee
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#define PLF_FPGA_STATUS_LEN 2
+#define PLF_FPGA_STATE_LEN 9
+#define PLF_BULK_TLEN 16384
+#define PLF_FPGA_MG 6 /* Magic check */
+#define PLF_XL_BUF_LEN 64
+#define PLF_MSG_STATUS_OFFSET 7
+
+#define PLF_USB_TIMEOUT 1000
+#define PLF_MSLEEP_TIME 200
+
+#define PURELIFI_URB_RETRY_MAX 5
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+
+/* Tx retry backoff timer (in milliseconds) */
+#define TX_RETRY_BACKOFF_MS 10
+#define STA_QUEUE_CLEANUP_MS 5000
+
+/* Tx retry backoff timer (in jiffies) */
+#define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+#define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+struct purelifi_usb_interrupt {
+	spinlock_t lock; /* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock; /* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct plf_station {
+   /*  7...3    |    2      |     1     |     0	    |
+    * Reserved  | Heartbeat | FIFO full | Connected |
+    */
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+struct firmware_file {
+	u32 total_files;
+	u32 total_size;
+	u32 size;
+	u32 start_addr;
+	u32 control_packets;
+} __packed;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+#define STATION_ACTIVE_FLAG    0xFD
+
+#define PURELIFI_SERIAL_LEN 256
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+
+struct purelifi_usb_tx {
+	unsigned long enabled;
+	spinlock_t lock; /* spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	bool stopped;
+	struct timer_list tx_retry_timer;
+	struct plf_station station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	bool rx_usb_enabled;
+	bool initialized;
+	bool was_running;
+	bool link_up;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+enum {
+	PLF_BIT_ENABLED = 1,
+	PLF_BIT_MAX = 2,
+};
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn, void *context);
+
+static inline struct usb_device *
+purelifi_usb_to_usbdev(struct purelifi_usb *usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_intf_to_hw(struct usb_interface *intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_usb_to_hw(struct purelifi_usb *usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/* Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v18 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-05 12:31   ` [PATCH] [v18 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
@ 2021-10-05 22:09     ` Jeff Johnson
  0 siblings, 0 replies; 108+ messages in thread
From: Jeff Johnson @ 2021-10-05 22:09 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

On 10/5/2021 5:31 AM, Srinivasan Raju wrote:> +		fw_data = 
kmemdup(&fw->data[fw_data_i], blk_tran_len,> +				  GFP_KERNEL);> +> +	 
for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {> +			/* u8 bit 
reverse */> +			fw_data[tbuf_idx] = bitrev8(fw_data[tbuf_idx]);> +		}
note kmemdup() can return NULL, hence the reference to fw_data[] can 
dereference NULL.

^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH] [v19 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (14 preceding siblings ...)
  2021-10-05 12:31   ` [PATCH] [v18 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
@ 2021-10-06 10:04   ` Srinivasan Raju
  2021-10-11  6:16     ` Kalle Valo
  2021-10-12 12:50   ` [PATCH 0/2] wireless: New Driver " Srinivasan Raju
                     ` (3 subsequent siblings)
  19 siblings, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2021-10-06 10:04 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

LiFi is a mobile wireless technology that uses light
rather than radio frequencies to transmit data.

802.11 bb is focused on introducing necessary changes to
IEEE 802.11 Stds to enable communications in the light medium

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

---
v19:
 - Fix kmemdup null case
v18:
 - Use light communication band
v16:
 - Fixed atomic variable misuses
 - Fixed comments spacing
 - Removed static variables used
 - Moved #defines to header file
 - Removed doxygen style comments
 - Removed magic numbers and cleanup code
v15:
 - resubmit v14 of the patch
v14:
 - Endianess comments addressed
 - Sparse checked and fixed warnings
 - Firmware files renamed to lowercase
 - All other review comments in v13 addressed
v13:
- Removed unused #defines
v12:
- Removed sysfs, procfs related code
- Addressed race condition bug
- Used macros instead of magic numbers in firmware.c
- Added copyright in all files
v11, v10:
- Addressed review comment on readability
- Changed firmware names to match products
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging
---
 MAINTAINERS                                   |   6 +
 drivers/net/wireless/Kconfig                  |   1 +
 drivers/net/wireless/Makefile                 |   1 +
 drivers/net/wireless/purelifi/Kconfig         |  17 +
 drivers/net/wireless/purelifi/Makefile        |   2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |  14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |   3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |  95 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |  89 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   | 275 +++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |  52 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    | 772 ++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    | 190 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 975 ++++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    | 196 ++++
 15 files changed, 2688 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index eeb4c70b3d5b..1d271e9e9223 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15193,6 +15193,12 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PURELIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+L:	linux-wireless@vger.kernel.org
+S:	Supported
+F:	drivers/net/wireless/purelifi/
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7add2002ff4c..404afe574920 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -28,6 +28,7 @@ source "drivers/net/wireless/intersil/Kconfig"
 source "drivers/net/wireless/marvell/Kconfig"
 source "drivers/net/wireless/mediatek/Kconfig"
 source "drivers/net/wireless/microchip/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 source "drivers/net/wireless/ralink/Kconfig"
 source "drivers/net/wireless/realtek/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e3345893c9c5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
 obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
 obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
 obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
 obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
 obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..e39afec3dcae
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+source "drivers/net/wireless/purelifi/plfxlc/Kconfig"
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..56ebf96bd298
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)		:= plfxlc/
diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
new file mode 100644
index 000000000000..400ab2ee660c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config PURELIFI_XLC
+	tristate "pureLiFi X, XL, XC device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This option adds support for pureLiFi LiFi wireless USB adapters.
+
+	   The pureLiFi X, XL, XC USB devices are based on 802.11 OFDM PHY.
+
+	   Supports common 802.11 encryption/authentication methods including
+	   Open, WPA, WPA2-Personal and WPA2-Enterprise (802.1X).
+
+	   To compile this driver as a module, choose m here. The module will
+	   be called purelifi_xlc.
diff --git a/drivers/net/wireless/purelifi/plfxlc/Makefile b/drivers/net/wireless/purelifi/plfxlc/Makefile
new file mode 100644
index 000000000000..3d66f485c024
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)	:= purelifi_xlc.o
+purelifi_xlc-objs 		+= chip.o firmware.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.c b/drivers/net/wireless/purelifi/plfxlc/chip.c
new file mode 100644
index 000000000000..ece74682d413
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set &&
+	     le16_to_cpu(chip->beacon_interval) == interval))
+		return 0;
+
+	chip->beacon_interval = cpu_to_le16(interval);
+	chip->beacon_set = true;
+	return plf_usb_wreq(&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	unsigned char *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %04x:%04x v%02x %pM %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr,
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value)
+{
+	int r;
+	__le16 radio_on = cpu_to_le16(value);
+
+	r = plf_usb_wreq(&radio_on, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	u8 value = 0;
+
+	plf_usb_wreq(&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = plf_usb_wreq(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.h b/drivers/net/wireless/purelifi/plfxlc/chip.h
new file mode 100644
index 000000000000..167e9b9bd7a7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type {
+	STA = 0,
+	AP = 1,
+};
+
+enum {
+	PLFXLC_RADIO_OFF = 0,
+	PLFXLC_RADIO_ON = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type unit_type;
+	u16 link_led;
+	u8 beacon_set;
+	__le16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low  = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1U << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/plfxlc/firmware.c b/drivers/net/wireless/purelifi/plfxlc/firmware.c
new file mode 100644
index 000000000000..30a8a63e13b6
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/firmware.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/firmware.h>
+#include <linux/bitrev.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+	int r, actual_length;
+	int fw_data_i, blk_tran_len = PLF_BULK_TLEN;
+	const char *fw_name;
+	unsigned char *fpga_dmabuff = NULL;
+	unsigned char *fw_data;
+	const struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-x.bin";
+		dev_dbg(&intf->dev, "bin file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-xc.bin";
+		dev_dbg(&intf->dev, "bin file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware(&fw, fw_name, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATUS_LEN, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ,
+			    fpga_dmabuff, PLF_FPGA_STATUS_LEN);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_SET_CMD, NULL, 0);
+
+	if (fpga_dmabuff[0] != PLF_FPGA_MG) {
+		dev_err(&intf->dev, "fpga_dmabuff[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+		if (!fw_data) {
+			r = -ENOMEM;
+			goto error_free_fw;
+		}
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			/* u8 bit reverse */
+			fw_data[tbuf_idx] = bitrev8(fw_data[tbuf_idx]);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_dmabuff[0] & 0xff),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2 * PLF_USB_TIMEOUT);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	kfree(fpga_dmabuff);
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATE_LEN, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xff, PLF_FPGA_STATE_LEN);
+
+	send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
+			    PLF_FPGA_STATE_LEN);
+
+	dev_dbg(&intf->dev, "%*ph\n", 8, fpga_dmabuff);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
+
+	msleep(PLF_MSLEEP_TIME);
+
+error_free_fw:
+	kfree(fpga_dmabuff);
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+	const struct firmware *fwp = NULL;
+	struct firmware_file file = {0};
+	int s, r;
+	u8 *buf;
+	u32 i;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
+	msleep(PLF_MSLEEP_TIME);
+
+	if (r) {
+		dev_err(&intf->dev, "vendor command failed (%d)\n", r);
+		return -EINVAL;
+	}
+	/* Code for single pack file download */
+
+	fw_pack = "plfxlc/lifi-xl.bin";
+
+	r = request_firmware(&fwp, fw_pack, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "Request_firmware failed (%d)\n", r);
+		return -EINVAL;
+	}
+	file.total_files = get_unaligned_le32(&fwp->data[0]);
+	file.total_size = get_unaligned_le32(&fwp->size);
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n",
+		file.total_files, file.total_size);
+
+	buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
+	if (!buf) {
+		release_firmware(fwp);
+		return -ENOMEM;
+	}
+
+	if (file.total_files > 10) {
+		dev_err(&intf->dev, "Too many files (%d)\n", file.total_files);
+		release_firmware(fwp);
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	/* Download firmware files in multiple steps */
+	for (s = 0; s < file.total_files; s++) {
+		buf[0] = s;
+		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
+					PLF_XL_BUF_LEN);
+
+		if (s < file.total_files - 1)
+			file.size = get_unaligned_le32(&fwp->data[4 + ((s + 1) * 4)])
+				    - get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+		else
+			file.size = file.total_size -
+				    get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+
+		if (file.size > file.total_size || file.size > 60000) {
+			dev_err(&intf->dev, "File size is too large (%d)\n", file.size);
+			break;
+		}
+
+		file.start_addr = get_unaligned_le32(&fwp->data[4 + (s * 4)]);
+
+		if (file.size % PLF_XL_BUF_LEN && s < 2)
+			file.size += PLF_XL_BUF_LEN - file.size % PLF_XL_BUF_LEN;
+
+		file.control_packets = file.size / PLF_XL_BUF_LEN;
+
+		for (i = 0; i < file.control_packets; i++) {
+			memcpy(buf,
+			       &fwp->data[file.start_addr + (i * PLF_XL_BUF_LEN)],
+			       PLF_XL_BUF_LEN);
+			r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
+						PLF_XL_BUF_LEN);
+		}
+		dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", s, r,
+			file.size);
+	}
+	release_firmware(fwp);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
+	dev_dbg(&intf->dev, "Download fpga (4) (%d)\n", r);
+
+	return 0;
+}
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);
+	if (!dma_buffer)
+		return -ENOMEM;
+
+	BUILD_BUG_ON(ETH_ALEN > PLF_SERIAL_LEN);
+	BUILD_BUG_ON(PLF_FW_VER_LEN > PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	memcpy(serial_number, dma_buffer, PLF_SERIAL_LEN);
+
+	memset(dma_buffer, 0x00, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, PLF_FW_VER_LEN);
+
+	memcpy(&firmware_version, dma_buffer, PLF_FW_VER_LEN);
+
+	dev_info(&intf->dev, "Firmware Version: %llu\n", firmware_version);
+	kfree(dma_buffer);
+
+	dev_dbg(&intf->dev, "Mac: %pM\n", hw_address);
+
+	return 0;
+}
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/intf.h b/drivers/net/wireless/purelifi/plfxlc/intf.h
new file mode 100644
index 000000000000..5ae89343b579
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/intf.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#define ETH_ALEN 6
+#define AP_USER_LIMIT 8
+
+#define PLF_VNDR_FPGA_STATE_REQ 0x30
+#define PLF_VNDR_FPGA_SET_REQ 0x33
+#define PLF_VNDR_FPGA_SET_CMD 0x34
+#define PLF_VNDR_FPGA_STATE_CMD 0x35
+
+#define PLF_VNDR_XL_FW_CMD 0x80
+#define PLF_VNDR_XL_DATA_CMD 0x81
+#define PLF_VNDR_XL_FILE_CMD 0x82
+#define PLF_VNDR_XL_EX_CMD 0x83
+
+#define PLF_MAC_VENDOR_REQUEST 0x36
+#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define PLF_SERIAL_LEN 14
+#define PLF_FW_VER_LEN 8
+
+struct rx_status {
+	__be16 rssi;
+	u8     rate_idx;
+	u8     pad;
+	__be64 crc_error_count;
+} __packed;
+
+enum plf_usb_req_enum {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct plf_usb_req {
+	__be32         id; /* should be plf_usb_req_enum */
+	__be32	       len;
+	u8             buf[512];
+};
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
new file mode 100644
index 000000000000..ba16687803ce
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.c
@@ -0,0 +1,772 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 }
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	regulatory_hint(hw->wiphy, "00");
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int plfxlc_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void plfxlc_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status)
+		success = !tx_status->failure;
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon)
+{
+	return plf_usb_wreq(beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	u32 temp_len = 0;
+	u32 temp_payload_len = 0;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_dbg(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (void *)skb_push(skb, sizeof(struct purelifi_ctrlset));
+	temp_payload_len = frag_len;
+	temp_len = temp_payload_len +
+		  sizeof(struct purelifi_ctrlset) -
+		  sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/* check if 32 bit aligned and align data */
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memmove(dest_pt, src_pt, len);
+			} else {
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		temp_len += 4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data */
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen because
+				 * sufficient headroom was reserved
+				 */
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		temp_len += 4;
+	}
+
+	cs->id = cpu_to_be32(USB_REQ_DATA_TX);
+	cs->len = cpu_to_be32(temp_len);
+	cs->payload_len_nw = cpu_to_be32(temp_payload_len);
+
+	return 0;
+}
+
+static void plfxlc_op_tx(struct ieee80211_hw *hw,
+			 struct ieee80211_tx_control *control,
+			 struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct purelifi_header *plhdr = (void *)skb->data;
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+
+	info->rate_driver_data[0] = hw;
+
+	if (plhdr->frametype  == IEEE80211_FTYPE_DATA) {
+		u8 *dst_mac = plhdr->dmac;
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			goto fail;
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = plf_usb_wreq_async(&mac->chip.usb, skb->data, skb->len,
+				       USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+static int purelifi_filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+			       struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	bool found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_dbg(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_LC;
+	mac->rssi      = -15 * be16_to_cpu(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	mac->crc_errors = be64_to_cpu(status->crc_error_count);
+
+	if (!bad_frame &&
+	    purelifi_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = get_unaligned_be32(buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	switch (buffer[0]) {
+	case IEEE80211_STYPE_PROBE_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
+		break;
+	case IEEE80211_STYPE_ASSOC_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Association request\n");
+		break;
+	case IEEE80211_STYPE_AUTH:
+		dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+		break;
+	case IEEE80211_FTYPE_DATA:
+		dev_dbg(purelifi_mac_dev(mac), "802.11 data frame\n");
+		break;
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+
+	if (need_padding)
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+
+	skb_put_data(skb, buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int plfxlc_op_add_interface(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_dbg(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_dbg(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void plfxlc_op_remove_interface(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int plfxlc_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void plfxlc_op_configure_filter(struct ieee80211_hw *hw,
+				       unsigned int changed_flags,
+				       unsigned int *new_flags,
+				       u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by plfxlc_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void plfxlc_op_bss_info_changed(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *bss_conf,
+				       u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_dbg(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+static const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops plfxlc_ops = {
+	.tx                 = plfxlc_op_tx,
+	.start              = plfxlc_op_start,
+	.stop               = plfxlc_op_stop,
+	.add_interface      = plfxlc_op_add_interface,
+	.remove_interface   = plfxlc_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = plfxlc_op_config,
+	.configure_filter   = plfxlc_op_configure_filter,
+	.bss_info_changed   = plfxlc_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &plfxlc_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+	hw->wiphy->bands[NL80211_BAND_LC] = &mac->band;
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+
+	ieee80211_hw_set(hw, RX_INCLUDES_FCS);
+	ieee80211_hw_set(hw, SIGNAL_DBM);
+	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+	ieee80211_hw_set(hw, MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+	hw->max_signal = 100;
+	hw->queues = 1;
+	/* 4 for 32 bit alignment if no tailroom */
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.h b/drivers/net/wireless/purelifi/plfxlc/mac.h
new file mode 100644
index 000000000000..3852d7343a40
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.h
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) purelifi_chip_dev(&(mac)->chip)
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	/* id should be plf_usb_req_enum */
+	__be32		id;
+	__be32		len;
+	u8              modulation;
+	u8              control;
+	u8              service;
+	u8		pad;
+	__le16		packet_length;
+	__le16		current_length;
+	__le16		next_frame_length;
+	__le16		tx_length;
+	__be32		payload_len_nw;
+} __packed;
+
+/* overlay */
+struct purelifi_header {
+	struct purelifi_ctrlset plf_ctrl;
+	u32    frametype;
+	u8    *dmac;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	bool pass_failed_fcs;
+
+	/* whether to pass control frames to stack */
+	bool pass_ctrl;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[PURELIFI_SERIAL_LEN];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *
+purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *
+purelifi_chip_to_mac(struct purelifi_chip *chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *
+purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int plfxlc_op_start(struct ieee80211_hw *hw);
+void plfxlc_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
new file mode 100644
index 000000000000..8cd9f223025e
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -0,0 +1,975 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+struct usb_interface *ez_usb_interface;
+
+static const struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		plf_usb_wreq_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				   tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		pr_err("urb is NULL\n");
+		return;
+	} else if (!urb->context) {
+		pr_err("urb ctx is NULL\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1) {
+		pr_err("usb is not initialized\n");
+		return;
+	}
+
+	tx = &usb->tx;
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		if (tx->submitted_urbs++ < PURELIFI_URB_RETRY_MAX) {
+			dev_dbg(urb_dev(urb), "urb %p resubmit %d", urb,
+				tx->submitted_urbs++);
+			goto resubmit;
+		} else {
+			dev_dbg(urb_dev(urb), "urb %p  max resubmits reached", urb);
+			tx->submitted_urbs = 0;
+			return;
+		}
+	}
+
+	buffer = urb->transfer_buffer;
+	length = le32_to_cpu(*(__le32 *)(buffer + sizeof(struct rx_status)))
+		 + sizeof(u32);
+
+	if (urb->actual_length != (PLF_MSG_STATUS_OFFSET + 1)) {
+		if (usb->initialized && usb->link_up)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[PLF_MSG_STATUS_OFFSET];
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_dbg(&usb->intf->dev,
+			"FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_dbg(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= STATION_ACTIVE_FLAG;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		usb->link_up = 1;
+		dev_dbg(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		usb->link_up = 0;
+		dev_dbg(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_dbg(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit fail (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0;
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		usb->rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	usb->rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	set_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!test_bit(PLF_BIT_ENABLED, &tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	plfxlc_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, void *buffer,
+			u32 buffer_len, enum plf_usb_req_enum usb_req_id,
+			struct plf_usb_req *usb_req)
+{
+	u8 *buffer_dst = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	__be32 payload_len_nw = cpu_to_be32(buffer_len + FCS_LEN);
+	u32 temp_usb_len = 0;
+
+	usb_req->id = cpu_to_be32(usb_req_id);
+	usb_req->len  = cpu_to_be32(0);
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == cpu_to_be32(USB_REQ_BEACON_WR)) {
+		memcpy(buffer_dst, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst += sizeof(payload_len_nw);
+		temp_usb_len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst, buffer_src_p, buffer_len);
+	buffer_dst += buffer_len;
+	buffer_src_p += buffer_len;
+	temp_usb_len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst, 0, FCS_LEN);
+	buffer_dst += FCS_LEN;
+	temp_usb_len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (temp_usb_len %
+			PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len %
+				PURELIFI_BYTE_NUM_ALIGNMENT);
+		temp_usb_len += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->len = cpu_to_be32(temp_usb_len);
+}
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn,
+		       void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "Async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct plf_usb_req usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(__le32) + sizeof(__le32) +
+			   be32_to_cpu(usb_req.len);
+
+	dma_buffer = kmemdup(&usb_req, usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	u8 hw_address[ETH_ALEN];
+	u8 serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+	dev_err(&intf->dev, "Unit type is station");
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_err(&intf->dev, "Init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_err(&intf->dev, "Register device failed (%d)\n", r);
+		goto error;
+	}
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_err(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_switch_radio(chip, PLFXLC_RADIO_ON);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plf_usb_wreq(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	dev_err(&intf->dev, "pureLifi:Device error");
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = plfxlc_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"Restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	plfxlc_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+#ifdef CONFIG_PM
+	.suspend        = suspend,
+	.resume         = resume,
+#endif
+	.disable_hub_initiated_lpm = 1,
+};
+
+static struct workqueue_struct *plfxlc_workqueue;
+
+static int __init usb_init(void)
+{
+	int r;
+
+	plfxlc_workqueue = create_singlethread_workqueue(driver.name);
+	if (!plfxlc_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(plfxlc_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	usb_deregister(&driver);
+	destroy_workqueue(plfxlc_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("plfxlc/lifi-x.bin");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.h b/drivers/net/wireless/purelifi/plfxlc/usb.h
new file mode 100644
index 000000000000..b44d446d87ee
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#define PLF_FPGA_STATUS_LEN 2
+#define PLF_FPGA_STATE_LEN 9
+#define PLF_BULK_TLEN 16384
+#define PLF_FPGA_MG 6 /* Magic check */
+#define PLF_XL_BUF_LEN 64
+#define PLF_MSG_STATUS_OFFSET 7
+
+#define PLF_USB_TIMEOUT 1000
+#define PLF_MSLEEP_TIME 200
+
+#define PURELIFI_URB_RETRY_MAX 5
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+
+/* Tx retry backoff timer (in milliseconds) */
+#define TX_RETRY_BACKOFF_MS 10
+#define STA_QUEUE_CLEANUP_MS 5000
+
+/* Tx retry backoff timer (in jiffies) */
+#define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+#define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+struct purelifi_usb_interrupt {
+	spinlock_t lock; /* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock; /* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct plf_station {
+   /*  7...3    |    2      |     1     |     0	    |
+    * Reserved  | Heartbeat | FIFO full | Connected |
+    */
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+struct firmware_file {
+	u32 total_files;
+	u32 total_size;
+	u32 size;
+	u32 start_addr;
+	u32 control_packets;
+} __packed;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+#define STATION_ACTIVE_FLAG    0xFD
+
+#define PURELIFI_SERIAL_LEN 256
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+
+struct purelifi_usb_tx {
+	unsigned long enabled;
+	spinlock_t lock; /* spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	bool stopped;
+	struct timer_list tx_retry_timer;
+	struct plf_station station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	bool rx_usb_enabled;
+	bool initialized;
+	bool was_running;
+	bool link_up;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+enum {
+	PLF_BIT_ENABLED = 1,
+	PLF_BIT_MAX = 2,
+};
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn, void *context);
+
+static inline struct usb_device *
+purelifi_usb_to_usbdev(struct purelifi_usb *usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_intf_to_hw(struct usb_interface *intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_usb_to_hw(struct purelifi_usb *usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/* Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH] [v19 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-06 10:04   ` [PATCH] [v19 " Srinivasan Raju
@ 2021-10-11  6:16     ` Kalle Valo
  0 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2021-10-11  6:16 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
> and LiFi-XL USB devices.
>
> LiFi is a mobile wireless technology that uses light
> rather than radio frequencies to transmit data.
>
> 802.11 bb is focused on introducing necessary changes to
> IEEE 802.11 Stds to enable communications in the light medium
>
> This driver implementation has been based on the zd1211rw driver.
>
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management.
>
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture.
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

When you are submitting a patchset you should always submit all patches
at the same time. Also including a cover letter is very much preferred.
And remember to use git send-email when submitting the patchset.

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches#sending_large_patches_or_multiple_patches

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH 0/2] wireless: New Driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (15 preceding siblings ...)
  2021-10-06 10:04   ` [PATCH] [v19 " Srinivasan Raju
@ 2021-10-12 12:50   ` Srinivasan Raju
  2021-10-12 12:50     ` [PATCH 1/2] [v19 1/2] nl80211: Add LC placeholder band definition to enum nl80211_band Srinivasan Raju
  2021-10-12 12:50     ` [PATCH 2/2] [v19 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  2021-10-18 10:00   ` [PATCH v20 0/2] wireless: New Driver " Srinivasan Raju
                     ` (2 subsequent siblings)
  19 siblings, 2 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-10-12 12:50 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

LiFi is a mobile wireless technology that uses light 
rather than radio frequencies to transmit data.

802.11 bb is focused on introducing necessary changes to 
IEEE 802.11 Stds to enable communications in the light medium

---
v19:
 - Fix kmemdup null case
v18:
 - Use light communication band 
v16:
 - Fixed atomic variable misuses
 - Fixed comments spacing
 - Removed static variables used
 - Moved #defines to header file
 - Removed doxygen style comments
 - Removed magic numbers and cleanup code
v15:
 - resubmit v14 of the patch
v14:
 - Endianess comments addressed
 - Sparse checked and fixed warnings
 - Firmware files renamed to lowercase
 - All other review comments in v13 addressed
v13:
- Removed unused #defines
v12:
- Removed sysfs, procfs related code
- Addressed race condition bug
- Used macros instead of magic numbers in firmware.c
- Added copyright in all files
v11, v10:
- Addressed review comment on readability
- Changed firmware names to match products
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging


Srinivasan Raju (2):
  [v18 1/2] nl80211: Add LC placeholder band definition to enum
    nl80211_band
  [v19 2/2] wireless: Initial driver submission for pureLiFi STA devices

 MAINTAINERS                                   |   6 +
 drivers/net/wireless/Kconfig                  |   1 +
 drivers/net/wireless/Makefile                 |   1 +
 drivers/net/wireless/purelifi/Kconfig         |  17 +
 drivers/net/wireless/purelifi/Makefile        |   2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |  14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |   3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |  95 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |  89 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   | 275 +++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |  52 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    | 772 ++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    | 190 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 975 ++++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    | 196 ++++
 include/uapi/linux/nl80211.h                  |   2 +
 net/mac80211/mlme.c                           |   1 +
 net/mac80211/sta_info.c                       |   1 +
 net/mac80211/tx.c                             |   3 +-
 net/wireless/nl80211.c                        |   1 +
 net/wireless/util.c                           |   2 +
 21 files changed, 2697 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

-- 
2.25.1


^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH 1/2] [v19 1/2] nl80211: Add LC placeholder band definition to enum nl80211_band
  2021-10-12 12:50   ` [PATCH 0/2] wireless: New Driver " Srinivasan Raju
@ 2021-10-12 12:50     ` Srinivasan Raju
  2021-10-12 12:50     ` [PATCH 2/2] [v19 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  1 sibling, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-10-12 12:50 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Johannes Berg, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Define LC band which is a draft under IEEE 802.11 bb
Current NL80211_BAND_LC is a placeholder band
The band will be redefined as IEEE 802.11 bb progresses

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
---
 include/uapi/linux/nl80211.h | 2 ++
 net/mac80211/mlme.c          | 1 +
 net/mac80211/sta_info.c      | 1 +
 net/mac80211/tx.c            | 3 ++-
 net/wireless/nl80211.c       | 1 +
 net/wireless/util.c          | 2 ++
 6 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index c2efea98e060..cb06fb604a60 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -4929,6 +4929,7 @@ enum nl80211_txrate_gi {
  * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz)
  * @NL80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.2 GHz)
  * @NL80211_BAND_S1GHZ: around 900MHz, supported by S1G PHYs
+ * @NL80211_BAND_LC: light communication band (placeholder)
  * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
  *	since newer kernel versions may support more bands
  */
@@ -4938,6 +4939,7 @@ enum nl80211_band {
 	NL80211_BAND_60GHZ,
 	NL80211_BAND_6GHZ,
 	NL80211_BAND_S1GHZ,
+	NL80211_BAND_LC,
 
 	NUM_NL80211_BANDS,
 };
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index c0ea3b1aa9e1..c577d03ab128 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1490,6 +1490,7 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
 		fallthrough;
 	case NL80211_BAND_2GHZ:
 	case NL80211_BAND_60GHZ:
+	case NL80211_BAND_LC:
 		chan_increment = 1;
 		break;
 	case NL80211_BAND_5GHZ:
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 2b5acb37587f..36524101d11f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -444,6 +444,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
 		switch (i) {
 		case NL80211_BAND_2GHZ:
+		case NL80211_BAND_LC:
 			/*
 			 * We use both here, even if we cannot really know for
 			 * sure the station will support both, but the only use
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 2d1193ed3eb5..d311937f2add 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -146,7 +146,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
 			rate = DIV_ROUND_UP(r->bitrate, 1 << shift);
 
 		switch (sband->band) {
-		case NL80211_BAND_2GHZ: {
+		case NL80211_BAND_2GHZ:
+		case NL80211_BAND_LC: {
 			u32 flag;
 			if (tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
 				flag = IEEE80211_RATE_MANDATORY_G;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index bf7cd4752547..cf1434049abb 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -853,6 +853,7 @@ nl80211_match_band_rssi_policy[NUM_NL80211_BANDS] = {
 	[NL80211_BAND_5GHZ] = { .type = NLA_S32 },
 	[NL80211_BAND_6GHZ] = { .type = NLA_S32 },
 	[NL80211_BAND_60GHZ] = { .type = NLA_S32 },
+	[NL80211_BAND_LC]    = { .type = NLA_S32 },
 };
 
 static const struct nla_policy
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 18dba3d7c638..2991f711491a 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -80,6 +80,7 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
 		return 0; /* not supported */
 	switch (band) {
 	case NL80211_BAND_2GHZ:
+	case NL80211_BAND_LC:
 		if (chan == 14)
 			return MHZ_TO_KHZ(2484);
 		else if (chan < 14)
@@ -209,6 +210,7 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband)
 		WARN_ON(want);
 		break;
 	case NL80211_BAND_2GHZ:
+	case NL80211_BAND_LC:
 		want = 7;
 		for (i = 0; i < sband->n_bitrates; i++) {
 			switch (sband->bitrates[i].bitrate) {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 2/2] [v19 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-12 12:50   ` [PATCH 0/2] wireless: New Driver " Srinivasan Raju
  2021-10-12 12:50     ` [PATCH 1/2] [v19 1/2] nl80211: Add LC placeholder band definition to enum nl80211_band Srinivasan Raju
@ 2021-10-12 12:50     ` Srinivasan Raju
  2021-10-14  6:03       ` kernel test robot
  2021-10-24 17:58       ` kernel test robot
  1 sibling, 2 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-10-12 12:50 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This driver implementation has been based on the zd1211rw driver.

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management.

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture.

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

---
 MAINTAINERS                                   |   6 +
 drivers/net/wireless/Kconfig                  |   1 +
 drivers/net/wireless/Makefile                 |   1 +
 drivers/net/wireless/purelifi/Kconfig         |  17 +
 drivers/net/wireless/purelifi/Makefile        |   2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |  14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |   3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |  95 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |  89 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   | 275 +++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |  52 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    | 772 ++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    | 190 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 975 ++++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    | 196 ++++
 15 files changed, 2688 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index eeb4c70b3d5b..1d271e9e9223 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15193,6 +15193,12 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PURELIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+L:	linux-wireless@vger.kernel.org
+S:	Supported
+F:	drivers/net/wireless/purelifi/
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7add2002ff4c..404afe574920 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -28,6 +28,7 @@ source "drivers/net/wireless/intersil/Kconfig"
 source "drivers/net/wireless/marvell/Kconfig"
 source "drivers/net/wireless/mediatek/Kconfig"
 source "drivers/net/wireless/microchip/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 source "drivers/net/wireless/ralink/Kconfig"
 source "drivers/net/wireless/realtek/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e3345893c9c5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
 obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
 obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
 obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
 obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
 obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..e39afec3dcae
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+source "drivers/net/wireless/purelifi/plfxlc/Kconfig"
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..56ebf96bd298
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)		:= plfxlc/
diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
new file mode 100644
index 000000000000..400ab2ee660c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config PURELIFI_XLC
+	tristate "pureLiFi X, XL, XC device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This option adds support for pureLiFi LiFi wireless USB adapters.
+
+	   The pureLiFi X, XL, XC USB devices are based on 802.11 OFDM PHY.
+
+	   Supports common 802.11 encryption/authentication methods including
+	   Open, WPA, WPA2-Personal and WPA2-Enterprise (802.1X).
+
+	   To compile this driver as a module, choose m here. The module will
+	   be called purelifi_xlc.
diff --git a/drivers/net/wireless/purelifi/plfxlc/Makefile b/drivers/net/wireless/purelifi/plfxlc/Makefile
new file mode 100644
index 000000000000..3d66f485c024
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)	:= purelifi_xlc.o
+purelifi_xlc-objs 		+= chip.o firmware.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.c b/drivers/net/wireless/purelifi/plfxlc/chip.c
new file mode 100644
index 000000000000..ece74682d413
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set &&
+	     le16_to_cpu(chip->beacon_interval) == interval))
+		return 0;
+
+	chip->beacon_interval = cpu_to_le16(interval);
+	chip->beacon_set = true;
+	return plf_usb_wreq(&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	unsigned char *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %04x:%04x v%02x %pM %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr,
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value)
+{
+	int r;
+	__le16 radio_on = cpu_to_le16(value);
+
+	r = plf_usb_wreq(&radio_on, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	u8 value = 0;
+
+	plf_usb_wreq(&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = plf_usb_wreq(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.h b/drivers/net/wireless/purelifi/plfxlc/chip.h
new file mode 100644
index 000000000000..167e9b9bd7a7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type {
+	STA = 0,
+	AP = 1,
+};
+
+enum {
+	PLFXLC_RADIO_OFF = 0,
+	PLFXLC_RADIO_ON = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type unit_type;
+	u16 link_led;
+	u8 beacon_set;
+	__le16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low  = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1U << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/plfxlc/firmware.c b/drivers/net/wireless/purelifi/plfxlc/firmware.c
new file mode 100644
index 000000000000..30a8a63e13b6
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/firmware.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/firmware.h>
+#include <linux/bitrev.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+	int r, actual_length;
+	int fw_data_i, blk_tran_len = PLF_BULK_TLEN;
+	const char *fw_name;
+	unsigned char *fpga_dmabuff = NULL;
+	unsigned char *fw_data;
+	const struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-x.bin";
+		dev_dbg(&intf->dev, "bin file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-xc.bin";
+		dev_dbg(&intf->dev, "bin file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware(&fw, fw_name, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATUS_LEN, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ,
+			    fpga_dmabuff, PLF_FPGA_STATUS_LEN);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_SET_CMD, NULL, 0);
+
+	if (fpga_dmabuff[0] != PLF_FPGA_MG) {
+		dev_err(&intf->dev, "fpga_dmabuff[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+		if (!fw_data) {
+			r = -ENOMEM;
+			goto error_free_fw;
+		}
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			/* u8 bit reverse */
+			fw_data[tbuf_idx] = bitrev8(fw_data[tbuf_idx]);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_dmabuff[0] & 0xff),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2 * PLF_USB_TIMEOUT);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	kfree(fpga_dmabuff);
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATE_LEN, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xff, PLF_FPGA_STATE_LEN);
+
+	send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
+			    PLF_FPGA_STATE_LEN);
+
+	dev_dbg(&intf->dev, "%*ph\n", 8, fpga_dmabuff);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
+
+	msleep(PLF_MSLEEP_TIME);
+
+error_free_fw:
+	kfree(fpga_dmabuff);
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+	const struct firmware *fwp = NULL;
+	struct firmware_file file = {0};
+	int s, r;
+	u8 *buf;
+	u32 i;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
+	msleep(PLF_MSLEEP_TIME);
+
+	if (r) {
+		dev_err(&intf->dev, "vendor command failed (%d)\n", r);
+		return -EINVAL;
+	}
+	/* Code for single pack file download */
+
+	fw_pack = "plfxlc/lifi-xl.bin";
+
+	r = request_firmware(&fwp, fw_pack, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "Request_firmware failed (%d)\n", r);
+		return -EINVAL;
+	}
+	file.total_files = get_unaligned_le32(&fwp->data[0]);
+	file.total_size = get_unaligned_le32(&fwp->size);
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n",
+		file.total_files, file.total_size);
+
+	buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
+	if (!buf) {
+		release_firmware(fwp);
+		return -ENOMEM;
+	}
+
+	if (file.total_files > 10) {
+		dev_err(&intf->dev, "Too many files (%d)\n", file.total_files);
+		release_firmware(fwp);
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	/* Download firmware files in multiple steps */
+	for (s = 0; s < file.total_files; s++) {
+		buf[0] = s;
+		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
+					PLF_XL_BUF_LEN);
+
+		if (s < file.total_files - 1)
+			file.size = get_unaligned_le32(&fwp->data[4 + ((s + 1) * 4)])
+				    - get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+		else
+			file.size = file.total_size -
+				    get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+
+		if (file.size > file.total_size || file.size > 60000) {
+			dev_err(&intf->dev, "File size is too large (%d)\n", file.size);
+			break;
+		}
+
+		file.start_addr = get_unaligned_le32(&fwp->data[4 + (s * 4)]);
+
+		if (file.size % PLF_XL_BUF_LEN && s < 2)
+			file.size += PLF_XL_BUF_LEN - file.size % PLF_XL_BUF_LEN;
+
+		file.control_packets = file.size / PLF_XL_BUF_LEN;
+
+		for (i = 0; i < file.control_packets; i++) {
+			memcpy(buf,
+			       &fwp->data[file.start_addr + (i * PLF_XL_BUF_LEN)],
+			       PLF_XL_BUF_LEN);
+			r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
+						PLF_XL_BUF_LEN);
+		}
+		dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", s, r,
+			file.size);
+	}
+	release_firmware(fwp);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
+	dev_dbg(&intf->dev, "Download fpga (4) (%d)\n", r);
+
+	return 0;
+}
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);
+	if (!dma_buffer)
+		return -ENOMEM;
+
+	BUILD_BUG_ON(ETH_ALEN > PLF_SERIAL_LEN);
+	BUILD_BUG_ON(PLF_FW_VER_LEN > PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	memcpy(serial_number, dma_buffer, PLF_SERIAL_LEN);
+
+	memset(dma_buffer, 0x00, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, PLF_FW_VER_LEN);
+
+	memcpy(&firmware_version, dma_buffer, PLF_FW_VER_LEN);
+
+	dev_info(&intf->dev, "Firmware Version: %llu\n", firmware_version);
+	kfree(dma_buffer);
+
+	dev_dbg(&intf->dev, "Mac: %pM\n", hw_address);
+
+	return 0;
+}
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/intf.h b/drivers/net/wireless/purelifi/plfxlc/intf.h
new file mode 100644
index 000000000000..5ae89343b579
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/intf.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#define ETH_ALEN 6
+#define AP_USER_LIMIT 8
+
+#define PLF_VNDR_FPGA_STATE_REQ 0x30
+#define PLF_VNDR_FPGA_SET_REQ 0x33
+#define PLF_VNDR_FPGA_SET_CMD 0x34
+#define PLF_VNDR_FPGA_STATE_CMD 0x35
+
+#define PLF_VNDR_XL_FW_CMD 0x80
+#define PLF_VNDR_XL_DATA_CMD 0x81
+#define PLF_VNDR_XL_FILE_CMD 0x82
+#define PLF_VNDR_XL_EX_CMD 0x83
+
+#define PLF_MAC_VENDOR_REQUEST 0x36
+#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define PLF_SERIAL_LEN 14
+#define PLF_FW_VER_LEN 8
+
+struct rx_status {
+	__be16 rssi;
+	u8     rate_idx;
+	u8     pad;
+	__be64 crc_error_count;
+} __packed;
+
+enum plf_usb_req_enum {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct plf_usb_req {
+	__be32         id; /* should be plf_usb_req_enum */
+	__be32	       len;
+	u8             buf[512];
+};
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
new file mode 100644
index 000000000000..ba16687803ce
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.c
@@ -0,0 +1,772 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 }
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	regulatory_hint(hw->wiphy, "00");
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int plfxlc_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void plfxlc_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status)
+		success = !tx_status->failure;
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon)
+{
+	return plf_usb_wreq(beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	u32 temp_len = 0;
+	u32 temp_payload_len = 0;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_dbg(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (void *)skb_push(skb, sizeof(struct purelifi_ctrlset));
+	temp_payload_len = frag_len;
+	temp_len = temp_payload_len +
+		  sizeof(struct purelifi_ctrlset) -
+		  sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/* check if 32 bit aligned and align data */
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memmove(dest_pt, src_pt, len);
+			} else {
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		temp_len += 4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data */
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen because
+				 * sufficient headroom was reserved
+				 */
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		temp_len += 4;
+	}
+
+	cs->id = cpu_to_be32(USB_REQ_DATA_TX);
+	cs->len = cpu_to_be32(temp_len);
+	cs->payload_len_nw = cpu_to_be32(temp_payload_len);
+
+	return 0;
+}
+
+static void plfxlc_op_tx(struct ieee80211_hw *hw,
+			 struct ieee80211_tx_control *control,
+			 struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct purelifi_header *plhdr = (void *)skb->data;
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+
+	info->rate_driver_data[0] = hw;
+
+	if (plhdr->frametype  == IEEE80211_FTYPE_DATA) {
+		u8 *dst_mac = plhdr->dmac;
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			goto fail;
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = plf_usb_wreq_async(&mac->chip.usb, skb->data, skb->len,
+				       USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+static int purelifi_filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+			       struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	bool found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_dbg(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	static unsigned short int min_exp_seq_nmb;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_LC;
+	mac->rssi      = -15 * be16_to_cpu(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	mac->crc_errors = be64_to_cpu(status->crc_error_count);
+
+	if (!bad_frame &&
+	    purelifi_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = get_unaligned_be32(buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	switch (buffer[0]) {
+	case IEEE80211_STYPE_PROBE_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
+		break;
+	case IEEE80211_STYPE_ASSOC_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Association request\n");
+		break;
+	case IEEE80211_STYPE_AUTH:
+		dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
+		min_exp_seq_nmb = 0;
+		break;
+	case IEEE80211_FTYPE_DATA:
+		dev_dbg(purelifi_mac_dev(mac), "802.11 data frame\n");
+		break;
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+
+	if (need_padding)
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+
+	skb_put_data(skb, buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int plfxlc_op_add_interface(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_dbg(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_dbg(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void plfxlc_op_remove_interface(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int plfxlc_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void plfxlc_op_configure_filter(struct ieee80211_hw *hw,
+				       unsigned int changed_flags,
+				       unsigned int *new_flags,
+				       u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by plfxlc_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void plfxlc_op_bss_info_changed(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *bss_conf,
+				       u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_dbg(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+static const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops plfxlc_ops = {
+	.tx                 = plfxlc_op_tx,
+	.start              = plfxlc_op_start,
+	.stop               = plfxlc_op_stop,
+	.add_interface      = plfxlc_op_add_interface,
+	.remove_interface   = plfxlc_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = plfxlc_op_config,
+	.configure_filter   = plfxlc_op_configure_filter,
+	.bss_info_changed   = plfxlc_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &plfxlc_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+	hw->wiphy->bands[NL80211_BAND_LC] = &mac->band;
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+
+	ieee80211_hw_set(hw, RX_INCLUDES_FCS);
+	ieee80211_hw_set(hw, SIGNAL_DBM);
+	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+	ieee80211_hw_set(hw, MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+	hw->max_signal = 100;
+	hw->queues = 1;
+	/* 4 for 32 bit alignment if no tailroom */
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.h b/drivers/net/wireless/purelifi/plfxlc/mac.h
new file mode 100644
index 000000000000..3852d7343a40
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.h
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) purelifi_chip_dev(&(mac)->chip)
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	/* id should be plf_usb_req_enum */
+	__be32		id;
+	__be32		len;
+	u8              modulation;
+	u8              control;
+	u8              service;
+	u8		pad;
+	__le16		packet_length;
+	__le16		current_length;
+	__le16		next_frame_length;
+	__le16		tx_length;
+	__be32		payload_len_nw;
+} __packed;
+
+/* overlay */
+struct purelifi_header {
+	struct purelifi_ctrlset plf_ctrl;
+	u32    frametype;
+	u8    *dmac;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	bool pass_failed_fcs;
+
+	/* whether to pass control frames to stack */
+	bool pass_ctrl;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[PURELIFI_SERIAL_LEN];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *
+purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *
+purelifi_chip_to_mac(struct purelifi_chip *chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *
+purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int plfxlc_op_start(struct ieee80211_hw *hw);
+void plfxlc_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
new file mode 100644
index 000000000000..8cd9f223025e
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -0,0 +1,975 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+struct usb_interface *ez_usb_interface;
+
+static const struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		plf_usb_wreq_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				   tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		pr_err("urb is NULL\n");
+		return;
+	} else if (!urb->context) {
+		pr_err("urb ctx is NULL\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1) {
+		pr_err("usb is not initialized\n");
+		return;
+	}
+
+	tx = &usb->tx;
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		if (tx->submitted_urbs++ < PURELIFI_URB_RETRY_MAX) {
+			dev_dbg(urb_dev(urb), "urb %p resubmit %d", urb,
+				tx->submitted_urbs++);
+			goto resubmit;
+		} else {
+			dev_dbg(urb_dev(urb), "urb %p  max resubmits reached", urb);
+			tx->submitted_urbs = 0;
+			return;
+		}
+	}
+
+	buffer = urb->transfer_buffer;
+	length = le32_to_cpu(*(__le32 *)(buffer + sizeof(struct rx_status)))
+		 + sizeof(u32);
+
+	if (urb->actual_length != (PLF_MSG_STATUS_OFFSET + 1)) {
+		if (usb->initialized && usb->link_up)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[PLF_MSG_STATUS_OFFSET];
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_dbg(&usb->intf->dev,
+			"FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_dbg(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= STATION_ACTIVE_FLAG;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		usb->link_up = 1;
+		dev_dbg(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		usb->link_up = 0;
+		dev_dbg(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_dbg(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit fail (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0;
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		usb->rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	usb->rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	set_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!test_bit(PLF_BIT_ENABLED, &tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	plfxlc_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, void *buffer,
+			u32 buffer_len, enum plf_usb_req_enum usb_req_id,
+			struct plf_usb_req *usb_req)
+{
+	u8 *buffer_dst = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	__be32 payload_len_nw = cpu_to_be32(buffer_len + FCS_LEN);
+	u32 temp_usb_len = 0;
+
+	usb_req->id = cpu_to_be32(usb_req_id);
+	usb_req->len  = cpu_to_be32(0);
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == cpu_to_be32(USB_REQ_BEACON_WR)) {
+		memcpy(buffer_dst, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst += sizeof(payload_len_nw);
+		temp_usb_len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst, buffer_src_p, buffer_len);
+	buffer_dst += buffer_len;
+	buffer_src_p += buffer_len;
+	temp_usb_len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst, 0, FCS_LEN);
+	buffer_dst += FCS_LEN;
+	temp_usb_len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (temp_usb_len %
+			PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len %
+				PURELIFI_BYTE_NUM_ALIGNMENT);
+		temp_usb_len += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->len = cpu_to_be32(temp_usb_len);
+}
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn,
+		       void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "Async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct plf_usb_req usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(__le32) + sizeof(__le32) +
+			   be32_to_cpu(usb_req.len);
+
+	dma_buffer = kmemdup(&usb_req, usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	u8 hw_address[ETH_ALEN];
+	u8 serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+	dev_err(&intf->dev, "Unit type is station");
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_err(&intf->dev, "Init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_err(&intf->dev, "Register device failed (%d)\n", r);
+		goto error;
+	}
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_err(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_switch_radio(chip, PLFXLC_RADIO_ON);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plf_usb_wreq(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	dev_err(&intf->dev, "pureLifi:Device error");
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = plfxlc_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"Restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	plfxlc_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+#ifdef CONFIG_PM
+	.suspend        = suspend,
+	.resume         = resume,
+#endif
+	.disable_hub_initiated_lpm = 1,
+};
+
+static struct workqueue_struct *plfxlc_workqueue;
+
+static int __init usb_init(void)
+{
+	int r;
+
+	plfxlc_workqueue = create_singlethread_workqueue(driver.name);
+	if (!plfxlc_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(plfxlc_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	usb_deregister(&driver);
+	destroy_workqueue(plfxlc_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("plfxlc/lifi-x.bin");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.h b/drivers/net/wireless/purelifi/plfxlc/usb.h
new file mode 100644
index 000000000000..b44d446d87ee
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#define PLF_FPGA_STATUS_LEN 2
+#define PLF_FPGA_STATE_LEN 9
+#define PLF_BULK_TLEN 16384
+#define PLF_FPGA_MG 6 /* Magic check */
+#define PLF_XL_BUF_LEN 64
+#define PLF_MSG_STATUS_OFFSET 7
+
+#define PLF_USB_TIMEOUT 1000
+#define PLF_MSLEEP_TIME 200
+
+#define PURELIFI_URB_RETRY_MAX 5
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+
+/* Tx retry backoff timer (in milliseconds) */
+#define TX_RETRY_BACKOFF_MS 10
+#define STA_QUEUE_CLEANUP_MS 5000
+
+/* Tx retry backoff timer (in jiffies) */
+#define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+#define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+struct purelifi_usb_interrupt {
+	spinlock_t lock; /* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock; /* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct plf_station {
+   /*  7...3    |    2      |     1     |     0	    |
+    * Reserved  | Heartbeat | FIFO full | Connected |
+    */
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+struct firmware_file {
+	u32 total_files;
+	u32 total_size;
+	u32 size;
+	u32 start_addr;
+	u32 control_packets;
+} __packed;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+#define STATION_ACTIVE_FLAG    0xFD
+
+#define PURELIFI_SERIAL_LEN 256
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+
+struct purelifi_usb_tx {
+	unsigned long enabled;
+	spinlock_t lock; /* spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	bool stopped;
+	struct timer_list tx_retry_timer;
+	struct plf_station station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	bool rx_usb_enabled;
+	bool initialized;
+	bool was_running;
+	bool link_up;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+enum {
+	PLF_BIT_ENABLED = 1,
+	PLF_BIT_MAX = 2,
+};
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn, void *context);
+
+static inline struct usb_device *
+purelifi_usb_to_usbdev(struct purelifi_usb *usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_intf_to_hw(struct usb_interface *intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_usb_to_hw(struct purelifi_usb *usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/* Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH 2/2] [v19 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-12 12:50     ` [PATCH 2/2] [v19 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
@ 2021-10-14  6:03       ` kernel test robot
  2021-10-24 17:58       ` kernel test robot
  1 sibling, 0 replies; 108+ messages in thread
From: kernel test robot @ 2021-10-14  6:03 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: kbuild-all, mostafa.afgani, Srinivasan Raju, Kalle Valo,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS)

[-- Attachment #1: Type: text/plain, Size: 5422 bytes --]

Hi Srinivasan,

I love your patch! Perhaps something to improve:

[auto build test WARNING on kvalo-wireless-drivers-next/master]
[also build test WARNING on kvalo-wireless-drivers/master jberg-mac80211-next/master jberg-mac80211/master v5.15-rc5 next-20211013]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Srinivasan-Raju/nl80211-Add-LC-placeholder-band-definition-to-enum-nl80211_band/20211012-215200
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next.git master
config: x86_64-allmodconfig (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce (this is a W=1 build):
        # https://github.com/0day-ci/linux/commit/7a077cc97d6f22264e6c9ac98d9e904ca908ccba
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Srinivasan-Raju/nl80211-Add-LC-placeholder-band-definition-to-enum-nl80211_band/20211012-215200
        git checkout 7a077cc97d6f22264e6c9ac98d9e904ca908ccba
        # save the attached .config to linux build tree
        make W=1 ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/net/wireless/purelifi/plfxlc/mac.c: In function 'purelifi_mac_rx':
>> drivers/net/wireless/purelifi/plfxlc/mac.c:424:28: warning: variable 'min_exp_seq_nmb' set but not used [-Wunused-but-set-variable]
     424 |  static unsigned short int min_exp_seq_nmb;
         |                            ^~~~~~~~~~~~~~~


vim +/min_exp_seq_nmb +424 drivers/net/wireless/purelifi/plfxlc/mac.c

   412	
   413	int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
   414			    unsigned int length)
   415	{
   416		struct purelifi_mac *mac = purelifi_hw_mac(hw);
   417		struct ieee80211_rx_status stats;
   418		const struct rx_status *status;
   419		struct sk_buff *skb;
   420		int bad_frame = 0;
   421		__le16 fc;
   422		int need_padding;
   423		unsigned int payload_length;
 > 424		static unsigned short int min_exp_seq_nmb;
   425		int sidx;
   426		struct purelifi_usb_tx *tx;
   427		/* Packet blockade during disabled interface. */
   428		if (!mac->vif)
   429			return 0;
   430	
   431		memset(&stats, 0, sizeof(stats));
   432		status = (struct rx_status *)buffer;
   433	
   434		stats.flag     = 0;
   435		stats.freq     = 2412;
   436		stats.band     = NL80211_BAND_LC;
   437		mac->rssi      = -15 * be16_to_cpu(status->rssi) / 10;
   438	
   439		stats.signal   = mac->rssi;
   440	
   441		if (status->rate_idx > 7)
   442			stats.rate_idx = 0;
   443		else
   444			stats.rate_idx = status->rate_idx;
   445	
   446		mac->crc_errors = be64_to_cpu(status->crc_error_count);
   447	
   448		if (!bad_frame &&
   449		    purelifi_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
   450		    !mac->pass_ctrl)
   451			return 0;
   452	
   453		buffer += sizeof(struct rx_status);
   454		payload_length = get_unaligned_be32(buffer);
   455	
   456		/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
   457		if (payload_length > 1560) {
   458			dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
   459			return 0;
   460		}
   461		buffer += sizeof(u32);
   462	
   463		fc = get_unaligned((__le16 *)buffer);
   464		need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
   465	
   466		tx = &mac->chip.usb.tx;
   467	
   468		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
   469			if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
   470				continue;
   471			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
   472				tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
   473				break;
   474			}
   475		}
   476	
   477		if (sidx == MAX_STA_NUM - 1) {
   478			for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
   479				if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
   480					continue;
   481				memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
   482				tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
   483				tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
   484				break;
   485			}
   486		}
   487	
   488		switch (buffer[0]) {
   489		case IEEE80211_STYPE_PROBE_REQ:
   490			dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
   491			break;
   492		case IEEE80211_STYPE_ASSOC_REQ:
   493			dev_dbg(purelifi_mac_dev(mac), "Association request\n");
   494			break;
   495		case IEEE80211_STYPE_AUTH:
   496			dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
   497			min_exp_seq_nmb = 0;
   498			break;
   499		case IEEE80211_FTYPE_DATA:
   500			dev_dbg(purelifi_mac_dev(mac), "802.11 data frame\n");
   501			break;
   502		}
   503	
   504		skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
   505		if (!skb)
   506			return -ENOMEM;
   507	
   508		if (need_padding)
   509			/* Make sure that the payload data is 4 byte aligned. */
   510			skb_reserve(skb, 2);
   511	
   512		skb_put_data(skb, buffer, payload_length);
   513		memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
   514		ieee80211_rx_irqsafe(hw, skb);
   515		return 0;
   516	}
   517	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 65585 bytes --]

^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH v20 0/2] wireless: New Driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (16 preceding siblings ...)
  2021-10-12 12:50   ` [PATCH 0/2] wireless: New Driver " Srinivasan Raju
@ 2021-10-18 10:00   ` Srinivasan Raju
  2021-10-18 10:00     ` [PATCH v20 1/2] nl80211: Add LC placeholder band definition to nl80211_band Srinivasan Raju
  2021-10-18 10:00     ` [PATCH v20 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  2021-10-31 13:10   ` [PATCH v21 0/2] wireless: New Driver " Srinivasan Raju
  2022-02-24 18:20   ` [PATCH v22 0/2] wireless: New Driver " Srinivasan Raju
  19 siblings, 2 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-10-18 10:00 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Johannes Berg, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

LiFi is a mobile wireless technology that uses light 
rather than radio frequencies to transmit data.

802.11 bb is focused on introducing necessary changes to 
IEEE 802.11 Stds to enable communications in the light medium

---
v20:
 - Remove unused static variable
v19:
 - Fix kmemdup null case
v18:
 - Use light communication band 
v16:
 - Fixed atomic variable misuses
 - Fixed comments spacing
 - Removed static variables used
 - Moved #defines to header file
 - Removed doxygen style comments
 - Removed magic numbers and cleanup code
v15:
 - resubmit v14 of the patch
v14:
 - Endianess comments addressed
 - Sparse checked and fixed warnings
 - Firmware files renamed to lowercase
 - All other review comments in v13 addressed
v13:
- Removed unused #defines
v12:
- Removed sysfs, procfs related code
- Addressed race condition bug
- Used macros instead of magic numbers in firmware.c
- Added copyright in all files
v11, v10:
- Addressed review comment on readability
- Changed firmware names to match products
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging

Srinivasan Raju (2):
  [v20 1/2] nl80211: Add LC placeholder band definition to nl80211_band
  [v20 2/2] wireless: Initial driver submission for pureLiFi STA devices

 MAINTAINERS                                   |   6 +
 drivers/net/wireless/Kconfig                  |   1 +
 drivers/net/wireless/Makefile                 |   1 +
 drivers/net/wireless/purelifi/Kconfig         |  17 +
 drivers/net/wireless/purelifi/Makefile        |   2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |  14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |   3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |  95 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |  89 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   | 275 +++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |  52 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    | 770 ++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    | 190 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 975 ++++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    | 196 ++++
 include/uapi/linux/nl80211.h                  |   2 +
 net/mac80211/mlme.c                           |   1 +
 net/mac80211/sta_info.c                       |   1 +
 net/mac80211/tx.c                             |   3 +-
 net/wireless/nl80211.c                        |   1 +
 net/wireless/util.c                           |   2 +
 21 files changed, 2695 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

-- 
2.25.1


^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH v20 1/2] nl80211: Add LC placeholder band definition to nl80211_band
  2021-10-18 10:00   ` [PATCH v20 0/2] wireless: New Driver " Srinivasan Raju
@ 2021-10-18 10:00     ` Srinivasan Raju
  2021-10-18 10:00     ` [PATCH v20 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  1 sibling, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-10-18 10:00 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Johannes Berg, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Define LC band which is a draft under IEEE 802.11 bb
Current NL80211_BAND_LC is a placeholder band
The band will be redefined as IEEE 802.11 bb progresses

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
---
 include/uapi/linux/nl80211.h | 2 ++
 net/mac80211/mlme.c          | 1 +
 net/mac80211/sta_info.c      | 1 +
 net/mac80211/tx.c            | 3 ++-
 net/wireless/nl80211.c       | 1 +
 net/wireless/util.c          | 2 ++
 6 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index c2efea98e060..cb06fb604a60 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -4929,6 +4929,7 @@ enum nl80211_txrate_gi {
  * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz)
  * @NL80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.2 GHz)
  * @NL80211_BAND_S1GHZ: around 900MHz, supported by S1G PHYs
+ * @NL80211_BAND_LC: light communication band (placeholder)
  * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
  *	since newer kernel versions may support more bands
  */
@@ -4938,6 +4939,7 @@ enum nl80211_band {
 	NL80211_BAND_60GHZ,
 	NL80211_BAND_6GHZ,
 	NL80211_BAND_S1GHZ,
+	NL80211_BAND_LC,
 
 	NUM_NL80211_BANDS,
 };
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index c0ea3b1aa9e1..c577d03ab128 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1490,6 +1490,7 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
 		fallthrough;
 	case NL80211_BAND_2GHZ:
 	case NL80211_BAND_60GHZ:
+	case NL80211_BAND_LC:
 		chan_increment = 1;
 		break;
 	case NL80211_BAND_5GHZ:
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 2b5acb37587f..36524101d11f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -444,6 +444,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
 		switch (i) {
 		case NL80211_BAND_2GHZ:
+		case NL80211_BAND_LC:
 			/*
 			 * We use both here, even if we cannot really know for
 			 * sure the station will support both, but the only use
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 2d1193ed3eb5..d311937f2add 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -146,7 +146,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
 			rate = DIV_ROUND_UP(r->bitrate, 1 << shift);
 
 		switch (sband->band) {
-		case NL80211_BAND_2GHZ: {
+		case NL80211_BAND_2GHZ:
+		case NL80211_BAND_LC: {
 			u32 flag;
 			if (tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
 				flag = IEEE80211_RATE_MANDATORY_G;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index bf7cd4752547..cf1434049abb 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -853,6 +853,7 @@ nl80211_match_band_rssi_policy[NUM_NL80211_BANDS] = {
 	[NL80211_BAND_5GHZ] = { .type = NLA_S32 },
 	[NL80211_BAND_6GHZ] = { .type = NLA_S32 },
 	[NL80211_BAND_60GHZ] = { .type = NLA_S32 },
+	[NL80211_BAND_LC]    = { .type = NLA_S32 },
 };
 
 static const struct nla_policy
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 18dba3d7c638..2991f711491a 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -80,6 +80,7 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
 		return 0; /* not supported */
 	switch (band) {
 	case NL80211_BAND_2GHZ:
+	case NL80211_BAND_LC:
 		if (chan == 14)
 			return MHZ_TO_KHZ(2484);
 		else if (chan < 14)
@@ -209,6 +210,7 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband)
 		WARN_ON(want);
 		break;
 	case NL80211_BAND_2GHZ:
+	case NL80211_BAND_LC:
 		want = 7;
 		for (i = 0; i < sband->n_bitrates; i++) {
 			switch (sband->bitrates[i].bitrate) {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v20 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-18 10:00   ` [PATCH v20 0/2] wireless: New Driver " Srinivasan Raju
  2021-10-18 10:00     ` [PATCH v20 1/2] nl80211: Add LC placeholder band definition to nl80211_band Srinivasan Raju
@ 2021-10-18 10:00     ` Srinivasan Raju
  2021-10-25  9:59       ` Kari Argillander
                         ` (2 more replies)
  1 sibling, 3 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-10-18 10:00 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Johannes Berg, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This driver implementation has been based on the zd1211rw driver

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
---
 MAINTAINERS                                   |   6 +
 drivers/net/wireless/Kconfig                  |   1 +
 drivers/net/wireless/Makefile                 |   1 +
 drivers/net/wireless/purelifi/Kconfig         |  17 +
 drivers/net/wireless/purelifi/Makefile        |   2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |  14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |   3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |  95 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |  89 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   | 275 +++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |  52 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    | 770 ++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    | 190 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 975 ++++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    | 196 ++++
 15 files changed, 2686 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index eeb4c70b3d5b..1d271e9e9223 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15193,6 +15193,12 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PURELIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+L:	linux-wireless@vger.kernel.org
+S:	Supported
+F:	drivers/net/wireless/purelifi/
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7add2002ff4c..404afe574920 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -28,6 +28,7 @@ source "drivers/net/wireless/intersil/Kconfig"
 source "drivers/net/wireless/marvell/Kconfig"
 source "drivers/net/wireless/mediatek/Kconfig"
 source "drivers/net/wireless/microchip/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 source "drivers/net/wireless/ralink/Kconfig"
 source "drivers/net/wireless/realtek/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e3345893c9c5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
 obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
 obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
 obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
 obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
 obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..e39afec3dcae
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+source "drivers/net/wireless/purelifi/plfxlc/Kconfig"
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..56ebf96bd298
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)		:= plfxlc/
diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
new file mode 100644
index 000000000000..400ab2ee660c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config PURELIFI_XLC
+	tristate "pureLiFi X, XL, XC device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This option adds support for pureLiFi LiFi wireless USB adapters.
+
+	   The pureLiFi X, XL, XC USB devices are based on 802.11 OFDM PHY.
+
+	   Supports common 802.11 encryption/authentication methods including
+	   Open, WPA, WPA2-Personal and WPA2-Enterprise (802.1X).
+
+	   To compile this driver as a module, choose m here. The module will
+	   be called purelifi_xlc.
diff --git a/drivers/net/wireless/purelifi/plfxlc/Makefile b/drivers/net/wireless/purelifi/plfxlc/Makefile
new file mode 100644
index 000000000000..3d66f485c024
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)	:= purelifi_xlc.o
+purelifi_xlc-objs 		+= chip.o firmware.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.c b/drivers/net/wireless/purelifi/plfxlc/chip.c
new file mode 100644
index 000000000000..ece74682d413
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	purelifi_usb_init(&chip->usb, hw, intf);
+}
+
+void purelifi_chip_release(struct purelifi_chip *chip)
+{
+	purelifi_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set &&
+	     le16_to_cpu(chip->beacon_interval) == interval))
+		return 0;
+
+	chip->beacon_interval = cpu_to_le16(interval);
+	chip->beacon_set = true;
+	return plf_usb_wreq(&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip)
+{
+	unsigned char *addr = purelifi_mac_get_perm_addr(purelifi_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("purelifi chip %04x:%04x v%02x %pM %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr,
+		purelifi_speed(udev->speed));
+
+	return purelifi_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value)
+{
+	int r;
+	__le16 radio_on = cpu_to_le16(value);
+
+	r = plf_usb_wreq(&radio_on, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip)
+{
+	purelifi_usb_enable_tx(&chip->usb);
+	return purelifi_usb_enable_rx(&chip->usb);
+}
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip)
+{
+	u8 value = 0;
+
+	plf_usb_wreq(&value, sizeof(value), USB_REQ_RXTX_WR);
+	purelifi_usb_disable_rx(&chip->usb);
+	purelifi_usb_disable_tx(&chip->usb);
+}
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = plf_usb_wreq(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(purelifi_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.h b/drivers/net/wireless/purelifi/plfxlc/chip.h
new file mode 100644
index 000000000000..167e9b9bd7a7
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type {
+	STA = 0,
+	AP = 1,
+};
+
+enum {
+	PLFXLC_RADIO_OFF = 0,
+	PLFXLC_RADIO_ON = 1,
+};
+
+struct purelifi_chip {
+	struct purelifi_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type unit_type;
+	u16 link_led;
+	u8 beacon_set;
+	__le16 beacon_interval;
+};
+
+struct purelifi_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define purelifi_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void purelifi_chip_init(struct purelifi_chip *chip,
+			struct ieee80211_hw *hw,
+			struct usb_interface *intf);
+
+void purelifi_chip_release(struct purelifi_chip *chip);
+
+void purelifi_chip_disable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_init_hw(struct purelifi_chip *chip);
+
+int purelifi_chip_enable_rxtx(struct purelifi_chip *chip);
+
+int purelifi_chip_set_rate(struct purelifi_chip *chip, u8 rate);
+
+int purelifi_set_beacon_interval(struct purelifi_chip *chip, u16 interval,
+				 u8 dtim_period, int type);
+
+int purelifi_chip_switch_radio(struct purelifi_chip *chip, u16 value);
+
+static inline struct purelifi_chip *purelifi_usb_to_chip(struct purelifi_usb
+							 *usb)
+{
+	return container_of(usb, struct purelifi_chip, usb);
+}
+
+static inline void purelifi_mc_clear(struct purelifi_mc_hash *hash)
+{
+	hash->low = 0;
+	/* The interfaces must always received broadcasts.
+	 * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+	 */
+	hash->high = 0x80000000;
+}
+
+static inline void purelifi_mc_add_all(struct purelifi_mc_hash *hash)
+{
+	hash->low  = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+static inline void purelifi_mc_add_addr(struct purelifi_mc_hash *hash,
+					u8 *addr)
+{
+	unsigned int i = addr[5] >> 2;
+
+	if (i < 32)
+		hash->low |= 1U << i;
+	else
+		hash->high |= 1 << (i - 32);
+}
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/plfxlc/firmware.c b/drivers/net/wireless/purelifi/plfxlc/firmware.c
new file mode 100644
index 000000000000..30a8a63e13b6
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/firmware.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/firmware.h>
+#include <linux/bitrev.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+	int r, actual_length;
+	int fw_data_i, blk_tran_len = PLF_BULK_TLEN;
+	const char *fw_name;
+	unsigned char *fpga_dmabuff = NULL;
+	unsigned char *fw_data;
+	const struct firmware *fw = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-x.bin";
+		dev_dbg(&intf->dev, "bin file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-xc.bin";
+		dev_dbg(&intf->dev, "bin file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware(&fw, fw_name, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATUS_LEN, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ,
+			    fpga_dmabuff, PLF_FPGA_STATUS_LEN);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_SET_CMD, NULL, 0);
+
+	if (fpga_dmabuff[0] != PLF_FPGA_MG) {
+		dev_err(&intf->dev, "fpga_dmabuff[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+		if (!fw_data) {
+			r = -ENOMEM;
+			goto error_free_fw;
+		}
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			/* u8 bit reverse */
+			fw_data[tbuf_idx] = bitrev8(fw_data[tbuf_idx]);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_dmabuff[0] & 0xff),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2 * PLF_USB_TIMEOUT);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	kfree(fpga_dmabuff);
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATE_LEN, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xff, PLF_FPGA_STATE_LEN);
+
+	send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
+			    PLF_FPGA_STATE_LEN);
+
+	dev_dbg(&intf->dev, "%*ph\n", 8, fpga_dmabuff);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
+
+	msleep(PLF_MSLEEP_TIME);
+
+error_free_fw:
+	kfree(fpga_dmabuff);
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+	const struct firmware *fwp = NULL;
+	struct firmware_file file = {0};
+	int s, r;
+	u8 *buf;
+	u32 i;
+	const char *fw_pack;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
+	msleep(PLF_MSLEEP_TIME);
+
+	if (r) {
+		dev_err(&intf->dev, "vendor command failed (%d)\n", r);
+		return -EINVAL;
+	}
+	/* Code for single pack file download */
+
+	fw_pack = "plfxlc/lifi-xl.bin";
+
+	r = request_firmware(&fwp, fw_pack, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "Request_firmware failed (%d)\n", r);
+		return -EINVAL;
+	}
+	file.total_files = get_unaligned_le32(&fwp->data[0]);
+	file.total_size = get_unaligned_le32(&fwp->size);
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n",
+		file.total_files, file.total_size);
+
+	buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
+	if (!buf) {
+		release_firmware(fwp);
+		return -ENOMEM;
+	}
+
+	if (file.total_files > 10) {
+		dev_err(&intf->dev, "Too many files (%d)\n", file.total_files);
+		release_firmware(fwp);
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	/* Download firmware files in multiple steps */
+	for (s = 0; s < file.total_files; s++) {
+		buf[0] = s;
+		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
+					PLF_XL_BUF_LEN);
+
+		if (s < file.total_files - 1)
+			file.size = get_unaligned_le32(&fwp->data[4 + ((s + 1) * 4)])
+				    - get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+		else
+			file.size = file.total_size -
+				    get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+
+		if (file.size > file.total_size || file.size > 60000) {
+			dev_err(&intf->dev, "File size is too large (%d)\n", file.size);
+			break;
+		}
+
+		file.start_addr = get_unaligned_le32(&fwp->data[4 + (s * 4)]);
+
+		if (file.size % PLF_XL_BUF_LEN && s < 2)
+			file.size += PLF_XL_BUF_LEN - file.size % PLF_XL_BUF_LEN;
+
+		file.control_packets = file.size / PLF_XL_BUF_LEN;
+
+		for (i = 0; i < file.control_packets; i++) {
+			memcpy(buf,
+			       &fwp->data[file.start_addr + (i * PLF_XL_BUF_LEN)],
+			       PLF_XL_BUF_LEN);
+			r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
+						PLF_XL_BUF_LEN);
+		}
+		dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", s, r,
+			file.size);
+	}
+	release_firmware(fwp);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish*/
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
+	dev_dbg(&intf->dev, "Download fpga (4) (%d)\n", r);
+
+	return 0;
+}
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	unsigned char *dma_buffer = NULL;
+	unsigned long long firmware_version;
+
+	dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);
+	if (!dma_buffer)
+		return -ENOMEM;
+
+	BUILD_BUG_ON(ETH_ALEN > PLF_SERIAL_LEN);
+	BUILD_BUG_ON(PLF_FW_VER_LEN > PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	memcpy(serial_number, dma_buffer, PLF_SERIAL_LEN);
+
+	memset(dma_buffer, 0x00, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, PLF_FW_VER_LEN);
+
+	memcpy(&firmware_version, dma_buffer, PLF_FW_VER_LEN);
+
+	dev_info(&intf->dev, "Firmware Version: %llu\n", firmware_version);
+	kfree(dma_buffer);
+
+	dev_dbg(&intf->dev, "Mac: %pM\n", hw_address);
+
+	return 0;
+}
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/intf.h b/drivers/net/wireless/purelifi/plfxlc/intf.h
new file mode 100644
index 000000000000..5ae89343b579
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/intf.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#define ETH_ALEN 6
+#define AP_USER_LIMIT 8
+
+#define PLF_VNDR_FPGA_STATE_REQ 0x30
+#define PLF_VNDR_FPGA_SET_REQ 0x33
+#define PLF_VNDR_FPGA_SET_CMD 0x34
+#define PLF_VNDR_FPGA_STATE_CMD 0x35
+
+#define PLF_VNDR_XL_FW_CMD 0x80
+#define PLF_VNDR_XL_DATA_CMD 0x81
+#define PLF_VNDR_XL_FILE_CMD 0x82
+#define PLF_VNDR_XL_EX_CMD 0x83
+
+#define PLF_MAC_VENDOR_REQUEST 0x36
+#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define PLF_SERIAL_LEN 14
+#define PLF_FW_VER_LEN 8
+
+struct rx_status {
+	__be16 rssi;
+	u8     rate_idx;
+	u8     pad;
+	__be64 crc_error_count;
+} __packed;
+
+enum plf_usb_req_enum {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct plf_usb_req {
+	__be32         id; /* should be plf_usb_req_enum */
+	__be32	       len;
+	u8             buf[512];
+};
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
new file mode 100644
index 000000000000..6e403d5ac84c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.c
@@ -0,0 +1,770 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+static const struct ieee80211_rate purelifi_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 }
+};
+
+static const struct ieee80211_channel purelifi_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+int purelifi_mac_init_hw(struct ieee80211_hw *hw)
+{
+	int r;
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_chip *chip = &mac->chip;
+
+	r = purelifi_chip_init_hw(chip);
+	if (r) {
+		dev_warn(purelifi_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(purelifi_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	regulatory_hint(hw->wiphy, "00");
+	return r;
+}
+
+void purelifi_mac_release(struct purelifi_mac *mac)
+{
+	purelifi_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int plfxlc_op_start(struct ieee80211_hw *hw)
+{
+	purelifi_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void plfxlc_op_stop(struct ieee80211_hw *hw)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int purelifi_restore_settings(struct purelifi_mac *mac)
+{
+	struct sk_buff *beacon;
+	int beacon_interval, beacon_period;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			purelifi_mac_config_beacon(mac->hw, beacon);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 *  driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
+				     beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+static void purelifi_mac_tx_status(struct ieee80211_hw *hw,
+				   struct sk_buff *skb,
+				   int ackssi,
+				   struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1;
+
+	ieee80211_tx_info_clear_status(info);
+
+	if (tx_status)
+		success = !tx_status->failure;
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct purelifi_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+	} else {
+		struct sk_buff_head *q = &mac->ack_wait_queue;
+
+		skb_queue_tail(q, skb);
+		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+			purelifi_mac_tx_status(hw, skb_dequeue(q),
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+	}
+}
+
+static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
+				      struct sk_buff *beacon)
+{
+	return plf_usb_wreq(beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	unsigned int tmp;
+	u32 temp_len = 0;
+	u32 temp_payload_len = 0;
+	struct purelifi_ctrlset *cs;
+
+	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
+		dev_dbg(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (void *)skb_push(skb, sizeof(struct purelifi_ctrlset));
+	temp_payload_len = frag_len;
+	temp_len = temp_payload_len +
+		  sizeof(struct purelifi_ctrlset) -
+		  sizeof(cs->id) - sizeof(cs->len);
+
+	/* data packet lengths must be multiple of four bytes
+	 * and must not be a multiple of 512
+	 * bytes. First, it is attempted to append the
+	 * data packet in the tailroom of the skb. In rare
+	 * ocasions, the tailroom is too small. In this case,
+	 * the content of the packet is shifted into
+	 * the headroom of the skb by memcpy. Headroom is allocated
+	 * at startup (below in this file). Therefore,
+	 * there will be always enough headroom. The call skb_headroom
+	 * is an additional safety which might be
+	 * dropped.
+	 */
+
+	/* check if 32 bit aligned and align data */
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memmove(dest_pt, src_pt, len);
+			} else {
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		temp_len += 4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data */
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memcpy(dest_pt, src_pt, len);
+			} else {
+				/* should never happen because
+				 * sufficient headroom was reserved
+				 */
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		temp_len += 4;
+	}
+
+	cs->id = cpu_to_be32(USB_REQ_DATA_TX);
+	cs->len = cpu_to_be32(temp_len);
+	cs->payload_len_nw = cpu_to_be32(temp_payload_len);
+
+	return 0;
+}
+
+static void plfxlc_op_tx(struct ieee80211_hw *hw,
+			 struct ieee80211_tx_control *control,
+			 struct sk_buff *skb)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct purelifi_usb *usb = &mac->chip.usb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct purelifi_header *plhdr = (void *)skb->data;
+	unsigned long flags;
+	int r;
+
+	r = fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+
+	info->rate_driver_data[0] = hw;
+
+	if (plhdr->frametype  == IEEE80211_FTYPE_DATA) {
+		u8 *dst_mac = plhdr->dmac;
+		u8 sidx;
+		bool found = false;
+		struct purelifi_usb_tx *tx = &usb->tx;
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
+				found = true;
+				break;
+			}
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			ieee80211_stop_queues(purelifi_usb_to_hw(usb));
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
+			skb_queue_tail(&tx->station[sidx].data_list, skb);
+			purelifi_send_packet_from_data_queue(usb);
+		} else {
+			goto fail;
+		}
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = plf_usb_wreq_async(&mac->chip.usb, skb->data, skb->len,
+				       USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+static int purelifi_filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+			       struct ieee80211_rx_status *stats)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct sk_buff *skb;
+	struct sk_buff_head *q;
+	unsigned long flags;
+	bool found = 0;
+	int i, position = 0;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_dbg(purelifi_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		else if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			purelifi_mac_tx_status(hw, skb,
+					       mac->ack_pending ?
+					       mac->ack_signal : 0,
+					       NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+	int bad_frame = 0;
+	__le16 fc;
+	int need_padding;
+	unsigned int payload_length;
+	int sidx;
+	struct purelifi_usb_tx *tx;
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	memset(&stats, 0, sizeof(stats));
+	status = (struct rx_status *)buffer;
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_LC;
+	mac->rssi      = -15 * be16_to_cpu(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	mac->crc_errors = be64_to_cpu(status->crc_error_count);
+
+	if (!bad_frame &&
+	    purelifi_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = get_unaligned_be32(buffer);
+
+	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
+	if (payload_length > 1560) {
+		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	switch (buffer[0]) {
+	case IEEE80211_STYPE_PROBE_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
+		break;
+	case IEEE80211_STYPE_ASSOC_REQ:
+		dev_dbg(purelifi_mac_dev(mac), "Association request\n");
+		break;
+	case IEEE80211_STYPE_AUTH:
+		dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
+		break;
+	case IEEE80211_FTYPE_DATA:
+		dev_dbg(purelifi_mac_dev(mac), "802.11 data frame\n");
+		break;
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+
+	if (need_padding)
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+
+	skb_put_data(skb, buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int plfxlc_op_add_interface(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_dbg(purelifi_mac_dev(mac), "%s %s\n", __func__,
+			iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+	} else {
+		dev_dbg(purelifi_mac_dev(mac), "unsupported iftype\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static void plfxlc_op_remove_interface(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int plfxlc_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void plfxlc_op_configure_filter(struct ieee80211_hw *hw,
+				       unsigned int changed_flags,
+				       unsigned int *new_flags,
+				       u64 multicast)
+{
+	struct purelifi_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	changed_flags &= SUPPORTED_FIF_FLAGS;
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by plfxlc_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		purelifi_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void plfxlc_op_bss_info_changed(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *bss_conf,
+				       u32 changes)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+	int associated;
+
+	dev_dbg(purelifi_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			purelifi_mac_config_beacon(hw, beacon);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		purelifi_set_beacon_interval(&mac->chip, interval,
+					     period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int purelifi_get_stats(struct ieee80211_hw *hw,
+			      struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+static const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int purelifi_get_et_sset_count(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void purelifi_get_et_strings(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void purelifi_get_et_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ethtool_stats *stats, u64 *data)
+{
+	struct purelifi_mac *mac = purelifi_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int purelifi_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops plfxlc_ops = {
+	.tx                 = plfxlc_op_tx,
+	.start              = plfxlc_op_start,
+	.stop               = plfxlc_op_stop,
+	.add_interface      = plfxlc_op_add_interface,
+	.remove_interface   = plfxlc_op_remove_interface,
+	.set_rts_threshold  = purelifi_set_rts_threshold,
+	.config             = plfxlc_op_config,
+	.configure_filter   = plfxlc_op_configure_filter,
+	.bss_info_changed   = plfxlc_op_bss_info_changed,
+	.get_stats          = purelifi_get_stats,
+	.get_et_sset_count  = purelifi_get_et_sset_count,
+	.get_et_stats       = purelifi_get_et_stats,
+	.get_et_strings     = purelifi_get_et_strings,
+};
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct purelifi_mac *mac;
+	struct ieee80211_hw *hw;
+
+	hw = ieee80211_alloc_hw(sizeof(struct purelifi_mac), &plfxlc_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = purelifi_hw_mac(hw);
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, purelifi_channels, sizeof(purelifi_channels));
+	memcpy(mac->rates, purelifi_rates, sizeof(purelifi_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(purelifi_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(purelifi_channels);
+	mac->band.channels = mac->channels;
+	hw->wiphy->bands[NL80211_BAND_LC] = &mac->band;
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+
+	ieee80211_hw_set(hw, RX_INCLUDES_FCS);
+	ieee80211_hw_set(hw, SIGNAL_DBM);
+	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+	ieee80211_hw_set(hw, MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+	hw->max_signal = 100;
+	hw->queues = 1;
+	/* 4 for 32 bit alignment if no tailroom */
+	hw->extra_tx_headroom = sizeof(struct purelifi_ctrlset) + 4;
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	purelifi_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.h b/drivers/net/wireless/purelifi/plfxlc/mac.h
new file mode 100644
index 000000000000..3852d7343a40
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.h
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define purelifi_mac_dev(mac) purelifi_chip_dev(&(mac)->chip)
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct purelifi_ctrlset {
+	/* id should be plf_usb_req_enum */
+	__be32		id;
+	__be32		len;
+	u8              modulation;
+	u8              control;
+	u8              service;
+	u8		pad;
+	__le16		packet_length;
+	__le16		current_length;
+	__le16		next_frame_length;
+	__le16		tx_length;
+	__be32		payload_len_nw;
+} __packed;
+
+/* overlay */
+struct purelifi_header {
+	struct purelifi_ctrlset plf_ctrl;
+	u32    frametype;
+	u8    *dmac;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum purelifi_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct purelifi_mac {
+	struct purelifi_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct purelifi_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 channel;
+	int type;
+	int associated;
+	unsigned long flags;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+
+	/* whether to pass frames with CRC errors to stack */
+	bool pass_failed_fcs;
+
+	/* whether to pass control frames to stack */
+	bool pass_ctrl;
+
+	/* whether we have received a 802.11 ACK that is pending */
+	bool ack_pending;
+
+	/* signal strength of the last 802.11 ACK received */
+	int ack_signal;
+
+	unsigned char hw_address[ETH_ALEN];
+	char serial_number[PURELIFI_SERIAL_LEN];
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct purelifi_mac *
+purelifi_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct purelifi_mac *
+purelifi_chip_to_mac(struct purelifi_chip *chip)
+{
+	return container_of(chip, struct purelifi_mac, chip);
+}
+
+static inline struct purelifi_mac *
+purelifi_usb_to_mac(struct purelifi_usb *usb)
+{
+	return purelifi_chip_to_mac(purelifi_usb_to_chip(usb));
+}
+
+static inline u8 *purelifi_mac_get_perm_addr(struct purelifi_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *purelifi_mac_alloc_hw(struct usb_interface *intf);
+void purelifi_mac_release(struct purelifi_mac *mac);
+
+int purelifi_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address);
+int purelifi_mac_init_hw(struct ieee80211_hw *hw);
+
+int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		    unsigned int length);
+void purelifi_mac_tx_failed(struct urb *urb);
+void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error);
+int plfxlc_op_start(struct ieee80211_hw *hw);
+void plfxlc_op_stop(struct ieee80211_hw *hw);
+int purelifi_restore_settings(struct purelifi_mac *mac);
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
new file mode 100644
index 000000000000..8cd9f223025e
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -0,0 +1,975 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+
+struct usb_interface *ez_usb_interface;
+
+static const struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+static inline u16 get_bcd_device(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
+{
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	static u8 sidx;
+	u8 last_served_sidx;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = sidx;
+	do {
+		sidx = (sidx + 1) % MAX_STA_NUM;
+		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+			continue;
+		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[sidx].data_list);
+	} while ((sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[sidx].data_list);
+		plf_usb_wreq_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				   tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
+			ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct purelifi_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	purelifi_mac_rx(purelifi_usb_to_hw(usb), buffer, length);
+}
+
+static void rx_urb_complete(struct urb *urb)
+{
+	int r;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	const u8 *buffer;
+	unsigned int length;
+	u16 status;
+	u8 sidx;
+
+	if (!urb) {
+		pr_err("urb is NULL\n");
+		return;
+	} else if (!urb->context) {
+		pr_err("urb ctx is NULL\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1) {
+		pr_err("usb is not initialized\n");
+		return;
+	}
+
+	tx = &usb->tx;
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		if (tx->submitted_urbs++ < PURELIFI_URB_RETRY_MAX) {
+			dev_dbg(urb_dev(urb), "urb %p resubmit %d", urb,
+				tx->submitted_urbs++);
+			goto resubmit;
+		} else {
+			dev_dbg(urb_dev(urb), "urb %p  max resubmits reached", urb);
+			tx->submitted_urbs = 0;
+			return;
+		}
+	}
+
+	buffer = urb->transfer_buffer;
+	length = le32_to_cpu(*(__le32 *)(buffer + sizeof(struct rx_status)))
+		 + sizeof(u32);
+
+	if (urb->actual_length != (PLF_MSG_STATUS_OFFSET + 1)) {
+		if (usb->initialized && usb->link_up)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[PLF_MSG_STATUS_OFFSET];
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_dbg(&usb->intf->dev,
+			"FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_dbg(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= STATION_ACTIVE_FLAG;
+
+		purelifi_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		usb->link_up = 1;
+		dev_dbg(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		usb->link_up = 0;
+		dev_dbg(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_dbg(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit fail (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct purelifi_usb *usb)
+{
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int i, r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(purelifi_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0;
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int purelifi_usb_enable_rx(struct purelifi_usb *usb)
+{
+	int r;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		usb->rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct purelifi_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+}
+
+void purelifi_usb_disable_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	usb->rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+/**
+ * purelifi_usb_disable_tx - disable transmission
+ * @usb: the driver USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void purelifi_usb_disable_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following purelifi_usb_enable_tx().
+	 */
+}
+
+/**
+ * purelifi_usb_enable_tx - enables transmission
+ * @usb: a &struct purelifi_usb pointer
+ *
+ * This function enables transmission and prepares the &purelifi_usb_tx data
+ * structure.
+ */
+void purelifi_usb_enable_tx(struct purelifi_usb *usb)
+{
+	unsigned long flags;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	set_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(purelifi_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+void tx_urb_complete(struct urb *urb)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct purelifi_usb *usb;
+
+	skb = urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by purelifi_mac_tx_to_dev or mac80211)
+	 */
+	usb = &purelifi_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	purelifi_mac_tx_to_dev(skb, urb->status);
+	purelifi_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
+{
+	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
+	struct urb *urb;
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	if (!test_bit(PLF_BIT_ENABLED, &tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  skb->data, skb->len, tx_urb_complete, skb);
+
+	info->rate_driver_data[1] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
+	usb_anchor_urb(urb, &tx->submitted);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
+			urb, r);
+		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
+		goto error;
+	}
+	return 0;
+error:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_rx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(purelifi_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct purelifi_usb *usb)
+{
+	struct purelifi_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void purelifi_usb_release(struct purelifi_usb *usb)
+{
+	plfxlc_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+}
+
+const char *purelifi_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int purelifi_usb_init_hw(struct purelifi_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(purelifi_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(purelifi_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, void *buffer,
+			u32 buffer_len, enum plf_usb_req_enum usb_req_id,
+			struct plf_usb_req *usb_req)
+{
+	u8 *buffer_dst = usb_req->buf;
+	const u8 *buffer_src_p = buffer;
+	__be32 payload_len_nw = cpu_to_be32(buffer_len + FCS_LEN);
+	u32 temp_usb_len = 0;
+
+	usb_req->id = cpu_to_be32(usb_req_id);
+	usb_req->len  = cpu_to_be32(0);
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == cpu_to_be32(USB_REQ_BEACON_WR)) {
+		memcpy(buffer_dst, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst += sizeof(payload_len_nw);
+		temp_usb_len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst, buffer_src_p, buffer_len);
+	buffer_dst += buffer_len;
+	buffer_src_p += buffer_len;
+	temp_usb_len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst, 0, FCS_LEN);
+	buffer_dst += FCS_LEN;
+	temp_usb_len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (temp_usb_len %
+			PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len %
+				PURELIFI_BYTE_NUM_ALIGNMENT);
+		temp_usb_len += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->len = cpu_to_be32(temp_usb_len);
+}
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn,
+		       void *context)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+
+	if (r)
+		dev_err(&udev->dev, "Async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id)
+{
+	int r;
+	int actual_length;
+	int usb_bulk_msg_len;
+	unsigned char *dma_buffer = NULL;
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct plf_usb_req usb_req;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(__le32) + sizeof(__le32) +
+			   be32_to_cpu(usb_req.len);
+
+	dma_buffer = kmemdup(&usb_req, usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r)
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	purelifi_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct purelifi_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct purelifi_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	int r = 0;
+	struct purelifi_chip *chip;
+	struct purelifi_usb *usb;
+	struct purelifi_usb_tx *tx;
+	struct ieee80211_hw *hw = NULL;
+	u8 hw_address[ETH_ALEN];
+	u8 serial_number[PURELIFI_SERIAL_LEN];
+	unsigned int i;
+
+	ez_usb_interface = intf;
+
+	hw = purelifi_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &purelifi_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+	chip->unit_type = STA;
+	dev_err(&intf->dev, "Unit type is station");
+
+	r = purelifi_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_err(&intf->dev, "Init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_err(&intf->dev, "Register device failed (%d)\n", r);
+		goto error;
+	}
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_usb_init_hw(usb);
+	if (r < 0) {
+		dev_err(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_switch_radio(chip, PLFXLC_RADIO_ON);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = purelifi_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plf_usb_wreq(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	purelifi_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	usb->initialized = 1;
+	return 0;
+error:
+	if (hw) {
+		purelifi_mac_release(purelifi_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	dev_err(&intf->dev, "pureLifi:Device error");
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here,
+	 * the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	purelifi_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void purelifi_usb_resume(struct purelifi_usb *usb)
+{
+	struct purelifi_mac *mac = purelifi_usb_to_mac(usb);
+	int r;
+
+	r = plfxlc_op_start(purelifi_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(purelifi_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = purelifi_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(purelifi_usb_dev(usb),
+				"Restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void purelifi_usb_stop(struct purelifi_usb *usb)
+{
+	plfxlc_op_stop(purelifi_usb_to_hw(usb));
+	purelifi_usb_disable_tx(usb);
+	purelifi_usb_disable_rx(usb);
+
+	usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	purelifi_usb_stop(usb);
+
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct purelifi_mac *mac;
+	struct purelifi_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = purelifi_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	if (usb->was_running)
+		purelifi_usb_resume(usb);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
+	struct purelifi_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with
+	 * a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = purelifi_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+	struct purelifi_mac *mac = purelifi_usb_to_mac(pl);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	purelifi_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct purelifi_usb *pl = get_purelifi_usb(interface);
+
+	if (!pl || !purelifi_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		purelifi_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name        = KBUILD_MODNAME,
+	.id_table    = usb_ids,
+	.probe        = probe,
+	.disconnect    = disconnect,
+	.pre_reset    = pre_reset,
+	.post_reset    = post_reset,
+#ifdef CONFIG_PM
+	.suspend        = suspend,
+	.resume         = resume,
+#endif
+	.disable_hub_initiated_lpm = 1,
+};
+
+static struct workqueue_struct *plfxlc_workqueue;
+
+static int __init usb_init(void)
+{
+	int r;
+
+	plfxlc_workqueue = create_singlethread_workqueue(driver.name);
+	if (!plfxlc_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(plfxlc_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	usb_deregister(&driver);
+	destroy_workqueue(plfxlc_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("plfxlc/lifi-x.bin");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.h b/drivers/net/wireless/purelifi/plfxlc/usb.h
new file mode 100644
index 000000000000..b44d446d87ee
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#define PLF_FPGA_STATUS_LEN 2
+#define PLF_FPGA_STATE_LEN 9
+#define PLF_BULK_TLEN 16384
+#define PLF_FPGA_MG 6 /* Magic check */
+#define PLF_XL_BUF_LEN 64
+#define PLF_MSG_STATUS_OFFSET 7
+
+#define PLF_USB_TIMEOUT 1000
+#define PLF_MSLEEP_TIME 200
+
+#define PURELIFI_URB_RETRY_MAX 5
+
+#define purelifi_usb_dev(usb) (&(usb)->intf->dev)
+
+/* Tx retry backoff timer (in milliseconds) */
+#define TX_RETRY_BACKOFF_MS 10
+#define STA_QUEUE_CLEANUP_MS 5000
+
+/* Tx retry backoff timer (in jiffies) */
+#define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+#define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+struct purelifi_usb_interrupt {
+	spinlock_t lock; /* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct purelifi_usb_rx {
+	spinlock_t lock; /* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct plf_station {
+   /*  7...3    |    2      |     1     |     0	    |
+    * Reserved  | Heartbeat | FIFO full | Connected |
+    */
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+struct firmware_file {
+	u32 total_files;
+	u32 total_size;
+	u32 size;
+	u32 start_addr;
+	u32 control_packets;
+} __packed;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+#define STATION_ACTIVE_FLAG    0xFD
+
+#define PURELIFI_SERIAL_LEN 256
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+
+struct purelifi_usb_tx {
+	unsigned long enabled;
+	spinlock_t lock; /* spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	bool stopped;
+	struct timer_list tx_retry_timer;
+	struct plf_station station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct purelifi_usb {
+	struct timer_list sta_queue_cleanup;
+	struct purelifi_usb_rx rx;
+	struct purelifi_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* purelifi_usb_iowrite16v needs 62 bytes */
+	bool rx_usb_enabled;
+	bool initialized;
+	bool was_running;
+	bool link_up;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+enum {
+	PLF_BIT_ENABLED = 1,
+	PLF_BIT_MAX = 2,
+};
+
+int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn, void *context);
+
+static inline struct usb_device *
+purelifi_usb_to_usbdev(struct purelifi_usb *usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_intf_to_hw(struct usb_interface *intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *
+purelifi_usb_to_hw(struct purelifi_usb *usb)
+{
+	return purelifi_intf_to_hw(usb->intf);
+}
+
+void purelifi_usb_init(struct purelifi_usb *usb, struct ieee80211_hw *hw,
+		       struct usb_interface *intf);
+void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb);
+void purelifi_usb_release(struct purelifi_usb *usb);
+void purelifi_usb_disable_rx(struct purelifi_usb *usb);
+void purelifi_usb_enable_tx(struct purelifi_usb *usb);
+void purelifi_usb_disable_tx(struct purelifi_usb *usb);
+int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb);
+int purelifi_usb_enable_rx(struct purelifi_usb *usb);
+int purelifi_usb_init_hw(struct purelifi_usb *usb);
+const char *purelifi_speed(enum usb_device_speed speed);
+
+/* Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH 2/2] [v19 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-12 12:50     ` [PATCH 2/2] [v19 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  2021-10-14  6:03       ` kernel test robot
@ 2021-10-24 17:58       ` kernel test robot
  1 sibling, 0 replies; 108+ messages in thread
From: kernel test robot @ 2021-10-24 17:58 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: kbuild-all, mostafa.afgani, Srinivasan Raju, Kalle Valo,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS)

[-- Attachment #1: Type: text/plain, Size: 5475 bytes --]

Hi Srinivasan,

I love your patch! Yet something to improve:

[auto build test ERROR on kvalo-wireless-drivers-next/master]
[also build test ERROR on kvalo-wireless-drivers/master jberg-mac80211/master v5.15-rc6]
[cannot apply to jberg-mac80211-next/master next-20211022]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Srinivasan-Raju/nl80211-Add-LC-placeholder-band-definition-to-enum-nl80211_band/20211012-215200
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next.git master
config: i386-allyesconfig (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce (this is a W=1 build):
        # https://github.com/0day-ci/linux/commit/7a077cc97d6f22264e6c9ac98d9e904ca908ccba
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Srinivasan-Raju/nl80211-Add-LC-placeholder-band-definition-to-enum-nl80211_band/20211012-215200
        git checkout 7a077cc97d6f22264e6c9ac98d9e904ca908ccba
        # save the attached .config to linux build tree
        make W=1 ARCH=i386 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/net/wireless/purelifi/plfxlc/mac.c: In function 'purelifi_mac_rx':
>> drivers/net/wireless/purelifi/plfxlc/mac.c:424:28: error: variable 'min_exp_seq_nmb' set but not used [-Werror=unused-but-set-variable]
     424 |  static unsigned short int min_exp_seq_nmb;
         |                            ^~~~~~~~~~~~~~~
   cc1: all warnings being treated as errors


vim +/min_exp_seq_nmb +424 drivers/net/wireless/purelifi/plfxlc/mac.c

   412	
   413	int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
   414			    unsigned int length)
   415	{
   416		struct purelifi_mac *mac = purelifi_hw_mac(hw);
   417		struct ieee80211_rx_status stats;
   418		const struct rx_status *status;
   419		struct sk_buff *skb;
   420		int bad_frame = 0;
   421		__le16 fc;
   422		int need_padding;
   423		unsigned int payload_length;
 > 424		static unsigned short int min_exp_seq_nmb;
   425		int sidx;
   426		struct purelifi_usb_tx *tx;
   427		/* Packet blockade during disabled interface. */
   428		if (!mac->vif)
   429			return 0;
   430	
   431		memset(&stats, 0, sizeof(stats));
   432		status = (struct rx_status *)buffer;
   433	
   434		stats.flag     = 0;
   435		stats.freq     = 2412;
   436		stats.band     = NL80211_BAND_LC;
   437		mac->rssi      = -15 * be16_to_cpu(status->rssi) / 10;
   438	
   439		stats.signal   = mac->rssi;
   440	
   441		if (status->rate_idx > 7)
   442			stats.rate_idx = 0;
   443		else
   444			stats.rate_idx = status->rate_idx;
   445	
   446		mac->crc_errors = be64_to_cpu(status->crc_error_count);
   447	
   448		if (!bad_frame &&
   449		    purelifi_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
   450		    !mac->pass_ctrl)
   451			return 0;
   452	
   453		buffer += sizeof(struct rx_status);
   454		payload_length = get_unaligned_be32(buffer);
   455	
   456		/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */
   457		if (payload_length > 1560) {
   458			dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
   459			return 0;
   460		}
   461		buffer += sizeof(u32);
   462	
   463		fc = get_unaligned((__le16 *)buffer);
   464		need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
   465	
   466		tx = &mac->chip.usb.tx;
   467	
   468		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
   469			if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
   470				continue;
   471			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
   472				tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
   473				break;
   474			}
   475		}
   476	
   477		if (sidx == MAX_STA_NUM - 1) {
   478			for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
   479				if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
   480					continue;
   481				memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
   482				tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
   483				tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
   484				break;
   485			}
   486		}
   487	
   488		switch (buffer[0]) {
   489		case IEEE80211_STYPE_PROBE_REQ:
   490			dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
   491			break;
   492		case IEEE80211_STYPE_ASSOC_REQ:
   493			dev_dbg(purelifi_mac_dev(mac), "Association request\n");
   494			break;
   495		case IEEE80211_STYPE_AUTH:
   496			dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
   497			min_exp_seq_nmb = 0;
   498			break;
   499		case IEEE80211_FTYPE_DATA:
   500			dev_dbg(purelifi_mac_dev(mac), "802.11 data frame\n");
   501			break;
   502		}
   503	
   504		skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
   505		if (!skb)
   506			return -ENOMEM;
   507	
   508		if (need_padding)
   509			/* Make sure that the payload data is 4 byte aligned. */
   510			skb_reserve(skb, 2);
   511	
   512		skb_put_data(skb, buffer, payload_length);
   513		memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
   514		ieee80211_rx_irqsafe(hw, skb);
   515		return 0;
   516	}
   517	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 66087 bytes --]

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v20 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-18 10:00     ` [PATCH v20 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
@ 2021-10-25  9:59       ` Kari Argillander
       [not found]         ` <CWLP265MB321780AB502EF147F6AAF197E0839@CWLP265MB3217.GBRP265.PROD.OUTLOOK.COM>
  2021-10-27 11:34       ` kernel test robot
  2021-10-27 12:38       ` Kari Argillander
  2 siblings, 1 reply; 108+ messages in thread
From: Kari Argillander @ 2021-10-25  9:59 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Kalle Valo, David S. Miller, Jakub Kicinski,
	Johannes Berg, open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

On Mon, Oct 18, 2021 at 11:00:55AM +0100, Srinivasan Raju wrote:
> This driver implementation has been based on the zd1211rw driver
> 
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management
> 
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture

I have run some static analyzing tools against this driver. Here is my
findings. If you have CI system in house I strongly recommend that you
take these in use. Note that I have included just what might be real
warnings but they might also not be. Please check them.

------------------------------------------
Thease are just what I found myself.

drivers/net/wireless/purelifi/plfxlc/usb.c:47:
Has static. Will not work with multiple device.

drivers/net/wireless/purelifi/plfxlc/mac.h:75: You use spaces over tabs.
drivers/net/wireless/purelifi/plfxlc/mac.c:704: You use spaces over tabs.
drivers/net/wireless/purelifi/plfxlc/usb.c:920: You use spaces over tabs.
There is more of these. Please find all of them.

------------------------------------------
$ cppcheck drivers/net/wireless/purelifi/plfxlc/*.[ch] --enable=all

drivers/net/wireless/purelifi/plfxlc/usb.c:55:31: style:
Boolean result is used in bitwise operation. Clarify expression with
parentheses. [clarifyCondition]

drivers/net/wireless/purelifi/plfxlc/mac.c:447:6: style:
Condition '!bad_frame' is always true [knownConditionTrueFalse]

drivers/net/wireless/purelifi/plfxlc/mac.c:572:16: style:
Variable 'changed_flags' is assigned a value that is never used.
[unreadVariable]
        Please check next comment line 577. There you talk about use of
        changed_flags but you never use it.

Unused functions. I do not have opinion if you should remove these or
not, but some times there is bug if some function is unused. Please at
least comment if these are not mistakes.

drivers/net/wireless/purelifi/plfxlc/usb.c:38:0: style: The function 'get_bcd_device' is never used. [unusedFunction]
drivers/net/wireless/purelifi/plfxlc/usb.c:398:0: style: The function 'purelifi_usb_tx' is never used. [unusedFunction]
drivers/net/wireless/purelifi/plfxlc/chip.h:64:0: style: The function 'purelifi_mc_clear' is never used. [unusedFunction]
drivers/net/wireless/purelifi/plfxlc/chip.c:75:0: style: The function 'purelifi_chip_disable_rxtx' is never used. [unusedFunction]
drivers/net/wireless/purelifi/plfxlc/chip.h:79:0: style: The function 'purelifi_mc_add_addr' is never used. [unusedFunction]
drivers/net/wireless/purelifi/plfxlc/mac.c:89:0: style: The function 'purelifi_mac_init_hw' is never used. [unusedFunction]

------------------------------------------
$ codespell drivers/net/wireless/purelifi/plfxlc/*.[ch]

mac.c:237: ocasions ==> occasions
------------------------------------------
$ ./scripts/checkpatch.pl --strict drivers/net/wireless/purelifi/plfxlc/*.[ch]
$ make coccicheck M=drivers/net/wireless/purelifi/plfxlc/
$ flawfinder drivers/net/wireless/purelifi/plfxlc/*.[ch]

$ touch drivers/net/wireless/purelifi/plfxlc/*.[ch]
$ make -j6 W=1

These were all good.
------------------------------------------
$ touch drivers/net/wireless/purelifi/plfxlc/*.[ch]
$ make -j6 CC=clang W=1 drivers/net/wireless/purelifi/plfxlc/

drivers/net/wireless/purelifi/plfxlc/usb.c:38:19: warning:
unused function 'get_bcd_device' [-Wunused-function]

drivers/net/wireless/purelifi/plfxlc/usb.c:55:7: warning:
logical not is only applied to the left hand side of this bitwise
operator [-Wlogical-not-parentheses]
------------------------------------------
$ ~/smatch/smatch_scripts/build_kernel_data.sh
$ ~/smatch/smatch_scripts/kchecker drivers/net/wireless/purelifi/plfxlc/

drivers/net/wireless/purelifi/plfxlc/usb.c:55
purelifi_send_packet_from_data_queue() warn: add some parenthesis here?

drivers/net/wireless/purelifi/plfxlc/mac.c:685
purelifi_get_et_strings() error: memcpy() '*et_strings' too small (32 vs 64)

  Argillander

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [EXTERNAL] Re: [PATCH v20 2/2] wireless: Initial driver submission for pureLiFi STA devices
       [not found]         ` <CWLP265MB321780AB502EF147F6AAF197E0839@CWLP265MB3217.GBRP265.PROD.OUTLOOK.COM>
@ 2021-10-25 12:17           ` Kalle Valo
  0 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2021-10-25 12:17 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: Kari Argillander, Mostafa Afgani, David S. Miller,
	Jakub Kicinski, Johannes Berg, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

>> Thease are just what I found myself.
>
> Thanks ! I will check and fix.

Please don't use HTML, our lists drop those.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v20 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-18 10:00     ` [PATCH v20 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  2021-10-25  9:59       ` Kari Argillander
@ 2021-10-27 11:34       ` kernel test robot
  2021-10-27 12:38       ` Kari Argillander
  2 siblings, 0 replies; 108+ messages in thread
From: kernel test robot @ 2021-10-27 11:34 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: llvm, kbuild-all, mostafa.afgani, Srinivasan Raju, Kalle Valo,
	Jakub Kicinski, Johannes Berg, open list,
	open list:NETWORKING DRIVERS (WIRELESS)

[-- Attachment #1: Type: text/plain, Size: 4398 bytes --]

Hi Srinivasan,

I love your patch! Perhaps something to improve:

[auto build test WARNING on kvalo-wireless-drivers-next/master]
[also build test WARNING on kvalo-wireless-drivers/master jberg-mac80211/master v5.15-rc7]
[cannot apply to jberg-mac80211-next/master next-20211026]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Srinivasan-Raju/nl80211-Add-LC-placeholder-band-definition-to-nl80211_band/20211018-190152
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next.git master
config: mips-randconfig-r011-20211027 (attached as .config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project 5db7568a6a1fcb408eb8988abdaff2a225a8eb72)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install mips cross compiling tool for clang build
        # apt-get install binutils-mips-linux-gnu
        # https://github.com/0day-ci/linux/commit/ae5d24705bf40b9954a995fd7d768ab4cc879bc2
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Srinivasan-Raju/nl80211-Add-LC-placeholder-band-definition-to-nl80211_band/20211018-190152
        git checkout ae5d24705bf40b9954a995fd7d768ab4cc879bc2
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 ARCH=mips 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/net/wireless/purelifi/plfxlc/usb.c:55:7: warning: logical not is only applied to the left hand side of this bitwise operator [-Wlogical-not-parentheses]
                   if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
                       ^                       ~
   drivers/net/wireless/purelifi/plfxlc/usb.c:55:7: note: add parentheses after the '!' to evaluate the bitwise operator first
                   if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
                       ^
                        (                                              )
   drivers/net/wireless/purelifi/plfxlc/usb.c:55:7: note: add parentheses around left hand side expression to silence this warning
                   if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
                       ^
                       (                      )
   drivers/net/wireless/purelifi/plfxlc/usb.c:38:19: warning: unused function 'get_bcd_device' [-Wunused-function]
   static inline u16 get_bcd_device(const struct usb_device *udev)
                     ^
   2 warnings generated.
--
>> drivers/net/wireless/purelifi/plfxlc/mac.c:560:25: warning: parameter 'changed_flags' set but not used [-Wunused-but-set-parameter]
                                          unsigned int changed_flags,
                                                       ^
   1 warning generated.


vim +55 drivers/net/wireless/purelifi/plfxlc/usb.c

    42	
    43	void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
    44	{
    45		struct sk_buff *skb = NULL;
    46		unsigned long flags;
    47		static u8 sidx;
    48		u8 last_served_sidx;
    49		struct purelifi_usb_tx *tx = &usb->tx;
    50	
    51		spin_lock_irqsave(&tx->lock, flags);
    52		last_served_sidx = sidx;
    53		do {
    54			sidx = (sidx + 1) % MAX_STA_NUM;
  > 55			if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
    56				continue;
    57			if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
    58				skb = skb_peek(&tx->station[sidx].data_list);
    59		} while ((sidx != last_served_sidx) && (!skb));
    60	
    61		if (skb) {
    62			skb = skb_dequeue(&tx->station[sidx].data_list);
    63			plf_usb_wreq_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
    64					   tx_urb_complete, skb);
    65			if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
    66				ieee80211_wake_queues(purelifi_usb_to_hw(usb));
    67		}
    68		spin_unlock_irqrestore(&tx->lock, flags);
    69	}
    70	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 37065 bytes --]

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v20 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-18 10:00     ` [PATCH v20 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  2021-10-25  9:59       ` Kari Argillander
  2021-10-27 11:34       ` kernel test robot
@ 2021-10-27 12:38       ` Kari Argillander
  2021-10-28  7:24         ` Kalle Valo
  2 siblings, 1 reply; 108+ messages in thread
From: Kari Argillander @ 2021-10-27 12:38 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani@purelifi.com Kalle Valo, David S. Miller,
	Jakub Kicinski, Johannes Berg, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

On Mon, Oct 18, 2021 at 11:00:55AM +0100, Srinivasan Raju wrote:
> This driver implementation has been based on the zd1211rw driver
> 
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management
> 
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture

Just small style issues in this review.

> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
> ---
>  MAINTAINERS                                   |   6 +
>  drivers/net/wireless/Kconfig                  |   1 +
>  drivers/net/wireless/Makefile                 |   1 +
>  drivers/net/wireless/purelifi/Kconfig         |  17 +
>  drivers/net/wireless/purelifi/Makefile        |   2 +
>  drivers/net/wireless/purelifi/plfxlc/Kconfig  |  14 +
>  drivers/net/wireless/purelifi/plfxlc/Makefile |   3 +
>  drivers/net/wireless/purelifi/plfxlc/chip.c   |  95 ++
>  drivers/net/wireless/purelifi/plfxlc/chip.h   |  89 ++
>  .../net/wireless/purelifi/plfxlc/firmware.c   | 275 +++++
>  drivers/net/wireless/purelifi/plfxlc/intf.h   |  52 +
>  drivers/net/wireless/purelifi/plfxlc/mac.c    | 770 ++++++++++++++
>  drivers/net/wireless/purelifi/plfxlc/mac.h    | 190 ++++
>  drivers/net/wireless/purelifi/plfxlc/usb.c    | 975 ++++++++++++++++++
>  drivers/net/wireless/purelifi/plfxlc/usb.h    | 196 ++++
>  15 files changed, 2686 insertions(+)
>  create mode 100644 drivers/net/wireless/purelifi/Kconfig
>  create mode 100644 drivers/net/wireless/purelifi/Makefile
>  create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
>  create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
>  create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
>  create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
>  create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
>  create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
>  create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
>  create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
>  create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
>  create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h


> diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.h b/drivers/net/wireless/purelifi/plfxlc/chip.h
> new file mode 100644
> index 000000000000..167e9b9bd7a7
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
> @@ -0,0 +1,89 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2021 pureLiFi
> + */
> +
> +#ifndef _LF_X_CHIP_H
> +#define _LF_X_CHIP_H
> +
> +#include <net/mac80211.h>
> +
> +#include "usb.h"
> +
> +enum unit_type {
> +	STA = 0,
> +	AP = 1,
> +};
> +
> +enum {
> +	PLFXLC_RADIO_OFF = 0,
> +	PLFXLC_RADIO_ON = 1,
> +};
> +
> +struct purelifi_chip {
> +	struct purelifi_usb usb;
> +	struct mutex mutex; /* lock to protect chip data */
> +	enum unit_type unit_type;
> +	u16 link_led;
> +	u8 beacon_set;
> +	__le16 beacon_interval;

Does seem little wierd that one variable is __le16. You could change
this u16 because there will not be anymore conversion either way.

> +};

> +#endif /* _LF_X_CHIP_H */
> diff --git a/drivers/net/wireless/purelifi/plfxlc/firmware.c b/drivers/net/wireless/purelifi/plfxlc/firmware.c
> new file mode 100644
> index 000000000000..30a8a63e13b6
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/plfxlc/firmware.c

> +
> +int download_fpga(struct usb_interface *intf)
> +{
> +	int r, actual_length;
> +	int fw_data_i, blk_tran_len = PLF_BULK_TLEN;
> +	const char *fw_name;
> +	unsigned char *fpga_dmabuff = NULL;
> +	unsigned char *fw_data;
> +	const struct firmware *fw = NULL;
> +	struct usb_device *udev = interface_to_usbdev(intf);

Can you please make something for function variables. They seems totally
random. If I am correct net/ and drivers/net want's reverse christmas
tree format. 

> +int download_xl_firmware(struct usb_interface *intf)
> +{
> +	const struct firmware *fwp = NULL;
> +	struct firmware_file file = {0};
> +	int s, r;
> +	u8 *buf;
> +	u32 i;
> +	const char *fw_pack;
> +	struct usb_device *udev = interface_to_usbdev(intf);
> +
> +	r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
> +	msleep(PLF_MSLEEP_TIME);
> +
> +	if (r) {
> +		dev_err(&intf->dev, "vendor command failed (%d)\n", r);
> +		return -EINVAL;
> +	}
> +	/* Code for single pack file download */
> +
> +	fw_pack = "plfxlc/lifi-xl.bin";
> +
> +	r = request_firmware(&fwp, fw_pack, &intf->dev);
> +	if (r) {
> +		dev_err(&intf->dev, "Request_firmware failed (%d)\n", r);
> +		return -EINVAL;
> +	}
> +	file.total_files = get_unaligned_le32(&fwp->data[0]);
> +	file.total_size = get_unaligned_le32(&fwp->size);
> +
> +	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n",
> +		file.total_files, file.total_size);
> +
> +	buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
> +	if (!buf) {
> +		release_firmware(fwp);
> +		return -ENOMEM;
> +	}
> +
> +	if (file.total_files > 10) {
> +		dev_err(&intf->dev, "Too many files (%d)\n", file.total_files);
> +		release_firmware(fwp);
> +		kfree(buf);
> +		return -EINVAL;
> +	}
> +
> +	/* Download firmware files in multiple steps */
> +	for (s = 0; s < file.total_files; s++) {
> +		buf[0] = s;
> +		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
> +					PLF_XL_BUF_LEN);
> +
> +		if (s < file.total_files - 1)
> +			file.size = get_unaligned_le32(&fwp->data[4 + ((s + 1) * 4)])
> +				    - get_unaligned_le32(&fwp->data[4 + (s) * 4]);
> +		else
> +			file.size = file.total_size -
> +				    get_unaligned_le32(&fwp->data[4 + (s) * 4]);
> +
> +		if (file.size > file.total_size || file.size > 60000) {
> +			dev_err(&intf->dev, "File size is too large (%d)\n", file.size);
> +			break;
> +		}
> +
> +		file.start_addr = get_unaligned_le32(&fwp->data[4 + (s * 4)]);
> +
> +		if (file.size % PLF_XL_BUF_LEN && s < 2)
> +			file.size += PLF_XL_BUF_LEN - file.size % PLF_XL_BUF_LEN;
> +
> +		file.control_packets = file.size / PLF_XL_BUF_LEN;
> +
> +		for (i = 0; i < file.control_packets; i++) {
> +			memcpy(buf,
> +			       &fwp->data[file.start_addr + (i * PLF_XL_BUF_LEN)],
> +			       PLF_XL_BUF_LEN);
> +			r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
> +						PLF_XL_BUF_LEN);
> +		}
> +		dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", s, r,
> +			file.size);
> +	}
> +	release_firmware(fwp);
> +	kfree(buf);
> +
> +	/* Code for single pack file download ends fw download finish*/

space after finish

> +
> +	r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
> +	dev_dbg(&intf->dev, "Download fpga (4) (%d)\n", r);
> +
> +	return 0;
> +}

> diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
> new file mode 100644
> index 000000000000..6e403d5ac84c
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/plfxlc/mac.c


> +int purelifi_restore_settings(struct purelifi_mac *mac)
> +{
> +	struct sk_buff *beacon;
> +	int beacon_interval, beacon_period;
> +
> +	spin_lock_irq(&mac->lock);
> +	beacon_interval = mac->beacon.interval;
> +	beacon_period = mac->beacon.period;
> +	spin_unlock_irq(&mac->lock);
> +
> +	if (mac->type != NL80211_IFTYPE_ADHOC)
> +		return 0;
> +
> +	if (mac->vif) {
> +		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
> +		if (beacon) {
> +			purelifi_mac_config_beacon(mac->hw, beacon);
> +			kfree_skb(beacon);
> +			/* Returned skb is used only once and lowlevel
> +			 *  driver is responsible for freeing it.

after * there is extre space.

> +			 */
> +		}
> +	}
> +
> +	purelifi_set_beacon_interval(&mac->chip, beacon_interval,
> +				     beacon_period, mac->type);
> +
> +	spin_lock_irq(&mac->lock);
> +	mac->beacon.last_update = jiffies;
> +	spin_unlock_irq(&mac->lock);
> +
> +	return 0;
> +}

> +void purelifi_mac_tx_to_dev(struct sk_buff *skb, int error)
> +{
> +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +	struct ieee80211_hw *hw = info->rate_driver_data[0];
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +
> +	ieee80211_tx_info_clear_status(info);
> +	skb_pull(skb, sizeof(struct purelifi_ctrlset));
> +
> +	if (unlikely(error ||
> +		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
> +		ieee80211_tx_status_irqsafe(hw, skb);

If this is unlikely make this  return;

> +	} else {

and delete this else so you get lower indention level.

> +		struct sk_buff_head *q = &mac->ack_wait_queue;
> +
> +		skb_queue_tail(q, skb);
> +		while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
> +			purelifi_mac_tx_status(hw, skb_dequeue(q),
> +					       mac->ack_pending ?
> +					       mac->ack_signal : 0,
> +					       NULL);
> +			mac->ack_pending = 0;
> +		}
> +	}
> +}
> +
> +static int purelifi_mac_config_beacon(struct ieee80211_hw *hw,
> +				      struct sk_buff *beacon)
> +{
> +	return plf_usb_wreq(beacon->data, beacon->len,
> +			USB_REQ_BEACON_WR);
> +}
> +
> +static int fill_ctrlset(struct purelifi_mac *mac, struct sk_buff *skb)

Should this also have prefix purelifi.

> +{
> +	unsigned int frag_len = skb->len;
> +	unsigned int tmp;
> +	u32 temp_len = 0;
> +	u32 temp_payload_len = 0;
> +	struct purelifi_ctrlset *cs;
> +
> +	if (skb_headroom(skb) < sizeof(struct purelifi_ctrlset)) {
> +		dev_dbg(purelifi_mac_dev(mac), "Not enough hroom(1)\n");
> +		return 1;
> +	}
> +
> +	cs = (void *)skb_push(skb, sizeof(struct purelifi_ctrlset));
> +	temp_payload_len = frag_len;
> +	temp_len = temp_payload_len +
> +		  sizeof(struct purelifi_ctrlset) -
> +		  sizeof(cs->id) - sizeof(cs->len);
> +
> +	/* data packet lengths must be multiple of four bytes
> +	 * and must not be a multiple of 512
> +	 * bytes. First, it is attempted to append the
> +	 * data packet in the tailroom of the skb. In rare
> +	 * ocasions, the tailroom is too small. In this case,
> +	 * the content of the packet is shifted into
> +	 * the headroom of the skb by memcpy. Headroom is allocated
> +	 * at startup (below in this file). Therefore,
> +	 * there will be always enough headroom. The call skb_headroom
> +	 * is an additional safety which might be
> +	 * dropped.
> +	 */

This is not wrapped correctly. Should be like:

	/* Data packet lengths must be multiple of four bytes and must
	 * not be a multiple of 512 bytes. First, it is attempted to
	 * append the data packet in the tailroom of the skb. In rare
	 * ocasions, the tailroom is too small. In this case, the
	 * content of the packet is shifted into the headroom of the skb
	 * by memcpy. Headroom is allocated at startup (below in this
	 * file). Therefore, there will be always enough headroom. The
	 * call skb_headroom is an additional safety which might be
	 * dropped.
 	 */

> +
> +	/* check if 32 bit aligned and align data */
> +	tmp = skb->len & 3;
> +	if (tmp) {
> +		if (skb_tailroom(skb) < (3 - tmp)) {
> +			if (skb_headroom(skb) >= 4 - tmp) {
> +				u8 len;
> +				u8 *src_pt;
> +				u8 *dest_pt;
> +
> +				len = skb->len;
> +				src_pt = skb->data;
> +				dest_pt = skb_push(skb, 4 - tmp);
> +				memmove(dest_pt, src_pt, len);
> +			} else {
> +				return -ENOBUFS;
> +			}
> +		} else {
> +			skb_put(skb, 4 - tmp);
> +		}
> +		temp_len += 4 - tmp;
> +	}
> +
> +	/* check if not multiple of 512 and align data */
> +	tmp = skb->len & 0x1ff;
> +	if (!tmp) {
> +		if (skb_tailroom(skb) < 4) {
> +			if (skb_headroom(skb) >= 4) {
> +				u8 len = skb->len;
> +				u8 *src_pt = skb->data;
> +				u8 *dest_pt = skb_push(skb, 4);
> +
> +				memcpy(dest_pt, src_pt, len);

I notice that Joe has said that you should use memmove in first version.
Just asking if this can overlap. If you are not sure memmove is safer.

> +			} else {
> +				/* should never happen because
> +				 * sufficient headroom was reserved
> +				 */
> +				return -ENOBUFS;
> +			}
> +		} else {
> +			skb_put(skb, 4);
> +		}
> +		temp_len += 4;
> +	}

You use almoust same thing two times row and it is quite long. Maybe
do new inline function.

> +
> +	cs->id = cpu_to_be32(USB_REQ_DATA_TX);
> +	cs->len = cpu_to_be32(temp_len);
> +	cs->payload_len_nw = cpu_to_be32(temp_payload_len);
> +
> +	return 0;
> +}
> +
> +static void plfxlc_op_tx(struct ieee80211_hw *hw,
> +			 struct ieee80211_tx_control *control,
> +			 struct sk_buff *skb)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct purelifi_usb *usb = &mac->chip.usb;
> +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +	struct purelifi_header *plhdr = (void *)skb->data;
> +	unsigned long flags;
> +	int r;
> +
> +	r = fill_ctrlset(mac, skb);
> +	if (r)
> +		goto fail;
> +
> +	info->rate_driver_data[0] = hw;
> +
> +	if (plhdr->frametype  == IEEE80211_FTYPE_DATA) {
> +		u8 *dst_mac = plhdr->dmac;
> +		u8 sidx;
> +		bool found = false;
> +		struct purelifi_usb_tx *tx = &usb->tx;
> +
> +		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
> +			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
> +				continue;
> +			if (!memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN)) {
> +				found = true;
> +				break;
> +			}

			if (memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN))
 				continue;

			found = true;
			break;

> +		}
> +
> +		/* Default to broadcast address for unknown MACs */
> +		if (!found)
> +			sidx = STA_BROADCAST_INDEX;
> +
> +		/* Stop OS from sending packets, if the queue is half full */
> +		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
> +			ieee80211_stop_queues(purelifi_usb_to_hw(usb));
> +
> +		/* Schedule packet for transmission if queue is not full */
> +		if (skb_queue_len(&tx->station[sidx].data_list) < 256) {
> +			skb_queue_tail(&tx->station[sidx].data_list, skb);
> +			purelifi_send_packet_from_data_queue(usb);
> +		} else {
> +			goto fail;
> +		}

Reverse if statment and goto from there. Then no need for else.

> +	} else {
> +		spin_lock_irqsave(&usb->tx.lock, flags);
> +		r = plf_usb_wreq_async(&mac->chip.usb, skb->data, skb->len,
> +				       USB_REQ_DATA_TX, tx_urb_complete, skb);
> +		spin_unlock_irqrestore(&usb->tx.lock, flags);
> +		if (r)
> +			goto fail;
> +	}
> +	return;
> +
> +fail:
> +	dev_kfree_skb(skb);
> +}
> +
> +static int purelifi_filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
> +			       struct ieee80211_rx_status *stats)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct sk_buff *skb;
> +	struct sk_buff_head *q;
> +	unsigned long flags;
> +	bool found = 0;

bool is nicer if used with true/false.

> +	int i, position = 0;
> +
> +	if (!ieee80211_is_ack(rx_hdr->frame_control))
> +		return 0;
> +
> +	dev_dbg(purelifi_mac_dev(mac), "ACK Received\n");
> +
> +	/* code based on zy driver, this logic may need fix */
> +	q = &mac->ack_wait_queue;
> +	spin_lock_irqsave(&q->lock, flags);
> +
> +	skb_queue_walk(q, skb) {
> +		struct ieee80211_hdr *tx_hdr;
> +
> +		position++;
> +
> +		if (mac->ack_pending && skb_queue_is_first(q, skb))
> +			continue;
> +		else if (mac->ack_pending == 0)

if is enogh

> +			break;
> +
> +		tx_hdr = (struct ieee80211_hdr *)skb->data;
> +		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
> +			found = 1;
> +			break;
> +		}
> +	}
> +
> +	if (found) {
> +		for (i = 1; i < position; i++)
> +			skb = __skb_dequeue(q);
> +		if (i == position) {
> +			purelifi_mac_tx_status(hw, skb,
> +					       mac->ack_pending ?
> +					       mac->ack_signal : 0,
> +					       NULL);
> +			mac->ack_pending = 0;
> +		}
> +
> +		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
> +		mac->ack_signal = stats->signal;
> +	}
> +
> +	spin_unlock_irqrestore(&q->lock, flags);
> +	return 1;
> +}
> +
> +int purelifi_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
> +		    unsigned int length)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	struct ieee80211_rx_status stats;
> +	const struct rx_status *status;
> +	struct sk_buff *skb;
> +	int bad_frame = 0;
> +	__le16 fc;
> +	int need_padding;
> +	unsigned int payload_length;
> +	int sidx;
> +	struct purelifi_usb_tx *tx;

Add space

> +	/* Packet blockade during disabled interface. */
> +	if (!mac->vif)
> +		return 0;
> +
> +	memset(&stats, 0, sizeof(stats));
> +	status = (struct rx_status *)buffer;
> +
> +	stats.flag     = 0;
> +	stats.freq     = 2412;
> +	stats.band     = NL80211_BAND_LC;
> +	mac->rssi      = -15 * be16_to_cpu(status->rssi) / 10;
> +
> +	stats.signal   = mac->rssi;
> +
> +	if (status->rate_idx > 7)
> +		stats.rate_idx = 0;
> +	else
> +		stats.rate_idx = status->rate_idx;
> +
> +	mac->crc_errors = be64_to_cpu(status->crc_error_count);
> +
> +	if (!bad_frame &&
> +	    purelifi_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
> +	    !mac->pass_ctrl)
> +		return 0;
> +
> +	buffer += sizeof(struct rx_status);
> +	payload_length = get_unaligned_be32(buffer);
> +
> +	/* MTU = 1500, MAC header = 36, CRC = 4, sum = 1540 */

Seems little confusing that you write sum = 1540 and next line you use
1560.

> +	if (payload_length > 1560) {
> +		dev_err(purelifi_mac_dev(mac), " > MTU %u\n", payload_length);
> +		return 0;
> +	}
> +	buffer += sizeof(u32);
> +
> +	fc = get_unaligned((__le16 *)buffer);
> +	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
> +
> +	tx = &mac->chip.usb.tx;
> +
> +	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
> +		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
> +			continue;
> +		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
> +			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
> +			break;
> +		}
> +	}
> +
> +	if (sidx == MAX_STA_NUM - 1) {
> +		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
> +			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
> +				continue;
> +			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
> +			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
> +			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
> +			break;
> +		}
> +	}
> +
> +	switch (buffer[0]) {
> +	case IEEE80211_STYPE_PROBE_REQ:
> +		dev_dbg(purelifi_mac_dev(mac), "Probe request\n");
> +		break;
> +	case IEEE80211_STYPE_ASSOC_REQ:
> +		dev_dbg(purelifi_mac_dev(mac), "Association request\n");
> +		break;
> +	case IEEE80211_STYPE_AUTH:
> +		dev_dbg(purelifi_mac_dev(mac), "Authentication req\n");
> +		break;
> +	case IEEE80211_FTYPE_DATA:
> +		dev_dbg(purelifi_mac_dev(mac), "802.11 data frame\n");
> +		break;
> +	}
> +
> +	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
> +	if (!skb)
> +		return -ENOMEM;
> +
> +	if (need_padding)
> +		/* Make sure that the payload data is 4 byte aligned. */
> +		skb_reserve(skb, 2);
> +
> +	skb_put_data(skb, buffer, payload_length);
> +	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
> +	ieee80211_rx_irqsafe(hw, skb);
> +	return 0;
> +}
> +
> +static int plfxlc_op_add_interface(struct ieee80211_hw *hw,
> +				   struct ieee80211_vif *vif)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	static const char * const iftype80211[] = {
> +		[NL80211_IFTYPE_STATION]	= "Station",
> +		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
> +	};
> +
> +	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
> +		return -EOPNOTSUPP;
> +
> +	if (vif->type == NL80211_IFTYPE_ADHOC ||
> +	    vif->type == NL80211_IFTYPE_STATION) {
> +		dev_dbg(purelifi_mac_dev(mac), "%s %s\n", __func__,
> +			iftype80211[vif->type]);
> +		mac->type = vif->type;
> +		mac->vif = vif;
> +	} else {
> +		dev_dbg(purelifi_mac_dev(mac), "unsupported iftype\n");
> +		return -EOPNOTSUPP;
> +	}

Reverse if so no else needed.

> +
> +	return 0;
> +}

> +static void plfxlc_op_bss_info_changed(struct ieee80211_hw *hw,
> +				       struct ieee80211_vif *vif,
> +				       struct ieee80211_bss_conf *bss_conf,
> +				       u32 changes)
> +{
> +	struct purelifi_mac *mac = purelifi_hw_mac(hw);
> +	int associated;
> +
> +	dev_dbg(purelifi_mac_dev(mac), "changes: %x\n", changes);
> +
> +	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
> +		associated = is_valid_ether_addr(bss_conf->bssid);
> +		goto exit_all;
> +	}
> +	/* for ADHOC */
> +	associated = true;
> +	if (changes & BSS_CHANGED_BEACON) {
> +		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
> +
> +		if (beacon) {
> +			purelifi_mac_config_beacon(hw, beacon);
> +			kfree_skb(beacon);
> +			/*Returned skb is used only once and

space after *

> +			 * low-level driver is
> +			 * responsible for freeing it.
> +			 */
> +		}
> +	}
> +
> +	if (changes & BSS_CHANGED_BEACON_ENABLED) {
> +		u16 interval = 0;
> +		u8 period = 0;
> +
> +		if (bss_conf->enable_beacon) {
> +			period = bss_conf->dtim_period;
> +			interval = bss_conf->beacon_int;
> +		}
> +
> +		spin_lock_irq(&mac->lock);
> +		mac->beacon.period = period;
> +		mac->beacon.interval = interval;
> +		mac->beacon.last_update = jiffies;
> +		spin_unlock_irq(&mac->lock);
> +
> +		purelifi_set_beacon_interval(&mac->chip, interval,
> +					     period, mac->type);
> +	}
> +exit_all:
> +	spin_lock_irq(&mac->lock);
> +	mac->associated = associated;
> +	spin_unlock_irq(&mac->lock);
> +}

> +static const struct ieee80211_ops plfxlc_ops = {
> +	.tx                 = plfxlc_op_tx,
> +	.start              = plfxlc_op_start,
> +	.stop               = plfxlc_op_stop,
> +	.add_interface      = plfxlc_op_add_interface,
> +	.remove_interface   = plfxlc_op_remove_interface,
> +	.set_rts_threshold  = purelifi_set_rts_threshold,
> +	.config             = plfxlc_op_config,
> +	.configure_filter   = plfxlc_op_configure_filter,
> +	.bss_info_changed   = plfxlc_op_bss_info_changed,
> +	.get_stats          = purelifi_get_stats,
> +	.get_et_sset_count  = purelifi_get_et_sset_count,
> +	.get_et_stats       = purelifi_get_et_stats,
> +	.get_et_strings     = purelifi_get_et_strings,
> +};

Just asking why some prefixes are purelifi and some are plfxlc?

> diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.h b/drivers/net/wireless/purelifi/plfxlc/mac.h
> new file mode 100644
> index 000000000000..3852d7343a40
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/plfxlc/mac.h
> @@ -0,0 +1,190 @@

> +struct purelifi_mac {
> +	struct purelifi_chip chip;
> +	spinlock_t lock; /* lock for mac data */
> +	struct ieee80211_hw *hw;
> +	struct ieee80211_vif *vif;
> +	struct beacon beacon;
> +	struct work_struct set_rts_cts_work;
> +	struct work_struct process_intr;
> +	struct purelifi_mc_hash multicast_hash;
> +	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
> +	u8 regdomain;
> +	u8 default_regdomain;
> +	u8 channel;
> +	int type;
> +	int associated;
> +	unsigned long flags;
> +	struct sk_buff_head ack_wait_queue;
> +	struct ieee80211_channel channels[14];
> +	struct ieee80211_rate rates[12];
> +	struct ieee80211_supported_band band;

Maybe move these below other ieee8011_* structs. Will be muh more
readable. Right now this struct is just big mess to read.

> +
> +	/* whether to pass frames with CRC errors to stack */
> +	bool pass_failed_fcs;
> +
> +	/* whether to pass control frames to stack */
> +	bool pass_ctrl;
> +
> +	/* whether we have received a 802.11 ACK that is pending */
> +	bool ack_pending;
> +
> +	/* signal strength of the last 802.11 ACK received */
> +	int ack_signal;
> +
> +	unsigned char hw_address[ETH_ALEN];
> +	char serial_number[PURELIFI_SERIAL_LEN];
> +	u64 crc_errors;
> +	u64 rssi;
> +};

> diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
> new file mode 100644
> index 000000000000..8cd9f223025e
> --- /dev/null
> +++ b/drivers/net/wireless/purelifi/plfxlc/usb.c

> +void purelifi_send_packet_from_data_queue(struct purelifi_usb *usb)
> +{
> +	struct sk_buff *skb = NULL;
> +	unsigned long flags;
> +	static u8 sidx;

Static here, but I might already tell that in preview message, but here
for safety.

> +	u8 last_served_sidx;
> +	struct purelifi_usb_tx *tx = &usb->tx;
> +
> +	spin_lock_irqsave(&tx->lock, flags);
> +	last_served_sidx = sidx;
> +	do {
> +		sidx = (sidx + 1) % MAX_STA_NUM;
> +		if (!tx->station[sidx].flag & STATION_CONNECTED_FLAG)
> +			continue;
> +		if (!(tx->station[sidx].flag & STATION_FIFO_FULL_FLAG))
> +			skb = skb_peek(&tx->station[sidx].data_list);
> +	} while ((sidx != last_served_sidx) && (!skb));
> +
> +	if (skb) {
> +		skb = skb_dequeue(&tx->station[sidx].data_list);
> +		plf_usb_wreq_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
> +				   tx_urb_complete, skb);
> +		if (skb_queue_len(&tx->station[sidx].data_list) <= 60)
> +			ieee80211_wake_queues(purelifi_usb_to_hw(usb));
> +	}
> +	spin_unlock_irqrestore(&tx->lock, flags);
> +}

> +static void rx_urb_complete(struct urb *urb)
> +{
> +	int r;
> +	struct purelifi_usb *usb;
> +	struct purelifi_usb_tx *tx;
> +	const u8 *buffer;
> +	unsigned int length;
> +	u16 status;
> +	u8 sidx;
> +
> +	if (!urb) {
> +		pr_err("urb is NULL\n");
> +		return;
> +	} else if (!urb->context) {

Another if is as good as this.

> +		pr_err("urb ctx is NULL\n");
> +		return;
> +	}

> +int purelifi_usb_enable_rx(struct purelifi_usb *usb)
> +{
> +	int r;
> +	struct purelifi_usb_rx *rx = &usb->rx;
> +
> +	mutex_lock(&rx->setup_mutex);
> +	r = __lf_x_usb_enable_rx(usb);
> +	if (!r)
> +		usb->rx_usb_enabled = 1;

Use true.

> +
> +	mutex_unlock(&rx->setup_mutex);
> +
> +	return r;
> +}

> +int purelifi_usb_tx(struct purelifi_usb *usb, struct sk_buff *skb)
> +{
> +	int r;
> +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +	struct usb_device *udev = purelifi_usb_to_usbdev(usb);
> +	struct urb *urb;
> +	struct purelifi_usb_tx *tx = &usb->tx;
> +
> +	if (!test_bit(PLF_BIT_ENABLED, &tx->enabled)) {
> +		r = -ENOENT;
> +		goto out;
> +	}
> +
> +	urb = usb_alloc_urb(0, GFP_ATOMIC);
> +	if (!urb) {
> +		r = -ENOMEM;
> +		goto out;
> +	}
> +
> +	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
> +			  skb->data, skb->len, tx_urb_complete, skb);
> +
> +	info->rate_driver_data[1] = (void *)jiffies;
> +	skb_queue_tail(&tx->submitted_skbs, skb);
> +	usb_anchor_urb(urb, &tx->submitted);
> +
> +	r = usb_submit_urb(urb, GFP_ATOMIC);
> +	if (r) {
> +		dev_dbg(purelifi_usb_dev(usb), "urb %p submit failed (%d)\n",
> +			urb, r);
> +		usb_unanchor_urb(urb);
> +		skb_unlink(skb, &tx->submitted_skbs);
> +		goto error;
> +	}
> +	return 0;
> +error:
> +	usb_free_urb(urb);
> +out:
> +	return r;

Do need to error paths in this function. Just return when goto out and
you use error just in one place.

> +}

> +int plf_usb_wreq_async(struct purelifi_usb *usb, const u8 *buffer,
> +		       int buffer_len, enum plf_usb_req_enum usb_req_id,
> +		       usb_complete_t complete_fn,
> +		       void *context)
> +{
> +	int r;
> +	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
> +	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
> +
> +	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
> +			  (void *)buffer, buffer_len, complete_fn, context);
> +
> +	r = usb_submit_urb(urb, GFP_ATOMIC);
> +

Remove newline.

> +	if (r)
> +		dev_err(&udev->dev, "Async write submit failed (%d)\n", r);
> +
> +	return r;
> +}
> +
> +int plf_usb_wreq(void *buffer, int buffer_len,
> +		 enum plf_usb_req_enum usb_req_id)
> +{
> +	int r;
> +	int actual_length;
> +	int usb_bulk_msg_len;
> +	unsigned char *dma_buffer = NULL;
> +	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
> +	struct plf_usb_req usb_req;
> +
> +	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
> +	usb_bulk_msg_len = sizeof(__le32) + sizeof(__le32) +
> +			   be32_to_cpu(usb_req.len);
> +
> +	dma_buffer = kmemdup(&usb_req, usb_bulk_msg_len, GFP_KERNEL);
> +
> +	if (!dma_buffer) {
> +		r = -ENOMEM;
> +		goto error;
> +	}
> +
> +	r = usb_bulk_msg(udev,
> +			 usb_sndbulkpipe(udev, EP_DATA_OUT),
> +			 dma_buffer, usb_bulk_msg_len,
> +			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
> +	kfree(dma_buffer);
> +error:
> +	if (r)
> +		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);

Can also be enomem. So maybe return -ENOMEM in upper.

> +
> +	return r;
> +}

> +static int probe(struct usb_interface *intf,
> +		 const struct usb_device_id *id)
> +{
> +	int r = 0;
> +	struct purelifi_chip *chip;
> +	struct purelifi_usb *usb;
> +	struct purelifi_usb_tx *tx;
> +	struct ieee80211_hw *hw = NULL;
> +	u8 hw_address[ETH_ALEN];
> +	u8 serial_number[PURELIFI_SERIAL_LEN];
> +	unsigned int i;
> +
> +	ez_usb_interface = intf;
> +
> +	hw = purelifi_mac_alloc_hw(intf);
> +

Remove newline

> +	if (!hw) {
> +		r = -ENOMEM;
> +		goto error;

Just return straight away. This should not need message.

> +	}
> +
> +	chip = &purelifi_hw_mac(hw)->chip;
> +	usb = &chip->usb;
> +	tx = &usb->tx;
> +
> +	r = upload_mac_and_serial(intf, hw_address, serial_number);
> +

Remove newline.

<snip>

> +	timer_setup(&usb->sta_queue_cleanup,
> +		    sta_queue_cleanup_timer_callb, 0);
> +	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
> +	add_timer(&usb->sta_queue_cleanup);
> +
> +	usb->initialized = 1;

true

> +	return 0;
> +error:
> +	if (hw) {
> +		purelifi_mac_release(purelifi_hw_mac(hw));
> +		ieee80211_unregister_hw(hw);
> +		ieee80211_free_hw(hw);
> +	}
> +	dev_err(&intf->dev, "pureLifi:Device error");
> +	return r;
> +}
> +
> +static void disconnect(struct usb_interface *intf)
> +{
> +	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
> +	struct purelifi_mac *mac;
> +	struct purelifi_usb *usb;
> +
> +	/* Either something really bad happened, or
> +	 * we're just dealing with
> +	 * a DEVICE_INSTALLER.
> +	 */

wrapping

> +	if (!hw)
> +		return;
> +
> +	mac = purelifi_hw_mac(hw);
> +	usb = &mac->chip.usb;
> +
> +	del_timer_sync(&usb->tx.tx_retry_timer);
> +	del_timer_sync(&usb->sta_queue_cleanup);
> +
> +	ieee80211_unregister_hw(hw);
> +
> +	purelifi_usb_disable_tx(usb);
> +	purelifi_usb_disable_rx(usb);
> +
> +	/* If the disconnect has been caused by a removal of the
> +	 * driver module, the reset allows reloading of the driver. If the
> +	 * reset will not be executed here,
> +	 * the upload of the firmware in the
> +	 * probe function caused by the reloading of the driver will fail.
> +	 */

ditto

> +	usb_reset_device(interface_to_usbdev(intf));
> +
> +	purelifi_mac_release(mac);
> +	ieee80211_free_hw(hw);
> +}

> +static struct purelifi_usb *get_purelifi_usb(struct usb_interface *intf)
> +{
> +	struct ieee80211_hw *hw = purelifi_intf_to_hw(intf);
> +	struct purelifi_mac *mac;
> +
> +	/* Either something really bad happened, or
> +	 * we're just dealing with
> +	 * a DEVICE_INSTALLER.
> +	 */

wrapping

  Argillander

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v20 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-27 12:38       ` Kari Argillander
@ 2021-10-28  7:24         ` Kalle Valo
  0 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2021-10-28  7:24 UTC (permalink / raw)
  To: Kari Argillander
  Cc: Srinivasan Raju, David S. Miller, Jakub Kicinski, Johannes Berg,
	open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Kari Argillander <kari.argillander@gmail.com> writes:

> On Mon, Oct 18, 2021 at 11:00:55AM +0100, Srinivasan Raju wrote:
>> This driver implementation has been based on the zd1211rw driver
>> 
>> Driver is based on 802.11 softMAC Architecture and uses
>> native 802.11 for configuration and management
>> 
>> The driver is compiled and tested in ARM, x86 architectures and
>> compiled in powerpc architecture
>
> Just small style issues in this review.

Very good review comments, kiitos :)

>> +static const struct ieee80211_ops plfxlc_ops = {
>> +	.tx                 = plfxlc_op_tx,
>> +	.start              = plfxlc_op_start,
>> +	.stop               = plfxlc_op_stop,
>> +	.add_interface      = plfxlc_op_add_interface,
>> +	.remove_interface   = plfxlc_op_remove_interface,
>> +	.set_rts_threshold  = purelifi_set_rts_threshold,
>> +	.config             = plfxlc_op_config,
>> +	.configure_filter   = plfxlc_op_configure_filter,
>> +	.bss_info_changed   = plfxlc_op_bss_info_changed,
>> +	.get_stats          = purelifi_get_stats,
>> +	.get_et_sset_count  = purelifi_get_et_sset_count,
>> +	.get_et_stats       = purelifi_get_et_stats,
>> +	.get_et_strings     = purelifi_get_et_strings,
>> +};
>
> Just asking why some prefixes are purelifi and some are plfxlc?

Good point, I guess this is because the driver was called purelifi
before. I think throughout the driver "plfxlc_" should be used and
"purelifi_" should be dropped altogether.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH v21 0/2] wireless: New Driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (17 preceding siblings ...)
  2021-10-18 10:00   ` [PATCH v20 0/2] wireless: New Driver " Srinivasan Raju
@ 2021-10-31 13:10   ` Srinivasan Raju
  2021-10-31 13:10     ` [PATCH 1/2] nl80211: Add LC placeholder band definition to nl80211_band Srinivasan Raju
  2021-10-31 13:10     ` [PATCH 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  2022-02-24 18:20   ` [PATCH v22 0/2] wireless: New Driver " Srinivasan Raju
  19 siblings, 2 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-10-31 13:10 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Johannes Berg, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

LiFi is a mobile wireless technology that uses light 
rather than radio frequencies to transmit data.

802.11 bb is focused on introducing necessary changes to 
IEEE 802.11 Stds to enable communications in the light medium

---
v21:
 - Address style related comments
 - Fix static analysis warnings
v20:
 - Remove unused static variable
v19:
 - Fix kmemdup null case
v18:
 - Use light communication band 
v16:
 - Fixed atomic variable misuses
 - Fixed comments spacing
 - Removed static variables used
 - Moved #defines to header file
 - Removed doxygen style comments
 - Removed magic numbers and cleanup code
v15:
 - resubmit v14 of the patch
v14:
 - Endianess comments addressed
 - Sparse checked and fixed warnings
 - Firmware files renamed to lowercase
 - All other review comments in v13 addressed
v13:
- Removed unused #defines
v12:
- Removed sysfs, procfs related code
- Addressed race condition bug
- Used macros instead of magic numbers in firmware.c
- Added copyright in all files
v11, v10:
- Addressed review comment on readability
- Changed firmware names to match products
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging

Srinivasan Raju (2):
  [v21 1/2] nl80211: Add LC placeholder band definition to nl80211_band
  [v21 2/2] wireless: Initial driver submission for pureLiFi STA devices

 MAINTAINERS                                   |   6 +
 drivers/net/wireless/Kconfig                  |   1 +
 drivers/net/wireless/Makefile                 |   1 +
 drivers/net/wireless/purelifi/Kconfig         |  17 +
 drivers/net/wireless/purelifi/Makefile        |   2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |  14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |   3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |  95 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |  70 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   | 276 ++++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |  52 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    | 764 +++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    | 184 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 908 ++++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    | 197 ++++
 include/uapi/linux/nl80211.h                  |   2 +
 net/mac80211/mlme.c                           |   1 +
 net/mac80211/sta_info.c                       |   1 +
 net/mac80211/tx.c                             |   3 +-
 net/wireless/nl80211.c                        |   1 +
 net/wireless/util.c                           |   2 +
 21 files changed, 2599 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

-- 
2.25.1


^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH 1/2] nl80211: Add LC placeholder band definition to nl80211_band
  2021-10-31 13:10   ` [PATCH v21 0/2] wireless: New Driver " Srinivasan Raju
@ 2021-10-31 13:10     ` Srinivasan Raju
  2021-10-31 13:10     ` [PATCH 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  1 sibling, 0 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-10-31 13:10 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Johannes Berg, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Define LC band which is a draft under IEEE 802.11 bb
Current NL80211_BAND_LC is a placeholder band
The band will be redefined as IEEE 802.11 bb progresses

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
---
 include/uapi/linux/nl80211.h | 2 ++
 net/mac80211/mlme.c          | 1 +
 net/mac80211/sta_info.c      | 1 +
 net/mac80211/tx.c            | 3 ++-
 net/wireless/nl80211.c       | 1 +
 net/wireless/util.c          | 2 ++
 6 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index c2efea98e060..cb06fb604a60 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -4929,6 +4929,7 @@ enum nl80211_txrate_gi {
  * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz)
  * @NL80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.2 GHz)
  * @NL80211_BAND_S1GHZ: around 900MHz, supported by S1G PHYs
+ * @NL80211_BAND_LC: light communication band (placeholder)
  * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
  *	since newer kernel versions may support more bands
  */
@@ -4938,6 +4939,7 @@ enum nl80211_band {
 	NL80211_BAND_60GHZ,
 	NL80211_BAND_6GHZ,
 	NL80211_BAND_S1GHZ,
+	NL80211_BAND_LC,
 
 	NUM_NL80211_BANDS,
 };
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index c0ea3b1aa9e1..c577d03ab128 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1490,6 +1490,7 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
 		fallthrough;
 	case NL80211_BAND_2GHZ:
 	case NL80211_BAND_60GHZ:
+	case NL80211_BAND_LC:
 		chan_increment = 1;
 		break;
 	case NL80211_BAND_5GHZ:
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 2b5acb37587f..36524101d11f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -444,6 +444,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
 		switch (i) {
 		case NL80211_BAND_2GHZ:
+		case NL80211_BAND_LC:
 			/*
 			 * We use both here, even if we cannot really know for
 			 * sure the station will support both, but the only use
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 2d1193ed3eb5..d311937f2add 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -146,7 +146,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
 			rate = DIV_ROUND_UP(r->bitrate, 1 << shift);
 
 		switch (sband->band) {
-		case NL80211_BAND_2GHZ: {
+		case NL80211_BAND_2GHZ:
+		case NL80211_BAND_LC: {
 			u32 flag;
 			if (tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
 				flag = IEEE80211_RATE_MANDATORY_G;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index bf7cd4752547..cf1434049abb 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -853,6 +853,7 @@ nl80211_match_band_rssi_policy[NUM_NL80211_BANDS] = {
 	[NL80211_BAND_5GHZ] = { .type = NLA_S32 },
 	[NL80211_BAND_6GHZ] = { .type = NLA_S32 },
 	[NL80211_BAND_60GHZ] = { .type = NLA_S32 },
+	[NL80211_BAND_LC]    = { .type = NLA_S32 },
 };
 
 static const struct nla_policy
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 18dba3d7c638..2991f711491a 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -80,6 +80,7 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
 		return 0; /* not supported */
 	switch (band) {
 	case NL80211_BAND_2GHZ:
+	case NL80211_BAND_LC:
 		if (chan == 14)
 			return MHZ_TO_KHZ(2484);
 		else if (chan < 14)
@@ -209,6 +210,7 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband)
 		WARN_ON(want);
 		break;
 	case NL80211_BAND_2GHZ:
+	case NL80211_BAND_LC:
 		want = 7;
 		for (i = 0; i < sband->n_bitrates; i++) {
 			switch (sband->bitrates[i].bitrate) {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-31 13:10   ` [PATCH v21 0/2] wireless: New Driver " Srinivasan Raju
  2021-10-31 13:10     ` [PATCH 1/2] nl80211: Add LC placeholder band definition to nl80211_band Srinivasan Raju
@ 2021-10-31 13:10     ` Srinivasan Raju
  2021-12-20 19:13       ` Kalle Valo
  2022-02-24 15:35       ` Kalle Valo
  1 sibling, 2 replies; 108+ messages in thread
From: Srinivasan Raju @ 2021-10-31 13:10 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Johannes Berg, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This driver implementation has been based on the zd1211rw driver

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
---
 MAINTAINERS                                   |   6 +
 drivers/net/wireless/Kconfig                  |   1 +
 drivers/net/wireless/Makefile                 |   1 +
 drivers/net/wireless/purelifi/Kconfig         |  17 +
 drivers/net/wireless/purelifi/Makefile        |   2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |  14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |   3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |  95 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |  70 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   | 276 ++++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |  52 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    | 764 +++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    | 184 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 908 ++++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    | 197 ++++
 15 files changed, 2590 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index eeb4c70b3d5b..1d271e9e9223 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15193,6 +15193,12 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PURELIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+L:	linux-wireless@vger.kernel.org
+S:	Supported
+F:	drivers/net/wireless/purelifi/
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7add2002ff4c..404afe574920 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -28,6 +28,7 @@ source "drivers/net/wireless/intersil/Kconfig"
 source "drivers/net/wireless/marvell/Kconfig"
 source "drivers/net/wireless/mediatek/Kconfig"
 source "drivers/net/wireless/microchip/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 source "drivers/net/wireless/ralink/Kconfig"
 source "drivers/net/wireless/realtek/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e3345893c9c5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
 obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
 obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
 obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
 obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
 obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..e39afec3dcae
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+source "drivers/net/wireless/purelifi/plfxlc/Kconfig"
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..56ebf96bd298
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)		:= plfxlc/
diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
new file mode 100644
index 000000000000..400ab2ee660c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config PURELIFI_XLC
+	tristate "pureLiFi X, XL, XC device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This option adds support for pureLiFi LiFi wireless USB adapters.
+
+	   The pureLiFi X, XL, XC USB devices are based on 802.11 OFDM PHY.
+
+	   Supports common 802.11 encryption/authentication methods including
+	   Open, WPA, WPA2-Personal and WPA2-Enterprise (802.1X).
+
+	   To compile this driver as a module, choose m here. The module will
+	   be called purelifi_xlc.
diff --git a/drivers/net/wireless/purelifi/plfxlc/Makefile b/drivers/net/wireless/purelifi/plfxlc/Makefile
new file mode 100644
index 000000000000..3d66f485c024
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)	:= purelifi_xlc.o
+purelifi_xlc-objs 		+= chip.o firmware.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.c b/drivers/net/wireless/purelifi/plfxlc/chip.c
new file mode 100644
index 000000000000..2ec144f3f98c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void plfxlc_chip_init(struct plfxlc_chip *chip,
+		      struct ieee80211_hw *hw,
+		      struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	plfxlc_usb_init(&chip->usb, hw, intf);
+}
+
+void plfxlc_chip_release(struct plfxlc_chip *chip)
+{
+	plfxlc_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int plfxlc_set_beacon_interval(struct plfxlc_chip *chip, u16 interval,
+			       u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set &&
+	     le16_to_cpu(chip->beacon_interval) == interval))
+		return 0;
+
+	chip->beacon_interval = cpu_to_le16(interval);
+	chip->beacon_set = true;
+	return plf_usb_wreq(&chip->beacon_interval,
+			     sizeof(chip->beacon_interval),
+			     USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int plfxlc_chip_init_hw(struct plfxlc_chip *chip)
+{
+	unsigned char *addr = plfxlc_mac_get_perm_addr(plfxlc_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("plfxlc chip %04x:%04x v%02x %pM %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr,
+		plfxlc_speed(udev->speed));
+
+	return plfxlc_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int plfxlc_chip_switch_radio(struct plfxlc_chip *chip, u16 value)
+{
+	int r;
+	__le16 radio_on = cpu_to_le16(value);
+
+	r = plf_usb_wreq(&radio_on, sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(plfxlc_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int plfxlc_chip_enable_rxtx(struct plfxlc_chip *chip)
+{
+	plfxlc_usb_enable_tx(&chip->usb);
+	return plfxlc_usb_enable_rx(&chip->usb);
+}
+
+void plfxlc_chip_disable_rxtx(struct plfxlc_chip *chip)
+{
+	u8 value = 0;
+
+	plf_usb_wreq(&value, sizeof(value), USB_REQ_RXTX_WR);
+	plfxlc_usb_disable_rx(&chip->usb);
+	plfxlc_usb_disable_tx(&chip->usb);
+}
+
+int plfxlc_chip_set_rate(struct plfxlc_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = plf_usb_wreq(&rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(plfxlc_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.h b/drivers/net/wireless/purelifi/plfxlc/chip.h
new file mode 100644
index 000000000000..f087bb364277
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type {
+	STA = 0,
+	AP = 1,
+};
+
+enum {
+	PLFXLC_RADIO_OFF = 0,
+	PLFXLC_RADIO_ON = 1,
+};
+
+struct plfxlc_chip {
+	struct plfxlc_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type unit_type;
+	u16 link_led;
+	u8 beacon_set;
+	u16 beacon_interval;
+};
+
+struct plfxlc_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define plfxlc_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void plfxlc_chip_init(struct plfxlc_chip *chip,
+		      struct ieee80211_hw *hw,
+		      struct usb_interface *intf);
+
+void plfxlc_chip_release(struct plfxlc_chip *chip);
+
+void plfxlc_chip_disable_rxtx(struct plfxlc_chip *chip);
+
+int plfxlc_chip_init_hw(struct plfxlc_chip *chip);
+
+int plfxlc_chip_enable_rxtx(struct plfxlc_chip *chip);
+
+int plfxlc_chip_set_rate(struct plfxlc_chip *chip, u8 rate);
+
+int plfxlc_set_beacon_interval(struct plfxlc_chip *chip, u16 interval,
+			       u8 dtim_period, int type);
+
+int plfxlc_chip_switch_radio(struct plfxlc_chip *chip, u16 value);
+
+static inline struct plfxlc_chip *plfxlc_usb_to_chip(struct plfxlc_usb
+							 *usb)
+{
+	return container_of(usb, struct plfxlc_chip, usb);
+}
+
+static inline void plfxlc_mc_add_all(struct plfxlc_mc_hash *hash)
+{
+	hash->low  = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/plfxlc/firmware.c b/drivers/net/wireless/purelifi/plfxlc/firmware.c
new file mode 100644
index 000000000000..7ffffa897f82
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/firmware.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/firmware.h>
+#include <linux/bitrev.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+int download_fpga(struct usb_interface *intf)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	unsigned char *fpga_dmabuff = NULL;
+	const struct firmware *fw = NULL;
+	int blk_tran_len = PLF_BULK_TLEN;
+	unsigned char *fw_data;
+	const char *fw_name;
+	int r, actual_length;
+	int fw_data_i = 0;
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-x.bin";
+		dev_dbg(&intf->dev, "bin file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-xc.bin";
+		dev_dbg(&intf->dev, "bin file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware(&fw, fw_name, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATUS_LEN, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ,
+			    fpga_dmabuff, PLF_FPGA_STATUS_LEN);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_SET_CMD, NULL, 0);
+
+	if (fpga_dmabuff[0] != PLF_FPGA_MG) {
+		dev_err(&intf->dev, "fpga_dmabuff[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+		if (!fw_data) {
+			r = -ENOMEM;
+			goto error_free_fw;
+		}
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			/* u8 bit reverse */
+			fw_data[tbuf_idx] = bitrev8(fw_data[tbuf_idx]);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_dmabuff[0] & 0xff),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2 * PLF_USB_TIMEOUT);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	kfree(fpga_dmabuff);
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATE_LEN, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xff, PLF_FPGA_STATE_LEN);
+
+	send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
+			    PLF_FPGA_STATE_LEN);
+
+	dev_dbg(&intf->dev, "%*ph\n", 8, fpga_dmabuff);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
+
+	msleep(PLF_MSLEEP_TIME);
+
+error_free_fw:
+	kfree(fpga_dmabuff);
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int download_xl_firmware(struct usb_interface *intf)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	const struct firmware *fwp = NULL;
+	struct firmware_file file = {0};
+	const char *fw_pack;
+	int s, r;
+	u8 *buf;
+	u32 i;
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
+	msleep(PLF_MSLEEP_TIME);
+
+	if (r) {
+		dev_err(&intf->dev, "vendor command failed (%d)\n", r);
+		return -EINVAL;
+	}
+	/* Code for single pack file download */
+
+	fw_pack = "plfxlc/lifi-xl.bin";
+
+	r = request_firmware(&fwp, fw_pack, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "Request_firmware failed (%d)\n", r);
+		return -EINVAL;
+	}
+	file.total_files = get_unaligned_le32(&fwp->data[0]);
+	file.total_size = get_unaligned_le32(&fwp->size);
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n",
+		file.total_files, file.total_size);
+
+	buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
+	if (!buf) {
+		release_firmware(fwp);
+		return -ENOMEM;
+	}
+
+	if (file.total_files > 10) {
+		dev_err(&intf->dev, "Too many files (%d)\n", file.total_files);
+		release_firmware(fwp);
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	/* Download firmware files in multiple steps */
+	for (s = 0; s < file.total_files; s++) {
+		buf[0] = s;
+		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
+					PLF_XL_BUF_LEN);
+
+		if (s < file.total_files - 1)
+			file.size = get_unaligned_le32(&fwp->data[4 + ((s + 1) * 4)])
+				    - get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+		else
+			file.size = file.total_size -
+				    get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+
+		if (file.size > file.total_size || file.size > 60000) {
+			dev_err(&intf->dev, "File size is too large (%d)\n", file.size);
+			break;
+		}
+
+		file.start_addr = get_unaligned_le32(&fwp->data[4 + (s * 4)]);
+
+		if (file.size % PLF_XL_BUF_LEN && s < 2)
+			file.size += PLF_XL_BUF_LEN - file.size % PLF_XL_BUF_LEN;
+
+		file.control_packets = file.size / PLF_XL_BUF_LEN;
+
+		for (i = 0; i < file.control_packets; i++) {
+			memcpy(buf,
+			       &fwp->data[file.start_addr + (i * PLF_XL_BUF_LEN)],
+			       PLF_XL_BUF_LEN);
+			r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
+						PLF_XL_BUF_LEN);
+		}
+		dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", s, r,
+			file.size);
+	}
+	release_firmware(fwp);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish */
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
+	dev_dbg(&intf->dev, "Download fpga (4) (%d)\n", r);
+
+	return 0;
+}
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	unsigned long long firmware_version;
+	unsigned char *dma_buffer = NULL;
+
+	dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);
+	if (!dma_buffer)
+		return -ENOMEM;
+
+	BUILD_BUG_ON(ETH_ALEN > PLF_SERIAL_LEN);
+	BUILD_BUG_ON(PLF_FW_VER_LEN > PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	memcpy(serial_number, dma_buffer, PLF_SERIAL_LEN);
+
+	memset(dma_buffer, 0x00, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, PLF_FW_VER_LEN);
+
+	memcpy(&firmware_version, dma_buffer, PLF_FW_VER_LEN);
+
+	dev_info(&intf->dev, "Firmware Version: %llu\n", firmware_version);
+	kfree(dma_buffer);
+
+	dev_dbg(&intf->dev, "Mac: %pM\n", hw_address);
+
+	return 0;
+}
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/intf.h b/drivers/net/wireless/purelifi/plfxlc/intf.h
new file mode 100644
index 000000000000..5ae89343b579
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/intf.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#define ETH_ALEN 6
+#define AP_USER_LIMIT 8
+
+#define PLF_VNDR_FPGA_STATE_REQ 0x30
+#define PLF_VNDR_FPGA_SET_REQ 0x33
+#define PLF_VNDR_FPGA_SET_CMD 0x34
+#define PLF_VNDR_FPGA_STATE_CMD 0x35
+
+#define PLF_VNDR_XL_FW_CMD 0x80
+#define PLF_VNDR_XL_DATA_CMD 0x81
+#define PLF_VNDR_XL_FILE_CMD 0x82
+#define PLF_VNDR_XL_EX_CMD 0x83
+
+#define PLF_MAC_VENDOR_REQUEST 0x36
+#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define PLF_SERIAL_LEN 14
+#define PLF_FW_VER_LEN 8
+
+struct rx_status {
+	__be16 rssi;
+	u8     rate_idx;
+	u8     pad;
+	__be64 crc_error_count;
+} __packed;
+
+enum plf_usb_req_enum {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct plf_usb_req {
+	__be32         id; /* should be plf_usb_req_enum */
+	__be32	       len;
+	u8             buf[512];
+};
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
new file mode 100644
index 000000000000..bc6925eb6bec
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.c
@@ -0,0 +1,764 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+static const struct ieee80211_rate plfxlc_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 }
+};
+
+static const struct ieee80211_channel plfxlc_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+static int plfxlc_mac_config_beacon(struct ieee80211_hw *hw,
+				    struct sk_buff *beacon);
+
+int plfxlc_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+int plfxlc_mac_init_hw(struct ieee80211_hw *hw)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	struct plfxlc_chip *chip = &mac->chip;
+	int r;
+
+	r = plfxlc_chip_init_hw(chip);
+	if (r) {
+		dev_warn(plfxlc_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(plfxlc_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	regulatory_hint(hw->wiphy, "00");
+	return r;
+}
+
+void plfxlc_mac_release(struct plfxlc_mac *mac)
+{
+	plfxlc_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int plfxlc_op_start(struct ieee80211_hw *hw)
+{
+	plfxlc_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void plfxlc_op_stop(struct ieee80211_hw *hw)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int plfxlc_restore_settings(struct plfxlc_mac *mac)
+{
+	int beacon_interval, beacon_period;
+	struct sk_buff *beacon;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			plfxlc_mac_config_beacon(mac->hw, beacon);
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 * driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	plfxlc_set_beacon_interval(&mac->chip, beacon_interval,
+				   beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+static void plfxlc_mac_tx_status(struct ieee80211_hw *hw,
+				 struct sk_buff *skb,
+				 int ackssi,
+				 struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1;
+
+	ieee80211_tx_info_clear_status(info);
+	if (tx_status)
+		success = !tx_status->failure;
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+void plfxlc_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	struct sk_buff_head *q = NULL;
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct plfxlc_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+		return;
+	}
+
+	q = &mac->ack_wait_queue;
+
+	skb_queue_tail(q, skb);
+	while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+		plfxlc_mac_tx_status(hw, skb_dequeue(q),
+				     mac->ack_pending ?
+				     mac->ack_signal : 0,
+				     NULL);
+		mac->ack_pending = 0;
+	}
+}
+
+static int plfxlc_mac_config_beacon(struct ieee80211_hw *hw,
+				    struct sk_buff *beacon)
+{
+	return plf_usb_wreq(beacon->data, beacon->len,
+			USB_REQ_BEACON_WR);
+}
+
+static int plfxlc_fill_ctrlset(struct plfxlc_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	struct plfxlc_ctrlset *cs;
+	u32 temp_payload_len = 0;
+	unsigned int tmp;
+	u32 temp_len = 0;
+
+	if (skb_headroom(skb) < sizeof(struct plfxlc_ctrlset)) {
+		dev_dbg(plfxlc_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (void *)skb_push(skb, sizeof(struct plfxlc_ctrlset));
+	temp_payload_len = frag_len;
+	temp_len = temp_payload_len +
+		  sizeof(struct plfxlc_ctrlset) -
+		  sizeof(cs->id) - sizeof(cs->len);
+
+	/* Data packet lengths must be multiple of four bytes and must
+	 * not be a multiple of 512 bytes. First, it is attempted to
+	 * append the data packet in the tailroom of the skb. In rare
+	 * occasions, the tailroom is too small. In this case, the
+	 * content of the packet is shifted into the headroom of the skb
+	 * by memcpy. Headroom is allocated at startup (below in this
+	 * file). Therefore, there will be always enough headroom. The
+	 * call skb_headroom is an additional safety which might be
+	 * dropped.
+	 */
+	/* check if 32 bit aligned and align data */
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memmove(dest_pt, src_pt, len);
+			} else {
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		temp_len += 4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data */
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memmove(dest_pt, src_pt, len);
+			} else {
+				/* should never happen because
+				 * sufficient headroom was reserved
+				 */
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		temp_len += 4;
+	}
+
+	cs->id = cpu_to_be32(USB_REQ_DATA_TX);
+	cs->len = cpu_to_be32(temp_len);
+	cs->payload_len_nw = cpu_to_be32(temp_payload_len);
+
+	return 0;
+}
+
+static void plfxlc_op_tx(struct ieee80211_hw *hw,
+			 struct ieee80211_tx_control *control,
+			 struct sk_buff *skb)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct plfxlc_header *plhdr = (void *)skb->data;
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	struct plfxlc_usb *usb = &mac->chip.usb;
+	unsigned long flags;
+	int r;
+
+	r = plfxlc_fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+
+	info->rate_driver_data[0] = hw;
+
+	if (plhdr->frametype  == IEEE80211_FTYPE_DATA) {
+		u8 *dst_mac = plhdr->dmac;
+		u8 sidx;
+		bool found = false;
+		struct plfxlc_usb_tx *tx = &usb->tx;
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN))
+				continue;
+			found = true;
+			break;
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			ieee80211_stop_queues(plfxlc_usb_to_hw(usb));
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 256)
+			goto fail;
+		skb_queue_tail(&tx->station[sidx].data_list, skb);
+		plfxlc_send_packet_from_data_queue(usb);
+
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = plf_usb_wreq_async(&mac->chip.usb, skb->data, skb->len,
+				       USB_REQ_DATA_TX, tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+static int plfxlc_filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+			     struct ieee80211_rx_status *stats)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	struct sk_buff_head *q;
+	int i, position = 0;
+	unsigned long flags;
+	struct sk_buff *skb;
+	bool found = false;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_dbg(plfxlc_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			plfxlc_mac_tx_status(hw, skb,
+					     mac->ack_pending ?
+					     mac->ack_signal : 0,
+					     NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int plfxlc_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		  unsigned int length)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	unsigned int payload_length;
+	struct plfxlc_usb_tx *tx;
+	struct sk_buff *skb;
+	int need_padding;
+	__le16 fc;
+	int sidx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	status = (struct rx_status *)buffer;
+
+	memset(&stats, 0, sizeof(stats));
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_LC;
+	mac->rssi      = -15 * be16_to_cpu(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	mac->crc_errors = be64_to_cpu(status->crc_error_count);
+
+	/* TODO bad frame check for CRC error*/
+	if (plfxlc_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = get_unaligned_be32(buffer);
+
+	if (payload_length > 1560) {
+		dev_err(plfxlc_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	switch (buffer[0]) {
+	case IEEE80211_STYPE_PROBE_REQ:
+		dev_dbg(plfxlc_mac_dev(mac), "Probe request\n");
+		break;
+	case IEEE80211_STYPE_ASSOC_REQ:
+		dev_dbg(plfxlc_mac_dev(mac), "Association request\n");
+		break;
+	case IEEE80211_STYPE_AUTH:
+		dev_dbg(plfxlc_mac_dev(mac), "Authentication req\n");
+		break;
+	case IEEE80211_FTYPE_DATA:
+		dev_dbg(plfxlc_mac_dev(mac), "802.11 data frame\n");
+		break;
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+
+	if (need_padding)
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+
+	skb_put_data(skb, buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int plfxlc_op_add_interface(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_dbg(plfxlc_mac_dev(mac), "%s %s\n", __func__,
+			iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+		return 0;
+	}
+	dev_dbg(plfxlc_mac_dev(mac), "unsupported iftype\n");
+	return -EOPNOTSUPP;
+}
+
+static void plfxlc_op_remove_interface(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int plfxlc_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void plfxlc_op_configure_filter(struct ieee80211_hw *hw,
+				       unsigned int changed_flags,
+				       unsigned int *new_flags,
+				       u64 multicast)
+{
+	struct plfxlc_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by plfxlc_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		plfxlc_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void plfxlc_op_bss_info_changed(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *bss_conf,
+				       u32 changes)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	int associated;
+
+	dev_dbg(plfxlc_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			plfxlc_mac_config_beacon(hw, beacon);
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		plfxlc_set_beacon_interval(&mac->chip, interval,
+					   period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int plfxlc_get_stats(struct ieee80211_hw *hw,
+			    struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount   = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+static const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int plfxlc_get_et_sset_count(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void plfxlc_get_et_strings(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void plfxlc_get_et_stats(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif,
+				struct ethtool_stats *stats, u64 *data)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int plfxlc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops plfxlc_ops = {
+	.tx = plfxlc_op_tx,
+	.start = plfxlc_op_start,
+	.stop = plfxlc_op_stop,
+	.add_interface = plfxlc_op_add_interface,
+	.remove_interface = plfxlc_op_remove_interface,
+	.set_rts_threshold = plfxlc_set_rts_threshold,
+	.config = plfxlc_op_config,
+	.configure_filter = plfxlc_op_configure_filter,
+	.bss_info_changed = plfxlc_op_bss_info_changed,
+	.get_stats = plfxlc_get_stats,
+	.get_et_sset_count = plfxlc_get_et_sset_count,
+	.get_et_stats = plfxlc_get_et_stats,
+	.get_et_strings = plfxlc_get_et_strings,
+};
+
+struct ieee80211_hw *plfxlc_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw;
+	struct plfxlc_mac *mac;
+
+	hw = ieee80211_alloc_hw(sizeof(struct plfxlc_mac), &plfxlc_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = plfxlc_hw_mac(hw);
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, plfxlc_channels, sizeof(plfxlc_channels));
+	memcpy(mac->rates, plfxlc_rates, sizeof(plfxlc_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(plfxlc_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(plfxlc_channels);
+	mac->band.channels = mac->channels;
+	hw->wiphy->bands[NL80211_BAND_LC] = &mac->band;
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+
+	ieee80211_hw_set(hw, RX_INCLUDES_FCS);
+	ieee80211_hw_set(hw, SIGNAL_DBM);
+	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+	ieee80211_hw_set(hw, MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+	hw->max_signal = 100;
+	hw->queues = 1;
+	/* 4 for 32 bit alignment if no tailroom */
+	hw->extra_tx_headroom = sizeof(struct plfxlc_ctrlset) + 4;
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	plfxlc_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.h b/drivers/net/wireless/purelifi/plfxlc/mac.h
new file mode 100644
index 000000000000..ae1ba221e4f1
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.h
@@ -0,0 +1,184 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+#define PLF_RX_ERROR		0x80
+#define PLF_RX_CRC32_ERROR	0x10
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define plfxlc_mac_dev(mac) plfxlc_chip_dev(&(mac)->chip)
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct plfxlc_ctrlset {
+	/* id should be plf_usb_req_enum */
+	__be32		id;
+	__be32		len;
+	u8		modulation;
+	u8		control;
+	u8		service;
+	u8		pad;
+	__le16		packet_length;
+	__le16		current_length;
+	__le16		next_frame_length;
+	__le16		tx_length;
+	__be32		payload_len_nw;
+} __packed;
+
+/* overlay */
+struct plfxlc_header {
+	struct plfxlc_ctrlset plf_ctrl;
+	u32    frametype;
+	u8    *dmac;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum plfxlc_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct plfxlc_mac {
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct plfxlc_mc_hash multicast_hash;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+	struct plfxlc_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	char serial_number[PURELIFI_SERIAL_LEN];
+	unsigned char hw_address[ETH_ALEN];
+	u8 default_regdomain;
+	unsigned long flags;
+	bool pass_failed_fcs;
+	bool pass_ctrl;
+	bool ack_pending;
+	int ack_signal;
+	int associated;
+	u8 regdomain;
+	u8 channel;
+	int type;
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct plfxlc_mac *
+plfxlc_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct plfxlc_mac *
+plfxlc_chip_to_mac(struct plfxlc_chip *chip)
+{
+	return container_of(chip, struct plfxlc_mac, chip);
+}
+
+static inline struct plfxlc_mac *
+plfxlc_usb_to_mac(struct plfxlc_usb *usb)
+{
+	return plfxlc_chip_to_mac(plfxlc_usb_to_chip(usb));
+}
+
+static inline u8 *plfxlc_mac_get_perm_addr(struct plfxlc_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *plfxlc_mac_alloc_hw(struct usb_interface *intf);
+void plfxlc_mac_release(struct plfxlc_mac *mac);
+
+int plfxlc_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address);
+int plfxlc_mac_init_hw(struct ieee80211_hw *hw);
+
+int plfxlc_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		  unsigned int length);
+void plfxlc_mac_tx_failed(struct urb *urb);
+void plfxlc_mac_tx_to_dev(struct sk_buff *skb, int error);
+int plfxlc_op_start(struct ieee80211_hw *hw);
+void plfxlc_op_stop(struct ieee80211_hw *hw);
+int plfxlc_restore_settings(struct plfxlc_mac *mac);
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
new file mode 100644
index 000000000000..b55a25ebf5f8
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -0,0 +1,908 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+#include "chip.h"
+
+struct usb_interface *ez_usb_interface;
+
+static const struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+void plfxlc_send_packet_from_data_queue(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_tx *tx = &usb->tx;
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	u8 last_served_sidx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = usb->sidx;
+	do {
+		usb->sidx = (usb->sidx + 1) % MAX_STA_NUM;
+		if (!(tx->station[usb->sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (!(tx->station[usb->sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[usb->sidx].data_list);
+	} while ((usb->sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[usb->sidx].data_list);
+		plf_usb_wreq_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				   tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[usb->sidx].data_list) <= 60)
+			ieee80211_wake_queues(plfxlc_usb_to_hw(usb));
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct plfxlc_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	plfxlc_mac_rx(plfxlc_usb_to_hw(usb), buffer, length);
+}
+
+static void rx_urb_complete(struct urb *urb)
+{
+	struct plfxlc_usb_tx *tx;
+	struct plfxlc_usb *usb;
+	unsigned int length;
+	const u8 *buffer;
+	u16 status;
+	u8 sidx;
+	int r;
+
+	if (!urb) {
+		pr_err("urb is NULL\n");
+		return;
+	}
+	if (!urb->context) {
+		pr_err("urb ctx is NULL\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1) {
+		pr_err("usb is not initialized\n");
+		return;
+	}
+
+	tx = &usb->tx;
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		if (tx->submitted_urbs++ < PURELIFI_URB_RETRY_MAX) {
+			dev_dbg(urb_dev(urb), "urb %p resubmit %d", urb,
+				tx->submitted_urbs++);
+			goto resubmit;
+		} else {
+			dev_dbg(urb_dev(urb), "urb %p  max resubmits reached", urb);
+			tx->submitted_urbs = 0;
+			return;
+		}
+	}
+
+	buffer = urb->transfer_buffer;
+	length = le32_to_cpu(*(__le32 *)(buffer + sizeof(struct rx_status)))
+		 + sizeof(u32);
+
+	if (urb->actual_length != (PLF_MSG_STATUS_OFFSET + 1)) {
+		if (usb->initialized && usb->link_up)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[PLF_MSG_STATUS_OFFSET];
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_dbg(&usb->intf->dev,
+			"FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_dbg(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= STATION_ACTIVE_FLAG;
+
+		plfxlc_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		usb->link_up = 1;
+		dev_dbg(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		usb->link_up = 0;
+		dev_dbg(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_dbg(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(urb_dev(urb), "urb %p resubmit fail (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct plfxlc_usb *usb)
+{
+	struct usb_device *udev = plfxlc_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+	int i, r;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(plfxlc_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0;
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int plfxlc_usb_enable_rx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_rx *rx = &usb->rx;
+	int r;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		usb->rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_rx *rx = &usb->rx;
+	unsigned long flags;
+	unsigned int count;
+	struct urb **urbs;
+	int i;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+}
+
+void plfxlc_usb_disable_rx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	usb->rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+void plfxlc_usb_disable_tx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following plfxlc_usb_enable_tx().
+	 */
+}
+
+void plfxlc_usb_enable_tx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	set_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(plfxlc_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+void tx_urb_complete(struct urb *urb)
+{
+	struct ieee80211_tx_info *info;
+	struct plfxlc_usb *usb;
+	struct sk_buff *skb;
+
+	skb = urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by plfxlc_mac_tx_to_dev or mac80211)
+	 */
+	usb = &plfxlc_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	plfxlc_mac_tx_to_dev(skb, urb->status);
+	plfxlc_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+static inline void init_usb_rx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(plfxlc_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void plfxlc_usb_init(struct plfxlc_usb *usb, struct ieee80211_hw *hw,
+		     struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void plfxlc_usb_release(struct plfxlc_usb *usb)
+{
+	plfxlc_op_stop(plfxlc_usb_to_hw(usb));
+	plfxlc_usb_disable_tx(usb);
+	plfxlc_usb_disable_rx(usb);
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+}
+
+const char *plfxlc_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int plfxlc_usb_init_hw(struct plfxlc_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(plfxlc_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(plfxlc_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, void *buffer,
+			u32 buffer_len, enum plf_usb_req_enum usb_req_id,
+			struct plf_usb_req *usb_req)
+{
+	__be32 payload_len_nw = cpu_to_be32(buffer_len + FCS_LEN);
+	const u8 *buffer_src_p = buffer;
+	u8 *buffer_dst = usb_req->buf;
+	u32 temp_usb_len = 0;
+
+	usb_req->id = cpu_to_be32(usb_req_id);
+	usb_req->len  = cpu_to_be32(0);
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == cpu_to_be32(USB_REQ_BEACON_WR)) {
+		memcpy(buffer_dst, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst += sizeof(payload_len_nw);
+		temp_usb_len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst, buffer_src_p, buffer_len);
+	buffer_dst += buffer_len;
+	buffer_src_p += buffer_len;
+	temp_usb_len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst, 0, FCS_LEN);
+	buffer_dst += FCS_LEN;
+	temp_usb_len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (temp_usb_len %
+			PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len %
+				PURELIFI_BYTE_NUM_ALIGNMENT);
+		temp_usb_len += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->len = cpu_to_be32(temp_usb_len);
+}
+
+int plf_usb_wreq_async(struct plfxlc_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn,
+		       void *context)
+{
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+	int r;
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_err(&udev->dev, "Async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id)
+{
+	struct usb_device *udev = interface_to_usbdev(ez_usb_interface);
+	unsigned char *dma_buffer = NULL;
+	struct plf_usb_req usb_req;
+	int usb_bulk_msg_len;
+	int actual_length;
+	int r;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(__le32) + sizeof(__le32) +
+			   be32_to_cpu(usb_req.len);
+
+	dma_buffer = kmemdup(&usb_req, usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r) {
+		r = -ENOMEM;
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+	}
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct plfxlc_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	plfxlc_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct plfxlc_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct plfxlc_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	u8 serial_number[PURELIFI_SERIAL_LEN];
+	struct ieee80211_hw *hw = NULL;
+	struct plfxlc_usb_tx *tx;
+	struct plfxlc_chip *chip;
+	struct plfxlc_usb *usb;
+	u8 hw_address[ETH_ALEN];
+	unsigned int i;
+	int r = 0;
+
+	ez_usb_interface = intf;
+
+	hw = plfxlc_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &plfxlc_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+
+	chip->unit_type = STA;
+	dev_err(&intf->dev, "Unit type is station");
+
+	r = plfxlc_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_err(&intf->dev, "Init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_err(&intf->dev, "Register device failed (%d)\n", r);
+		goto error;
+	}
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = download_xl_firmware(intf);
+	} else {
+		r = download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plfxlc_usb_init_hw(usb);
+	if (r < 0) {
+		dev_err(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plfxlc_chip_switch_radio(chip, PLFXLC_RADIO_ON);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plfxlc_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plf_usb_wreq(hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	plfxlc_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	plfxlc_mac_init_hw(hw);
+	usb->initialized = true;
+	return 0;
+error:
+	if (hw) {
+		plfxlc_mac_release(plfxlc_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	dev_err(&intf->dev, "pureLifi:Device error");
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = plfxlc_intf_to_hw(intf);
+	struct plfxlc_mac *mac;
+	struct plfxlc_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = plfxlc_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	plfxlc_chip_disable_rxtx(&mac->chip);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here, the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	plfxlc_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void plfxlc_usb_resume(struct plfxlc_usb *usb)
+{
+	struct plfxlc_mac *mac = plfxlc_usb_to_mac(usb);
+	int r;
+
+	r = plfxlc_op_start(plfxlc_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(plfxlc_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = plfxlc_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(plfxlc_usb_dev(usb),
+				"Restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void plfxlc_usb_stop(struct plfxlc_usb *usb)
+{
+	plfxlc_op_stop(plfxlc_usb_to_hw(usb));
+	plfxlc_usb_disable_tx(usb);
+	plfxlc_usb_disable_rx(usb);
+
+	usb->initialized = false;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct plfxlc_mac *mac;
+	struct plfxlc_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = plfxlc_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	plfxlc_usb_stop(usb);
+
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct plfxlc_mac *mac;
+	struct plfxlc_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = plfxlc_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	if (usb->was_running)
+		plfxlc_usb_resume(usb);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static struct plfxlc_usb *get_plfxlc_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = plfxlc_intf_to_hw(intf);
+	struct plfxlc_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = plfxlc_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct plfxlc_usb *pl = get_plfxlc_usb(interface);
+	struct plfxlc_mac *mac = plfxlc_usb_to_mac(pl);
+
+	if (!pl || !plfxlc_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	plfxlc_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct plfxlc_usb *pl = get_plfxlc_usb(interface);
+
+	if (!pl || !plfxlc_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		plfxlc_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = usb_ids,
+	.probe = probe,
+	.disconnect = disconnect,
+	.pre_reset = pre_reset,
+	.post_reset = post_reset,
+#ifdef CONFIG_PM
+	.suspend = suspend,
+	.resume = resume,
+#endif
+	.disable_hub_initiated_lpm = 1,
+};
+
+static struct workqueue_struct *plfxlc_workqueue;
+
+static int __init usb_init(void)
+{
+	int r;
+
+	plfxlc_workqueue = create_singlethread_workqueue(driver.name);
+	if (!plfxlc_workqueue) {
+		pr_err("%s couldn't create workqueue\n", driver.name);
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_register(&driver);
+	if (r) {
+		destroy_workqueue(plfxlc_workqueue);
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+
+error:
+	return r;
+}
+
+static void __exit usb_exit(void)
+{
+	usb_deregister(&driver);
+	destroy_workqueue(plfxlc_workqueue);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("plfxlc/lifi-x.bin");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.h b/drivers/net/wireless/purelifi/plfxlc/usb.h
new file mode 100644
index 000000000000..3dd8b19a04c4
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#define PLF_FPGA_STATUS_LEN 2
+#define PLF_FPGA_STATE_LEN 9
+#define PLF_BULK_TLEN 16384
+#define PLF_FPGA_MG 6 /* Magic check */
+#define PLF_XL_BUF_LEN 64
+#define PLF_MSG_STATUS_OFFSET 7
+
+#define PLF_USB_TIMEOUT 1000
+#define PLF_MSLEEP_TIME 200
+
+#define PURELIFI_URB_RETRY_MAX 5
+
+#define plfxlc_usb_dev(usb) (&(usb)->intf->dev)
+
+/* Tx retry backoff timer (in milliseconds) */
+#define TX_RETRY_BACKOFF_MS 10
+#define STA_QUEUE_CLEANUP_MS 5000
+
+/* Tx retry backoff timer (in jiffies) */
+#define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+#define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int plf_usb_wreq(void *buffer, int buffer_len,
+		 enum plf_usb_req_enum usb_req_id);
+void tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+struct plfxlc_usb_interrupt {
+	spinlock_t lock; /* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct plfxlc_usb_rx {
+	spinlock_t lock; /* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct plf_station {
+   /*  7...3    |    2      |     1     |     0	    |
+    * Reserved  | Heartbeat | FIFO full | Connected |
+    */
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+struct firmware_file {
+	u32 total_files;
+	u32 total_size;
+	u32 size;
+	u32 start_addr;
+	u32 control_packets;
+} __packed;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+#define STATION_ACTIVE_FLAG    0xFD
+
+#define PURELIFI_SERIAL_LEN 256
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+
+struct plfxlc_usb_tx {
+	unsigned long enabled;
+	spinlock_t lock; /* spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	bool stopped;
+	struct timer_list tx_retry_timer;
+	struct plf_station station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct plfxlc_usb {
+	struct timer_list sta_queue_cleanup;
+	struct plfxlc_usb_rx rx;
+	struct plfxlc_usb_tx tx;
+	struct usb_interface *intf;
+	u8 req_buf[64]; /* plfxlc_usb_iowrite16v needs 62 bytes */
+	u8 sidx; /* store last served */
+	bool rx_usb_enabled;
+	bool initialized;
+	bool was_running;
+	bool link_up;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+enum {
+	PLF_BIT_ENABLED = 1,
+	PLF_BIT_MAX = 2,
+};
+
+int plf_usb_wreq_async(struct plfxlc_usb *usb, const u8 *buffer,
+		       int buffer_len, enum plf_usb_req_enum usb_req_id,
+		       usb_complete_t complete_fn, void *context);
+
+static inline struct usb_device *
+plfxlc_usb_to_usbdev(struct plfxlc_usb *usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *
+plfxlc_intf_to_hw(struct usb_interface *intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *
+plfxlc_usb_to_hw(struct plfxlc_usb *usb)
+{
+	return plfxlc_intf_to_hw(usb->intf);
+}
+
+void plfxlc_usb_init(struct plfxlc_usb *usb, struct ieee80211_hw *hw,
+		     struct usb_interface *intf);
+void plfxlc_send_packet_from_data_queue(struct plfxlc_usb *usb);
+void plfxlc_usb_release(struct plfxlc_usb *usb);
+void plfxlc_usb_disable_rx(struct plfxlc_usb *usb);
+void plfxlc_usb_enable_tx(struct plfxlc_usb *usb);
+void plfxlc_usb_disable_tx(struct plfxlc_usb *usb);
+int plfxlc_usb_tx(struct plfxlc_usb *usb, struct sk_buff *skb);
+int plfxlc_usb_enable_rx(struct plfxlc_usb *usb);
+int plfxlc_usb_init_hw(struct plfxlc_usb *usb);
+const char *plfxlc_speed(enum usb_device_speed speed);
+
+/* Firmware declarations */
+int download_xl_firmware(struct usb_interface *intf);
+int download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-31 13:10     ` [PATCH 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
@ 2021-12-20 19:13       ` Kalle Valo
  2022-02-24 15:35       ` Kalle Valo
  1 sibling, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2021-12-20 19:13 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Johannes Berg, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> wrote:

> This driver implementation has been based on the zd1211rw driver
> 
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management
> 
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture
> 
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

I pushed this to the pending branch for build testing:

https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next.git/commit/?h=pending&id=9d71991ef3e8e4c2e49063c8fee446b9d20f4c7d

-- 
https://patchwork.kernel.org/project/linux-wireless/patch/20211031131122.275386-3-srini.raju@purelifi.com/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH 2/2] wireless: Initial driver submission for pureLiFi STA devices
  2021-10-31 13:10     ` [PATCH 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  2021-12-20 19:13       ` Kalle Valo
@ 2022-02-24 15:35       ` Kalle Valo
  1 sibling, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2022-02-24 15:35 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Srinivasan Raju, David S. Miller, Jakub Kicinski,
	Johannes Berg, open list, linux-wireless, netdev

Srinivasan Raju <srini.raju@purelifi.com> wrote:

> This driver implementation has been based on the zd1211rw driver
> 
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management
> 
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture
> 
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

I was about to take this to wireless-next but found few issues still:

o rename these to include plfxlc_ prefix:

int download_fpga(struct usb_interface *intf)
int download_xl_firmware(struct usb_interface *intf)
int plf_usb_wreq(void *buffer, int buffer_len,
void tx_urb_complete(struct urb *urb)
struct firmware_file {
#define urb_dev(urb) (&(urb)->dev->dev)
int plf_usb_wreq_async(struct plfxlc_usb *usb, const u8 *buffer,
int plf_usb_wreq(void *buffer, int buffer_len,

* non-const global variable in usb.c, doesn't that prevent supporting multiple
  devices on the same host? It should be stored into a dynamically allocated
  location like struct plfxlc_usb.

struct usb_interface *ez_usb_interface;

* unused workqueue:

static struct workqueue_struct *plfxlc_workqueue;

-- 
https://patchwork.kernel.org/project/linux-wireless/patch/20211031131122.275386-3-srini.raju@purelifi.com/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches


^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH v22 0/2] wireless: New Driver submission for pureLiFi STA devices
  2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
                     ` (18 preceding siblings ...)
  2021-10-31 13:10   ` [PATCH v21 0/2] wireless: New Driver " Srinivasan Raju
@ 2022-02-24 18:20   ` Srinivasan Raju
  2022-02-24 18:20     ` [PATCH v22 1/2] nl80211: Add LC placeholder band definition to nl80211_band Srinivasan Raju
  2022-02-24 18:20     ` [PATCH v22 1/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  19 siblings, 2 replies; 108+ messages in thread
From: Srinivasan Raju @ 2022-02-24 18:20 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This introduces the pureLiFi LiFi driver for LiFi-X, LiFi-XC
and LiFi-XL USB devices.

LiFi is a mobile wireless technology that uses light 
rather than radio frequencies to transmit data.

802.11 bb is focused on introducing necessary changes to 
IEEE 802.11 Stds to enable communications in the light medium

---
v22:
 - Fix function names to match driver
 - Change non constant global to per device
 - Remove unused workqueue
v21:
 - Address style related comments
 - Fix static analysis warnings
v20:
 - Remove unused static variable
v19:
 - Fix kmemdup null case
v18:
 - Use light communication band 
v16:
 - Fixed atomic variable misuses
 - Fixed comments spacing
 - Removed static variables used
 - Moved #defines to header file
 - Removed doxygen style comments
 - Removed magic numbers and cleanup code
v15:
 - resubmit v14 of the patch
v14:
 - Endianess comments addressed
 - Sparse checked and fixed warnings
 - Firmware files renamed to lowercase
 - All other review comments in v13 addressed
v13:
- Removed unused #defines
v12:
- Removed sysfs, procfs related code
- Addressed race condition bug
- Used macros instead of magic numbers in firmware.c
- Added copyright in all files
v11, v10:
- Addressed review comment on readability
- Changed firmware names to match products
v9:
- Addressed review comments on style and content defects
- Used kmemdup instead of alloc and memcpy
v7 , v8:
- Magic numbers removed and used IEEE80211 macors
- Other code style and timer function fixes (mod_timer)
v6:
- Code style fix patch from Joe Perches
v5:
- Code refactoring for clarity and redundnacy removal
- Fix warnings from kernel test robot
v4:
- Code refactoring based on kernel code guidelines
- Remove multi level macors and use kernel debug macros
v3:
- Code style fixes kconfig fix
v2:
- Driver submitted to wireless-next
- Code style fixes and copyright statement fix
v1:
- Driver submitted to staging

Srinivasan Raju (2):
  [v21 1/2] nl80211: Add LC placeholder band definition to nl80211_band
  [v21 2/2] wireless: Initial driver submission for pureLiFi STA devices

 MAINTAINERS                                   |   6 +
 drivers/net/wireless/Kconfig                  |   1 +
 drivers/net/wireless/Makefile                 |   1 +
 drivers/net/wireless/purelifi/Kconfig         |  17 +
 drivers/net/wireless/purelifi/Makefile        |   2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |  14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |   3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |  95 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |  70 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   | 276 ++++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |  52 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    | 764 +++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    | 184 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 908 ++++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    | 197 ++++
 include/uapi/linux/nl80211.h                  |   2 +
 net/mac80211/mlme.c                           |   1 +
 net/mac80211/sta_info.c                       |   1 +
 net/mac80211/tx.c                             |   3 +-
 net/wireless/nl80211.c                        |   1 +
 net/wireless/util.c                           |   2 +
 21 files changed, 2599 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

-- 
2.25.1


^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH v22 1/2] nl80211: Add LC placeholder band definition to nl80211_band
  2022-02-24 18:20   ` [PATCH v22 0/2] wireless: New Driver " Srinivasan Raju
@ 2022-02-24 18:20     ` Srinivasan Raju
  2022-02-25  9:52       ` Kalle Valo
  2022-02-24 18:20     ` [PATCH v22 1/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  1 sibling, 1 reply; 108+ messages in thread
From: Srinivasan Raju @ 2022-02-24 18:20 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, Johannes Berg, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Define LC band which is a draft under IEEE 802.11 bb
Current NL80211_BAND_LC is a placeholder band
The band will be redefined as IEEE 802.11 bb progresses

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
---
 include/uapi/linux/nl80211.h | 2 ++
 net/mac80211/mlme.c          | 1 +
 net/mac80211/sta_info.c      | 1 +
 net/mac80211/tx.c            | 3 ++-
 net/wireless/nl80211.c       | 1 +
 net/wireless/util.c          | 2 ++
 6 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index c2efea98e060..cb06fb604a60 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -4929,6 +4929,7 @@ enum nl80211_txrate_gi {
  * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz)
  * @NL80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.2 GHz)
  * @NL80211_BAND_S1GHZ: around 900MHz, supported by S1G PHYs
+ * @NL80211_BAND_LC: light communication band (placeholder)
  * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
  *	since newer kernel versions may support more bands
  */
@@ -4938,6 +4939,7 @@ enum nl80211_band {
 	NL80211_BAND_60GHZ,
 	NL80211_BAND_6GHZ,
 	NL80211_BAND_S1GHZ,
+	NL80211_BAND_LC,
 
 	NUM_NL80211_BANDS,
 };
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index c0ea3b1aa9e1..c577d03ab128 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1490,6 +1490,7 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
 		fallthrough;
 	case NL80211_BAND_2GHZ:
 	case NL80211_BAND_60GHZ:
+	case NL80211_BAND_LC:
 		chan_increment = 1;
 		break;
 	case NL80211_BAND_5GHZ:
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 2b5acb37587f..36524101d11f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -444,6 +444,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
 		switch (i) {
 		case NL80211_BAND_2GHZ:
+		case NL80211_BAND_LC:
 			/*
 			 * We use both here, even if we cannot really know for
 			 * sure the station will support both, but the only use
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 2d1193ed3eb5..d311937f2add 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -146,7 +146,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
 			rate = DIV_ROUND_UP(r->bitrate, 1 << shift);
 
 		switch (sband->band) {
-		case NL80211_BAND_2GHZ: {
+		case NL80211_BAND_2GHZ:
+		case NL80211_BAND_LC: {
 			u32 flag;
 			if (tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
 				flag = IEEE80211_RATE_MANDATORY_G;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index bf7cd4752547..cf1434049abb 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -853,6 +853,7 @@ nl80211_match_band_rssi_policy[NUM_NL80211_BANDS] = {
 	[NL80211_BAND_5GHZ] = { .type = NLA_S32 },
 	[NL80211_BAND_6GHZ] = { .type = NLA_S32 },
 	[NL80211_BAND_60GHZ] = { .type = NLA_S32 },
+	[NL80211_BAND_LC]    = { .type = NLA_S32 },
 };
 
 static const struct nla_policy
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 18dba3d7c638..2991f711491a 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -80,6 +80,7 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
 		return 0; /* not supported */
 	switch (band) {
 	case NL80211_BAND_2GHZ:
+	case NL80211_BAND_LC:
 		if (chan == 14)
 			return MHZ_TO_KHZ(2484);
 		else if (chan < 14)
@@ -209,6 +210,7 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband)
 		WARN_ON(want);
 		break;
 	case NL80211_BAND_2GHZ:
+	case NL80211_BAND_LC:
 		want = 7;
 		for (i = 0; i < sband->n_bitrates; i++) {
 			switch (sband->bitrates[i].bitrate) {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v22 1/2] wireless: Initial driver submission for pureLiFi STA devices
  2022-02-24 18:20   ` [PATCH v22 0/2] wireless: New Driver " Srinivasan Raju
  2022-02-24 18:20     ` [PATCH v22 1/2] nl80211: Add LC placeholder band definition to nl80211_band Srinivasan Raju
@ 2022-02-24 18:20     ` Srinivasan Raju
  2022-04-25 13:06       ` Kalle Valo
  2022-04-27  4:55       ` Kalle Valo
  1 sibling, 2 replies; 108+ messages in thread
From: Srinivasan Raju @ 2022-02-24 18:20 UTC (permalink / raw)
  Cc: mostafa.afgani, Srinivasan Raju, Kalle Valo, David S. Miller,
	Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

This driver implementation has been based on the zd1211rw driver

Driver is based on 802.11 softMAC Architecture and uses
native 802.11 for configuration and management

The driver is compiled and tested in ARM, x86 architectures and
compiled in powerpc architecture

Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>
---
 MAINTAINERS                                   |   6 +
 drivers/net/wireless/Kconfig                  |   1 +
 drivers/net/wireless/Makefile                 |   1 +
 drivers/net/wireless/purelifi/Kconfig         |  17 +
 drivers/net/wireless/purelifi/Makefile        |   2 +
 drivers/net/wireless/purelifi/plfxlc/Kconfig  |  14 +
 drivers/net/wireless/purelifi/plfxlc/Makefile |   3 +
 drivers/net/wireless/purelifi/plfxlc/chip.c   |  99 ++
 drivers/net/wireless/purelifi/plfxlc/chip.h   |  70 ++
 .../net/wireless/purelifi/plfxlc/firmware.c   | 276 ++++++
 drivers/net/wireless/purelifi/plfxlc/intf.h   |  52 +
 drivers/net/wireless/purelifi/plfxlc/mac.c    | 754 +++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/mac.h    | 184 ++++
 drivers/net/wireless/purelifi/plfxlc/usb.c    | 892 ++++++++++++++++++
 drivers/net/wireless/purelifi/plfxlc/usb.h    | 198 ++++
 15 files changed, 2569 insertions(+)
 create mode 100644 drivers/net/wireless/purelifi/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Kconfig
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/Makefile
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/chip.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/firmware.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/intf.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/mac.h
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.c
 create mode 100644 drivers/net/wireless/purelifi/plfxlc/usb.h

diff --git a/MAINTAINERS b/MAINTAINERS
index eeb4c70b3d5b..1d271e9e9223 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15193,6 +15193,12 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/admin-guide/media/pulse8-cec.rst
 F:	drivers/media/cec/usb/pulse8/
 
+PURELIFI USB DRIVER
+M:	Srinivasan Raju <srini.raju@purelifi.com>
+L:	linux-wireless@vger.kernel.org
+S:	Supported
+F:	drivers/net/wireless/purelifi/
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:	Mike Isely <isely@pobox.com>
 L:	pvrusb2@isely.net	(subscribers-only)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7add2002ff4c..404afe574920 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -28,6 +28,7 @@ source "drivers/net/wireless/intersil/Kconfig"
 source "drivers/net/wireless/marvell/Kconfig"
 source "drivers/net/wireless/mediatek/Kconfig"
 source "drivers/net/wireless/microchip/Kconfig"
+source "drivers/net/wireless/purelifi/Kconfig"
 source "drivers/net/wireless/ralink/Kconfig"
 source "drivers/net/wireless/realtek/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 80b324499786..e3345893c9c5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
 obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
 obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
 obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
 obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
 obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
 obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
diff --git a/drivers/net/wireless/purelifi/Kconfig b/drivers/net/wireless/purelifi/Kconfig
new file mode 100644
index 000000000000..e39afec3dcae
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_PURELIFI
+	bool "pureLiFi devices"
+	default y
+	help
+	  If you have a pureLiFi device, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about these cards. If you say Y, you will be asked for
+	  your specific card in the following questions.
+
+if WLAN_VENDOR_PURELIFI
+
+source "drivers/net/wireless/purelifi/plfxlc/Kconfig"
+
+endif # WLAN_VENDOR_PURELIFI
diff --git a/drivers/net/wireless/purelifi/Makefile b/drivers/net/wireless/purelifi/Makefile
new file mode 100644
index 000000000000..56ebf96bd298
--- /dev/null
+++ b/drivers/net/wireless/purelifi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)		:= plfxlc/
diff --git a/drivers/net/wireless/purelifi/plfxlc/Kconfig b/drivers/net/wireless/purelifi/plfxlc/Kconfig
new file mode 100644
index 000000000000..400ab2ee660c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config PURELIFI_XLC
+	tristate "pureLiFi X, XL, XC device support"
+	depends on CFG80211 && MAC80211 && USB
+	help
+	   This option adds support for pureLiFi LiFi wireless USB adapters.
+
+	   The pureLiFi X, XL, XC USB devices are based on 802.11 OFDM PHY.
+
+	   Supports common 802.11 encryption/authentication methods including
+	   Open, WPA, WPA2-Personal and WPA2-Enterprise (802.1X).
+
+	   To compile this driver as a module, choose m here. The module will
+	   be called purelifi_xlc.
diff --git a/drivers/net/wireless/purelifi/plfxlc/Makefile b/drivers/net/wireless/purelifi/plfxlc/Makefile
new file mode 100644
index 000000000000..3d66f485c024
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PURELIFI_XLC)	:= purelifi_xlc.o
+purelifi_xlc-objs 		+= chip.o firmware.o usb.o mac.o
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.c b/drivers/net/wireless/purelifi/plfxlc/chip.c
new file mode 100644
index 000000000000..a5ec10b66ed5
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+void plfxlc_chip_init(struct plfxlc_chip *chip,
+		      struct ieee80211_hw *hw,
+		      struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	plfxlc_usb_init(&chip->usb, hw, intf);
+}
+
+void plfxlc_chip_release(struct plfxlc_chip *chip)
+{
+	plfxlc_usb_release(&chip->usb);
+	mutex_destroy(&chip->mutex);
+}
+
+int plfxlc_set_beacon_interval(struct plfxlc_chip *chip, u16 interval,
+			       u8 dtim_period, int type)
+{
+	if (!interval ||
+	    (chip->beacon_set &&
+	     le16_to_cpu(chip->beacon_interval) == interval))
+		return 0;
+
+	chip->beacon_interval = cpu_to_le16(interval);
+	chip->beacon_set = true;
+	return plfxlc_usb_wreq(chip->usb.ez_usb,
+			       &chip->beacon_interval,
+			       sizeof(chip->beacon_interval),
+			       USB_REQ_BEACON_INTERVAL_WR);
+}
+
+int plfxlc_chip_init_hw(struct plfxlc_chip *chip)
+{
+	unsigned char *addr = plfxlc_mac_get_perm_addr(plfxlc_chip_to_mac(chip));
+	struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
+
+	pr_info("plfxlc chip %04x:%04x v%02x %pM %s\n",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		le16_to_cpu(udev->descriptor.bcdDevice),
+		addr,
+		plfxlc_speed(udev->speed));
+
+	return plfxlc_set_beacon_interval(chip, 100, 0, 0);
+}
+
+int plfxlc_chip_switch_radio(struct plfxlc_chip *chip, u16 value)
+{
+	int r;
+	__le16 radio_on = cpu_to_le16(value);
+
+	r = plfxlc_usb_wreq(chip->usb.ez_usb, &radio_on,
+			    sizeof(value), USB_REQ_POWER_WR);
+	if (r)
+		dev_err(plfxlc_chip_dev(chip), "POWER_WR failed (%d)\n", r);
+	return r;
+}
+
+int plfxlc_chip_enable_rxtx(struct plfxlc_chip *chip)
+{
+	plfxlc_usb_enable_tx(&chip->usb);
+	return plfxlc_usb_enable_rx(&chip->usb);
+}
+
+void plfxlc_chip_disable_rxtx(struct plfxlc_chip *chip)
+{
+	u8 value = 0;
+
+	plfxlc_usb_wreq(chip->usb.ez_usb,
+			&value, sizeof(value), USB_REQ_RXTX_WR);
+	plfxlc_usb_disable_rx(&chip->usb);
+	plfxlc_usb_disable_tx(&chip->usb);
+}
+
+int plfxlc_chip_set_rate(struct plfxlc_chip *chip, u8 rate)
+{
+	int r;
+
+	if (!chip)
+		return -EINVAL;
+
+	r = plfxlc_usb_wreq(chip->usb.ez_usb,
+			    &rate, sizeof(rate), USB_REQ_RATE_WR);
+	if (r)
+		dev_err(plfxlc_chip_dev(chip), "RATE_WR failed (%d)\n", r);
+	return r;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/chip.h b/drivers/net/wireless/purelifi/plfxlc/chip.h
new file mode 100644
index 000000000000..f087bb364277
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/chip.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _LF_X_CHIP_H
+#define _LF_X_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "usb.h"
+
+enum unit_type {
+	STA = 0,
+	AP = 1,
+};
+
+enum {
+	PLFXLC_RADIO_OFF = 0,
+	PLFXLC_RADIO_ON = 1,
+};
+
+struct plfxlc_chip {
+	struct plfxlc_usb usb;
+	struct mutex mutex; /* lock to protect chip data */
+	enum unit_type unit_type;
+	u16 link_led;
+	u8 beacon_set;
+	u16 beacon_interval;
+};
+
+struct plfxlc_mc_hash {
+	u32 low;
+	u32 high;
+};
+
+#define plfxlc_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void plfxlc_chip_init(struct plfxlc_chip *chip,
+		      struct ieee80211_hw *hw,
+		      struct usb_interface *intf);
+
+void plfxlc_chip_release(struct plfxlc_chip *chip);
+
+void plfxlc_chip_disable_rxtx(struct plfxlc_chip *chip);
+
+int plfxlc_chip_init_hw(struct plfxlc_chip *chip);
+
+int plfxlc_chip_enable_rxtx(struct plfxlc_chip *chip);
+
+int plfxlc_chip_set_rate(struct plfxlc_chip *chip, u8 rate);
+
+int plfxlc_set_beacon_interval(struct plfxlc_chip *chip, u16 interval,
+			       u8 dtim_period, int type);
+
+int plfxlc_chip_switch_radio(struct plfxlc_chip *chip, u16 value);
+
+static inline struct plfxlc_chip *plfxlc_usb_to_chip(struct plfxlc_usb
+							 *usb)
+{
+	return container_of(usb, struct plfxlc_chip, usb);
+}
+
+static inline void plfxlc_mc_add_all(struct plfxlc_mc_hash *hash)
+{
+	hash->low  = 0xffffffff;
+	hash->high = 0xffffffff;
+}
+
+#endif /* _LF_X_CHIP_H */
diff --git a/drivers/net/wireless/purelifi/plfxlc/firmware.c b/drivers/net/wireless/purelifi/plfxlc/firmware.c
new file mode 100644
index 000000000000..69e08ce5f05c
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/firmware.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/firmware.h>
+#include <linux/bitrev.h>
+
+#include "mac.h"
+#include "usb.h"
+
+static int send_vendor_request(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_rcvctrlpipe(udev, 0),
+			       request, 0xC0, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+static int send_vendor_command(struct usb_device *udev, int request,
+			       unsigned char *buffer, int buffer_size)
+{
+	return usb_control_msg(udev,
+			       usb_sndctrlpipe(udev, 0),
+			       request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
+			       buffer, buffer_size, PLF_USB_TIMEOUT);
+}
+
+int plfxlc_download_fpga(struct usb_interface *intf)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	unsigned char *fpga_dmabuff = NULL;
+	const struct firmware *fw = NULL;
+	int blk_tran_len = PLF_BULK_TLEN;
+	unsigned char *fw_data;
+	const char *fw_name;
+	int r, actual_length;
+	int fw_data_i = 0;
+
+	if ((le16_to_cpu(udev->descriptor.idVendor) ==
+				PURELIFI_X_VENDOR_ID_0) &&
+	    (le16_to_cpu(udev->descriptor.idProduct) ==
+				PURELIFI_X_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-x.bin";
+		dev_dbg(&intf->dev, "bin file for X selected\n");
+
+	} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
+					PURELIFI_XC_VENDOR_ID_0 &&
+		   (le16_to_cpu(udev->descriptor.idProduct) ==
+					PURELIFI_XC_PRODUCT_ID_0)) {
+		fw_name = "plfxlc/lifi-xc.bin";
+		dev_dbg(&intf->dev, "bin file for XC selected\n");
+
+	} else {
+		r = -EINVAL;
+		goto error;
+	}
+
+	r = request_firmware(&fw, fw_name, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
+		goto error;
+	}
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATUS_LEN, GFP_KERNEL);
+
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ,
+			    fpga_dmabuff, PLF_FPGA_STATUS_LEN);
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_SET_CMD, NULL, 0);
+
+	if (fpga_dmabuff[0] != PLF_FPGA_MG) {
+		dev_err(&intf->dev, "fpga_dmabuff[0] is wrong\n");
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	for (fw_data_i = 0; fw_data_i < fw->size;) {
+		int tbuf_idx;
+
+		if ((fw->size - fw_data_i) < blk_tran_len)
+			blk_tran_len = fw->size - fw_data_i;
+
+		fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
+				  GFP_KERNEL);
+		if (!fw_data) {
+			r = -ENOMEM;
+			goto error_free_fw;
+		}
+
+		for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
+			/* u8 bit reverse */
+			fw_data[tbuf_idx] = bitrev8(fw_data[tbuf_idx]);
+		}
+		r = usb_bulk_msg(udev,
+				 usb_sndbulkpipe(interface_to_usbdev(intf),
+						 fpga_dmabuff[0] & 0xff),
+				 fw_data,
+				 blk_tran_len,
+				 &actual_length,
+				 2 * PLF_USB_TIMEOUT);
+
+		if (r)
+			dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
+
+		kfree(fw_data);
+		fw_data_i += blk_tran_len;
+	}
+
+	kfree(fpga_dmabuff);
+	fpga_dmabuff = kmalloc(PLF_FPGA_STATE_LEN, GFP_KERNEL);
+	if (!fpga_dmabuff) {
+		r = -ENOMEM;
+		goto error_free_fw;
+	}
+	memset(fpga_dmabuff, 0xff, PLF_FPGA_STATE_LEN);
+
+	send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
+			    PLF_FPGA_STATE_LEN);
+
+	dev_dbg(&intf->dev, "%*ph\n", 8, fpga_dmabuff);
+
+	if (fpga_dmabuff[0] != 0) {
+		r = -EINVAL;
+		goto error_free_fw;
+	}
+
+	send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
+
+	msleep(PLF_MSLEEP_TIME);
+
+error_free_fw:
+	kfree(fpga_dmabuff);
+	release_firmware(fw);
+error:
+	return r;
+}
+
+int plfxlc_download_xl_firmware(struct usb_interface *intf)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	const struct firmware *fwp = NULL;
+	struct plfxlc_firmware_file file = {0};
+	const char *fw_pack;
+	int s, r;
+	u8 *buf;
+	u32 i;
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
+	msleep(PLF_MSLEEP_TIME);
+
+	if (r) {
+		dev_err(&intf->dev, "vendor command failed (%d)\n", r);
+		return -EINVAL;
+	}
+	/* Code for single pack file download */
+
+	fw_pack = "plfxlc/lifi-xl.bin";
+
+	r = request_firmware(&fwp, fw_pack, &intf->dev);
+	if (r) {
+		dev_err(&intf->dev, "Request_firmware failed (%d)\n", r);
+		return -EINVAL;
+	}
+	file.total_files = get_unaligned_le32(&fwp->data[0]);
+	file.total_size = get_unaligned_le32(&fwp->size);
+
+	dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n",
+		file.total_files, file.total_size);
+
+	buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
+	if (!buf) {
+		release_firmware(fwp);
+		return -ENOMEM;
+	}
+
+	if (file.total_files > 10) {
+		dev_err(&intf->dev, "Too many files (%d)\n", file.total_files);
+		release_firmware(fwp);
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	/* Download firmware files in multiple steps */
+	for (s = 0; s < file.total_files; s++) {
+		buf[0] = s;
+		r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
+					PLF_XL_BUF_LEN);
+
+		if (s < file.total_files - 1)
+			file.size = get_unaligned_le32(&fwp->data[4 + ((s + 1) * 4)])
+				    - get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+		else
+			file.size = file.total_size -
+				    get_unaligned_le32(&fwp->data[4 + (s) * 4]);
+
+		if (file.size > file.total_size || file.size > 60000) {
+			dev_err(&intf->dev, "File size is too large (%d)\n", file.size);
+			break;
+		}
+
+		file.start_addr = get_unaligned_le32(&fwp->data[4 + (s * 4)]);
+
+		if (file.size % PLF_XL_BUF_LEN && s < 2)
+			file.size += PLF_XL_BUF_LEN - file.size % PLF_XL_BUF_LEN;
+
+		file.control_packets = file.size / PLF_XL_BUF_LEN;
+
+		for (i = 0; i < file.control_packets; i++) {
+			memcpy(buf,
+			       &fwp->data[file.start_addr + (i * PLF_XL_BUF_LEN)],
+			       PLF_XL_BUF_LEN);
+			r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
+						PLF_XL_BUF_LEN);
+		}
+		dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", s, r,
+			file.size);
+	}
+	release_firmware(fwp);
+	kfree(buf);
+
+	/* Code for single pack file download ends fw download finish */
+
+	r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
+	dev_dbg(&intf->dev, "Download fpga (4) (%d)\n", r);
+
+	return 0;
+}
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	unsigned long long firmware_version;
+	unsigned char *dma_buffer = NULL;
+
+	dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);
+	if (!dma_buffer)
+		return -ENOMEM;
+
+	BUILD_BUG_ON(ETH_ALEN > PLF_SERIAL_LEN);
+	BUILD_BUG_ON(PLF_FW_VER_LEN > PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_MAC_VENDOR_REQUEST, dma_buffer,
+			    ETH_ALEN);
+
+	memcpy(hw_address, dma_buffer, ETH_ALEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
+			    dma_buffer, PLF_SERIAL_LEN);
+
+	memcpy(serial_number, dma_buffer, PLF_SERIAL_LEN);
+
+	memset(dma_buffer, 0x00, PLF_SERIAL_LEN);
+
+	send_vendor_request(udev, PLF_FIRMWARE_VERSION_VENDOR_REQUEST,
+			    (unsigned char *)dma_buffer, PLF_FW_VER_LEN);
+
+	memcpy(&firmware_version, dma_buffer, PLF_FW_VER_LEN);
+
+	dev_info(&intf->dev, "Firmware Version: %llu\n", firmware_version);
+	kfree(dma_buffer);
+
+	dev_dbg(&intf->dev, "Mac: %pM\n", hw_address);
+
+	return 0;
+}
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/intf.h b/drivers/net/wireless/purelifi/plfxlc/intf.h
new file mode 100644
index 000000000000..5ae89343b579
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/intf.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#define PURELIFI_BYTE_NUM_ALIGNMENT 4
+#define ETH_ALEN 6
+#define AP_USER_LIMIT 8
+
+#define PLF_VNDR_FPGA_STATE_REQ 0x30
+#define PLF_VNDR_FPGA_SET_REQ 0x33
+#define PLF_VNDR_FPGA_SET_CMD 0x34
+#define PLF_VNDR_FPGA_STATE_CMD 0x35
+
+#define PLF_VNDR_XL_FW_CMD 0x80
+#define PLF_VNDR_XL_DATA_CMD 0x81
+#define PLF_VNDR_XL_FILE_CMD 0x82
+#define PLF_VNDR_XL_EX_CMD 0x83
+
+#define PLF_MAC_VENDOR_REQUEST 0x36
+#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
+#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
+#define PLF_SERIAL_LEN 14
+#define PLF_FW_VER_LEN 8
+
+struct rx_status {
+	__be16 rssi;
+	u8     rate_idx;
+	u8     pad;
+	__be64 crc_error_count;
+} __packed;
+
+enum plf_usb_req_enum {
+	USB_REQ_TEST_WR            = 0,
+	USB_REQ_MAC_WR             = 1,
+	USB_REQ_POWER_WR           = 2,
+	USB_REQ_RXTX_WR            = 3,
+	USB_REQ_BEACON_WR          = 4,
+	USB_REQ_BEACON_INTERVAL_WR = 5,
+	USB_REQ_RTS_CTS_RATE_WR    = 6,
+	USB_REQ_HASH_WR            = 7,
+	USB_REQ_DATA_TX            = 8,
+	USB_REQ_RATE_WR            = 9,
+	USB_REQ_SET_FREQ           = 15
+};
+
+struct plf_usb_req {
+	__be32         id; /* should be plf_usb_req_enum */
+	__be32	       len;
+	u8             buf[512];
+};
+
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c
new file mode 100644
index 000000000000..90e552532701
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.c
@@ -0,0 +1,754 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "chip.h"
+#include "mac.h"
+#include "usb.h"
+
+static const struct ieee80211_rate plfxlc_rates[] = {
+	{ .bitrate = 10,
+		.hw_value = PURELIFI_CCK_RATE_1M,
+		.flags = 0 },
+	{ .bitrate = 20,
+		.hw_value = PURELIFI_CCK_RATE_2M,
+		.hw_value_short = PURELIFI_CCK_RATE_2M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 55,
+		.hw_value = PURELIFI_CCK_RATE_5_5M,
+		.hw_value_short = PURELIFI_CCK_RATE_5_5M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 110,
+		.hw_value = PURELIFI_CCK_RATE_11M,
+		.hw_value_short = PURELIFI_CCK_RATE_11M
+			| PURELIFI_CCK_PREA_SHORT,
+		.flags = IEEE80211_RATE_SHORT_PREAMBLE },
+	{ .bitrate = 60,
+		.hw_value = PURELIFI_OFDM_RATE_6M,
+		.flags = 0 },
+	{ .bitrate = 90,
+		.hw_value = PURELIFI_OFDM_RATE_9M,
+		.flags = 0 },
+	{ .bitrate = 120,
+		.hw_value = PURELIFI_OFDM_RATE_12M,
+		.flags = 0 },
+	{ .bitrate = 180,
+		.hw_value = PURELIFI_OFDM_RATE_18M,
+		.flags = 0 },
+	{ .bitrate = 240,
+		.hw_value = PURELIFI_OFDM_RATE_24M,
+		.flags = 0 },
+	{ .bitrate = 360,
+		.hw_value = PURELIFI_OFDM_RATE_36M,
+		.flags = 0 },
+	{ .bitrate = 480,
+		.hw_value = PURELIFI_OFDM_RATE_48M,
+		.flags = 0 },
+	{ .bitrate = 540,
+		.hw_value = PURELIFI_OFDM_RATE_54M,
+		.flags = 0 }
+};
+
+static const struct ieee80211_channel plfxlc_channels[] = {
+	{ .center_freq = 2412, .hw_value = 1 },
+	{ .center_freq = 2417, .hw_value = 2 },
+	{ .center_freq = 2422, .hw_value = 3 },
+	{ .center_freq = 2427, .hw_value = 4 },
+	{ .center_freq = 2432, .hw_value = 5 },
+	{ .center_freq = 2437, .hw_value = 6 },
+	{ .center_freq = 2442, .hw_value = 7 },
+	{ .center_freq = 2447, .hw_value = 8 },
+	{ .center_freq = 2452, .hw_value = 9 },
+	{ .center_freq = 2457, .hw_value = 10 },
+	{ .center_freq = 2462, .hw_value = 11 },
+	{ .center_freq = 2467, .hw_value = 12 },
+	{ .center_freq = 2472, .hw_value = 13 },
+	{ .center_freq = 2484, .hw_value = 14 },
+};
+
+int plfxlc_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address)
+{
+	SET_IEEE80211_PERM_ADDR(hw, hw_address);
+	return 0;
+}
+
+int plfxlc_mac_init_hw(struct ieee80211_hw *hw)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	struct plfxlc_chip *chip = &mac->chip;
+	int r;
+
+	r = plfxlc_chip_init_hw(chip);
+	if (r) {
+		dev_warn(plfxlc_mac_dev(mac), "init hw failed (%d)\n", r);
+		return r;
+	}
+
+	dev_dbg(plfxlc_mac_dev(mac), "irq_disabled (%d)\n", irqs_disabled());
+	regulatory_hint(hw->wiphy, "00");
+	return r;
+}
+
+void plfxlc_mac_release(struct plfxlc_mac *mac)
+{
+	plfxlc_chip_release(&mac->chip);
+	lockdep_assert_held(&mac->lock);
+}
+
+int plfxlc_op_start(struct ieee80211_hw *hw)
+{
+	plfxlc_hw_mac(hw)->chip.usb.initialized = 1;
+	return 0;
+}
+
+void plfxlc_op_stop(struct ieee80211_hw *hw)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+
+	clear_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+}
+
+int plfxlc_restore_settings(struct plfxlc_mac *mac)
+{
+	int beacon_interval, beacon_period;
+	struct sk_buff *beacon;
+
+	spin_lock_irq(&mac->lock);
+	beacon_interval = mac->beacon.interval;
+	beacon_period = mac->beacon.period;
+	spin_unlock_irq(&mac->lock);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC)
+		return 0;
+
+	if (mac->vif) {
+		beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+		if (beacon) {
+			/*beacon is hardcoded in firmware */
+			kfree_skb(beacon);
+			/* Returned skb is used only once and lowlevel
+			 * driver is responsible for freeing it.
+			 */
+		}
+	}
+
+	plfxlc_set_beacon_interval(&mac->chip, beacon_interval,
+				   beacon_period, mac->type);
+
+	spin_lock_irq(&mac->lock);
+	mac->beacon.last_update = jiffies;
+	spin_unlock_irq(&mac->lock);
+
+	return 0;
+}
+
+static void plfxlc_mac_tx_status(struct ieee80211_hw *hw,
+				 struct sk_buff *skb,
+				 int ackssi,
+				 struct tx_status *tx_status)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	int success = 1;
+
+	ieee80211_tx_info_clear_status(info);
+	if (tx_status)
+		success = !tx_status->failure;
+
+	if (success)
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+	info->status.ack_signal = 50;
+	ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+void plfxlc_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hw *hw = info->rate_driver_data[0];
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	struct sk_buff_head *q = NULL;
+
+	ieee80211_tx_info_clear_status(info);
+	skb_pull(skb, sizeof(struct plfxlc_ctrlset));
+
+	if (unlikely(error ||
+		     (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+		ieee80211_tx_status_irqsafe(hw, skb);
+		return;
+	}
+
+	q = &mac->ack_wait_queue;
+
+	skb_queue_tail(q, skb);
+	while (skb_queue_len(q)/* > PURELIFI_MAC_MAX_ACK_WAITERS*/) {
+		plfxlc_mac_tx_status(hw, skb_dequeue(q),
+				     mac->ack_pending ?
+				     mac->ack_signal : 0,
+				     NULL);
+		mac->ack_pending = 0;
+	}
+}
+
+static int plfxlc_fill_ctrlset(struct plfxlc_mac *mac, struct sk_buff *skb)
+{
+	unsigned int frag_len = skb->len;
+	struct plfxlc_ctrlset *cs;
+	u32 temp_payload_len = 0;
+	unsigned int tmp;
+	u32 temp_len = 0;
+
+	if (skb_headroom(skb) < sizeof(struct plfxlc_ctrlset)) {
+		dev_dbg(plfxlc_mac_dev(mac), "Not enough hroom(1)\n");
+		return 1;
+	}
+
+	cs = (void *)skb_push(skb, sizeof(struct plfxlc_ctrlset));
+	temp_payload_len = frag_len;
+	temp_len = temp_payload_len +
+		  sizeof(struct plfxlc_ctrlset) -
+		  sizeof(cs->id) - sizeof(cs->len);
+
+	/* Data packet lengths must be multiple of four bytes and must
+	 * not be a multiple of 512 bytes. First, it is attempted to
+	 * append the data packet in the tailroom of the skb. In rare
+	 * occasions, the tailroom is too small. In this case, the
+	 * content of the packet is shifted into the headroom of the skb
+	 * by memcpy. Headroom is allocated at startup (below in this
+	 * file). Therefore, there will be always enough headroom. The
+	 * call skb_headroom is an additional safety which might be
+	 * dropped.
+	 */
+	/* check if 32 bit aligned and align data */
+	tmp = skb->len & 3;
+	if (tmp) {
+		if (skb_tailroom(skb) < (3 - tmp)) {
+			if (skb_headroom(skb) >= 4 - tmp) {
+				u8 len;
+				u8 *src_pt;
+				u8 *dest_pt;
+
+				len = skb->len;
+				src_pt = skb->data;
+				dest_pt = skb_push(skb, 4 - tmp);
+				memmove(dest_pt, src_pt, len);
+			} else {
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4 - tmp);
+		}
+		temp_len += 4 - tmp;
+	}
+
+	/* check if not multiple of 512 and align data */
+	tmp = skb->len & 0x1ff;
+	if (!tmp) {
+		if (skb_tailroom(skb) < 4) {
+			if (skb_headroom(skb) >= 4) {
+				u8 len = skb->len;
+				u8 *src_pt = skb->data;
+				u8 *dest_pt = skb_push(skb, 4);
+
+				memmove(dest_pt, src_pt, len);
+			} else {
+				/* should never happen because
+				 * sufficient headroom was reserved
+				 */
+				return -ENOBUFS;
+			}
+		} else {
+			skb_put(skb, 4);
+		}
+		temp_len += 4;
+	}
+
+	cs->id = cpu_to_be32(USB_REQ_DATA_TX);
+	cs->len = cpu_to_be32(temp_len);
+	cs->payload_len_nw = cpu_to_be32(temp_payload_len);
+
+	return 0;
+}
+
+static void plfxlc_op_tx(struct ieee80211_hw *hw,
+			 struct ieee80211_tx_control *control,
+			 struct sk_buff *skb)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct plfxlc_header *plhdr = (void *)skb->data;
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	struct plfxlc_usb *usb = &mac->chip.usb;
+	unsigned long flags;
+	int r;
+
+	r = plfxlc_fill_ctrlset(mac, skb);
+	if (r)
+		goto fail;
+
+	info->rate_driver_data[0] = hw;
+
+	if (plhdr->frametype  == IEEE80211_FTYPE_DATA) {
+		u8 *dst_mac = plhdr->dmac;
+		u8 sidx;
+		bool found = false;
+		struct plfxlc_usb_tx *tx = &usb->tx;
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++) {
+			if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+				continue;
+			if (memcmp(tx->station[sidx].mac, dst_mac, ETH_ALEN))
+				continue;
+			found = true;
+			break;
+		}
+
+		/* Default to broadcast address for unknown MACs */
+		if (!found)
+			sidx = STA_BROADCAST_INDEX;
+
+		/* Stop OS from sending packets, if the queue is half full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 60)
+			ieee80211_stop_queues(plfxlc_usb_to_hw(usb));
+
+		/* Schedule packet for transmission if queue is not full */
+		if (skb_queue_len(&tx->station[sidx].data_list) > 256)
+			goto fail;
+		skb_queue_tail(&tx->station[sidx].data_list, skb);
+		plfxlc_send_packet_from_data_queue(usb);
+
+	} else {
+		spin_lock_irqsave(&usb->tx.lock, flags);
+		r = plfxlc_usb_wreq_async(&mac->chip.usb, skb->data, skb->len,
+					  USB_REQ_DATA_TX, plfxlc_tx_urb_complete, skb);
+		spin_unlock_irqrestore(&usb->tx.lock, flags);
+		if (r)
+			goto fail;
+	}
+	return;
+
+fail:
+	dev_kfree_skb(skb);
+}
+
+static int plfxlc_filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+			     struct ieee80211_rx_status *stats)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	struct sk_buff_head *q;
+	int i, position = 0;
+	unsigned long flags;
+	struct sk_buff *skb;
+	bool found = false;
+
+	if (!ieee80211_is_ack(rx_hdr->frame_control))
+		return 0;
+
+	dev_dbg(plfxlc_mac_dev(mac), "ACK Received\n");
+
+	/* code based on zy driver, this logic may need fix */
+	q = &mac->ack_wait_queue;
+	spin_lock_irqsave(&q->lock, flags);
+
+	skb_queue_walk(q, skb) {
+		struct ieee80211_hdr *tx_hdr;
+
+		position++;
+
+		if (mac->ack_pending && skb_queue_is_first(q, skb))
+			continue;
+		if (mac->ack_pending == 0)
+			break;
+
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		for (i = 1; i < position; i++)
+			skb = __skb_dequeue(q);
+		if (i == position) {
+			plfxlc_mac_tx_status(hw, skb,
+					     mac->ack_pending ?
+					     mac->ack_signal : 0,
+					     NULL);
+			mac->ack_pending = 0;
+		}
+
+		mac->ack_pending = skb_queue_len(q) ? 1 : 0;
+		mac->ack_signal = stats->signal;
+	}
+
+	spin_unlock_irqrestore(&q->lock, flags);
+	return 1;
+}
+
+int plfxlc_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		  unsigned int length)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	struct ieee80211_rx_status stats;
+	const struct rx_status *status;
+	unsigned int payload_length;
+	struct plfxlc_usb_tx *tx;
+	struct sk_buff *skb;
+	int need_padding;
+	__le16 fc;
+	int sidx;
+
+	/* Packet blockade during disabled interface. */
+	if (!mac->vif)
+		return 0;
+
+	status = (struct rx_status *)buffer;
+
+	memset(&stats, 0, sizeof(stats));
+
+	stats.flag     = 0;
+	stats.freq     = 2412;
+	stats.band     = NL80211_BAND_LC;
+	mac->rssi      = -15 * be16_to_cpu(status->rssi) / 10;
+
+	stats.signal   = mac->rssi;
+
+	if (status->rate_idx > 7)
+		stats.rate_idx = 0;
+	else
+		stats.rate_idx = status->rate_idx;
+
+	mac->crc_errors = be64_to_cpu(status->crc_error_count);
+
+	/* TODO bad frame check for CRC error*/
+	if (plfxlc_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) &&
+	    !mac->pass_ctrl)
+		return 0;
+
+	buffer += sizeof(struct rx_status);
+	payload_length = get_unaligned_be32(buffer);
+
+	if (payload_length > 1560) {
+		dev_err(plfxlc_mac_dev(mac), " > MTU %u\n", payload_length);
+		return 0;
+	}
+	buffer += sizeof(u32);
+
+	fc = get_unaligned((__le16 *)buffer);
+	need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+	tx = &mac->chip.usb.tx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (memcmp(&buffer[10], tx->station[sidx].mac, ETH_ALEN))
+			continue;
+		if (tx->station[sidx].flag & STATION_CONNECTED_FLAG) {
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	if (sidx == MAX_STA_NUM - 1) {
+		for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+			if (tx->station[sidx].flag & STATION_CONNECTED_FLAG)
+				continue;
+			memcpy(tx->station[sidx].mac, &buffer[10], ETH_ALEN);
+			tx->station[sidx].flag |= STATION_CONNECTED_FLAG;
+			tx->station[sidx].flag |= STATION_HEARTBEAT_FLAG;
+			break;
+		}
+	}
+
+	switch (buffer[0]) {
+	case IEEE80211_STYPE_PROBE_REQ:
+		dev_dbg(plfxlc_mac_dev(mac), "Probe request\n");
+		break;
+	case IEEE80211_STYPE_ASSOC_REQ:
+		dev_dbg(plfxlc_mac_dev(mac), "Association request\n");
+		break;
+	case IEEE80211_STYPE_AUTH:
+		dev_dbg(plfxlc_mac_dev(mac), "Authentication req\n");
+		break;
+	case IEEE80211_FTYPE_DATA:
+		dev_dbg(plfxlc_mac_dev(mac), "802.11 data frame\n");
+		break;
+	}
+
+	skb = dev_alloc_skb(payload_length + (need_padding ? 2 : 0));
+	if (!skb)
+		return -ENOMEM;
+
+	if (need_padding)
+		/* Make sure that the payload data is 4 byte aligned. */
+		skb_reserve(skb, 2);
+
+	skb_put_data(skb, buffer, payload_length);
+	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+	ieee80211_rx_irqsafe(hw, skb);
+	return 0;
+}
+
+static int plfxlc_op_add_interface(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	static const char * const iftype80211[] = {
+		[NL80211_IFTYPE_STATION]	= "Station",
+		[NL80211_IFTYPE_ADHOC]		= "Adhoc"
+	};
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+		return -EOPNOTSUPP;
+
+	if (vif->type == NL80211_IFTYPE_ADHOC ||
+	    vif->type == NL80211_IFTYPE_STATION) {
+		dev_dbg(plfxlc_mac_dev(mac), "%s %s\n", __func__,
+			iftype80211[vif->type]);
+		mac->type = vif->type;
+		mac->vif = vif;
+		return 0;
+	}
+	dev_dbg(plfxlc_mac_dev(mac), "unsupported iftype\n");
+	return -EOPNOTSUPP;
+}
+
+static void plfxlc_op_remove_interface(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+	mac->vif = NULL;
+}
+
+static int plfxlc_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	return 0;
+}
+
+#define SUPPORTED_FIF_FLAGS \
+	(FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+	 FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void plfxlc_op_configure_filter(struct ieee80211_hw *hw,
+				       unsigned int changed_flags,
+				       unsigned int *new_flags,
+				       u64 multicast)
+{
+	struct plfxlc_mc_hash hash = {
+		.low = multicast,
+		.high = multicast >> 32,
+	};
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	unsigned long flags;
+
+	/* Only deal with supported flags */
+	*new_flags &= SUPPORTED_FIF_FLAGS;
+
+	/* If multicast parameter
+	 * (as returned by plfxlc_op_prepare_multicast)
+	 * has changed, no bit in changed_flags is set. To handle this
+	 * situation, we do not return if changed_flags is 0. If we do so,
+	 * we will have some issue with IPv6 which uses multicast for link
+	 * layer address resolution.
+	 */
+	if (*new_flags & (FIF_ALLMULTI))
+		plfxlc_mc_add_all(&hash);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+	mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+	mac->multicast_hash = hash;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	/* no handling required for FIF_OTHER_BSS as we don't currently
+	 * do BSSID filtering
+	 */
+	/* FIXME: in future it would be nice to enable the probe response
+	 * filter (so that the driver doesn't see them) until
+	 * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+	 * have to schedule work to enable prbresp reception, which might
+	 * happen too late. For now we'll just listen and forward them all the
+	 * time.
+	 */
+}
+
+static void plfxlc_op_bss_info_changed(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *bss_conf,
+				       u32 changes)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+	int associated;
+
+	dev_dbg(plfxlc_mac_dev(mac), "changes: %x\n", changes);
+
+	if (mac->type != NL80211_IFTYPE_ADHOC) { /* for STATION */
+		associated = is_valid_ether_addr(bss_conf->bssid);
+		goto exit_all;
+	}
+	/* for ADHOC */
+	associated = true;
+	if (changes & BSS_CHANGED_BEACON) {
+		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+		if (beacon) {
+			/*beacon is hardcoded in firmware */
+			kfree_skb(beacon);
+			/*Returned skb is used only once and
+			 * low-level driver is
+			 * responsible for freeing it.
+			 */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED) {
+		u16 interval = 0;
+		u8 period = 0;
+
+		if (bss_conf->enable_beacon) {
+			period = bss_conf->dtim_period;
+			interval = bss_conf->beacon_int;
+		}
+
+		spin_lock_irq(&mac->lock);
+		mac->beacon.period = period;
+		mac->beacon.interval = interval;
+		mac->beacon.last_update = jiffies;
+		spin_unlock_irq(&mac->lock);
+
+		plfxlc_set_beacon_interval(&mac->chip, interval,
+					   period, mac->type);
+	}
+exit_all:
+	spin_lock_irq(&mac->lock);
+	mac->associated = associated;
+	spin_unlock_irq(&mac->lock);
+}
+
+static int plfxlc_get_stats(struct ieee80211_hw *hw,
+			    struct ieee80211_low_level_stats *stats)
+{
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount   = 0;
+	stats->dot11RTSSuccessCount = 0;
+	return 0;
+}
+
+static const char et_strings[][ETH_GSTRING_LEN] = {
+	"phy_rssi",
+	"phy_rx_crc_err"
+};
+
+static int plfxlc_get_et_sset_count(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif, int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return ARRAY_SIZE(et_strings);
+
+	return 0;
+}
+
+static void plfxlc_get_et_strings(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  u32 sset, u8 *data)
+{
+	if (sset == ETH_SS_STATS)
+		memcpy(data, *et_strings, sizeof(et_strings));
+}
+
+static void plfxlc_get_et_stats(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif,
+				struct ethtool_stats *stats, u64 *data)
+{
+	struct plfxlc_mac *mac = plfxlc_hw_mac(hw);
+
+	data[0] = mac->rssi;
+	data[1] = mac->crc_errors;
+}
+
+static int plfxlc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static const struct ieee80211_ops plfxlc_ops = {
+	.tx = plfxlc_op_tx,
+	.start = plfxlc_op_start,
+	.stop = plfxlc_op_stop,
+	.add_interface = plfxlc_op_add_interface,
+	.remove_interface = plfxlc_op_remove_interface,
+	.set_rts_threshold = plfxlc_set_rts_threshold,
+	.config = plfxlc_op_config,
+	.configure_filter = plfxlc_op_configure_filter,
+	.bss_info_changed = plfxlc_op_bss_info_changed,
+	.get_stats = plfxlc_get_stats,
+	.get_et_sset_count = plfxlc_get_et_sset_count,
+	.get_et_stats = plfxlc_get_et_stats,
+	.get_et_strings = plfxlc_get_et_strings,
+};
+
+struct ieee80211_hw *plfxlc_mac_alloc_hw(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw;
+	struct plfxlc_mac *mac;
+
+	hw = ieee80211_alloc_hw(sizeof(struct plfxlc_mac), &plfxlc_ops);
+	if (!hw) {
+		dev_dbg(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+	set_wiphy_dev(hw->wiphy, &intf->dev);
+
+	mac = plfxlc_hw_mac(hw);
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->hw = hw;
+
+	mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+	memcpy(mac->channels, plfxlc_channels, sizeof(plfxlc_channels));
+	memcpy(mac->rates, plfxlc_rates, sizeof(plfxlc_rates));
+	mac->band.n_bitrates = ARRAY_SIZE(plfxlc_rates);
+	mac->band.bitrates = mac->rates;
+	mac->band.n_channels = ARRAY_SIZE(plfxlc_channels);
+	mac->band.channels = mac->channels;
+	hw->wiphy->bands[NL80211_BAND_LC] = &mac->band;
+	hw->conf.chandef.width = NL80211_CHAN_WIDTH_20;
+
+	ieee80211_hw_set(hw, RX_INCLUDES_FCS);
+	ieee80211_hw_set(hw, SIGNAL_DBM);
+	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+	ieee80211_hw_set(hw, MFP_CAPABLE);
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+	hw->max_signal = 100;
+	hw->queues = 1;
+	/* 4 for 32 bit alignment if no tailroom */
+	hw->extra_tx_headroom = sizeof(struct plfxlc_ctrlset) + 4;
+	/* Tell mac80211 that we support multi rate retries */
+	hw->max_rates = IEEE80211_TX_MAX_RATES;
+	hw->max_rate_tries = 18;   /* 9 rates * 2 retries/rate */
+
+	skb_queue_head_init(&mac->ack_wait_queue);
+	mac->ack_pending = 0;
+
+	plfxlc_chip_init(&mac->chip, hw, intf);
+
+	SET_IEEE80211_DEV(hw, &intf->dev);
+	return hw;
+}
diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.h b/drivers/net/wireless/purelifi/plfxlc/mac.h
new file mode 100644
index 000000000000..ae1ba221e4f1
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/mac.h
@@ -0,0 +1,184 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_MAC_H
+#define _PURELIFI_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "chip.h"
+
+#define PURELIFI_CCK                  0x00
+#define PURELIFI_OFDM                 0x10
+#define PURELIFI_CCK_PREA_SHORT       0x20
+
+#define PURELIFI_OFDM_PLCP_RATE_6M	0xb
+#define PURELIFI_OFDM_PLCP_RATE_9M	0xf
+#define PURELIFI_OFDM_PLCP_RATE_12M	0xa
+#define PURELIFI_OFDM_PLCP_RATE_18M	0xe
+#define PURELIFI_OFDM_PLCP_RATE_24M	0x9
+#define PURELIFI_OFDM_PLCP_RATE_36M	0xd
+#define PURELIFI_OFDM_PLCP_RATE_48M	0x8
+#define PURELIFI_OFDM_PLCP_RATE_54M	0xc
+
+#define PURELIFI_CCK_RATE_1M	(PURELIFI_CCK | 0x00)
+#define PURELIFI_CCK_RATE_2M	(PURELIFI_CCK | 0x01)
+#define PURELIFI_CCK_RATE_5_5M	(PURELIFI_CCK | 0x02)
+#define PURELIFI_CCK_RATE_11M	(PURELIFI_CCK | 0x03)
+#define PURELIFI_OFDM_RATE_6M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
+#define PURELIFI_OFDM_RATE_9M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
+#define PURELIFI_OFDM_RATE_12M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
+#define PURELIFI_OFDM_RATE_18M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
+#define PURELIFI_OFDM_RATE_24M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
+#define PURELIFI_OFDM_RATE_36M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
+#define PURELIFI_OFDM_RATE_48M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
+#define PURELIFI_OFDM_RATE_54M	(PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
+
+#define PURELIFI_RX_ERROR		0x80
+#define PURELIFI_RX_CRC32_ERROR		0x10
+
+#define PLF_REGDOMAIN_FCC	0x10
+#define PLF_REGDOMAIN_IC	0x20
+#define PLF_REGDOMAIN_ETSI	0x30
+#define PLF_REGDOMAIN_SPAIN	0x31
+#define PLF_REGDOMAIN_FRANCE	0x32
+#define PLF_REGDOMAIN_JAPAN_2	0x40
+#define PLF_REGDOMAIN_JAPAN	0x41
+#define PLF_REGDOMAIN_JAPAN_3	0x49
+
+#define PLF_RX_ERROR		0x80
+#define PLF_RX_CRC32_ERROR	0x10
+
+enum {
+	MODULATION_RATE_BPSK_1_2 = 0,
+	MODULATION_RATE_BPSK_3_4,
+	MODULATION_RATE_QPSK_1_2,
+	MODULATION_RATE_QPSK_3_4,
+	MODULATION_RATE_QAM16_1_2,
+	MODULATION_RATE_QAM16_3_4,
+	MODULATION_RATE_QAM64_1_2,
+	MODULATION_RATE_QAM64_3_4,
+	MODULATION_RATE_AUTO,
+	MODULATION_RATE_NUM
+};
+
+#define plfxlc_mac_dev(mac) plfxlc_chip_dev(&(mac)->chip)
+
+#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
+#define PURELIFI_MAC_MAX_ACK_WAITERS 50
+
+struct plfxlc_ctrlset {
+	/* id should be plf_usb_req_enum */
+	__be32		id;
+	__be32		len;
+	u8		modulation;
+	u8		control;
+	u8		service;
+	u8		pad;
+	__le16		packet_length;
+	__le16		current_length;
+	__le16		next_frame_length;
+	__le16		tx_length;
+	__be32		payload_len_nw;
+} __packed;
+
+/* overlay */
+struct plfxlc_header {
+	struct plfxlc_ctrlset plf_ctrl;
+	u32    frametype;
+	u8    *dmac;
+} __packed;
+
+struct tx_status {
+	u8 type;
+	u8 id;
+	u8 rate;
+	u8 pad;
+	u8 mac[ETH_ALEN];
+	u8 retry;
+	u8 failure;
+} __packed;
+
+struct beacon {
+	struct delayed_work watchdog_work;
+	struct sk_buff *cur_beacon;
+	unsigned long last_update;
+	u16 interval;
+	u8 period;
+};
+
+enum plfxlc_device_flags {
+	PURELIFI_DEVICE_RUNNING,
+};
+
+struct plfxlc_mac {
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	struct beacon beacon;
+	struct work_struct set_rts_cts_work;
+	struct work_struct process_intr;
+	struct plfxlc_mc_hash multicast_hash;
+	struct sk_buff_head ack_wait_queue;
+	struct ieee80211_channel channels[14];
+	struct ieee80211_rate rates[12];
+	struct ieee80211_supported_band band;
+	struct plfxlc_chip chip;
+	spinlock_t lock; /* lock for mac data */
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+	char serial_number[PURELIFI_SERIAL_LEN];
+	unsigned char hw_address[ETH_ALEN];
+	u8 default_regdomain;
+	unsigned long flags;
+	bool pass_failed_fcs;
+	bool pass_ctrl;
+	bool ack_pending;
+	int ack_signal;
+	int associated;
+	u8 regdomain;
+	u8 channel;
+	int type;
+	u64 crc_errors;
+	u64 rssi;
+};
+
+static inline struct plfxlc_mac *
+plfxlc_hw_mac(struct ieee80211_hw *hw)
+{
+	return hw->priv;
+}
+
+static inline struct plfxlc_mac *
+plfxlc_chip_to_mac(struct plfxlc_chip *chip)
+{
+	return container_of(chip, struct plfxlc_mac, chip);
+}
+
+static inline struct plfxlc_mac *
+plfxlc_usb_to_mac(struct plfxlc_usb *usb)
+{
+	return plfxlc_chip_to_mac(plfxlc_usb_to_chip(usb));
+}
+
+static inline u8 *plfxlc_mac_get_perm_addr(struct plfxlc_mac *mac)
+{
+	return mac->hw->wiphy->perm_addr;
+}
+
+struct ieee80211_hw *plfxlc_mac_alloc_hw(struct usb_interface *intf);
+void plfxlc_mac_release(struct plfxlc_mac *mac);
+
+int plfxlc_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address);
+int plfxlc_mac_init_hw(struct ieee80211_hw *hw);
+
+int plfxlc_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
+		  unsigned int length);
+void plfxlc_mac_tx_failed(struct urb *urb);
+void plfxlc_mac_tx_to_dev(struct sk_buff *skb, int error);
+int plfxlc_op_start(struct ieee80211_hw *hw);
+void plfxlc_op_stop(struct ieee80211_hw *hw);
+int plfxlc_restore_settings(struct plfxlc_mac *mac);
+
+#endif
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
new file mode 100644
index 000000000000..a8a830ad69c1
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -0,0 +1,892 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include "mac.h"
+#include "usb.h"
+#include "chip.h"
+
+static const struct usb_device_id usb_ids[] = {
+	{ USB_DEVICE(PURELIFI_X_VENDOR_ID_0, PURELIFI_X_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_X },
+	{ USB_DEVICE(PURELIFI_XC_VENDOR_ID_0, PURELIFI_XC_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XC },
+	{ USB_DEVICE(PURELIFI_XL_VENDOR_ID_0, PURELIFI_XL_PRODUCT_ID_0),
+	  .driver_info = DEVICE_LIFI_XL },
+	{}
+};
+
+void plfxlc_send_packet_from_data_queue(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_tx *tx = &usb->tx;
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+	u8 last_served_sidx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	last_served_sidx = usb->sidx;
+	do {
+		usb->sidx = (usb->sidx + 1) % MAX_STA_NUM;
+		if (!(tx->station[usb->sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (!(tx->station[usb->sidx].flag & STATION_FIFO_FULL_FLAG))
+			skb = skb_peek(&tx->station[usb->sidx].data_list);
+	} while ((usb->sidx != last_served_sidx) && (!skb));
+
+	if (skb) {
+		skb = skb_dequeue(&tx->station[usb->sidx].data_list);
+		plfxlc_usb_wreq_async(usb, skb->data, skb->len, USB_REQ_DATA_TX,
+				      plfxlc_tx_urb_complete, skb);
+		if (skb_queue_len(&tx->station[usb->sidx].data_list) <= 60)
+			ieee80211_wake_queues(plfxlc_usb_to_hw(usb));
+	}
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void handle_rx_packet(struct plfxlc_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	plfxlc_mac_rx(plfxlc_usb_to_hw(usb), buffer, length);
+}
+
+static void rx_urb_complete(struct urb *urb)
+{
+	struct plfxlc_usb_tx *tx;
+	struct plfxlc_usb *usb;
+	unsigned int length;
+	const u8 *buffer;
+	u16 status;
+	u8 sidx;
+	int r;
+
+	if (!urb) {
+		pr_err("urb is NULL\n");
+		return;
+	}
+	if (!urb->context) {
+		pr_err("urb ctx is NULL\n");
+		return;
+	}
+	usb = urb->context;
+
+	if (usb->initialized != 1) {
+		pr_err("usb is not initialized\n");
+		return;
+	}
+
+	tx = &usb->tx;
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(plfxlc_urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	default:
+		dev_dbg(plfxlc_urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		if (tx->submitted_urbs++ < PURELIFI_URB_RETRY_MAX) {
+			dev_dbg(plfxlc_urb_dev(urb), "urb %p resubmit %d", urb,
+				tx->submitted_urbs++);
+			goto resubmit;
+		} else {
+			dev_dbg(plfxlc_urb_dev(urb), "urb %p  max resubmits reached", urb);
+			tx->submitted_urbs = 0;
+			return;
+		}
+	}
+
+	buffer = urb->transfer_buffer;
+	length = le32_to_cpu(*(__le32 *)(buffer + sizeof(struct rx_status)))
+		 + sizeof(u32);
+
+	if (urb->actual_length != (PLF_MSG_STATUS_OFFSET + 1)) {
+		if (usb->initialized && usb->link_up)
+			handle_rx_packet(usb, buffer, length);
+		goto resubmit;
+	}
+
+	status = buffer[PLF_MSG_STATUS_OFFSET];
+
+	switch (status) {
+	case STATION_FIFO_ALMOST_FULL_NOT_MESSAGE:
+		dev_dbg(&usb->intf->dev,
+			"FIFO full not packet receipt\n");
+		tx->mac_fifo_full = 1;
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag |= STATION_FIFO_FULL_FLAG;
+		break;
+	case STATION_FIFO_ALMOST_FULL_MESSAGE:
+		dev_dbg(&usb->intf->dev, "FIFO full packet receipt\n");
+
+		for (sidx = 0; sidx < MAX_STA_NUM; sidx++)
+			tx->station[sidx].flag &= STATION_ACTIVE_FLAG;
+
+		plfxlc_send_packet_from_data_queue(usb);
+		break;
+	case STATION_CONNECT_MESSAGE:
+		usb->link_up = 1;
+		dev_dbg(&usb->intf->dev, "ST_CONNECT_MSG packet receipt\n");
+		break;
+	case STATION_DISCONNECT_MESSAGE:
+		usb->link_up = 0;
+		dev_dbg(&usb->intf->dev, "ST_DISCONN_MSG packet receipt\n");
+		break;
+	default:
+		dev_dbg(&usb->intf->dev, "Unknown packet receipt\n");
+		break;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_dbg(plfxlc_urb_dev(urb), "urb %p resubmit fail (%d)\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct plfxlc_usb *usb)
+{
+	struct usb_device *udev = plfxlc_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return NULL;
+
+	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+				    &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+			  buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+static int __lf_x_usb_enable_rx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+	int i, r;
+
+	r = -ENOMEM;
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	if (!urbs)
+		goto error;
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	spin_lock_irq(&rx->lock);
+
+	dev_dbg(plfxlc_usb_dev(usb), "irq_disabled %d\n", irqs_disabled());
+
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = RX_URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_KERNEL);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0;
+
+error_submit:
+	for (i = 0; i < RX_URBS_COUNT; i++)
+		usb_kill_urb(urbs[i]);
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
+	}
+	return r;
+}
+
+int plfxlc_usb_enable_rx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_rx *rx = &usb->rx;
+	int r;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __lf_x_usb_enable_rx(usb);
+	if (!r)
+		usb->rx_usb_enabled = 1;
+
+	mutex_unlock(&rx->setup_mutex);
+
+	return r;
+}
+
+static void __lf_x_usb_disable_rx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_rx *rx = &usb->rx;
+	unsigned long flags;
+	unsigned int count;
+	struct urb **urbs;
+	int i;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
+	}
+	kfree(urbs);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+}
+
+void plfxlc_usb_disable_rx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__lf_x_usb_disable_rx(usb);
+	usb->rx_usb_enabled = 0;
+	mutex_unlock(&rx->setup_mutex);
+}
+
+void plfxlc_usb_disable_tx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
+
+	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+	WARN_ON(tx->submitted_urbs != 0);
+	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
+	/* The stopped state is ignored, relying on ieee80211_wake_queues()
+	 * in a potentionally following plfxlc_usb_enable_tx().
+	 */
+}
+
+void plfxlc_usb_enable_tx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	set_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->submitted_urbs = 0;
+	ieee80211_wake_queues(plfxlc_usb_to_hw(usb));
+	tx->stopped = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+void plfxlc_tx_urb_complete(struct urb *urb)
+{
+	struct ieee80211_tx_info *info;
+	struct plfxlc_usb *usb;
+	struct sk_buff *skb;
+
+	skb = urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/* grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by plfxlc_mac_tx_to_dev or mac80211)
+	 */
+	usb = &plfxlc_hw_mac(info->rate_driver_data[0])->chip.usb;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPIPE:
+		dev_dbg(plfxlc_urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	default:
+		dev_dbg(plfxlc_urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		return;
+	}
+
+	plfxlc_mac_tx_to_dev(skb, urb->status);
+	plfxlc_send_packet_from_data_queue(usb);
+	usb_free_urb(urb);
+}
+
+static inline void init_usb_rx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_rx *rx = &usb->rx;
+
+	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
+
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH)
+		rx->usb_packet_size = 512;
+	else
+		rx->usb_packet_size = 64;
+
+	if (rx->fragment_length != 0)
+		dev_dbg(plfxlc_usb_dev(usb), "fragment_length error\n");
+}
+
+static inline void init_usb_tx(struct plfxlc_usb *usb)
+{
+	struct plfxlc_usb_tx *tx = &usb->tx;
+
+	spin_lock_init(&tx->lock);
+	clear_bit(PLF_BIT_ENABLED, &tx->enabled);
+	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
+	init_usb_anchor(&tx->submitted);
+}
+
+void plfxlc_usb_init(struct plfxlc_usb *usb, struct ieee80211_hw *hw,
+		     struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, hw);
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+void plfxlc_usb_release(struct plfxlc_usb *usb)
+{
+	plfxlc_op_stop(plfxlc_usb_to_hw(usb));
+	plfxlc_usb_disable_tx(usb);
+	plfxlc_usb_disable_rx(usb);
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+}
+
+const char *plfxlc_speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown";
+	}
+}
+
+int plfxlc_usb_init_hw(struct plfxlc_usb *usb)
+{
+	int r;
+
+	r = usb_reset_configuration(plfxlc_usb_to_usbdev(usb));
+	if (r) {
+		dev_err(plfxlc_usb_dev(usb), "cfg reset failed (%d)\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void get_usb_req(struct usb_device *udev, void *buffer,
+			u32 buffer_len, enum plf_usb_req_enum usb_req_id,
+			struct plf_usb_req *usb_req)
+{
+	__be32 payload_len_nw = cpu_to_be32(buffer_len + FCS_LEN);
+	const u8 *buffer_src_p = buffer;
+	u8 *buffer_dst = usb_req->buf;
+	u32 temp_usb_len = 0;
+
+	usb_req->id = cpu_to_be32(usb_req_id);
+	usb_req->len  = cpu_to_be32(0);
+
+	/* Copy buffer length into the transmitted buffer, as it is important
+	 * for the Rx MAC to know its exact length.
+	 */
+	if (usb_req->id == cpu_to_be32(USB_REQ_BEACON_WR)) {
+		memcpy(buffer_dst, &payload_len_nw, sizeof(payload_len_nw));
+		buffer_dst += sizeof(payload_len_nw);
+		temp_usb_len += sizeof(payload_len_nw);
+	}
+
+	memcpy(buffer_dst, buffer_src_p, buffer_len);
+	buffer_dst += buffer_len;
+	buffer_src_p += buffer_len;
+	temp_usb_len +=  buffer_len;
+
+	/* Set the FCS_LEN (4) bytes as 0 for CRC checking. */
+	memset(buffer_dst, 0, FCS_LEN);
+	buffer_dst += FCS_LEN;
+	temp_usb_len += FCS_LEN;
+
+	/* Round the packet to be transmitted to 4 bytes. */
+	if (temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT) {
+		memset(buffer_dst, 0, PURELIFI_BYTE_NUM_ALIGNMENT -
+		       (temp_usb_len %
+			PURELIFI_BYTE_NUM_ALIGNMENT));
+		buffer_dst += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len %
+				PURELIFI_BYTE_NUM_ALIGNMENT);
+		temp_usb_len += PURELIFI_BYTE_NUM_ALIGNMENT -
+				(temp_usb_len % PURELIFI_BYTE_NUM_ALIGNMENT);
+	}
+
+	usb_req->len = cpu_to_be32(temp_usb_len);
+}
+
+int plfxlc_usb_wreq_async(struct plfxlc_usb *usb, const u8 *buffer,
+			  int buffer_len, enum plf_usb_req_enum usb_req_id,
+			  usb_complete_t complete_fn,
+			  void *context)
+{
+	struct usb_device *udev = interface_to_usbdev(usb->ez_usb);
+	struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+	int r;
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+			  (void *)buffer, buffer_len, complete_fn, context);
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_err(&udev->dev, "Async write submit failed (%d)\n", r);
+
+	return r;
+}
+
+int plfxlc_usb_wreq(struct usb_interface *ez_usb, void *buffer, int buffer_len,
+		    enum plf_usb_req_enum usb_req_id)
+{
+	struct usb_device *udev = interface_to_usbdev(ez_usb);
+	unsigned char *dma_buffer = NULL;
+	struct plf_usb_req usb_req;
+	int usb_bulk_msg_len;
+	int actual_length;
+	int r;
+
+	get_usb_req(udev, buffer, buffer_len, usb_req_id, &usb_req);
+	usb_bulk_msg_len = sizeof(__le32) + sizeof(__le32) +
+			   be32_to_cpu(usb_req.len);
+
+	dma_buffer = kmemdup(&usb_req, usb_bulk_msg_len, GFP_KERNEL);
+
+	if (!dma_buffer) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = usb_bulk_msg(udev,
+			 usb_sndbulkpipe(udev, EP_DATA_OUT),
+			 dma_buffer, usb_bulk_msg_len,
+			 &actual_length, USB_BULK_MSG_TIMEOUT_MS);
+	kfree(dma_buffer);
+error:
+	if (r) {
+		r = -ENOMEM;
+		dev_err(&udev->dev, "usb_bulk_msg failed (%d)\n", r);
+	}
+
+	return r;
+}
+
+static void slif_data_plane_sap_timer_callb(struct timer_list *t)
+{
+	struct plfxlc_usb *usb = from_timer(usb, t, tx.tx_retry_timer);
+
+	plfxlc_send_packet_from_data_queue(usb);
+	timer_setup(&usb->tx.tx_retry_timer,
+		    slif_data_plane_sap_timer_callb, 0);
+	mod_timer(&usb->tx.tx_retry_timer, jiffies + TX_RETRY_BACKOFF_JIFF);
+}
+
+static void sta_queue_cleanup_timer_callb(struct timer_list *t)
+{
+	struct plfxlc_usb *usb = from_timer(usb, t, sta_queue_cleanup);
+	struct plfxlc_usb_tx *tx = &usb->tx;
+	int sidx;
+
+	for (sidx = 0; sidx < MAX_STA_NUM - 1; sidx++) {
+		if (!(tx->station[sidx].flag & STATION_CONNECTED_FLAG))
+			continue;
+		if (tx->station[sidx].flag & STATION_HEARTBEAT_FLAG) {
+			tx->station[sidx].flag ^= STATION_HEARTBEAT_FLAG;
+		} else {
+			memset(tx->station[sidx].mac, 0, ETH_ALEN);
+			tx->station[sidx].flag = 0;
+		}
+	}
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	mod_timer(&usb->sta_queue_cleanup, jiffies + STA_QUEUE_CLEANUP_JIFF);
+}
+
+static int probe(struct usb_interface *intf,
+		 const struct usb_device_id *id)
+{
+	u8 serial_number[PURELIFI_SERIAL_LEN];
+	struct ieee80211_hw *hw = NULL;
+	struct plfxlc_usb_tx *tx;
+	struct plfxlc_chip *chip;
+	struct plfxlc_usb *usb;
+	u8 hw_address[ETH_ALEN];
+	unsigned int i;
+	int r = 0;
+
+	hw = plfxlc_mac_alloc_hw(intf);
+
+	if (!hw) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	chip = &plfxlc_hw_mac(hw)->chip;
+	usb = &chip->usb;
+	usb->ez_usb = intf;
+	tx = &usb->tx;
+
+	r = upload_mac_and_serial(intf, hw_address, serial_number);
+	if (r) {
+		dev_err(&intf->dev, "MAC and Serial upload failed (%d)\n", r);
+		goto error;
+	}
+
+	chip->unit_type = STA;
+	dev_err(&intf->dev, "Unit type is station");
+
+	r = plfxlc_mac_preinit_hw(hw, hw_address);
+	if (r) {
+		dev_err(&intf->dev, "Init mac failed (%d)\n", r);
+		goto error;
+	}
+
+	r = ieee80211_register_hw(hw);
+	if (r) {
+		dev_err(&intf->dev, "Register device failed (%d)\n", r);
+		goto error;
+	}
+
+	if ((le16_to_cpu(interface_to_usbdev(intf)->descriptor.idVendor) ==
+				PURELIFI_XL_VENDOR_ID_0) &&
+	    (le16_to_cpu(interface_to_usbdev(intf)->descriptor.idProduct) ==
+				PURELIFI_XL_PRODUCT_ID_0)) {
+		r = plfxlc_download_xl_firmware(intf);
+	} else {
+		r = plfxlc_download_fpga(intf);
+	}
+	if (r != 0) {
+		dev_err(&intf->dev, "FPGA download failed (%d)\n", r);
+		goto error;
+	}
+
+	tx->mac_fifo_full = 0;
+	spin_lock_init(&tx->lock);
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plfxlc_usb_init_hw(usb);
+	if (r < 0) {
+		dev_err(&intf->dev, "usb_init_hw failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plfxlc_chip_switch_radio(chip, PLFXLC_RADIO_ON);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_switch_radio_on failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plfxlc_chip_set_rate(chip, 8);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "chip_set_rate failed (%d)\n", r);
+		goto error;
+	}
+
+	msleep(PLF_MSLEEP_TIME);
+	r = plfxlc_usb_wreq(usb->ez_usb,
+			    hw_address, ETH_ALEN, USB_REQ_MAC_WR);
+	if (r < 0) {
+		dev_dbg(&intf->dev, "MAC_WR failure (%d)\n", r);
+		goto error;
+	}
+
+	plfxlc_chip_enable_rxtx(chip);
+
+	/* Initialise the data plane Tx queue */
+	for (i = 0; i < MAX_STA_NUM; i++) {
+		skb_queue_head_init(&tx->station[i].data_list);
+		tx->station[i].flag = 0;
+	}
+
+	tx->station[STA_BROADCAST_INDEX].flag |= STATION_CONNECTED_FLAG;
+	for (i = 0; i < ETH_ALEN; i++)
+		tx->station[STA_BROADCAST_INDEX].mac[i] = 0xFF;
+
+	timer_setup(&tx->tx_retry_timer, slif_data_plane_sap_timer_callb, 0);
+	tx->tx_retry_timer.expires = jiffies + TX_RETRY_BACKOFF_JIFF;
+	add_timer(&tx->tx_retry_timer);
+
+	timer_setup(&usb->sta_queue_cleanup,
+		    sta_queue_cleanup_timer_callb, 0);
+	usb->sta_queue_cleanup.expires = jiffies + STA_QUEUE_CLEANUP_JIFF;
+	add_timer(&usb->sta_queue_cleanup);
+
+	plfxlc_mac_init_hw(hw);
+	usb->initialized = true;
+	return 0;
+error:
+	if (hw) {
+		plfxlc_mac_release(plfxlc_hw_mac(hw));
+		ieee80211_unregister_hw(hw);
+		ieee80211_free_hw(hw);
+	}
+	dev_err(&intf->dev, "pureLifi:Device error");
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = plfxlc_intf_to_hw(intf);
+	struct plfxlc_mac *mac;
+	struct plfxlc_usb *usb;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return;
+
+	mac = plfxlc_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	del_timer_sync(&usb->tx.tx_retry_timer);
+	del_timer_sync(&usb->sta_queue_cleanup);
+
+	ieee80211_unregister_hw(hw);
+
+	plfxlc_chip_disable_rxtx(&mac->chip);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here, the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	plfxlc_mac_release(mac);
+	ieee80211_free_hw(hw);
+}
+
+static void plfxlc_usb_resume(struct plfxlc_usb *usb)
+{
+	struct plfxlc_mac *mac = plfxlc_usb_to_mac(usb);
+	int r;
+
+	r = plfxlc_op_start(plfxlc_usb_to_hw(usb));
+	if (r < 0) {
+		dev_warn(plfxlc_usb_dev(usb),
+			 "Device resume failed (%d)\n", r);
+
+		if (usb->was_running)
+			set_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+		usb_queue_reset_device(usb->intf);
+		return;
+	}
+
+	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+		r = plfxlc_restore_settings(mac);
+		if (r < 0) {
+			dev_dbg(plfxlc_usb_dev(usb),
+				"Restore failed (%d)\n", r);
+			return;
+		}
+	}
+}
+
+static void plfxlc_usb_stop(struct plfxlc_usb *usb)
+{
+	plfxlc_op_stop(plfxlc_usb_to_hw(usb));
+	plfxlc_usb_disable_tx(usb);
+	plfxlc_usb_disable_rx(usb);
+
+	usb->initialized = false;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct plfxlc_mac *mac;
+	struct plfxlc_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = plfxlc_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	usb->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+
+	plfxlc_usb_stop(usb);
+
+	return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = usb_get_intfdata(intf);
+	struct plfxlc_mac *mac;
+	struct plfxlc_usb *usb;
+
+	if (!hw || intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	mac = plfxlc_hw_mac(hw);
+	usb = &mac->chip.usb;
+
+	if (usb->was_running)
+		plfxlc_usb_resume(usb);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static struct plfxlc_usb *get_plfxlc_usb(struct usb_interface *intf)
+{
+	struct ieee80211_hw *hw = plfxlc_intf_to_hw(intf);
+	struct plfxlc_mac *mac;
+
+	/* Either something really bad happened, or
+	 * we're just dealing with a DEVICE_INSTALLER.
+	 */
+	if (!hw)
+		return NULL;
+
+	mac = plfxlc_hw_mac(hw);
+	return &mac->chip.usb;
+}
+
+static int suspend(struct usb_interface *interface,
+		   pm_message_t message)
+{
+	struct plfxlc_usb *pl = get_plfxlc_usb(interface);
+	struct plfxlc_mac *mac = plfxlc_usb_to_mac(pl);
+
+	if (!pl || !plfxlc_usb_dev(pl))
+		return -ENODEV;
+	if (pl->initialized == 0)
+		return 0;
+	pl->was_running = test_bit(PURELIFI_DEVICE_RUNNING, &mac->flags);
+	plfxlc_usb_stop(pl);
+	return 0;
+}
+
+static int resume(struct usb_interface *interface)
+{
+	struct plfxlc_usb *pl = get_plfxlc_usb(interface);
+
+	if (!pl || !plfxlc_usb_dev(pl))
+		return -ENODEV;
+	if (pl->was_running)
+		plfxlc_usb_resume(pl);
+	return 0;
+}
+
+#endif
+
+static struct usb_driver driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = usb_ids,
+	.probe = probe,
+	.disconnect = disconnect,
+	.pre_reset = pre_reset,
+	.post_reset = post_reset,
+#ifdef CONFIG_PM
+	.suspend = suspend,
+	.resume = resume,
+#endif
+	.disable_hub_initiated_lpm = 1,
+};
+
+static int __init usb_init(void)
+{
+	int r;
+
+	r = usb_register(&driver);
+	if (r) {
+		pr_err("%s usb_register() failed %d\n", driver.name, r);
+		return r;
+	}
+
+	pr_debug("Driver initialized :%s\n", driver.name);
+	return 0;
+}
+
+static void __exit usb_exit(void)
+{
+	usb_deregister(&driver);
+	pr_debug("%s %s\n", driver.name, __func__);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for pureLiFi devices");
+MODULE_AUTHOR("pureLiFi");
+MODULE_VERSION("1.0");
+MODULE_FIRMWARE("plfxlc/lifi-x.bin");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+module_init(usb_init);
+module_exit(usb_exit);
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.h b/drivers/net/wireless/purelifi/plfxlc/usb.h
new file mode 100644
index 000000000000..9b64908e9a97
--- /dev/null
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.h
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021 pureLiFi
+ */
+
+#ifndef _PURELIFI_USB_H
+#define _PURELIFI_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "intf.h"
+
+#define USB_BULK_MSG_TIMEOUT_MS 2000
+
+#define PURELIFI_X_VENDOR_ID_0   0x16C1
+#define PURELIFI_X_PRODUCT_ID_0  0x1CDE
+#define PURELIFI_XC_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XC_PRODUCT_ID_0 0x0008
+#define PURELIFI_XL_VENDOR_ID_0  0x2EF5
+#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
+
+#define PLF_FPGA_STATUS_LEN 2
+#define PLF_FPGA_STATE_LEN 9
+#define PLF_BULK_TLEN 16384
+#define PLF_FPGA_MG 6 /* Magic check */
+#define PLF_XL_BUF_LEN 64
+#define PLF_MSG_STATUS_OFFSET 7
+
+#define PLF_USB_TIMEOUT 1000
+#define PLF_MSLEEP_TIME 200
+
+#define PURELIFI_URB_RETRY_MAX 5
+
+#define plfxlc_usb_dev(usb) (&(usb)->intf->dev)
+
+/* Tx retry backoff timer (in milliseconds) */
+#define TX_RETRY_BACKOFF_MS 10
+#define STA_QUEUE_CLEANUP_MS 5000
+
+/* Tx retry backoff timer (in jiffies) */
+#define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
+#define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+#define plfxlc_urb_dev(urb) (&(urb)->dev->dev)
+
+#define STATION_FIFO_ALMOST_FULL_MESSAGE     0
+#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
+#define STATION_CONNECT_MESSAGE              2
+#define STATION_DISCONNECT_MESSAGE           3
+
+int plfxlc_usb_wreq(struct usb_interface *ez_usb, void *buffer, int buffer_len,
+		    enum plf_usb_req_enum usb_req_id);
+void plfxlc_tx_urb_complete(struct urb *urb);
+
+enum {
+	USB_MAX_RX_SIZE       = 4800,
+	USB_MAX_EP_INT_BUFFER = 64,
+};
+
+struct plfxlc_usb_interrupt {
+	spinlock_t lock; /* spin lock for usb interrupt buffer */
+	struct urb *urb;
+	void *buffer;
+	int interval;
+};
+
+#define RX_URBS_COUNT 5
+
+struct plfxlc_usb_rx {
+	spinlock_t lock; /* spin lock for rx urb */
+	struct mutex setup_mutex; /* mutex lockt for rx urb */
+	u8 fragment[2 * USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct plf_station {
+   /*  7...3    |    2      |     1     |     0	    |
+    * Reserved  | Heartbeat | FIFO full | Connected |
+    */
+	unsigned char flag;
+	unsigned char mac[ETH_ALEN];
+	struct sk_buff_head data_list;
+};
+
+struct plfxlc_firmware_file {
+	u32 total_files;
+	u32 total_size;
+	u32 size;
+	u32 start_addr;
+	u32 control_packets;
+} __packed;
+
+#define STATION_CONNECTED_FLAG 0x1
+#define STATION_FIFO_FULL_FLAG 0x2
+#define STATION_HEARTBEAT_FLAG 0x4
+#define STATION_ACTIVE_FLAG    0xFD
+
+#define PURELIFI_SERIAL_LEN 256
+#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
+#define MAX_STA_NUM         (AP_USER_LIMIT + 1)
+
+struct plfxlc_usb_tx {
+	unsigned long enabled;
+	spinlock_t lock; /* spinlock for USB tx */
+	u8 mac_fifo_full;
+	struct sk_buff_head submitted_skbs;
+	struct usb_anchor submitted;
+	int submitted_urbs;
+	bool stopped;
+	struct timer_list tx_retry_timer;
+	struct plf_station station[MAX_STA_NUM];
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct plfxlc_usb {
+	struct timer_list sta_queue_cleanup;
+	struct plfxlc_usb_rx rx;
+	struct plfxlc_usb_tx tx;
+	struct usb_interface *intf;
+	struct usb_interface *ez_usb;
+	u8 req_buf[64]; /* plfxlc_usb_iowrite16v needs 62 bytes */
+	u8 sidx; /* store last served */
+	bool rx_usb_enabled;
+	bool initialized;
+	bool was_running;
+	bool link_up;
+};
+
+enum endpoints {
+	EP_DATA_IN  = 2,
+	EP_DATA_OUT = 8,
+};
+
+enum devicetype {
+	DEVICE_LIFI_X  = 0,
+	DEVICE_LIFI_XC  = 1,
+	DEVICE_LIFI_XL  = 1,
+};
+
+enum {
+	PLF_BIT_ENABLED = 1,
+	PLF_BIT_MAX = 2,
+};
+
+int plfxlc_usb_wreq_async(struct plfxlc_usb *usb, const u8 *buffer,
+			  int buffer_len, enum plf_usb_req_enum usb_req_id,
+			  usb_complete_t complete_fn, void *context);
+
+static inline struct usb_device *
+plfxlc_usb_to_usbdev(struct plfxlc_usb *usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *
+plfxlc_intf_to_hw(struct usb_interface *intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *
+plfxlc_usb_to_hw(struct plfxlc_usb *usb)
+{
+	return plfxlc_intf_to_hw(usb->intf);
+}
+
+void plfxlc_usb_init(struct plfxlc_usb *usb, struct ieee80211_hw *hw,
+		     struct usb_interface *intf);
+void plfxlc_send_packet_from_data_queue(struct plfxlc_usb *usb);
+void plfxlc_usb_release(struct plfxlc_usb *usb);
+void plfxlc_usb_disable_rx(struct plfxlc_usb *usb);
+void plfxlc_usb_enable_tx(struct plfxlc_usb *usb);
+void plfxlc_usb_disable_tx(struct plfxlc_usb *usb);
+int plfxlc_usb_tx(struct plfxlc_usb *usb, struct sk_buff *skb);
+int plfxlc_usb_enable_rx(struct plfxlc_usb *usb);
+int plfxlc_usb_init_hw(struct plfxlc_usb *usb);
+const char *plfxlc_speed(enum usb_device_speed speed);
+
+/* Firmware declarations */
+int plfxlc_download_xl_firmware(struct usb_interface *intf);
+int plfxlc_download_fpga(struct usb_interface *intf);
+
+int upload_mac_and_serial(struct usb_interface *intf,
+			  unsigned char *hw_address,
+			  unsigned char *serial_number);
+
+#endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* Re: [PATCH v22 1/2] nl80211: Add LC placeholder band definition to nl80211_band
  2022-02-24 18:20     ` [PATCH v22 1/2] nl80211: Add LC placeholder band definition to nl80211_band Srinivasan Raju
@ 2022-02-25  9:52       ` Kalle Valo
  0 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2022-02-25  9:52 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski, Johannes Berg,
	open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

> Define LC band which is a draft under IEEE 802.11 bb
> Current NL80211_BAND_LC is a placeholder band
> The band will be redefined as IEEE 802.11 bb progresses
>
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

No need to submit this patch anymore, Johannes has already applied it:

https://git.kernel.org/linus/63fa04266629

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v22 1/2] wireless: Initial driver submission for pureLiFi STA devices
  2022-02-24 18:20     ` [PATCH v22 1/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
@ 2022-04-25 13:06       ` Kalle Valo
       [not found]         ` <CWLP265MB32173F6188304F6B2CB90C79E0F89@CWLP265MB3217.GBRP265.PROD.OUTLOOK.COM>
  2022-04-27  4:55       ` Kalle Valo
  1 sibling, 1 reply; 108+ messages in thread
From: Kalle Valo @ 2022-04-25 13:06 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, Srinivasan Raju, David S. Miller, Jakub Kicinski,
	open list, open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> wrote:

> This driver implementation has been based on the zd1211rw driver
> 
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management
> 
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture
> 
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

While reviewing this for the last time I did some minor changes in the pending branch:

https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git/commit/?h=pending&id=68d57a07bfe5bb29b80cd8b8fa24c9d1ea104124

Here's the commit log I plan to use:

    wireless: add plfxlc driver for pureLiFi X, XL, XC devices
    
    This is a driver for pureLiFi X, XL, XC devices which use light to transmit
    data, so they are not compatible with normal Wi-Fi devices. The driver uses
    separate NL80211_BAND_LC band to distinguish from Wi-Fi.  The driver is based
    on 802.11 softMAC Architecture and uses native 802.11 for configuration and
    management. Station and Ad-Hoc modes are supported.
    
    The driver is compiled and tested in ARM, x86 architectures and compiled in
    powerpc architecture. This driver implementation has been based on the zd1211rw
    driver.

And the list of changes I made:

* rewrote the commit log

* change MAINTAINER to be only for plfxlc

* CONFIG_PURELIFI_XLC -> CONFIG_PLFXLC

* purelifi_xlc.ko -> plfxlc.ko

* improve Kconfig help text

* _LF_X_CHIP_H -> PLFXLC_CHIP_H

* PURELIFI_MAC_H -> PLFXLC_MAC_H

* _PURELIFI_USB_H -> PLFXLC_USB_H

* upload_mac_and_serial() -> plfxlc_upload_mac_and_serial()

Unless I don't get any comments I'm planning to merge this on Wednesday.

-- 
https://patchwork.kernel.org/project/linux-wireless/patch/20220224182042.132466-3-srini.raju@purelifi.com/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [EXTERNAL] Re: [PATCH v22 1/2] wireless: Initial driver submission for pureLiFi STA devices
       [not found]         ` <CWLP265MB32173F6188304F6B2CB90C79E0F89@CWLP265MB3217.GBRP265.PROD.OUTLOOK.COM>
@ 2022-04-26  4:17           ` Kalle Valo
  0 siblings, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2022-04-26  4:17 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: Mostafa Afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> writes:

>> Unless I don't get any comments I'm planning to merge this on Wednesday.
>
> Thanks Kalle , I do not have any comments.

Please don't use HTML, our lists drop all HTML mail.

-- 
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v22 1/2] wireless: Initial driver submission for pureLiFi STA devices
  2022-02-24 18:20     ` [PATCH v22 1/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
  2022-04-25 13:06       ` Kalle Valo
@ 2022-04-27  4:55       ` Kalle Valo
  1 sibling, 0 replies; 108+ messages in thread
From: Kalle Valo @ 2022-04-27  4:55 UTC (permalink / raw)
  To: Srinivasan Raju
  Cc: mostafa.afgani, David S. Miller, Jakub Kicinski, open list,
	open list:NETWORKING DRIVERS (WIRELESS),
	open list:NETWORKING DRIVERS

Srinivasan Raju <srini.raju@purelifi.com> wrote:

> This driver implementation has been based on the zd1211rw driver
> 
> Driver is based on 802.11 softMAC Architecture and uses
> native 802.11 for configuration and management
> 
> The driver is compiled and tested in ARM, x86 architectures and
> compiled in powerpc architecture
> 
> Signed-off-by: Srinivasan Raju <srini.raju@purelifi.com>

Applied with my changes to wireless-next:

68d57a07bfe5 wireless: add plfxlc driver for pureLiFi X, XL, XC devices

-- 
https://patchwork.kernel.org/project/linux-wireless/patch/20220224182042.132466-3-srini.raju@purelifi.com/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches


^ permalink raw reply	[flat|nested] 108+ messages in thread

end of thread, other threads:[~2022-04-27  4:55 UTC | newest]

Thread overview: 108+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20200924151910.21693-1-srini.raju@purelifi.com>
2020-09-28 10:19 ` [PATCH] [v2] wireless: Initial driver submission for pureLiFi devices Srinivasan Raju
2020-09-28 12:07   ` Joe Perches
2020-09-28 12:53     ` Srinivasan Raju
2020-09-30  5:16   ` Leon Romanovsky
2020-09-30  5:29     ` Srinivasan Raju
2020-09-30  8:01     ` Kalle Valo
2020-09-30  9:55       ` Leon Romanovsky
2020-09-30 10:11         ` Johannes Berg
2020-09-30 10:44           ` Leon Romanovsky
2020-10-16  8:23             ` Kalle Valo
2020-09-30  8:05     ` Kalle Valo
2020-09-30 10:04       ` Leon Romanovsky
2020-10-14  6:19   ` [PATCH] [PATCH] [v3] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
2020-10-14 10:17     ` kernel test robot
2020-10-15 22:35     ` Joe Perches
2020-10-16  6:36       ` Srinivasan Raju
2020-10-16  6:34   ` [PATCH] [v4] " Srinivasan Raju
2020-10-16  8:58     ` Joe Perches
2020-10-16 10:13       ` Srinivasan Raju
2020-10-19  3:17     ` [PATCH] [v5] " Srinivasan Raju
2020-10-19  4:55       ` Joe Perches
2020-10-19  6:05         ` Srinivasan Raju
2020-10-19  8:38       ` [PATCH] [v6] " Srinivasan Raju
2020-10-19 16:07         ` Krishna Chaitanya
2020-10-19 16:40           ` Srinivasan Raju
2020-10-19 16:54             ` Joe Perches
2020-10-19 17:05               ` Srinivasan Raju
2020-11-16  9:22     ` [PATCH] [v7] " Srinivasan Raju
2020-11-16 20:45       ` Joe Perches
2020-11-18  3:24         ` Srinivasan Raju
2020-11-24 14:44       ` Kalle Valo
     [not found]       ` <20201124144448.4E95EC43460@smtp.codeaurora.org>
2020-11-26  5:01         ` Srinivasan Raju
2020-12-03  4:43           ` Srinivasan Raju
2020-12-03 15:58             ` Kalle Valo
2020-12-03 16:50               ` Srinivasan Raju
2020-12-19 13:15                 ` Kalle Valo
2020-12-03  4:38   ` [PATCH] [v8] " Srinivasan Raju
2020-12-03  5:09   ` [PATCH] [v9] " Srinivasan Raju
2020-12-03  7:53     ` Joe Perches
2020-12-08  5:53   ` [PATCH] [v10] " Srinivasan Raju
2020-12-08 11:57   ` [PATCH] [v11] " Srinivasan Raju
2020-12-08 14:37     ` Kalle Valo
2020-12-19 12:51     ` Kalle Valo
2020-12-19 13:06     ` Kalle Valo
2020-12-19 13:14     ` Kalle Valo
2020-12-21  5:52       ` Srinivasan Raju
2020-12-21  5:57         ` Kalle Valo
2021-01-15 12:13           ` Srinivasan Raju
2021-01-05 13:19   ` [PATCH] [PATCH] [v12] " Srinivasan Raju
2021-02-12 11:49   ` [PATCH] [v13] " Srinivasan Raju
2021-02-12 13:44     ` Johannes Berg
2021-02-17 10:05       ` Kalle Valo
2021-02-19  5:15       ` Srinivasan Raju
2021-02-19  8:25         ` Johannes Berg
2021-02-24 10:41           ` Srinivasan Raju
2021-02-12 15:06     ` kernel test robot
2021-02-12 17:57     ` kernel test robot
2021-02-17 10:02     ` Kalle Valo
2021-02-17 10:13       ` Kalle Valo
2021-02-17 10:16         ` Srinivasan Raju
2021-02-17 10:09     ` Kalle Valo
2021-02-17 10:19     ` Kalle Valo
2021-02-24 10:44       ` Srinivasan Raju
2021-02-26 13:07   ` [PATCH] [v14] " Srinivasan Raju
2021-04-19 11:52     ` Srinivasan Raju
2021-08-10 13:02       ` Srinivasan Raju
2021-08-21 13:42         ` Kalle Valo
2021-08-18 14:13     ` [PATCH] [v15] " Srinivasan Raju
2021-09-20 13:05       ` Kalle Valo
     [not found]         ` <CWLP265MB3217BB5AA5F102629A3AD204E0A19@CWLP265MB3217.GBRP265.PROD.OUTLOOK.COM>
2021-09-21 12:30           ` [EXTERNAL] " Kalle Valo
2021-09-22  7:33             ` Johannes Berg
2021-09-24 13:27               ` [EXTERNAL] " Srinivasan Raju
2021-09-20 14:11       ` Kalle Valo
2021-09-24 11:11       ` Kalle Valo
2021-09-24 13:26   ` [PATCH] [v16] wireless: Initial driver submission for pureLiFi LiFi Station Srinivasan Raju
2021-09-24 13:40     ` Kalle Valo
2021-10-05 11:22   ` [PATCH] [v17] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
2021-10-05 11:26     ` Johannes Berg
2021-10-05 12:30   ` [PATCH] [v18 1/2] nl80211: Add LC placeholder band definition to enum nl80211_band Srinivasan Raju
2021-10-05 12:31   ` [PATCH] [v18 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
2021-10-05 22:09     ` Jeff Johnson
2021-10-06 10:04   ` [PATCH] [v19 " Srinivasan Raju
2021-10-11  6:16     ` Kalle Valo
2021-10-12 12:50   ` [PATCH 0/2] wireless: New Driver " Srinivasan Raju
2021-10-12 12:50     ` [PATCH 1/2] [v19 1/2] nl80211: Add LC placeholder band definition to enum nl80211_band Srinivasan Raju
2021-10-12 12:50     ` [PATCH 2/2] [v19 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
2021-10-14  6:03       ` kernel test robot
2021-10-24 17:58       ` kernel test robot
2021-10-18 10:00   ` [PATCH v20 0/2] wireless: New Driver " Srinivasan Raju
2021-10-18 10:00     ` [PATCH v20 1/2] nl80211: Add LC placeholder band definition to nl80211_band Srinivasan Raju
2021-10-18 10:00     ` [PATCH v20 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
2021-10-25  9:59       ` Kari Argillander
     [not found]         ` <CWLP265MB321780AB502EF147F6AAF197E0839@CWLP265MB3217.GBRP265.PROD.OUTLOOK.COM>
2021-10-25 12:17           ` [EXTERNAL] " Kalle Valo
2021-10-27 11:34       ` kernel test robot
2021-10-27 12:38       ` Kari Argillander
2021-10-28  7:24         ` Kalle Valo
2021-10-31 13:10   ` [PATCH v21 0/2] wireless: New Driver " Srinivasan Raju
2021-10-31 13:10     ` [PATCH 1/2] nl80211: Add LC placeholder band definition to nl80211_band Srinivasan Raju
2021-10-31 13:10     ` [PATCH 2/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
2021-12-20 19:13       ` Kalle Valo
2022-02-24 15:35       ` Kalle Valo
2022-02-24 18:20   ` [PATCH v22 0/2] wireless: New Driver " Srinivasan Raju
2022-02-24 18:20     ` [PATCH v22 1/2] nl80211: Add LC placeholder band definition to nl80211_band Srinivasan Raju
2022-02-25  9:52       ` Kalle Valo
2022-02-24 18:20     ` [PATCH v22 1/2] wireless: Initial driver submission for pureLiFi STA devices Srinivasan Raju
2022-04-25 13:06       ` Kalle Valo
     [not found]         ` <CWLP265MB32173F6188304F6B2CB90C79E0F89@CWLP265MB3217.GBRP265.PROD.OUTLOOK.COM>
2022-04-26  4:17           ` [EXTERNAL] " Kalle Valo
2022-04-27  4:55       ` Kalle Valo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).