This patch provides the mwlwifi driver for Marvell 8863, 8864 and 8897
chipsets.
This driver was developed as part of the openwrt.org project to support
Linksys WRT1900AC and is maintained on https://github.com/kaloz/mwlwifi.
The mwlwifi driver differs from existing mwifiex driver:
o mwlwifi is a "softmac driver" using the kernel mac802.11 subsystem
to provide full AP/Wireless Bridge functionality (routers).
o mwifiex is a "fullmac driver" which provides a comprehensive set of
client functions (laptops/embedded devices)
o only mwlwifi supports Marvell AP chip 886X series
NOTE: Users with Marvell 88W8897 chipsets currently should enable
(CONFIG=Y or M) either CONFIG_MWIFIEX or CONFIG_MWLWIFI, NOT BOTH.
mwlwifi driver leveraged code from existing MWL8K driver in the
following areas:
- 802.11n setting for mac80211
- Functions needed to hook up to mac80211
- Interactions with mac80211 to establish BA streams
- Partial firmware APIs, some data fields
- Method to pass Rx packets to mac80211 except 11ac rates
In addition, mwlwifi driver supports:
- future scalability and future development (refactored source code)
- Marvell 802.11ac chipsets, including combo BT devices
- 802.11ac related settings and functions
- concurrent AP+STA functionalities with single firmware per chip
- firmware APIs for the supported chipset
- communicating new mac80211 settings to firmware
- Different TX/RX datapath where applicable
- A-MSDU and A-MPDU
- Refined the code to establish BA streams
Signed-off-by: David Lin <[email protected]>
---
MAINTAINERS | 6 +
drivers/net/wireless/marvell/Kconfig | 1 +
drivers/net/wireless/marvell/Makefile | 1 +
drivers/net/wireless/marvell/mwlwifi/Kconfig | 23 +
drivers/net/wireless/marvell/mwlwifi/Makefile | 13 +
drivers/net/wireless/marvell/mwlwifi/debugfs.c | 830 +++++++
drivers/net/wireless/marvell/mwlwifi/debugfs.h | 24 +
drivers/net/wireless/marvell/mwlwifi/dev.h | 516 +++++
drivers/net/wireless/marvell/mwlwifi/fwcmd.c | 2837 +++++++++++++++++++++++
drivers/net/wireless/marvell/mwlwifi/fwcmd.h | 223 ++
drivers/net/wireless/marvell/mwlwifi/fwdl.c | 186 ++
drivers/net/wireless/marvell/mwlwifi/fwdl.h | 25 +
drivers/net/wireless/marvell/mwlwifi/hostcmd.h | 913 ++++++++
drivers/net/wireless/marvell/mwlwifi/isr.c | 172 ++
drivers/net/wireless/marvell/mwlwifi/isr.h | 27 +
drivers/net/wireless/marvell/mwlwifi/mac80211.c | 719 ++++++
drivers/net/wireless/marvell/mwlwifi/main.c | 840 +++++++
drivers/net/wireless/marvell/mwlwifi/rx.c | 513 ++++
drivers/net/wireless/marvell/mwlwifi/rx.h | 25 +
drivers/net/wireless/marvell/mwlwifi/sysadpt.h | 83 +
drivers/net/wireless/marvell/mwlwifi/thermal.c | 182 ++
drivers/net/wireless/marvell/mwlwifi/thermal.h | 42 +
drivers/net/wireless/marvell/mwlwifi/tx.c | 1250 ++++++++++
drivers/net/wireless/marvell/mwlwifi/tx.h | 37 +
24 files changed, 9488 insertions(+)
create mode 100644 drivers/net/wireless/marvell/mwlwifi/Kconfig
create mode 100644 drivers/net/wireless/marvell/mwlwifi/Makefile
create mode 100644 drivers/net/wireless/marvell/mwlwifi/debugfs.c
create mode 100644 drivers/net/wireless/marvell/mwlwifi/debugfs.h
create mode 100644 drivers/net/wireless/marvell/mwlwifi/dev.h
create mode 100644 drivers/net/wireless/marvell/mwlwifi/fwcmd.c
create mode 100644 drivers/net/wireless/marvell/mwlwifi/fwcmd.h
create mode 100644 drivers/net/wireless/marvell/mwlwifi/fwdl.c
create mode 100644 drivers/net/wireless/marvell/mwlwifi/fwdl.h
create mode 100644 drivers/net/wireless/marvell/mwlwifi/hostcmd.h
create mode 100644 drivers/net/wireless/marvell/mwlwifi/isr.c
create mode 100644 drivers/net/wireless/marvell/mwlwifi/isr.h
create mode 100644 drivers/net/wireless/marvell/mwlwifi/mac80211.c
create mode 100644 drivers/net/wireless/marvell/mwlwifi/main.c
create mode 100644 drivers/net/wireless/marvell/mwlwifi/rx.c
create mode 100644 drivers/net/wireless/marvell/mwlwifi/rx.h
create mode 100644 drivers/net/wireless/marvell/mwlwifi/sysadpt.h
create mode 100644 drivers/net/wireless/marvell/mwlwifi/thermal.c
create mode 100644 drivers/net/wireless/marvell/mwlwifi/thermal.h
create mode 100644 drivers/net/wireless/marvell/mwlwifi/tx.c
create mode 100644 drivers/net/wireless/marvell/mwlwifi/tx.h
diff --git a/MAINTAINERS b/MAINTAINERS
index e773ad5..b7410b1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7624,6 +7624,12 @@ L: [email protected]
S: Odd Fixes
F: drivers/net/wireless/marvell/mwl8k.c
+MARVELL MWLWIFI WIRELESS DRIVER
+M: David Lin <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/net/wireless/marvell/mwlwifi/
+
MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
M: Nicolas Pitre <[email protected]>
S: Odd Fixes
diff --git a/drivers/net/wireless/marvell/Kconfig b/drivers/net/wireless/marvell/Kconfig
index 4938c7e..c7c7e0e4 100644
--- a/drivers/net/wireless/marvell/Kconfig
+++ b/drivers/net/wireless/marvell/Kconfig
@@ -14,6 +14,7 @@ if WLAN_VENDOR_MARVELL
source "drivers/net/wireless/marvell/libertas/Kconfig"
source "drivers/net/wireless/marvell/libertas_tf/Kconfig"
source "drivers/net/wireless/marvell/mwifiex/Kconfig"
+source "drivers/net/wireless/marvell/mwlwifi/Kconfig"
config MWL8K
tristate "Marvell 88W8xxx PCI/PCIe Wireless support"
diff --git a/drivers/net/wireless/marvell/Makefile b/drivers/net/wireless/marvell/Makefile
index 1b0a7d2..04dff33 100644
--- a/drivers/net/wireless/marvell/Makefile
+++ b/drivers/net/wireless/marvell/Makefile
@@ -2,5 +2,6 @@ obj-$(CONFIG_LIBERTAS) += libertas/
obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/
obj-$(CONFIG_MWIFIEX) += mwifiex/
+obj-$(CONFIG_MWLWIFI) += mwlwifi/
obj-$(CONFIG_MWL8K) += mwl8k.o
diff --git a/drivers/net/wireless/marvell/mwlwifi/Kconfig b/drivers/net/wireless/marvell/mwlwifi/Kconfig
new file mode 100644
index 0000000..a9bcb9c
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/Kconfig
@@ -0,0 +1,23 @@
+config MWLWIFI
+ tristate "Marvell Avastar 88W8864/88W8897 PCIe driver (mac80211 compatible)"
+ depends on PCI && MAC80211
+ select FW_LOADER
+ ---help---
+ Select to build the driver supporting the:
+
+ Marvell Wireless Wi-Fi 88W8864 modules
+ Marvell Wireless Wi-Fi 88W8897 modules
+
+ This driver uses the kernel's mac80211 subsystem.
+
+ If you want to compile the driver as a module (= code which can be
+ inserted in and removed from the running kernel whenever you want),
+ say M here and read <file:Documentation/kbuild/modules.txt>. The
+ module will be called mwlwifi.
+
+ NOTE: Selecting this driver may cause conflict with MWIFIEX driver
+ that also operates on the same part number 88W8897. Users should
+ select either MWIFIEX or MWLWIFI, not both. MWIFIEX is fullmac,
+ supporting more comprehensive client functions for laptops/embedded
+ devices. MWLWIFI is mac80211-based for full AP/Wireless Bridge.
+
diff --git a/drivers/net/wireless/marvell/mwlwifi/Makefile b/drivers/net/wireless/marvell/mwlwifi/Makefile
new file mode 100644
index 0000000..37af74f
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/Makefile
@@ -0,0 +1,13 @@
+obj-$(CONFIG_MWLWIFI) += mwlwifi.o
+
+mwlwifi-objs += main.o
+mwlwifi-objs += mac80211.o
+mwlwifi-objs += fwdl.o
+mwlwifi-objs += fwcmd.o
+mwlwifi-objs += tx.o
+mwlwifi-objs += rx.o
+mwlwifi-objs += isr.o
+mwlwifi-$(CONFIG_THERMAL) += thermal.o
+mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.c b/drivers/net/wireless/marvell/mwlwifi/debugfs.c
new file mode 100644
index 0000000..631ce6a
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.c
@@ -0,0 +1,830 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file implements debug fs related functions. */
+
+#include <linux/debugfs.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "hostcmd.h"
+#include "fwcmd.h"
+#include "thermal.h"
+#include "debugfs.h"
+
+#define MWLWIFI_DEBUGFS_ADD_FILE(name) do { \
+ if (!debugfs_create_file(#name, 0644, priv->debugfs_phy, \
+ priv, &mwl_debugfs_##name##_fops)) \
+ return; \
+} while (0)
+
+#define MWLWIFI_DEBUGFS_FILE_OPS(name) \
+static const struct file_operations mwl_debugfs_##name##_fops = { \
+ .read = mwl_debugfs_##name##_read, \
+ .write = mwl_debugfs_##name##_write, \
+ .open = simple_open, \
+}
+
+#define MWLWIFI_DEBUGFS_FILE_READ_OPS(name) \
+static const struct file_operations mwl_debugfs_##name##_fops = { \
+ .read = mwl_debugfs_##name##_read, \
+ .open = simple_open, \
+}
+
+#define MWLWIFI_DEBUGFS_FILE_WRITE_OPS(name) \
+static const struct file_operations mwl_debugfs_##name##_fops = { \
+ .write = mwl_debugfs_##name##_write, \
+ .open = simple_open, \
+}
+
+static void dump_data(char *p, int size, int *len, u8 *data,
+ int data_len, char *title)
+{
+ int cur_byte = 0;
+ int i;
+
+ *len += scnprintf(p + *len, size - *len, "%s\n", title);
+
+ for (cur_byte = 0; cur_byte < data_len; cur_byte += 8) {
+ if ((cur_byte + 8) < data_len) {
+ for (i = 0; i < 8; i++)
+ *len += scnprintf(p + *len, size - *len,
+ "0x%02x ",
+ *(data + cur_byte + i));
+ *len += scnprintf(p + *len, size - *len, "\n");
+ } else {
+ for (i = 0; i < (data_len - cur_byte); i++)
+ *len += scnprintf(p + *len, size - *len,
+ "0x%02x ",
+ *(data + cur_byte + i));
+ *len += scnprintf(p + *len, size - *len, "\n");
+ break;
+ }
+ }
+}
+
+static ssize_t mwl_debugfs_info_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
+ ssize_t ret;
+
+ if (!p)
+ return -ENOMEM;
+
+ len += scnprintf(p + len, size - len, "\n");
+ len += scnprintf(p + len, size - len,
+ "driver name: %s\n", MWL_DRV_NAME);
+ len += scnprintf(p + len, size - len, "chip type: %s\n",
+ (priv->chip_type == MWL8864) ? "88W8864" : "88W8897");
+ len += scnprintf(p + len, size - len,
+ "hw version: %X\n", priv->hw_data.hw_version);
+ len += scnprintf(p + len, size - len, "firmware version: 0x%08x\n",
+ priv->hw_data.fw_release_num);
+ len += scnprintf(p + len, size - len,
+ "power table loaded from dts: %s\n",
+ priv->forbidden_setting ? "no" : "yes");
+ len += scnprintf(p + len, size - len, "firmware region code: 0x%x\n",
+ priv->fw_region_code);
+ len += scnprintf(p + len, size - len,
+ "mac address: %pM\n", priv->hw_data.mac_addr);
+ len += scnprintf(p + len, size - len,
+ "2g: %s\n", priv->disable_2g ? "disable" : "enable");
+ len += scnprintf(p + len, size - len,
+ "5g: %s\n", priv->disable_5g ? "disable" : "enable");
+ len += scnprintf(p + len, size - len, "antenna: %d %d\n",
+ (priv->antenna_tx == ANTENNA_TX_4_AUTO) ? 4 : 2,
+ (priv->antenna_rx == ANTENNA_TX_4_AUTO) ? 4 : 2);
+ len += scnprintf(p + len, size - len, "irq number: %d\n", priv->irq);
+ len += scnprintf(p + len, size - len, "iobase0: %p\n", priv->iobase0);
+ len += scnprintf(p + len, size - len, "iobase1: %p\n", priv->iobase1);
+ len += scnprintf(p + len, size - len,
+ "tx limit: %d\n", priv->txq_limit);
+ len += scnprintf(p + len, size - len,
+ "rx limit: %d\n", priv->recv_limit);
+ len += scnprintf(p + len, size - len, "ap macid support: %08x\n",
+ priv->ap_macids_supported);
+ len += scnprintf(p + len, size - len, "sta macid support: %08x\n",
+ priv->sta_macids_supported);
+ len += scnprintf(p + len, size - len,
+ "macid used: %08x\n", priv->macids_used);
+ len += scnprintf(p + len, size - len,
+ "qe trigger number: %d\n", priv->qe_trigger_num);
+ len += scnprintf(p + len, size - len, "\n");
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
+ free_page(page);
+
+ return ret;
+}
+
+static ssize_t mwl_debugfs_vif_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
+ struct mwl_vif *mwl_vif;
+ struct ieee80211_vif *vif;
+ char ssid[IEEE80211_MAX_SSID_LEN + 1];
+ ssize_t ret;
+
+ if (!p)
+ return -ENOMEM;
+
+ len += scnprintf(p + len, size - len, "\n");
+ spin_lock_bh(&priv->vif_lock);
+ list_for_each_entry(mwl_vif, &priv->vif_list, list) {
+ vif = container_of((char *)mwl_vif, struct ieee80211_vif,
+ drv_priv[0]);
+ len += scnprintf(p + len, size - len,
+ "macid: %d\n", mwl_vif->macid);
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ len += scnprintf(p + len, size - len, "type: ap\n");
+ memcpy(ssid, vif->bss_conf.ssid,
+ vif->bss_conf.ssid_len);
+ ssid[vif->bss_conf.ssid_len] = 0;
+ len += scnprintf(p + len, size - len,
+ "ssid: %s\n", ssid);
+ len += scnprintf(p + len, size - len,
+ "mac address: %pM\n", mwl_vif->bssid);
+ break;
+ case NL80211_IFTYPE_STATION:
+ len += scnprintf(p + len, size - len, "type: sta\n");
+ len += scnprintf(p + len, size - len,
+ "mac address: %pM\n",
+ mwl_vif->sta_mac);
+ break;
+ default:
+ len += scnprintf(p + len, size - len,
+ "type: unknown\n");
+ break;
+ }
+ len += scnprintf(p + len, size - len, "hw_crypto_enabled: %s\n",
+ mwl_vif->is_hw_crypto_enabled ?
+ "true" : "false");
+ len += scnprintf(p + len, size - len,
+ "key idx: %d\n", mwl_vif->keyidx);
+ len += scnprintf(p + len, size - len,
+ "IV: %08x%04x\n", mwl_vif->iv32,
+ mwl_vif->iv16);
+ dump_data(p, size, &len, mwl_vif->beacon_info.ie_wmm_ptr,
+ mwl_vif->beacon_info.ie_wmm_len, "WMM:");
+ dump_data(p, size, &len, mwl_vif->beacon_info.ie_rsn_ptr,
+ mwl_vif->beacon_info.ie_rsn_len, "RSN:");
+ dump_data(p, size, &len, mwl_vif->beacon_info.ie_rsn48_ptr,
+ mwl_vif->beacon_info.ie_rsn48_len, "RSN48:");
+ dump_data(p, size, &len, mwl_vif->beacon_info.ie_ht_ptr,
+ mwl_vif->beacon_info.ie_ht_len, "HT:");
+ dump_data(p, size, &len, mwl_vif->beacon_info.ie_vht_ptr,
+ mwl_vif->beacon_info.ie_vht_len, "VHT:");
+ len += scnprintf(p + len, size - len, "\n");
+ }
+ spin_unlock_bh(&priv->vif_lock);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
+ free_page(page);
+
+ return ret;
+}
+
+static ssize_t mwl_debugfs_sta_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
+ struct mwl_sta *sta_info;
+ struct ieee80211_sta *sta;
+ ssize_t ret;
+
+ if (!p)
+ return -ENOMEM;
+
+ len += scnprintf(p + len, size - len, "\n");
+ spin_lock_bh(&priv->sta_lock);
+ list_for_each_entry(sta_info, &priv->sta_list, list) {
+ sta = container_of((char *)sta_info, struct ieee80211_sta,
+ drv_priv[0]);
+ len += scnprintf(p + len, size - len,
+ "mac address: %pM\n", sta->addr);
+ len += scnprintf(p + len, size - len, "aid: %u\n", sta->aid);
+ len += scnprintf(p + len, size - len, "ampdu: %s\n",
+ sta_info->is_ampdu_allowed ? "true" : "false");
+ len += scnprintf(p + len, size - len, "amsdu: %s\n",
+ sta_info->is_amsdu_allowed ? "true" : "false");
+ if (sta_info->is_amsdu_allowed) {
+ len += scnprintf(p + len, size - len,
+ "amsdu cap: 0x%02x\n",
+ sta_info->amsdu_ctrl.cap);
+ }
+ if (sta->ht_cap.ht_supported) {
+ len += scnprintf(p + len, size - len,
+ "ht_cap: 0x%04x, ampdu: %02x, %02x\n",
+ sta->ht_cap.cap,
+ sta->ht_cap.ampdu_factor,
+ sta->ht_cap.ampdu_density);
+ len += scnprintf(p + len, size - len,
+ "rx_mask: 0x%02x, %02x, %02x, %02x\n",
+ sta->ht_cap.mcs.rx_mask[0],
+ sta->ht_cap.mcs.rx_mask[1],
+ sta->ht_cap.mcs.rx_mask[2],
+ sta->ht_cap.mcs.rx_mask[3]);
+ }
+ if (sta->vht_cap.vht_supported) {
+ len += scnprintf(p + len, size - len,
+ "vht_cap: 0x%08x, mcs: %02x, %02x\n",
+ sta->vht_cap.cap,
+ sta->vht_cap.vht_mcs.rx_mcs_map,
+ sta->vht_cap.vht_mcs.tx_mcs_map);
+ }
+ len += scnprintf(p + len, size - len, "rx_bw: %d, rx_nss: %d\n",
+ sta->bandwidth, sta->rx_nss);
+ len += scnprintf(p + len, size - len,
+ "tdls: %d, tdls_init: %d\n",
+ sta->tdls, sta->tdls_initiator);
+ len += scnprintf(p + len, size - len, "wme: %d, mfp: %d\n",
+ sta->wme, sta->mfp);
+ len += scnprintf(p + len, size - len, "IV: %08x%04x\n",
+ sta_info->iv32, sta_info->iv16);
+ len += scnprintf(p + len, size - len, "\n");
+ }
+ spin_unlock_bh(&priv->sta_lock);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
+ free_page(page);
+
+ return ret;
+}
+
+static ssize_t mwl_debugfs_ampdu_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
+ struct mwl_ampdu_stream *stream;
+ int i;
+ struct mwl_sta *sta_info;
+ struct ieee80211_sta *sta;
+ ssize_t ret;
+
+ if (!p)
+ return -ENOMEM;
+
+ len += scnprintf(p + len, size - len, "\n");
+ spin_lock_bh(&priv->stream_lock);
+ for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) {
+ stream = &priv->ampdu[i];
+ len += scnprintf(p + len, size - len, "stream: %d\n", i);
+ len += scnprintf(p + len, size - len, "idx: %u\n", stream->idx);
+ len += scnprintf(p + len, size - len,
+ "state: %u\n", stream->state);
+ if (stream->sta) {
+ len += scnprintf(p + len, size - len,
+ "mac address: %pM\n",
+ stream->sta->addr);
+ len += scnprintf(p + len, size - len,
+ "tid: %u\n", stream->tid);
+ }
+ }
+ spin_unlock_bh(&priv->stream_lock);
+ spin_lock_bh(&priv->sta_lock);
+ list_for_each_entry(sta_info, &priv->sta_list, list) {
+ for (i = 0; i < MWL_MAX_TID; i++) {
+ if (sta_info->check_ba_failed[i]) {
+ sta = container_of((char *)sta_info,
+ struct ieee80211_sta,
+ drv_priv[0]);
+ len += scnprintf(p + len, size - len,
+ "%pM(%d): %d\n",
+ sta->addr, i,
+ sta_info->check_ba_failed[i]);
+ }
+ }
+ }
+ spin_unlock_bh(&priv->sta_lock);
+ len += scnprintf(p + len, size - len, "\n");
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
+ free_page(page);
+
+ return ret;
+}
+
+static ssize_t mwl_debugfs_device_pwrtbl_read(struct file *file,
+ char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
+ int i, j;
+ ssize_t ret;
+
+ if (!p)
+ return -ENOMEM;
+
+ len += scnprintf(p + len, size - len, "\n");
+ len += scnprintf(p + len, size - len,
+ "power table loaded from dts: %s\n",
+ priv->forbidden_setting ? "no" : "yes");
+ len += scnprintf(p + len, size - len, "firmware region code: 0x%x\n",
+ priv->fw_region_code);
+ len += scnprintf(p + len, size - len, "number of channel: %d\n",
+ priv->number_of_channels);
+ for (i = 0; i < priv->number_of_channels; i++) {
+ len += scnprintf(p + len, size - len, "%3d ",
+ priv->device_pwr_tbl[i].channel);
+ for (j = 0; j < SYSADPT_TX_POWER_LEVEL_TOTAL; j++)
+ len += scnprintf(p + len, size - len, "%3d ",
+ priv->device_pwr_tbl[i].tx_pwr[j]);
+ len += scnprintf(p + len, size - len, "%3d ",
+ priv->device_pwr_tbl[i].dfs_capable);
+ len += scnprintf(p + len, size - len, "%3d ",
+ priv->device_pwr_tbl[i].ax_ant);
+ len += scnprintf(p + len, size - len, "%3d\n",
+ priv->device_pwr_tbl[i].cdd);
+ }
+ len += scnprintf(p + len, size - len, "\n");
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
+ free_page(page);
+
+ return ret;
+}
+
+static ssize_t mwl_debugfs_tx_desc_read(struct file *file,
+ char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
+ struct mwl_desc_data *desc;
+ int i, num, write_item = -1, free_item = -1;
+ ssize_t ret;
+
+ spin_lock_bh(&priv->tx_desc_lock);
+ num = priv->tx_desc_num;
+ desc = &priv->desc_data[num];
+ len += scnprintf(p + len, size - len, "num: %i fw_desc_cnt:%i\n",
+ num, priv->fw_desc_cnt[num]);
+ for (i = 0; i < SYSADPT_MAX_NUM_TX_DESC; i++) {
+ len += scnprintf(p + len, size - len, "%3i %x\n", i,
+ desc->tx_hndl[i].pdesc->status);
+ if (desc->pnext_tx_hndl == &desc->tx_hndl[i])
+ write_item = i;
+ if (desc->pstale_tx_hndl == &desc->tx_hndl[i])
+ free_item = i;
+ }
+ len += scnprintf(p + len, size - len, "next:%i stale:%i\n",
+ write_item, free_item);
+ len += scnprintf(p + len, size - len, "\n");
+ spin_unlock_bh(&priv->tx_desc_lock);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
+ free_page(page);
+
+ return ret;
+}
+
+static ssize_t mwl_debugfs_tx_desc_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ int tx_desc_num = 0;
+ ssize_t ret;
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, ubuf, buf_size)) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ if (kstrtoint(buf, 0, &tx_desc_num)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if ((tx_desc_num < 0) || (tx_desc_num >= SYSADPT_NUM_OF_DESC_DATA)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ priv->tx_desc_num = tx_desc_num;
+ ret = count;
+
+err:
+ free_page(addr);
+ return ret;
+}
+
+static ssize_t mwl_debugfs_dfs_channel_read(struct file *file,
+ char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *channel;
+ int i;
+ ssize_t ret;
+
+ if (!p)
+ return -ENOMEM;
+
+ sband = priv->hw->wiphy->bands[NL80211_BAND_5GHZ];
+ if (!sband)
+ return -EINVAL;
+
+ len += scnprintf(p + len, size - len, "\n");
+ for (i = 0; i < sband->n_channels; i++) {
+ channel = &sband->channels[i];
+ if (channel->flags & IEEE80211_CHAN_RADAR) {
+ len += scnprintf(p + len, size - len,
+ "%d(%d): flags: %08x dfs_state: %d\n",
+ channel->hw_value,
+ channel->center_freq,
+ channel->flags, channel->dfs_state);
+ len += scnprintf(p + len, size - len,
+ "cac timer: %d ms\n",
+ channel->dfs_cac_ms);
+ }
+ }
+ len += scnprintf(p + len, size - len, "\n");
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
+ free_page(page);
+
+ return ret;
+}
+
+static ssize_t mwl_debugfs_dfs_channel_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ struct ieee80211_supported_band *sband;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ int dfs_state = 0;
+ int cac_time = -1;
+ struct ieee80211_channel *channel;
+ int i;
+ ssize_t ret;
+
+ if (!buf)
+ return -ENOMEM;
+
+ sband = priv->hw->wiphy->bands[NL80211_BAND_5GHZ];
+ if (!sband) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (copy_from_user(buf, ubuf, buf_size)) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ ret = sscanf(buf, "%d %d", &dfs_state, &cac_time);
+
+ if ((ret < 1) || (ret > 2)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (i = 0; i < sband->n_channels; i++) {
+ channel = &sband->channels[i];
+ if (channel->flags & IEEE80211_CHAN_RADAR) {
+ channel->dfs_state = dfs_state;
+ if (cac_time != -1)
+ channel->dfs_cac_ms = cac_time * 1000;
+ }
+ }
+ ret = count;
+
+err:
+ free_page(addr);
+ return ret;
+}
+
+static ssize_t mwl_debugfs_dfs_radar_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
+ ssize_t ret;
+
+ if (!p)
+ return -ENOMEM;
+
+ len += scnprintf(p + len, size - len, "\n");
+ len += scnprintf(p + len, size - len,
+ "csa_active: %d\n", priv->csa_active);
+ len += scnprintf(p + len, size - len,
+ "dfs_region: %d\n", priv->dfs_region);
+ len += scnprintf(p + len, size - len,
+ "chirp_count_min: %d\n", priv->dfs_chirp_count_min);
+ len += scnprintf(p + len, size - len, "chirp_time_interval: %d\n",
+ priv->dfs_chirp_time_interval);
+ len += scnprintf(p + len, size - len,
+ "pw_filter: %d\n", priv->dfs_pw_filter);
+ len += scnprintf(p + len, size - len,
+ "min_num_radar: %d\n", priv->dfs_min_num_radar);
+ len += scnprintf(p + len, size - len,
+ "min_pri_count: %d\n", priv->dfs_min_pri_count);
+ len += scnprintf(p + len, size - len, "\n");
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
+ free_page(page);
+
+ return ret;
+}
+
+static ssize_t mwl_debugfs_dfs_radar_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+
+ wiphy_info(priv->hw->wiphy, "simulate radar detected\n");
+ ieee80211_radar_detected(priv->hw);
+
+ return count;
+}
+
+static ssize_t mwl_debugfs_thermal_read(struct file *file,
+ char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
+ ssize_t ret;
+
+ if (!p)
+ return -ENOMEM;
+
+ mwl_fwcmd_get_temp(priv->hw, &priv->temperature);
+
+ len += scnprintf(p + len, size - len, "\n");
+ len += scnprintf(p + len, size - len, "quiet period: %d\n",
+ priv->quiet_period);
+ len += scnprintf(p + len, size - len, "throttle state: %d\n",
+ priv->throttle_state);
+ len += scnprintf(p + len, size - len, "temperature: %d\n",
+ priv->temperature);
+ len += scnprintf(p + len, size - len, "\n");
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
+ free_page(page);
+
+ return ret;
+}
+
+static ssize_t mwl_debugfs_thermal_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ int throttle_state;
+ ssize_t ret;
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, ubuf, buf_size)) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ if (kstrtoint(buf, 0, &throttle_state)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (throttle_state > SYSADPT_THERMAL_THROTTLE_MAX) {
+ wiphy_warn(priv->hw->wiphy,
+ "throttle state %d is exceeding the limit %d\n",
+ throttle_state, SYSADPT_THERMAL_THROTTLE_MAX);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ priv->throttle_state = throttle_state;
+ mwl_thermal_set_throttling(priv);
+ ret = count;
+
+err:
+ free_page(addr);
+ return ret;
+}
+
+static int mwl_debugfs_reg_access(struct mwl_priv *priv, bool write)
+{
+ struct ieee80211_hw *hw = priv->hw;
+ u8 set;
+ u32 *addr_val;
+ int ret = 0;
+
+ set = write ? WL_SET : WL_GET;
+
+ switch (priv->reg_type) {
+ case MWL_ACCESS_ADDR0:
+ if (set == WL_GET)
+ priv->reg_value =
+ readl(priv->iobase0 + priv->reg_offset);
+ else
+ writel(priv->reg_value,
+ priv->iobase0 + priv->reg_offset);
+ break;
+ case MWL_ACCESS_ADDR1:
+ if (set == WL_GET)
+ priv->reg_value =
+ readl(priv->iobase1 + priv->reg_offset);
+ else
+ writel(priv->reg_value,
+ priv->iobase1 + priv->reg_offset);
+ break;
+ case MWL_ACCESS_ADDR:
+ addr_val = kmalloc(64 * sizeof(u32), GFP_KERNEL);
+ if (addr_val) {
+ memset(addr_val, 0, 64 * sizeof(u32));
+ addr_val[0] = priv->reg_value;
+ ret = mwl_fwcmd_get_addr_value(hw, priv->reg_offset,
+ 4, addr_val, set);
+ if ((!ret) && (set == WL_GET))
+ priv->reg_value = addr_val[0];
+ kfree(addr_val);
+ } else {
+ ret = -ENOMEM;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static ssize_t mwl_debugfs_regrdwr_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
+ int ret = 0;
+
+ if (!p)
+ return -ENOMEM;
+
+ if (!priv->reg_type) {
+ /* No command has been given */
+ len += scnprintf(p + len, size - len, "0");
+ goto none;
+ }
+
+ /* Set command has been given */
+ if (priv->reg_value != UINT_MAX) {
+ ret = mwl_debugfs_reg_access(priv, true);
+ goto done;
+ }
+ /* Get command has been given */
+ ret = mwl_debugfs_reg_access(priv, false);
+
+done:
+ if (!ret)
+ len += scnprintf(p + len, size - len, "%u 0x%08x 0x%08x\n",
+ priv->reg_type, priv->reg_offset,
+ priv->reg_value);
+ else
+ len += scnprintf(p + len, size - len,
+ "error: %d(%u 0x%08x 0x%08x)\n",
+ ret, priv->reg_type, priv->reg_offset,
+ priv->reg_value);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
+
+none:
+
+ free_page(page);
+ return ret;
+}
+
+static ssize_t mwl_debugfs_regrdwr_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ int ret;
+ u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX;
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, ubuf, buf_size)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ ret = sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value);
+
+ if (!reg_type) {
+ ret = -EINVAL;
+ goto done;
+ } else {
+ priv->reg_type = reg_type;
+ priv->reg_offset = reg_offset;
+ priv->reg_value = reg_value;
+ ret = count;
+ }
+done:
+
+ free_page(addr);
+ return ret;
+}
+
+MWLWIFI_DEBUGFS_FILE_READ_OPS(info);
+MWLWIFI_DEBUGFS_FILE_READ_OPS(vif);
+MWLWIFI_DEBUGFS_FILE_READ_OPS(sta);
+MWLWIFI_DEBUGFS_FILE_READ_OPS(ampdu);
+MWLWIFI_DEBUGFS_FILE_READ_OPS(device_pwrtbl);
+MWLWIFI_DEBUGFS_FILE_OPS(tx_desc);
+MWLWIFI_DEBUGFS_FILE_OPS(dfs_channel);
+MWLWIFI_DEBUGFS_FILE_OPS(dfs_radar);
+MWLWIFI_DEBUGFS_FILE_OPS(thermal);
+MWLWIFI_DEBUGFS_FILE_OPS(regrdwr);
+
+void mwl_debugfs_init(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+
+ if (!priv->debugfs_phy)
+ priv->debugfs_phy = debugfs_create_dir("mwlwifi",
+ hw->wiphy->debugfsdir);
+
+ if (!priv->debugfs_phy)
+ return;
+
+ MWLWIFI_DEBUGFS_ADD_FILE(info);
+ MWLWIFI_DEBUGFS_ADD_FILE(vif);
+ MWLWIFI_DEBUGFS_ADD_FILE(sta);
+ MWLWIFI_DEBUGFS_ADD_FILE(ampdu);
+ MWLWIFI_DEBUGFS_ADD_FILE(device_pwrtbl);
+ MWLWIFI_DEBUGFS_ADD_FILE(tx_desc);
+ MWLWIFI_DEBUGFS_ADD_FILE(dfs_channel);
+ MWLWIFI_DEBUGFS_ADD_FILE(dfs_radar);
+ MWLWIFI_DEBUGFS_ADD_FILE(thermal);
+ MWLWIFI_DEBUGFS_ADD_FILE(regrdwr);
+}
+
+void mwl_debugfs_remove(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+
+ debugfs_remove(priv->debugfs_phy);
+ priv->debugfs_phy = NULL;
+}
diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.h b/drivers/net/wireless/marvell/mwlwifi/debugfs.h
new file mode 100644
index 0000000..b4c3eb3
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file defines debug fs related functions. */
+
+#ifndef _MWL_DEBUGFS_H_
+#define _MWL_DEBUGFS_H_
+
+void mwl_debugfs_init(struct ieee80211_hw *hw);
+void mwl_debugfs_remove(struct ieee80211_hw *hw);
+
+#endif /* _MWL_DEBUGFS_H_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/dev.h b/drivers/net/wireless/marvell/mwlwifi/dev.h
new file mode 100644
index 0000000..c7b10ac
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/dev.h
@@ -0,0 +1,516 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file defines device related information. */
+
+#ifndef _DEV_H_
+#define _DEV_H_
+
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <net/mac80211.h>
+
+#define MWL_DRV_NAME KBUILD_MODNAME
+
+/* Map to 0x80000000 (Bus control) on BAR0 */
+#define MACREG_REG_H2A_INTERRUPT_EVENTS 0x00000C18 /* (From host to ARM) */
+#define MACREG_REG_H2A_INTERRUPT_CAUSE 0x00000C1C /* (From host to ARM) */
+#define MACREG_REG_H2A_INTERRUPT_MASK 0x00000C20 /* (From host to ARM) */
+#define MACREG_REG_H2A_INTERRUPT_CLEAR_SEL 0x00000C24 /* (From host to ARM) */
+#define MACREG_REG_H2A_INTERRUPT_STATUS_MASK 0x00000C28 /* (From host to ARM) */
+
+#define MACREG_REG_A2H_INTERRUPT_EVENTS 0x00000C2C /* (From ARM to host) */
+#define MACREG_REG_A2H_INTERRUPT_CAUSE 0x00000C30 /* (From ARM to host) */
+#define MACREG_REG_A2H_INTERRUPT_MASK 0x00000C34 /* (From ARM to host) */
+#define MACREG_REG_A2H_INTERRUPT_CLEAR_SEL 0x00000C38 /* (From ARM to host) */
+#define MACREG_REG_A2H_INTERRUPT_STATUS_MASK 0x00000C3C /* (From ARM to host) */
+
+/* Map to 0x80000000 on BAR1 */
+#define MACREG_REG_GEN_PTR 0x00000C10
+#define MACREG_REG_INT_CODE 0x00000C14
+
+/* Bit definition for MACREG_REG_A2H_INTERRUPT_CAUSE (A2HRIC) */
+#define MACREG_A2HRIC_BIT_TX_DONE BIT(0)
+#define MACREG_A2HRIC_BIT_RX_RDY BIT(1)
+#define MACREG_A2HRIC_BIT_OPC_DONE BIT(2)
+#define MACREG_A2HRIC_BIT_MAC_EVENT BIT(3)
+#define MACREG_A2HRIC_BIT_RX_PROBLEM BIT(4)
+#define MACREG_A2HRIC_BIT_RADIO_OFF BIT(5)
+#define MACREG_A2HRIC_BIT_RADIO_ON BIT(6)
+#define MACREG_A2HRIC_BIT_RADAR_DETECT BIT(7)
+#define MACREG_A2HRIC_BIT_ICV_ERROR BIT(8)
+#define MACREG_A2HRIC_BIT_WEAKIV_ERROR BIT(9)
+#define MACREG_A2HRIC_BIT_QUE_EMPTY BIT(10)
+#define MACREG_A2HRIC_BIT_QUE_FULL BIT(11)
+#define MACREG_A2HRIC_BIT_CHAN_SWITCH BIT(12)
+#define MACREG_A2HRIC_BIT_TX_WATCHDOG BIT(13)
+#define MACREG_A2HRIC_BA_WATCHDOG BIT(14)
+/* 15 taken by ISR_TXACK */
+#define MACREG_A2HRIC_BIT_SSU_DONE BIT(16)
+#define MACREG_A2HRIC_CONSEC_TXFAIL BIT(17)
+
+#define ISR_SRC_BITS (MACREG_A2HRIC_BIT_RX_RDY | \
+ MACREG_A2HRIC_BIT_TX_DONE | \
+ MACREG_A2HRIC_BIT_OPC_DONE | \
+ MACREG_A2HRIC_BIT_MAC_EVENT | \
+ MACREG_A2HRIC_BIT_WEAKIV_ERROR | \
+ MACREG_A2HRIC_BIT_ICV_ERROR | \
+ MACREG_A2HRIC_BIT_SSU_DONE | \
+ MACREG_A2HRIC_BIT_RADAR_DETECT | \
+ MACREG_A2HRIC_BIT_CHAN_SWITCH | \
+ MACREG_A2HRIC_BIT_TX_WATCHDOG | \
+ MACREG_A2HRIC_BIT_QUE_EMPTY | \
+ MACREG_A2HRIC_BA_WATCHDOG | \
+ MACREG_A2HRIC_CONSEC_TXFAIL)
+
+#define MACREG_A2HRIC_BIT_MASK ISR_SRC_BITS
+
+/* Bit definition for MACREG_REG_H2A_INTERRUPT_CAUSE (H2ARIC) */
+#define MACREG_H2ARIC_BIT_PPA_READY BIT(0)
+#define MACREG_H2ARIC_BIT_DOOR_BELL BIT(1)
+#define MACREG_H2ARIC_BIT_PS BIT(2)
+#define MACREG_H2ARIC_BIT_PSPOLL BIT(3)
+#define ISR_RESET BIT(15)
+#define ISR_RESET_AP33 BIT(26)
+
+/* Data descriptor related constants */
+#define EAGLE_RXD_CTRL_DRIVER_OWN 0x00
+#define EAGLE_RXD_CTRL_DMA_OWN 0x80
+
+#define EAGLE_RXD_STATUS_OK 0x01
+
+#define EAGLE_TXD_STATUS_IDLE 0x00000000
+#define EAGLE_TXD_STATUS_OK 0x00000001
+#define EAGLE_TXD_STATUS_FW_OWNED 0x80000000
+
+/* Antenna control */
+#define ANTENNA_TX_4_AUTO 0
+#define ANTENNA_TX_2 3
+#define ANTENNA_RX_4_AUTO 0
+#define ANTENNA_RX_2 2
+
+/* Band related constants */
+#define BAND_24_CHANNEL_NUM 14
+#define BAND_24_RATE_NUM 13
+#define BAND_50_CHANNEL_NUM 24
+#define BAND_50_RATE_NUM 8
+
+/* vif and station */
+#define NUM_WEP_KEYS 4
+#define MWL_MAX_TID 8
+#define MWL_AMSDU_SIZE_4K 0
+#define MWL_AMSDU_SIZE_8K 1
+
+/* power init */
+#define MWL_POWER_INIT_1 1
+#define MWL_POWER_INIT_2 2
+
+enum {
+ MWL8864 = 0,
+ MWL8897,
+ MWLUNKNOWN,
+};
+
+enum {
+ AP_MODE_11AC = 0x10, /* generic 11ac indication mode */
+ AP_MODE_2_4GHZ_11AC_MIXED = 0x17,
+};
+
+enum {
+ AMPDU_NO_STREAM,
+ AMPDU_STREAM_NEW,
+ AMPDU_STREAM_IN_PROGRESS,
+ AMPDU_STREAM_ACTIVE,
+};
+
+struct mwl_chip_info {
+ const char *part_name;
+ const char *fw_image;
+ int antenna_tx;
+ int antenna_rx;
+};
+
+struct mwl_device_pwr_tbl {
+ u8 channel;
+ u8 tx_pwr[SYSADPT_TX_POWER_LEVEL_TOTAL];
+ u8 dfs_capable;
+ u8 ax_ant;
+ u8 cdd;
+};
+
+struct mwl_tx_pwr_tbl {
+ u8 channel;
+ u8 setcap;
+ u16 txantenna2;
+ u16 tx_power[SYSADPT_TX_POWER_LEVEL_TOTAL];
+ bool cdd;
+};
+
+struct mwl_hw_data {
+ u32 fw_release_num; /* MajNbr:MinNbr:SubMin:PatchLevel */
+ u8 hw_version; /* plain number indicating version */
+ u8 host_interface; /* plain number of interface */
+ u16 max_num_tx_desc; /* max number of TX descriptors */
+ u16 max_num_mc_addr; /* max number multicast addresses */
+ u16 num_antennas; /* number antennas used */
+ u16 region_code; /* region (eg. 0x10 for USA FCC) */
+ unsigned char mac_addr[ETH_ALEN]; /* well known -> AA:BB:CC:DD:EE:FF */
+};
+
+#define MWL_TX_RATE_FORMAT_MASK 0x00000003
+#define MWL_TX_RATE_BANDWIDTH_MASK 0x00000030
+#define MWL_TX_RATE_BANDWIDTH_SHIFT 4
+#define MWL_TX_RATE_SHORTGI_MASK 0x00000040
+#define MWL_TX_RATE_SHORTGI_SHIFT 6
+#define MWL_TX_RATE_RATEIDMCS_MASK 0x00007F00
+#define MWL_TX_RATE_RATEIDMCS_SHIFT 8
+
+struct mwl_tx_desc {
+ u8 data_rate;
+ u8 tx_priority;
+ __le16 qos_ctrl;
+ __le32 pkt_ptr;
+ __le16 pkt_len;
+ u8 dest_addr[ETH_ALEN];
+ __le32 pphys_next;
+ __le32 sap_pkt_info;
+ __le32 rate_info;
+ u8 type;
+ u8 xmit_control; /* bit 0: use rateinfo, bit 1: disable ampdu */
+ __le16 reserved;
+ __le32 tcpack_sn;
+ __le32 tcpack_src_dst;
+ __le32 reserved1;
+ __le32 reserved2;
+ u8 reserved3[2];
+ u8 packet_info;
+ u8 packet_id;
+ __le16 packet_len_and_retry;
+ __le16 packet_rate_info;
+ __le32 reserved4;
+ __le32 status;
+} __packed;
+
+struct mwl_tx_hndl {
+ struct sk_buff *psk_buff;
+ struct mwl_tx_desc *pdesc;
+ struct mwl_tx_hndl *pnext;
+};
+
+#define MWL_RX_RATE_FORMAT_MASK 0x0007
+#define MWL_RX_RATE_NSS_MASK 0x0018
+#define MWL_RX_RATE_NSS_SHIFT 3
+#define MWL_RX_RATE_BW_MASK 0x0060
+#define MWL_RX_RATE_BW_SHIFT 5
+#define MWL_RX_RATE_GI_MASK 0x0080
+#define MWL_RX_RATE_GI_SHIFT 7
+#define MWL_RX_RATE_RT_MASK 0xFF00
+#define MWL_RX_RATE_RT_SHIFT 8
+
+struct mwl_rx_desc {
+ __le16 pkt_len; /* total length of received data */
+ __le16 rate; /* receive rate information */
+ __le32 pphys_buff_data; /* physical address of payload data */
+ __le32 pphys_next; /* physical address of next RX desc */
+ __le16 qos_ctrl; /* received QosCtrl field variable */
+ __le16 ht_sig2; /* like name states */
+ __le32 hw_rssi_info;
+ __le32 hw_noise_floor_info;
+ u8 noise_floor;
+ u8 reserved[3];
+ u8 rssi; /* received signal strengt indication */
+ u8 status; /* status field containing USED bit */
+ u8 channel; /* channel this pkt was received on */
+ u8 rx_control; /* the control element of the desc */
+ __le32 reserved1[3];
+} __packed;
+
+struct mwl_rx_hndl {
+ struct sk_buff *psk_buff; /* associated sk_buff for Linux */
+ struct mwl_rx_desc *pdesc;
+ struct mwl_rx_hndl *pnext;
+};
+
+struct mwl_desc_data {
+ dma_addr_t pphys_tx_ring; /* ptr to first TX desc (phys.) */
+ struct mwl_tx_desc *ptx_ring; /* ptr to first TX desc (virt.) */
+ struct mwl_tx_hndl *tx_hndl;
+ struct mwl_tx_hndl *pnext_tx_hndl; /* next TX handle that can be used */
+ struct mwl_tx_hndl *pstale_tx_hndl;/* the staled TX handle */
+ dma_addr_t pphys_rx_ring; /* ptr to first RX desc (phys.) */
+ struct mwl_rx_desc *prx_ring; /* ptr to first RX desc (virt.) */
+ struct mwl_rx_hndl *rx_hndl;
+ struct mwl_rx_hndl *pnext_rx_hndl; /* next RX handle that can be used */
+ u32 wcb_base; /* FW base offset for registers */
+ u32 rx_desc_write; /* FW descriptor write position */
+ u32 rx_desc_read; /* FW descriptor read position */
+ u32 rx_buf_size; /* length of the RX buffers */
+};
+
+struct mwl_ampdu_stream {
+ struct ieee80211_sta *sta;
+ u8 tid;
+ u8 state;
+ u8 idx;
+};
+
+#ifdef CONFIG_DEBUG_FS
+#define MAC_REG_ADDR_PCI(offset) ((priv->iobase1 + 0xA000) + (offset))
+
+#define MWL_ACCESS_MAC 1
+#define MWL_ACCESS_RF 2
+#define MWL_ACCESS_BBP 3
+#define MWL_ACCESS_CAU 4
+#define MWL_ACCESS_ADDR0 5
+#define MWL_ACCESS_ADDR1 6
+#define MWL_ACCESS_ADDR 7
+#endif
+
+struct mwl_priv {
+ struct ieee80211_hw *hw;
+ struct firmware *fw_ucode;
+ bool fw_device_pwrtbl;
+ bool forbidden_setting;
+ bool regulatory_set;
+ u32 fw_region_code;
+ char fw_alpha2[2];
+ u8 number_of_channels;
+ struct mwl_device_pwr_tbl device_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS];
+ int chip_type;
+
+ struct device_node *dt_node;
+ struct device_node *pwr_node;
+ bool disable_2g;
+ bool disable_5g;
+ int antenna_tx;
+ int antenna_rx;
+
+ struct mwl_tx_pwr_tbl tx_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS];
+ bool cdd;
+ u16 txantenna2;
+ u8 powinited;
+ u16 max_tx_pow[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* max tx power (dBm) */
+ u16 target_powers[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* target powers */
+
+ struct pci_dev *pdev;
+ struct device *dev;
+ void __iomem *iobase0; /* MEM Base Address Register 0 */
+ void __iomem *iobase1; /* MEM Base Address Register 1 */
+ u32 next_bar_num;
+
+ struct mutex fwcmd_mutex; /* for firmware command */
+ unsigned short *pcmd_buf; /* pointer to CmdBuf (virtual) */
+ dma_addr_t pphys_cmd_buf; /* pointer to CmdBuf (physical) */
+ bool in_send_cmd;
+
+ int irq;
+ struct mwl_hw_data hw_data; /* Adapter HW specific info */
+
+ /* various descriptor data */
+ /* for tx descriptor data */
+ spinlock_t tx_desc_lock ____cacheline_aligned_in_smp;
+ struct mwl_desc_data desc_data[SYSADPT_NUM_OF_DESC_DATA];
+ struct sk_buff_head txq[SYSADPT_NUM_OF_DESC_DATA];
+ struct sk_buff_head delay_q;
+ /* number of descriptors owned by fw at any one time */
+ int fw_desc_cnt[SYSADPT_NUM_OF_DESC_DATA];
+
+ struct tasklet_struct tx_task;
+ struct tasklet_struct tx_done_task;
+ struct tasklet_struct rx_task;
+ struct tasklet_struct qe_task;
+ int txq_limit;
+ bool is_tx_done_schedule;
+ int recv_limit;
+ bool is_rx_schedule;
+ bool is_qe_schedule;
+ u32 qe_trigger_num;
+ unsigned long qe_trigger_time;
+
+ struct timer_list period_timer;
+
+ s8 noise; /* Most recently reported noise in dBm */
+ struct ieee80211_supported_band band_24;
+ struct ieee80211_channel channels_24[BAND_24_CHANNEL_NUM];
+ struct ieee80211_rate rates_24[BAND_24_RATE_NUM];
+ struct ieee80211_supported_band band_50;
+ struct ieee80211_channel channels_50[BAND_50_CHANNEL_NUM];
+ struct ieee80211_rate rates_50[BAND_50_RATE_NUM];
+
+ u32 ap_macids_supported;
+ u32 sta_macids_supported;
+ u32 macids_used;
+ u32 running_bsses; /* bitmap of running BSSes */
+
+ struct {
+ spinlock_t vif_lock; /* for private interface info */
+ struct list_head vif_list; /* List of interfaces. */
+ } ____cacheline_aligned_in_smp;
+
+ struct {
+ spinlock_t sta_lock; /* for private sta info */
+ struct list_head sta_list; /* List of stations */
+ } ____cacheline_aligned_in_smp;
+
+ bool radio_on;
+ bool radio_short_preamble;
+ bool wmm_enabled;
+ struct ieee80211_tx_queue_params wmm_params[SYSADPT_TX_WMM_QUEUES];
+
+ /* ampdu stream information */
+ /* for ampdu stream */
+ struct {
+ spinlock_t stream_lock; /* for BA stream */
+ struct mwl_ampdu_stream ampdu[SYSADPT_TX_AMPDU_QUEUES];
+ } ____cacheline_aligned_in_smp;
+ struct work_struct watchdog_ba_handle;
+
+ bool csa_active;
+ struct work_struct chnl_switch_handle;
+ enum nl80211_dfs_regions dfs_region;
+ u16 dfs_chirp_count_min;
+ u16 dfs_chirp_time_interval;
+ u16 dfs_pw_filter;
+ u16 dfs_min_num_radar;
+ u16 dfs_min_pri_count;
+
+ struct thermal_cooling_device *cdev;
+ u32 throttle_state;
+ u32 quiet_period;
+ int temperature;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_phy;
+ u32 reg_type;
+ u32 reg_offset;
+ u32 reg_value;
+ int tx_desc_num;
+#endif
+};
+
+struct beacon_info {
+ bool valid;
+ u16 cap_info;
+ u8 power_constraint;
+ u8 b_rate_set[SYSADPT_MAX_DATA_RATES_G];
+ u8 op_rate_set[SYSADPT_MAX_DATA_RATES_G];
+ u8 ie_list_ht[148];
+ u8 ie_list_vht[24];
+ u8 *ie_wmm_ptr;
+ u8 *ie_wsc_ptr;
+ u8 *ie_rsn_ptr;
+ u8 *ie_rsn48_ptr;
+ u8 *ie_ht_ptr;
+ u8 *ie_vht_ptr;
+ u8 *ie_country_ptr;
+ u8 ie_wmm_len;
+ u8 ie_wsc_len;
+ u8 ie_rsn_len;
+ u8 ie_rsn48_len;
+ u8 ie_ht_len;
+ u8 ie_vht_len;
+ u8 ie_country_len;
+};
+
+struct mwl_vif {
+ struct list_head list;
+ int macid; /* Firmware macid for this vif. */
+ u16 seqno; /* Non AMPDU sequence number assigned by driver. */
+ struct { /* Saved WEP keys */
+ u8 enabled;
+ u8 key[sizeof(struct ieee80211_key_conf) + WLAN_KEY_LEN_WEP104];
+ } wep_key_conf[NUM_WEP_KEYS];
+ u8 bssid[ETH_ALEN]; /* BSSID */
+ u8 sta_mac[ETH_ALEN]; /* Station mac address */
+ /* A flag to indicate is HW crypto is enabled for this bssid */
+ bool is_hw_crypto_enabled;
+ /* Indicate if this is station mode */
+ struct beacon_info beacon_info;
+ u16 iv16;
+ u32 iv32;
+ s8 keyidx;
+};
+
+struct mwl_tx_info {
+ unsigned long start_time;
+ u32 pkts;
+};
+
+struct mwl_amsdu_frag {
+ struct sk_buff *skb;
+ u8 *cur_pos;
+ unsigned long jiffies;
+ u8 pad;
+ u8 num;
+};
+
+struct mwl_amsdu_ctrl {
+ struct mwl_amsdu_frag frag[SYSADPT_TX_WMM_QUEUES];
+ u8 cap;
+};
+
+struct mwl_sta {
+ struct list_head list;
+ bool is_mesh_node;
+ bool is_ampdu_allowed;
+ struct mwl_tx_info tx_stats[MWL_MAX_TID];
+ u32 check_ba_failed[MWL_MAX_TID];
+ bool is_amsdu_allowed;
+ /* for amsdu aggregation */
+ struct {
+ spinlock_t amsdu_lock; /* for amsdu */
+ struct mwl_amsdu_ctrl amsdu_ctrl;
+ } ____cacheline_aligned_in_smp;
+ u16 iv16;
+ u32 iv32;
+};
+
+/* DMA header used by firmware and hardware. */
+struct mwl_dma_data {
+ __le16 fwlen;
+ struct ieee80211_hdr wh;
+ char data[0];
+} __packed;
+
+/* Transmission information to transmit a socket buffer. */
+struct mwl_tx_ctrl {
+ void *vif;
+ void *sta;
+ void *k_conf;
+ void *amsdu_pkts;
+ u8 tx_priority;
+ u8 type;
+ u16 qos_ctrl;
+ u8 xmit_control;
+};
+
+static inline struct mwl_vif *mwl_dev_get_vif(const struct ieee80211_vif *vif)
+{
+ return (struct mwl_vif *)&vif->drv_priv;
+}
+
+static inline struct mwl_sta *mwl_dev_get_sta(const struct ieee80211_sta *sta)
+{
+ return (struct mwl_sta *)&sta->drv_priv;
+}
+
+/* Defined in mac80211.c. */
+extern const struct ieee80211_ops mwl_mac80211_ops;
+
+#endif /* _DEV_H_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/fwcmd.c b/drivers/net/wireless/marvell/mwlwifi/fwcmd.c
new file mode 100644
index 0000000..9c3ccf9
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/fwcmd.c
@@ -0,0 +1,2837 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file implements firmware host command related
+ * functions.
+ */
+
+#include <linux/etherdevice.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwcmd.h"
+#include "hostcmd.h"
+
+#define MAX_WAIT_FW_COMPLETE_ITERATIONS 2000
+#define MAX_WAIT_GET_HW_SPECS_ITERATONS 3
+
+struct cmd_header {
+ __le16 command;
+ __le16 len;
+} __packed;
+
+static bool mwl_fwcmd_chk_adapter(struct mwl_priv *priv)
+{
+ u32 regval;
+
+ regval = readl(priv->iobase1 + MACREG_REG_INT_CODE);
+
+ if (regval == 0xffffffff) {
+ wiphy_err(priv->hw->wiphy, "adapter does not exist\n");
+ return false;
+ }
+
+ return true;
+}
+
+static void mwl_fwcmd_send_cmd(struct mwl_priv *priv)
+{
+ writel(priv->pphys_cmd_buf, priv->iobase1 + MACREG_REG_GEN_PTR);
+ writel(MACREG_H2ARIC_BIT_DOOR_BELL,
+ priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
+}
+
+static char *mwl_fwcmd_get_cmd_string(unsigned short cmd)
+{
+ int max_entries = 0;
+ int curr_cmd = 0;
+
+ static const struct {
+ u16 cmd;
+ char *cmd_string;
+ } cmds[] = {
+ { HOSTCMD_CMD_GET_HW_SPEC, "GetHwSpecifications" },
+ { HOSTCMD_CMD_SET_HW_SPEC, "SetHwSepcifications" },
+ { HOSTCMD_CMD_802_11_GET_STAT, "80211GetStat" },
+ { HOSTCMD_CMD_802_11_RADIO_CONTROL, "80211RadioControl" },
+ { HOSTCMD_CMD_MEM_ADDR_ACCESS, "MEMAddrAccess" },
+ { HOSTCMD_CMD_802_11_TX_POWER, "80211TxPower" },
+ { HOSTCMD_CMD_802_11_RF_ANTENNA, "80211RfAntenna" },
+ { HOSTCMD_CMD_BROADCAST_SSID_ENABLE, "broadcast_ssid_enable" },
+ { HOSTCMD_CMD_SET_RF_CHANNEL, "SetRfChannel" },
+ { HOSTCMD_CMD_SET_AID, "SetAid" },
+ { HOSTCMD_CMD_SET_INFRA_MODE, "SetInfraMode" },
+ { HOSTCMD_CMD_802_11_RTS_THSD, "80211RtsThreshold" },
+ { HOSTCMD_CMD_SET_EDCA_PARAMS, "SetEDCAParams" },
+ { HOSTCMD_CMD_802_11H_DETECT_RADAR, "80211hDetectRadar" },
+ { HOSTCMD_CMD_SET_WMM_MODE, "SetWMMMode" },
+ { HOSTCMD_CMD_HT_GUARD_INTERVAL, "HtGuardInterval" },
+ { HOSTCMD_CMD_SET_FIXED_RATE, "SetFixedRate" },
+ { HOSTCMD_CMD_SET_IES, "SetInformationElements" },
+ { HOSTCMD_CMD_SET_LINKADAPT_CS_MODE, "LinkAdaptCsMode" },
+ { HOSTCMD_CMD_SET_MAC_ADDR, "SetMacAddr" },
+ { HOSTCMD_CMD_SET_RATE_ADAPT_MODE, "SetRateAdaptationMode" },
+ { HOSTCMD_CMD_GET_WATCHDOG_BITMAP, "GetWatchdogBitMap" },
+ { HOSTCMD_CMD_DEL_MAC_ADDR, "DelMacAddr" },
+ { HOSTCMD_CMD_BSS_START, "BssStart" },
+ { HOSTCMD_CMD_AP_BEACON, "SetApBeacon" },
+ { HOSTCMD_CMD_SET_NEW_STN, "SetNewStation" },
+ { HOSTCMD_CMD_SET_APMODE, "SetApMode" },
+ { HOSTCMD_CMD_SET_SWITCH_CHANNEL, "SetSwitchChannel" },
+ { HOSTCMD_CMD_UPDATE_ENCRYPTION, "UpdateEncryption" },
+ { HOSTCMD_CMD_BASTREAM, "BAStream" },
+ { HOSTCMD_CMD_SET_SPECTRUM_MGMT, "SetSpectrumMgmt" },
+ { HOSTCMD_CMD_SET_POWER_CONSTRAINT, "SetPowerConstraint" },
+ { HOSTCMD_CMD_SET_COUNTRY_CODE, "SetCountryCode" },
+ { HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL, "SetOptimizationLevel" },
+ { HOSTCMD_CMD_SET_WSC_IE, "SetWscIE" },
+ { HOSTCMD_CMD_DWDS_ENABLE, "DwdsEnable" },
+ { HOSTCMD_CMD_FW_FLUSH_TIMER, "FwFlushTimer" },
+ { HOSTCMD_CMD_SET_CDD, "SetCDD" },
+ { HOSTCMD_CMD_GET_TEMP, "GetTemp" },
+ { HOSTCMD_CMD_GET_FW_REGION_CODE, "GetFwRegionCode" },
+ { HOSTCMD_CMD_GET_DEVICE_PWR_TBL, "GetDevicePwrTbl" },
+ { HOSTCMD_CMD_QUIET_MODE, "QuietMode" },
+ };
+
+ max_entries = ARRAY_SIZE(cmds);
+
+ for (curr_cmd = 0; curr_cmd < max_entries; curr_cmd++)
+ if ((cmd & 0x7fff) == cmds[curr_cmd].cmd)
+ return cmds[curr_cmd].cmd_string;
+
+ return "unknown";
+}
+
+static int mwl_fwcmd_wait_complete(struct mwl_priv *priv, unsigned short cmd)
+{
+ unsigned int curr_iteration = MAX_WAIT_FW_COMPLETE_ITERATIONS;
+ unsigned short int_code = 0;
+
+ do {
+ int_code = le16_to_cpu(*((__le16 *)&priv->pcmd_buf[0]));
+ usleep_range(1000, 2000);
+ } while ((int_code != cmd) && (--curr_iteration));
+
+ if (curr_iteration == 0) {
+ wiphy_err(priv->hw->wiphy, "cmd 0x%04x=%s timed out\n",
+ cmd, mwl_fwcmd_get_cmd_string(cmd));
+ wiphy_err(priv->hw->wiphy, "return code: 0x%04x\n", int_code);
+ return -EIO;
+ }
+
+ usleep_range(3000, 5000);
+
+ return 0;
+}
+
+static int mwl_fwcmd_exec_cmd(struct mwl_priv *priv, unsigned short cmd)
+{
+ bool busy = false;
+
+ might_sleep();
+
+ if (!mwl_fwcmd_chk_adapter(priv)) {
+ wiphy_err(priv->hw->wiphy, "adapter does not exist\n");
+ priv->in_send_cmd = false;
+ return -EIO;
+ }
+
+ if (!priv->in_send_cmd) {
+ priv->in_send_cmd = true;
+ mwl_fwcmd_send_cmd(priv);
+ if (mwl_fwcmd_wait_complete(priv, 0x8000 | cmd)) {
+ wiphy_err(priv->hw->wiphy, "timeout: 0x%04x\n", cmd);
+ priv->in_send_cmd = false;
+ return -EIO;
+ }
+ } else {
+ wiphy_warn(priv->hw->wiphy,
+ "previous command is still running\n");
+ busy = true;
+ }
+
+ if (!busy)
+ priv->in_send_cmd = false;
+
+ return 0;
+}
+
+static int mwl_fwcmd_802_11_radio_control(struct mwl_priv *priv,
+ bool enable, bool force)
+{
+ struct hostcmd_cmd_802_11_radio_control *pcmd;
+
+ if (enable == priv->radio_on && !force)
+ return 0;
+
+ pcmd = (struct hostcmd_cmd_802_11_radio_control *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_RADIO_CONTROL);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le16(WL_SET);
+ pcmd->control = cpu_to_le16(priv->radio_short_preamble ?
+ WL_AUTO_PREAMBLE : WL_LONG_PREAMBLE);
+ pcmd->radio_on = cpu_to_le16(enable ? WL_ENABLE : WL_DISABLE);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_RADIO_CONTROL)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ priv->radio_on = enable;
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+static int mwl_fwcmd_get_tx_powers(struct mwl_priv *priv, u16 *powlist, u16 ch,
+ u16 band, u16 width, u16 sub_ch)
+{
+ struct hostcmd_cmd_802_11_tx_power *pcmd;
+ int i;
+
+ pcmd = (struct hostcmd_cmd_802_11_tx_power *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_TX_POWER);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_GET_LIST);
+ pcmd->ch = cpu_to_le16(ch);
+ pcmd->bw = cpu_to_le16(width);
+ pcmd->band = cpu_to_le16(band);
+ pcmd->sub_ch = cpu_to_le16(sub_ch);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_TX_POWER)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++)
+ powlist[i] = le16_to_cpu(pcmd->power_level_list[i]);
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+static int mwl_fwcmd_set_tx_powers(struct mwl_priv *priv, u16 txpow[],
+ u8 action, u16 ch, u16 band,
+ u16 width, u16 sub_ch)
+{
+ struct hostcmd_cmd_802_11_tx_power *pcmd;
+ int i;
+
+ pcmd = (struct hostcmd_cmd_802_11_tx_power *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_TX_POWER);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le16(action);
+ pcmd->ch = cpu_to_le16(ch);
+ pcmd->bw = cpu_to_le16(width);
+ pcmd->band = cpu_to_le16(band);
+ pcmd->sub_ch = cpu_to_le16(sub_ch);
+
+ for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++)
+ pcmd->power_level_list[i] = cpu_to_le16(txpow[i]);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_TX_POWER)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+static u8 mwl_fwcmd_get_80m_pri_chnl(u8 channel)
+{
+ u8 act_primary = ACT_PRIMARY_CHAN_0;
+
+ switch (channel) {
+ case 36:
+ act_primary = ACT_PRIMARY_CHAN_0;
+ break;
+ case 40:
+ act_primary = ACT_PRIMARY_CHAN_1;
+ break;
+ case 44:
+ act_primary = ACT_PRIMARY_CHAN_2;
+ break;
+ case 48:
+ act_primary = ACT_PRIMARY_CHAN_3;
+ break;
+ case 52:
+ act_primary = ACT_PRIMARY_CHAN_0;
+ break;
+ case 56:
+ act_primary = ACT_PRIMARY_CHAN_1;
+ break;
+ case 60:
+ act_primary = ACT_PRIMARY_CHAN_2;
+ break;
+ case 64:
+ act_primary = ACT_PRIMARY_CHAN_3;
+ break;
+ case 100:
+ act_primary = ACT_PRIMARY_CHAN_0;
+ break;
+ case 104:
+ act_primary = ACT_PRIMARY_CHAN_1;
+ break;
+ case 108:
+ act_primary = ACT_PRIMARY_CHAN_2;
+ break;
+ case 112:
+ act_primary = ACT_PRIMARY_CHAN_3;
+ break;
+ case 116:
+ act_primary = ACT_PRIMARY_CHAN_0;
+ break;
+ case 120:
+ act_primary = ACT_PRIMARY_CHAN_1;
+ break;
+ case 124:
+ act_primary = ACT_PRIMARY_CHAN_2;
+ break;
+ case 128:
+ act_primary = ACT_PRIMARY_CHAN_3;
+ break;
+ case 132:
+ act_primary = ACT_PRIMARY_CHAN_0;
+ break;
+ case 136:
+ act_primary = ACT_PRIMARY_CHAN_1;
+ break;
+ case 140:
+ act_primary = ACT_PRIMARY_CHAN_2;
+ break;
+ case 144:
+ act_primary = ACT_PRIMARY_CHAN_3;
+ break;
+ case 149:
+ act_primary = ACT_PRIMARY_CHAN_0;
+ break;
+ case 153:
+ act_primary = ACT_PRIMARY_CHAN_1;
+ break;
+ case 157:
+ act_primary = ACT_PRIMARY_CHAN_2;
+ break;
+ case 161:
+ act_primary = ACT_PRIMARY_CHAN_3;
+ break;
+ }
+
+ return act_primary;
+}
+
+static void mwl_fwcmd_parse_beacon(struct mwl_priv *priv,
+ struct mwl_vif *vif, u8 *beacon, int len)
+{
+ struct ieee80211_mgmt *mgmt;
+ struct beacon_info *beacon_info;
+ int baselen;
+ u8 *pos;
+ size_t left;
+ bool elem_parse_failed;
+
+ mgmt = (struct ieee80211_mgmt *)beacon;
+
+ baselen = (u8 *)mgmt->u.beacon.variable - (u8 *)mgmt;
+ if (baselen > len)
+ return;
+
+ beacon_info = &vif->beacon_info;
+ memset(beacon_info, 0, sizeof(struct beacon_info));
+ beacon_info->valid = false;
+ beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0];
+ beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0];
+
+ beacon_info->cap_info = le16_to_cpu(mgmt->u.beacon.capab_info);
+ beacon_info->power_constraint = 0;
+
+ pos = (u8 *)mgmt->u.beacon.variable;
+ left = len - baselen;
+
+ elem_parse_failed = false;
+
+ while (left >= 2) {
+ u8 id, elen;
+
+ id = *pos++;
+ elen = *pos++;
+ left -= 2;
+
+ if (elen > left) {
+ elem_parse_failed = true;
+ break;
+ }
+
+ switch (id) {
+ case WLAN_EID_COUNTRY:
+ beacon_info->ie_country_len = (elen + 2);
+ beacon_info->ie_country_ptr = (pos - 2);
+ break;
+ case WLAN_EID_SUPP_RATES:
+ case WLAN_EID_EXT_SUPP_RATES:
+ {
+ int idx, bi, oi;
+ u8 rate;
+
+ for (bi = 0; bi < SYSADPT_MAX_DATA_RATES_G;
+ bi++) {
+ if (beacon_info->b_rate_set[bi] == 0)
+ break;
+ }
+
+ for (oi = 0; oi < SYSADPT_MAX_DATA_RATES_G;
+ oi++) {
+ if (beacon_info->op_rate_set[oi] == 0)
+ break;
+ }
+
+ for (idx = 0; idx < elen; idx++) {
+ rate = pos[idx];
+ if ((rate & 0x80) != 0) {
+ if (bi < SYSADPT_MAX_DATA_RATES_G)
+ beacon_info->b_rate_set[bi++]
+ = rate & 0x7f;
+ else {
+ elem_parse_failed = true;
+ break;
+ }
+ }
+ if (oi < SYSADPT_MAX_DATA_RATES_G)
+ beacon_info->op_rate_set[oi++] =
+ rate & 0x7f;
+ else {
+ elem_parse_failed = true;
+ break;
+ }
+ }
+ }
+ break;
+ case WLAN_EID_PWR_CONSTRAINT:
+ if (elen == 1)
+ beacon_info->power_constraint = *pos;
+ break;
+ case WLAN_EID_RSN:
+ beacon_info->ie_rsn48_len = (elen + 2);
+ beacon_info->ie_rsn48_ptr = (pos - 2);
+ break;
+ case WLAN_EID_HT_CAPABILITY:
+ case WLAN_EID_HT_OPERATION:
+ case WLAN_EID_OVERLAP_BSS_SCAN_PARAM:
+ case WLAN_EID_EXT_CAPABILITY:
+ beacon_info->ie_ht_len += (elen + 2);
+ if (beacon_info->ie_ht_len >
+ sizeof(beacon_info->ie_list_ht)) {
+ elem_parse_failed = true;
+ } else {
+ *beacon_info->ie_ht_ptr++ = id;
+ *beacon_info->ie_ht_ptr++ = elen;
+ memcpy(beacon_info->ie_ht_ptr, pos, elen);
+ beacon_info->ie_ht_ptr += elen;
+ }
+ break;
+ case WLAN_EID_VHT_CAPABILITY:
+ case WLAN_EID_VHT_OPERATION:
+ case WLAN_EID_OPMODE_NOTIF:
+ beacon_info->ie_vht_len += (elen + 2);
+ if (beacon_info->ie_vht_len >
+ sizeof(beacon_info->ie_list_vht)) {
+ elem_parse_failed = true;
+ } else {
+ *beacon_info->ie_vht_ptr++ = id;
+ *beacon_info->ie_vht_ptr++ = elen;
+ memcpy(beacon_info->ie_vht_ptr, pos, elen);
+ beacon_info->ie_vht_ptr += elen;
+ }
+ break;
+ case WLAN_EID_VENDOR_SPECIFIC:
+ if ((pos[0] == 0x00) && (pos[1] == 0x50) &&
+ (pos[2] == 0xf2)) {
+ if (pos[3] == 0x01) {
+ beacon_info->ie_rsn_len = (elen + 2);
+ beacon_info->ie_rsn_ptr = (pos - 2);
+ }
+
+ if (pos[3] == 0x02) {
+ beacon_info->ie_wmm_len = (elen + 2);
+ beacon_info->ie_wmm_ptr = (pos - 2);
+ }
+
+ if (pos[3] == 0x04) {
+ beacon_info->ie_wsc_len = (elen + 2);
+ beacon_info->ie_wsc_ptr = (pos - 2);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ left -= elen;
+ pos += elen;
+ }
+
+ if (!elem_parse_failed) {
+ beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0];
+ beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0];
+ beacon_info->valid = true;
+ }
+}
+
+static int mwl_fwcmd_set_ies(struct mwl_priv *priv, struct mwl_vif *mwl_vif)
+{
+ struct hostcmd_cmd_set_ies *pcmd;
+ struct beacon_info *beacon = &mwl_vif->beacon_info;
+ u16 ie_list_len_proprietary = 0;
+
+ if (beacon->ie_ht_len > sizeof(pcmd->ie_list_ht))
+ goto einval;
+
+ if (beacon->ie_vht_len > sizeof(pcmd->ie_list_vht))
+ goto einval;
+
+ pcmd = (struct hostcmd_cmd_set_ies *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_IES);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_SET);
+
+ memcpy(pcmd->ie_list_ht, beacon->ie_ht_ptr, beacon->ie_ht_len);
+ pcmd->ie_list_len_ht = cpu_to_le16(beacon->ie_ht_len);
+
+ memcpy(pcmd->ie_list_vht, beacon->ie_vht_ptr, beacon->ie_vht_len);
+ pcmd->ie_list_len_vht = cpu_to_le16(beacon->ie_vht_len);
+
+ if (priv->chip_type == MWL8897) {
+ memcpy(pcmd->ie_list_proprietary + ie_list_len_proprietary,
+ beacon->ie_wmm_ptr, beacon->ie_wmm_len);
+ ie_list_len_proprietary += mwl_vif->beacon_info.ie_wmm_len;
+ }
+
+ pcmd->ie_list_len_proprietary = cpu_to_le16(ie_list_len_proprietary);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_IES)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+
+einval:
+
+ wiphy_err(priv->hw->wiphy, "length of IE is too long\n");
+
+ return -EINVAL;
+}
+
+static int mwl_fwcmd_set_ap_beacon(struct mwl_priv *priv,
+ struct mwl_vif *mwl_vif,
+ struct ieee80211_bss_conf *bss_conf)
+{
+ struct hostcmd_cmd_ap_beacon *pcmd;
+ struct ds_params *phy_ds_param_set;
+
+ /* wmm structure of start command is defined less one byte,
+ * due to following field country is not used, add byte one
+ * to bypass the check.
+ */
+ if (mwl_vif->beacon_info.ie_wmm_len >
+ (sizeof(pcmd->start_cmd.wmm_param) + 1))
+ goto ielenerr;
+
+ if (mwl_vif->beacon_info.ie_rsn_len > sizeof(pcmd->start_cmd.rsn_ie))
+ goto ielenerr;
+
+ if (mwl_vif->beacon_info.ie_rsn48_len >
+ sizeof(pcmd->start_cmd.rsn48_ie))
+ goto ielenerr;
+
+ pcmd = (struct hostcmd_cmd_ap_beacon *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_AP_BEACON);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+ ether_addr_copy(pcmd->start_cmd.sta_mac_addr, mwl_vif->bssid);
+ memcpy(pcmd->start_cmd.ssid, bss_conf->ssid, bss_conf->ssid_len);
+ pcmd->start_cmd.bss_type = 1;
+ pcmd->start_cmd.bcn_period = cpu_to_le16(bss_conf->beacon_int);
+ pcmd->start_cmd.dtim_period = bss_conf->dtim_period; /* 8bit */
+
+ phy_ds_param_set = &pcmd->start_cmd.phy_param_set.ds_param_set;
+ phy_ds_param_set->elem_id = WLAN_EID_DS_PARAMS;
+ phy_ds_param_set->len = sizeof(phy_ds_param_set->current_chnl);
+ phy_ds_param_set->current_chnl = bss_conf->chandef.chan->hw_value;
+
+ pcmd->start_cmd.probe_delay = cpu_to_le16(10);
+ pcmd->start_cmd.cap_info = cpu_to_le16(mwl_vif->beacon_info.cap_info);
+
+ memcpy(&pcmd->start_cmd.wmm_param, mwl_vif->beacon_info.ie_wmm_ptr,
+ mwl_vif->beacon_info.ie_wmm_len);
+
+ memcpy(&pcmd->start_cmd.rsn_ie, mwl_vif->beacon_info.ie_rsn_ptr,
+ mwl_vif->beacon_info.ie_rsn_len);
+
+ memcpy(&pcmd->start_cmd.rsn48_ie, mwl_vif->beacon_info.ie_rsn48_ptr,
+ mwl_vif->beacon_info.ie_rsn48_len);
+
+ memcpy(pcmd->start_cmd.b_rate_set, mwl_vif->beacon_info.b_rate_set,
+ SYSADPT_MAX_DATA_RATES_G);
+
+ memcpy(pcmd->start_cmd.op_rate_set, mwl_vif->beacon_info.op_rate_set,
+ SYSADPT_MAX_DATA_RATES_G);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_AP_BEACON)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+
+ielenerr:
+
+ wiphy_err(priv->hw->wiphy, "length of IE is too long\n");
+
+ return -EINVAL;
+}
+
+static int mwl_fwcmd_set_spectrum_mgmt(struct mwl_priv *priv, bool enable)
+{
+ struct hostcmd_cmd_set_spectrum_mgmt *pcmd;
+
+ pcmd = (struct hostcmd_cmd_set_spectrum_mgmt *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_SPECTRUM_MGMT);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->spectrum_mgmt = cpu_to_le32(enable);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_SPECTRUM_MGMT)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+static int mwl_fwcmd_set_power_constraint(struct mwl_priv *priv,
+ u32 power_constraint)
+{
+ struct hostcmd_cmd_set_power_constraint *pcmd;
+
+ pcmd = (struct hostcmd_cmd_set_power_constraint *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_POWER_CONSTRAINT);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->power_constraint = cpu_to_le32(power_constraint);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_POWER_CONSTRAINT)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+static int mwl_fwcmd_set_country_code(struct mwl_priv *priv,
+ struct mwl_vif *mwl_vif,
+ struct ieee80211_bss_conf *bss_conf)
+{
+ struct hostcmd_cmd_set_country_code *pcmd;
+ struct beacon_info *b_inf = &mwl_vif->beacon_info;
+ u8 chnl_len;
+ bool a_band;
+ bool enable = false;
+
+ if (b_inf->ie_country_ptr) {
+ if (bss_conf->chandef.chan->band == NL80211_BAND_2GHZ)
+ a_band = false;
+ else if (bss_conf->chandef.chan->band == NL80211_BAND_5GHZ)
+ a_band = true;
+ else
+ return -EINVAL;
+
+ chnl_len = b_inf->ie_country_len - 5;
+ if (a_band) {
+ if (chnl_len > sizeof(pcmd->domain_info.domain_entry_a))
+ return -EINVAL;
+ } else {
+ if (chnl_len > sizeof(pcmd->domain_info.domain_entry_g))
+ return -EINVAL;
+ }
+
+ enable = true;
+ }
+
+ pcmd = (struct hostcmd_cmd_set_country_code *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_COUNTRY_CODE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le32(enable);
+ if (enable) {
+ memcpy(pcmd->domain_info.country_string,
+ b_inf->ie_country_ptr + 2, 3);
+ if (a_band) {
+ pcmd->domain_info.g_chnl_len = 0;
+ pcmd->domain_info.a_chnl_len = chnl_len;
+ memcpy(pcmd->domain_info.domain_entry_a,
+ b_inf->ie_country_ptr + 5, chnl_len);
+ } else {
+ pcmd->domain_info.a_chnl_len = 0;
+ pcmd->domain_info.g_chnl_len = chnl_len;
+ memcpy(pcmd->domain_info.domain_entry_g,
+ b_inf->ie_country_ptr + 5, chnl_len);
+ }
+ }
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_COUNTRY_CODE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+static int mwl_fwcmd_encryption_set_cmd_info(struct hostcmd_cmd_set_key *cmd,
+ u8 *addr,
+ struct ieee80211_key_conf *key)
+{
+ cmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION);
+ cmd->cmd_hdr.len = cpu_to_le16(sizeof(*cmd));
+ cmd->key_param.length = cpu_to_le16(sizeof(*cmd) -
+ offsetof(struct hostcmd_cmd_set_key, key_param));
+ cmd->key_param.key_index = cpu_to_le32(key->keyidx);
+ cmd->key_param.key_len = cpu_to_le16(key->keylen);
+ ether_addr_copy(cmd->key_param.mac_addr, addr);
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ cmd->key_param.key_type_id = cpu_to_le16(KEY_TYPE_ID_WEP);
+ if (key->keyidx == 0)
+ cmd->key_param.key_info =
+ cpu_to_le32(ENCR_KEY_FLAG_WEP_TXKEY);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ cmd->key_param.key_type_id = cpu_to_le16(KEY_TYPE_ID_TKIP);
+ cmd->key_param.key_info =
+ (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
+ cpu_to_le32(ENCR_KEY_FLAG_PAIRWISE) :
+ cpu_to_le32(ENCR_KEY_FLAG_TXGROUPKEY);
+ cmd->key_param.key_info |=
+ cpu_to_le32(ENCR_KEY_FLAG_MICKEY_VALID |
+ ENCR_KEY_FLAG_TSC_VALID);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ cmd->key_param.key_type_id = cpu_to_le16(KEY_TYPE_ID_AES);
+ cmd->key_param.key_info =
+ (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
+ cpu_to_le32(ENCR_KEY_FLAG_PAIRWISE) :
+ cpu_to_le32(ENCR_KEY_FLAG_TXGROUPKEY);
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ return 1;
+ default:
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+void mwl_fwcmd_reset(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+
+ if (mwl_fwcmd_chk_adapter(priv))
+ writel(ISR_RESET,
+ priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
+}
+
+void mwl_fwcmd_int_enable(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+
+ if (mwl_fwcmd_chk_adapter(priv)) {
+ writel(0x00,
+ priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);
+ writel(MACREG_A2HRIC_BIT_MASK,
+ priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);
+ }
+}
+
+void mwl_fwcmd_int_disable(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+
+ if (mwl_fwcmd_chk_adapter(priv))
+ writel(0x00,
+ priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);
+}
+
+int mwl_fwcmd_get_hw_specs(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_get_hw_spec *pcmd;
+ int retry;
+ int i;
+
+ pcmd = (struct hostcmd_cmd_get_hw_spec *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ wiphy_debug(hw->wiphy, "pcmd = %p\n", pcmd);
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ eth_broadcast_addr(pcmd->permanent_addr);
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_HW_SPEC);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->fw_awake_cookie = cpu_to_le32(priv->pphys_cmd_buf + 2048);
+
+ retry = 0;
+ while (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_HW_SPEC)) {
+ if (retry++ > MAX_WAIT_GET_HW_SPECS_ITERATONS) {
+ wiphy_err(hw->wiphy, "can't get hw specs\n");
+ mutex_unlock(&priv->fwcmd_mutex);
+ return -EIO;
+ }
+
+ msleep(1000);
+ wiphy_debug(hw->wiphy,
+ "repeat command = %p\n", pcmd);
+ }
+
+ ether_addr_copy(&priv->hw_data.mac_addr[0], pcmd->permanent_addr);
+ priv->desc_data[0].wcb_base =
+ le32_to_cpu(pcmd->wcb_base0) & 0x0000ffff;
+ for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)
+ priv->desc_data[i].wcb_base =
+ le32_to_cpu(pcmd->wcb_base[i - 1]) & 0x0000ffff;
+ priv->desc_data[0].rx_desc_read =
+ le32_to_cpu(pcmd->rxpd_rd_ptr) & 0x0000ffff;
+ priv->desc_data[0].rx_desc_write =
+ le32_to_cpu(pcmd->rxpd_wr_ptr) & 0x0000ffff;
+ priv->hw_data.region_code = le16_to_cpu(pcmd->region_code) & 0x00ff;
+ priv->hw_data.fw_release_num = le32_to_cpu(pcmd->fw_release_num);
+ priv->hw_data.max_num_tx_desc = le16_to_cpu(pcmd->num_wcb);
+ priv->hw_data.max_num_mc_addr = le16_to_cpu(pcmd->num_mcast_addr);
+ priv->hw_data.num_antennas = le16_to_cpu(pcmd->num_antenna);
+ priv->hw_data.hw_version = pcmd->version;
+ priv->hw_data.host_interface = pcmd->host_if;
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_set_hw_spec *pcmd;
+ int i;
+
+ pcmd = (struct hostcmd_cmd_set_hw_spec *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_HW_SPEC);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->wcb_base[0] = cpu_to_le32(priv->desc_data[0].pphys_tx_ring);
+ for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)
+ pcmd->wcb_base[i] =
+ cpu_to_le32(priv->desc_data[i].pphys_tx_ring);
+ pcmd->tx_wcb_num_per_queue = cpu_to_le32(SYSADPT_MAX_NUM_TX_DESC);
+ pcmd->num_tx_queues = cpu_to_le32(SYSADPT_NUM_OF_DESC_DATA);
+ pcmd->total_rx_wcb = cpu_to_le32(SYSADPT_MAX_NUM_RX_DESC);
+ pcmd->rxpd_wr_ptr = cpu_to_le32(priv->desc_data[0].pphys_rx_ring);
+ pcmd->features = 0;
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_HW_SPEC)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_get_stat(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_802_11_get_stat *pcmd;
+
+ pcmd = (struct hostcmd_cmd_802_11_get_stat *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_GET_STAT);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_GET_STAT)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ stats->dot11ACKFailureCount =
+ le32_to_cpu(pcmd->ack_failures);
+ stats->dot11RTSFailureCount =
+ le32_to_cpu(pcmd->rts_failures);
+ stats->dot11FCSErrorCount =
+ le32_to_cpu(pcmd->rx_fcs_errors);
+ stats->dot11RTSSuccessCount =
+ le32_to_cpu(pcmd->rts_successes);
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_radio_enable(struct ieee80211_hw *hw)
+{
+ return mwl_fwcmd_802_11_radio_control(hw->priv, true, false);
+}
+
+int mwl_fwcmd_radio_disable(struct ieee80211_hw *hw)
+{
+ return mwl_fwcmd_802_11_radio_control(hw->priv, false, false);
+}
+
+int mwl_fwcmd_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble)
+{
+ struct mwl_priv *priv = hw->priv;
+ int rc;
+
+ priv->radio_short_preamble = short_preamble;
+ rc = mwl_fwcmd_802_11_radio_control(priv, true, true);
+
+ return rc;
+}
+
+int mwl_fwcmd_get_addr_value(struct ieee80211_hw *hw, u32 addr, u32 len,
+ u32 *val, u16 set)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_mem_addr_access *pcmd;
+ int i;
+
+ pcmd = (struct hostcmd_cmd_mem_addr_access *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_MEM_ADDR_ACCESS);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->address = cpu_to_le32(addr);
+ pcmd->length = cpu_to_le16(len);
+ pcmd->value[0] = cpu_to_le32(*val);
+ pcmd->reserved = cpu_to_le16(set);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_MEM_ADDR_ACCESS)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ for (i = 0; i < len; i++)
+ val[i] = le32_to_cpu(pcmd->value[i]);
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_max_tx_power(struct ieee80211_hw *hw,
+ struct ieee80211_conf *conf, u8 fraction)
+{
+ struct ieee80211_channel *channel = conf->chandef.chan;
+ struct mwl_priv *priv = hw->priv;
+ int reduce_val = 0;
+ u16 band = 0, width = 0, sub_ch = 0;
+ u16 maxtxpow[SYSADPT_TX_POWER_LEVEL_TOTAL];
+ int i, tmp;
+ int rc = 0;
+
+ if (priv->forbidden_setting)
+ return rc;
+
+ switch (fraction) {
+ case 0:
+ reduce_val = 0; /* Max */
+ break;
+ case 1:
+ reduce_val = 2; /* 75% -1.25db */
+ break;
+ case 2:
+ reduce_val = 3; /* 50% -3db */
+ break;
+ case 3:
+ reduce_val = 6; /* 25% -6db */
+ break;
+ default:
+ /* larger than case 3, pCmd->MaxPowerLevel is min */
+ reduce_val = 0xff;
+ break;
+ }
+
+ if (channel->band == NL80211_BAND_2GHZ)
+ band = FREQ_BAND_2DOT4GHZ;
+ else if (channel->band == NL80211_BAND_5GHZ)
+ band = FREQ_BAND_5GHZ;
+
+ switch (conf->chandef.width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ width = CH_20_MHZ_WIDTH;
+ sub_ch = NO_EXT_CHANNEL;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ width = CH_40_MHZ_WIDTH;
+ if (conf->chandef.center_freq1 > channel->center_freq)
+ sub_ch = EXT_CH_ABOVE_CTRL_CH;
+ else
+ sub_ch = EXT_CH_BELOW_CTRL_CH;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ width = CH_80_MHZ_WIDTH;
+ if (conf->chandef.center_freq1 > channel->center_freq)
+ sub_ch = EXT_CH_ABOVE_CTRL_CH;
+ else
+ sub_ch = EXT_CH_BELOW_CTRL_CH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((priv->powinited & MWL_POWER_INIT_2) == 0) {
+ mwl_fwcmd_get_tx_powers(priv, priv->max_tx_pow,
+ channel->hw_value, band, width, sub_ch);
+ priv->powinited |= MWL_POWER_INIT_2;
+ }
+
+ if ((priv->powinited & MWL_POWER_INIT_1) == 0) {
+ mwl_fwcmd_get_tx_powers(priv, priv->target_powers,
+ channel->hw_value, band, width, sub_ch);
+ priv->powinited |= MWL_POWER_INIT_1;
+ }
+
+ for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++) {
+ if (priv->target_powers[i] > priv->max_tx_pow[i])
+ tmp = priv->max_tx_pow[i];
+ else
+ tmp = priv->target_powers[i];
+ maxtxpow[i] = ((tmp - reduce_val) > 0) ? (tmp - reduce_val) : 0;
+ }
+
+ rc = mwl_fwcmd_set_tx_powers(priv, maxtxpow, HOSTCMD_ACT_GEN_SET,
+ channel->hw_value, band, width, sub_ch);
+
+ return rc;
+}
+
+int mwl_fwcmd_tx_power(struct ieee80211_hw *hw,
+ struct ieee80211_conf *conf, u8 fraction)
+{
+ struct ieee80211_channel *channel = conf->chandef.chan;
+ struct mwl_priv *priv = hw->priv;
+ int reduce_val = 0;
+ u16 band = 0, width = 0, sub_ch = 0;
+ u16 txpow[SYSADPT_TX_POWER_LEVEL_TOTAL];
+ int index, found = 0;
+ int i, tmp;
+ int rc = 0;
+
+ if (priv->forbidden_setting)
+ return rc;
+
+ switch (fraction) {
+ case 0:
+ reduce_val = 0; /* Max */
+ break;
+ case 1:
+ reduce_val = 2; /* 75% -1.25db */
+ break;
+ case 2:
+ reduce_val = 3; /* 50% -3db */
+ break;
+ case 3:
+ reduce_val = 6; /* 25% -6db */
+ break;
+ default:
+ /* larger than case 3, pCmd->MaxPowerLevel is min */
+ reduce_val = 0xff;
+ break;
+ }
+
+ if (channel->band == NL80211_BAND_2GHZ)
+ band = FREQ_BAND_2DOT4GHZ;
+ else if (channel->band == NL80211_BAND_5GHZ)
+ band = FREQ_BAND_5GHZ;
+
+ switch (conf->chandef.width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ width = CH_20_MHZ_WIDTH;
+ sub_ch = NO_EXT_CHANNEL;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ width = CH_40_MHZ_WIDTH;
+ if (conf->chandef.center_freq1 > channel->center_freq)
+ sub_ch = EXT_CH_ABOVE_CTRL_CH;
+ else
+ sub_ch = EXT_CH_BELOW_CTRL_CH;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ width = CH_80_MHZ_WIDTH;
+ if (conf->chandef.center_freq1 > channel->center_freq)
+ sub_ch = EXT_CH_ABOVE_CTRL_CH;
+ else
+ sub_ch = EXT_CH_BELOW_CTRL_CH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* search tx power table if exist */
+ for (index = 0; index < SYSADPT_MAX_NUM_CHANNELS; index++) {
+ struct mwl_tx_pwr_tbl *tx_pwr;
+
+ tx_pwr = &priv->tx_pwr_tbl[index];
+
+ /* do nothing if table is not loaded */
+ if (tx_pwr->channel == 0)
+ break;
+
+ if (tx_pwr->channel == channel->hw_value) {
+ priv->cdd = tx_pwr->cdd;
+ priv->txantenna2 = tx_pwr->txantenna2;
+
+ if (tx_pwr->setcap)
+ priv->powinited = MWL_POWER_INIT_1;
+ else
+ priv->powinited = MWL_POWER_INIT_2;
+
+ for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++) {
+ if (tx_pwr->setcap)
+ priv->max_tx_pow[i] =
+ tx_pwr->tx_power[i];
+ else
+ priv->target_powers[i] =
+ tx_pwr->tx_power[i];
+ }
+
+ found = 1;
+ break;
+ }
+ }
+
+ if ((priv->powinited & MWL_POWER_INIT_2) == 0) {
+ mwl_fwcmd_get_tx_powers(priv, priv->max_tx_pow,
+ channel->hw_value, band, width, sub_ch);
+
+ priv->powinited |= MWL_POWER_INIT_2;
+ }
+
+ if ((priv->powinited & MWL_POWER_INIT_1) == 0) {
+ mwl_fwcmd_get_tx_powers(priv, priv->target_powers,
+ channel->hw_value, band, width, sub_ch);
+
+ priv->powinited |= MWL_POWER_INIT_1;
+ }
+
+ for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++) {
+ if (found) {
+ if ((priv->tx_pwr_tbl[index].setcap) &&
+ (priv->tx_pwr_tbl[index].tx_power[i] >
+ priv->max_tx_pow[i]))
+ tmp = priv->max_tx_pow[i];
+ else
+ tmp = priv->tx_pwr_tbl[index].tx_power[i];
+ } else {
+ if (priv->target_powers[i] > priv->max_tx_pow[i])
+ tmp = priv->max_tx_pow[i];
+ else
+ tmp = priv->target_powers[i];
+ }
+
+ txpow[i] = ((tmp - reduce_val) > 0) ? (tmp - reduce_val) : 0;
+ }
+
+ rc = mwl_fwcmd_set_tx_powers(priv, txpow, HOSTCMD_ACT_GEN_SET_LIST,
+ channel->hw_value, band, width, sub_ch);
+
+ return rc;
+}
+
+int mwl_fwcmd_rf_antenna(struct ieee80211_hw *hw, int dir, int antenna)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_802_11_rf_antenna *pcmd;
+
+ pcmd = (struct hostcmd_cmd_802_11_rf_antenna *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_RF_ANTENNA);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+ pcmd->action = cpu_to_le16(dir);
+
+ if (dir == WL_ANTENNATYPE_RX) {
+ u8 rx_antenna = 4; /* if auto, set 4 rx antennas in SC2 */
+
+ if (antenna != 0)
+ pcmd->antenna_mode = cpu_to_le16(antenna);
+ else
+ pcmd->antenna_mode = cpu_to_le16(rx_antenna);
+ } else {
+ u8 tx_antenna = 0xf; /* if auto, set 4 tx antennas in SC2 */
+
+ if (antenna != 0)
+ pcmd->antenna_mode = cpu_to_le16(antenna);
+ else
+ pcmd->antenna_mode = cpu_to_le16(tx_antenna);
+ }
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_RF_ANTENNA)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_broadcast_ssid_enable(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, bool enable)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_broadcast_ssid_enable *pcmd;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ pcmd = (struct hostcmd_cmd_broadcast_ssid_enable *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BROADCAST_SSID_ENABLE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+ pcmd->enable = cpu_to_le32(enable);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BROADCAST_SSID_ENABLE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_rf_channel(struct ieee80211_hw *hw,
+ struct ieee80211_conf *conf)
+{
+ struct ieee80211_channel *channel = conf->chandef.chan;
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_set_rf_channel *pcmd;
+ u32 chnl_flags, freq_band, chnl_width, act_primary;
+
+ pcmd = (struct hostcmd_cmd_set_rf_channel *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_RF_CHANNEL);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le16(WL_SET);
+ pcmd->curr_chnl = channel->hw_value;
+
+ if (channel->band == NL80211_BAND_2GHZ) {
+ freq_band = FREQ_BAND_2DOT4GHZ;
+ } else if (channel->band == NL80211_BAND_5GHZ) {
+ freq_band = FREQ_BAND_5GHZ;
+ } else {
+ mutex_unlock(&priv->fwcmd_mutex);
+ return -EINVAL;
+ }
+
+ switch (conf->chandef.width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ chnl_width = CH_20_MHZ_WIDTH;
+ act_primary = ACT_PRIMARY_CHAN_0;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ chnl_width = CH_40_MHZ_WIDTH;
+ if (conf->chandef.center_freq1 > channel->center_freq)
+ act_primary = ACT_PRIMARY_CHAN_0;
+ else
+ act_primary = ACT_PRIMARY_CHAN_1;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ chnl_width = CH_80_MHZ_WIDTH;
+ act_primary =
+ mwl_fwcmd_get_80m_pri_chnl(pcmd->curr_chnl);
+ break;
+ default:
+ mutex_unlock(&priv->fwcmd_mutex);
+ return -EINVAL;
+ }
+
+ chnl_flags = (freq_band & FREQ_BAND_MASK) |
+ ((chnl_width << CHNL_WIDTH_SHIFT) & CHNL_WIDTH_MASK) |
+ ((act_primary << ACT_PRIMARY_SHIFT) & ACT_PRIMARY_MASK);
+
+ pcmd->chnl_flags = cpu_to_le32(chnl_flags);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_RF_CHANNEL)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ if (pcmd->cmd_hdr.result != 0) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_aid(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *bssid, u16 aid)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_set_aid *pcmd;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ pcmd = (struct hostcmd_cmd_set_aid *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_AID);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+ pcmd->aid = cpu_to_le16(aid);
+ ether_addr_copy(pcmd->mac_addr, bssid);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_AID)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_infra_mode(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_set_infra_mode *pcmd;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ pcmd = (struct hostcmd_cmd_set_infra_mode *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_INFRA_MODE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_INFRA_MODE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_rts_threshold(struct ieee80211_hw *hw, int threshold)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_802_11_rts_thsd *pcmd;
+
+ pcmd = (struct hostcmd_cmd_802_11_rts_thsd *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_RTS_THSD);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le16(WL_SET);
+ pcmd->threshold = cpu_to_le16(threshold);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_RTS_THSD)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_edca_params(struct ieee80211_hw *hw, u8 index,
+ u16 cw_min, u16 cw_max, u8 aifs, u16 txop)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_set_edca_params *pcmd;
+
+ pcmd = (struct hostcmd_cmd_set_edca_params *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_EDCA_PARAMS);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+ pcmd->action = cpu_to_le16(0xffff);
+ pcmd->txop = cpu_to_le16(txop);
+ pcmd->cw_max = cpu_to_le32(ilog2(cw_max + 1));
+ pcmd->cw_min = cpu_to_le32(ilog2(cw_min + 1));
+ pcmd->aifsn = aifs;
+ pcmd->txq_num = index;
+
+ /* The array index defined in qos.h has a reversed bk and be.
+ * The HW queue was not used this way; the qos code needs to
+ * be changed or checked
+ */
+ if (index == 0)
+ pcmd->txq_num = 1;
+ else if (index == 1)
+ pcmd->txq_num = 0;
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_EDCA_PARAMS)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_radar_detect(struct ieee80211_hw *hw, u16 action)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_802_11h_detect_radar *pcmd;
+ u16 radar_type = RADAR_TYPE_CODE_0;
+ u8 channel = hw->conf.chandef.chan->hw_value;
+
+ pcmd = (struct hostcmd_cmd_802_11h_detect_radar *)&priv->pcmd_buf[0];
+
+ if (priv->dfs_region == NL80211_DFS_JP) {
+ if (channel >= 52 && channel <= 64)
+ radar_type = RADAR_TYPE_CODE_53;
+ else if (channel >= 100 && channel <= 140)
+ radar_type = RADAR_TYPE_CODE_56;
+ else
+ radar_type = RADAR_TYPE_CODE_0;
+ } else if (priv->dfs_region == NL80211_DFS_ETSI) {
+ radar_type = RADAR_TYPE_CODE_ETSI;
+ }
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11H_DETECT_RADAR);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le16(action);
+ pcmd->radar_type_code = cpu_to_le16(radar_type);
+ pcmd->min_chirp_cnt = cpu_to_le16(priv->dfs_chirp_count_min);
+ pcmd->chirp_time_intvl = cpu_to_le16(priv->dfs_chirp_time_interval);
+ pcmd->pw_filter = cpu_to_le16(priv->dfs_pw_filter);
+ pcmd->min_num_radar = cpu_to_le16(priv->dfs_min_num_radar);
+ pcmd->pri_min_num = cpu_to_le16(priv->dfs_min_pri_count);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11H_DETECT_RADAR)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_set_wmm_mode *pcmd;
+
+ pcmd = (struct hostcmd_cmd_set_wmm_mode *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_WMM_MODE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le16(enable ? WL_ENABLE : WL_DISABLE);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_WMM_MODE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_ht_guard_interval(struct ieee80211_hw *hw, u32 gi_type)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_ht_guard_interval *pcmd;
+
+ pcmd = (struct hostcmd_cmd_ht_guard_interval *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_HT_GUARD_INTERVAL);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le32(WL_SET);
+ pcmd->gi_type = cpu_to_le32(gi_type);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_HT_GUARD_INTERVAL)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_use_fixed_rate(struct ieee80211_hw *hw, int mcast, int mgmt)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_set_fixed_rate *pcmd;
+
+ pcmd = (struct hostcmd_cmd_set_fixed_rate *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_FIXED_RATE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+ pcmd->action = cpu_to_le32(HOSTCMD_ACT_NOT_USE_FIXED_RATE);
+ pcmd->multicast_rate = mcast;
+ pcmd->management_rate = mgmt;
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_FIXED_RATE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_linkadapt_cs_mode(struct ieee80211_hw *hw, u16 cs_mode)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_set_linkadapt_cs_mode *pcmd;
+
+ pcmd = (struct hostcmd_cmd_set_linkadapt_cs_mode *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_LINKADAPT_CS_MODE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_SET);
+ pcmd->cs_mode = cpu_to_le16(cs_mode);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_LINKADAPT_CS_MODE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_rate_adapt_mode(struct ieee80211_hw *hw, u16 mode)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_set_rate_adapt_mode *pcmd;
+
+ pcmd = (struct hostcmd_cmd_set_rate_adapt_mode *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_RATE_ADAPT_MODE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le16(WL_SET);
+ pcmd->rate_adapt_mode = cpu_to_le16(mode);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_RATE_ADAPT_MODE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_mac_addr_client(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *mac_addr)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_set_mac_addr *pcmd;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ pcmd = (struct hostcmd_cmd_set_mac_addr *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_MAC_ADDR);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+ pcmd->mac_type = cpu_to_le16(WL_MAC_TYPE_SECONDARY_CLIENT);
+ ether_addr_copy(pcmd->mac_addr, mac_addr);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_MAC_ADDR)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_get_watchdog_bitmap *pcmd;
+
+ pcmd = (struct hostcmd_cmd_get_watchdog_bitmap *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_WATCHDOG_BITMAP);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_WATCHDOG_BITMAP)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ *bitmap = pcmd->watchdog_bitmap;
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_remove_mac_addr(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *mac_addr)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_set_mac_addr *pcmd;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ pcmd = (struct hostcmd_cmd_set_mac_addr *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_DEL_MAC_ADDR);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+ ether_addr_copy(pcmd->mac_addr, mac_addr);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_DEL_MAC_ADDR)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_bss_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, bool enable)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_bss_start *pcmd;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ if (enable && (priv->running_bsses & (1 << mwl_vif->macid)))
+ return 0;
+
+ if (!enable && !(priv->running_bsses & (1 << mwl_vif->macid)))
+ return 0;
+
+ pcmd = (struct hostcmd_cmd_bss_start *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BSS_START);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+ if (enable) {
+ pcmd->enable = cpu_to_le32(WL_ENABLE);
+ } else {
+ if (mwl_vif->macid == 0)
+ pcmd->enable = cpu_to_le32(WL_DISABLE);
+ else
+ pcmd->enable = cpu_to_le32(WL_DISABLE_VMAC);
+ }
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BSS_START)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ if (enable)
+ priv->running_bsses |= (1 << mwl_vif->macid);
+ else
+ priv->running_bsses &= ~(1 << mwl_vif->macid);
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_beacon(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *beacon, int len)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct beacon_info *b_inf;
+ int rc;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+ b_inf = &mwl_vif->beacon_info;
+
+ mwl_fwcmd_parse_beacon(priv, mwl_vif, beacon, len);
+
+ if (!b_inf->valid)
+ goto err;
+
+ if (mwl_fwcmd_set_ies(priv, mwl_vif))
+ goto err;
+
+ if (mwl_fwcmd_set_wsc_ie(hw, b_inf->ie_wsc_len, b_inf->ie_wsc_ptr))
+ goto err;
+
+ if (mwl_fwcmd_set_ap_beacon(priv, mwl_vif, &vif->bss_conf))
+ goto err;
+
+ if (mwl_fwcmd_bss_start(hw, vif, true))
+ goto err;
+
+ if (b_inf->cap_info & WLAN_CAPABILITY_SPECTRUM_MGMT)
+ rc = mwl_fwcmd_set_spectrum_mgmt(priv, true);
+ else
+ rc = mwl_fwcmd_set_spectrum_mgmt(priv, false);
+ if (rc)
+ goto err;
+
+ if (b_inf->power_constraint)
+ rc = mwl_fwcmd_set_power_constraint(priv,
+ b_inf->power_constraint);
+ if (rc)
+ goto err;
+
+ if (mwl_fwcmd_set_country_code(priv, mwl_vif, &vif->bss_conf))
+ goto err;
+
+ b_inf->valid = false;
+
+ return 0;
+
+err:
+
+ b_inf->valid = false;
+
+ return -EIO;
+}
+
+int mwl_fwcmd_set_new_stn_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_set_new_stn *pcmd;
+ u32 rates;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_ADD);
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ pcmd->aid = 0;
+ pcmd->stn_id = 0;
+ } else {
+ pcmd->aid = cpu_to_le16(sta->aid);
+ pcmd->stn_id = cpu_to_le16(sta->aid);
+ }
+ ether_addr_copy(pcmd->mac_addr, sta->addr);
+
+ if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
+ rates = sta->supp_rates[NL80211_BAND_2GHZ];
+ else
+ rates = sta->supp_rates[NL80211_BAND_5GHZ] << 5;
+ pcmd->peer_info.legacy_rate_bitmap = cpu_to_le32(rates);
+
+ if (sta->ht_cap.ht_supported) {
+ pcmd->peer_info.ht_rates[0] = sta->ht_cap.mcs.rx_mask[0];
+ pcmd->peer_info.ht_rates[1] = sta->ht_cap.mcs.rx_mask[1];
+ pcmd->peer_info.ht_rates[2] = sta->ht_cap.mcs.rx_mask[2];
+ pcmd->peer_info.ht_rates[3] = sta->ht_cap.mcs.rx_mask[3];
+ pcmd->peer_info.ht_cap_info = cpu_to_le16(sta->ht_cap.cap);
+ pcmd->peer_info.mac_ht_param_info =
+ (sta->ht_cap.ampdu_factor & 3) |
+ ((sta->ht_cap.ampdu_density & 7) << 2);
+ }
+
+ if (sta->vht_cap.vht_supported) {
+ pcmd->peer_info.vht_max_rx_mcs =
+ cpu_to_le32(*((u32 *)
+ &sta->vht_cap.vht_mcs.rx_mcs_map));
+ pcmd->peer_info.vht_cap = cpu_to_le32(sta->vht_cap.cap);
+ pcmd->peer_info.vht_rx_channel_width = sta->bandwidth;
+ }
+
+ pcmd->is_qos_sta = sta->wme;
+ pcmd->qos_info = ((sta->uapsd_queues << 4) | (sta->max_sp << 1));
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_new_stn_add_self(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_set_new_stn *pcmd;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_ADD);
+ ether_addr_copy(pcmd->mac_addr, vif->addr);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_new_stn_del(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *addr)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_set_new_stn *pcmd;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+ pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_REMOVE);
+ ether_addr_copy(pcmd->mac_addr, addr);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_apmode(struct ieee80211_hw *hw, u8 apmode)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_set_apmode *pcmd;
+
+ pcmd = (struct hostcmd_cmd_set_apmode *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_APMODE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->apmode = apmode;
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_APMODE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_switch_channel(struct mwl_priv *priv,
+ struct ieee80211_channel_switch *ch_switch)
+{
+ struct hostcmd_cmd_set_switch_channel *pcmd;
+ struct cfg80211_chan_def *chandef = &ch_switch->chandef;
+ struct ieee80211_channel *channel = chandef->chan;
+ u32 chnl_flags, freq_band, chnl_width, act_primary, sec_chnl_offset;
+
+ if (priv->csa_active)
+ return 0;
+
+ if (channel->band == NL80211_BAND_2GHZ)
+ freq_band = FREQ_BAND_2DOT4GHZ;
+ else if (channel->band == NL80211_BAND_5GHZ)
+ freq_band = FREQ_BAND_5GHZ;
+ else
+ return -EINVAL;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ chnl_width = CH_20_MHZ_WIDTH;
+ act_primary = ACT_PRIMARY_CHAN_0;
+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ chnl_width = CH_40_MHZ_WIDTH;
+ if (chandef->center_freq1 > channel->center_freq) {
+ act_primary = ACT_PRIMARY_CHAN_0;
+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ } else {
+ act_primary = ACT_PRIMARY_CHAN_1;
+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ }
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ chnl_width = CH_80_MHZ_WIDTH;
+ act_primary =
+ mwl_fwcmd_get_80m_pri_chnl(channel->hw_value);
+ if ((act_primary == ACT_PRIMARY_CHAN_0) ||
+ (act_primary == ACT_PRIMARY_CHAN_2))
+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ else
+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ chnl_flags = (freq_band & FREQ_BAND_MASK) |
+ ((chnl_width << CHNL_WIDTH_SHIFT) & CHNL_WIDTH_MASK) |
+ ((act_primary << ACT_PRIMARY_SHIFT) & ACT_PRIMARY_MASK);
+
+ pcmd = (struct hostcmd_cmd_set_switch_channel *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_SWITCH_CHANNEL);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->next_11h_chnl = cpu_to_le32(channel->hw_value);
+ pcmd->mode = cpu_to_le32(ch_switch->block_tx);
+ pcmd->init_count = cpu_to_le32(ch_switch->count + 1);
+ pcmd->chnl_flags = cpu_to_le32(chnl_flags);
+ pcmd->next_ht_extchnl_offset = cpu_to_le32(sec_chnl_offset);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_SWITCH_CHANNEL)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ priv->csa_active = true;
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_update_encryption_enable(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u8 *addr, u8 encr_type)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_update_encryption *pcmd;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ pcmd = (struct hostcmd_cmd_update_encryption *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+ pcmd->action_type = cpu_to_le32(ENCR_ACTION_ENABLE_HW_ENCR);
+ ether_addr_copy(pcmd->mac_addr, addr);
+ pcmd->action_data[0] = encr_type;
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ if (ether_addr_equal(mwl_vif->bssid, addr))
+ ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac);
+ else
+ ether_addr_copy(pcmd->mac_addr, mwl_vif->bssid);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_encryption_set_key(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *addr,
+ struct ieee80211_key_conf *key)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_set_key *pcmd;
+ int rc;
+ int keymlen;
+ u32 action;
+ u8 idx;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ pcmd = (struct hostcmd_cmd_set_key *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+ rc = mwl_fwcmd_encryption_set_cmd_info(pcmd, addr, key);
+ if (rc) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ if (rc != 1)
+ wiphy_err(hw->wiphy, "encryption not support\n");
+ return rc;
+ }
+
+ idx = key->keyidx;
+
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+ action = ENCR_ACTION_TYPE_SET_KEY;
+ else
+ action = ENCR_ACTION_TYPE_SET_GROUP_KEY;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (!mwl_vif->wep_key_conf[idx].enabled) {
+ memcpy(mwl_vif->wep_key_conf[idx].key, key,
+ sizeof(*key) + key->keylen);
+ mwl_vif->wep_key_conf[idx].enabled = 1;
+ }
+
+ keymlen = key->keylen;
+ action = ENCR_ACTION_TYPE_SET_KEY;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ keymlen = key->keylen;
+ break;
+ default:
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "encryption not support\n");
+ return -ENOTSUPP;
+ }
+
+ memcpy((void *)&pcmd->key_param.key, key->key, keymlen);
+ pcmd->action_type = cpu_to_le32(action);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ if (ether_addr_equal(mwl_vif->bssid, addr))
+ ether_addr_copy(pcmd->key_param.mac_addr,
+ mwl_vif->sta_mac);
+ else
+ ether_addr_copy(pcmd->key_param.mac_addr,
+ mwl_vif->bssid);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_encryption_remove_key(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *addr,
+ struct ieee80211_key_conf *key)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_set_key *pcmd;
+ int rc;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ pcmd = (struct hostcmd_cmd_set_key *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+ rc = mwl_fwcmd_encryption_set_cmd_info(pcmd, addr, key);
+ if (rc) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ if (rc != 1)
+ wiphy_err(hw->wiphy, "encryption not support\n");
+ return rc;
+ }
+
+ pcmd->action_type = cpu_to_le32(ENCR_ACTION_TYPE_REMOVE_KEY);
+
+ if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ key->cipher == WLAN_CIPHER_SUITE_WEP104)
+ mwl_vif->wep_key_conf[key->keyidx].enabled = 0;
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_check_ba(struct ieee80211_hw *hw,
+ struct mwl_ampdu_stream *stream,
+ struct ieee80211_vif *vif)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_bastream *pcmd;
+ u32 ba_flags, ba_type, ba_direction;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BASTREAM);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+ pcmd->cmd_hdr.result = cpu_to_le16(0xffff);
+
+ pcmd->action_type = cpu_to_le32(BA_CHECK_STREAM);
+ ether_addr_copy(&pcmd->ba_info.create_params.peer_mac_addr[0],
+ stream->sta->addr);
+ pcmd->ba_info.create_params.tid = stream->tid;
+ ba_type = BASTREAM_FLAG_IMMEDIATE_TYPE;
+ ba_direction = BASTREAM_FLAG_DIRECTION_UPSTREAM;
+ ba_flags = (ba_type & BA_TYPE_MASK) |
+ ((ba_direction << BA_DIRECTION_SHIFT) & BA_DIRECTION_MASK);
+ pcmd->ba_info.create_params.flags = cpu_to_le32(ba_flags);
+ pcmd->ba_info.create_params.queue_id = stream->idx;
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BASTREAM)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "check ba failed execution\n");
+ return -EIO;
+ }
+
+ if (pcmd->cmd_hdr.result != 0) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_create_ba(struct ieee80211_hw *hw,
+ struct mwl_ampdu_stream *stream,
+ u8 buf_size, struct ieee80211_vif *vif)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct hostcmd_cmd_bastream *pcmd;
+ u32 ba_flags, ba_type, ba_direction;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BASTREAM);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+ pcmd->cmd_hdr.result = cpu_to_le16(0xffff);
+
+ pcmd->action_type = cpu_to_le32(BA_CREATE_STREAM);
+ pcmd->ba_info.create_params.bar_thrs = cpu_to_le32(buf_size);
+ pcmd->ba_info.create_params.window_size = cpu_to_le32(buf_size);
+ ether_addr_copy(&pcmd->ba_info.create_params.peer_mac_addr[0],
+ stream->sta->addr);
+ pcmd->ba_info.create_params.tid = stream->tid;
+ ba_type = BASTREAM_FLAG_IMMEDIATE_TYPE;
+ ba_direction = BASTREAM_FLAG_DIRECTION_UPSTREAM;
+ ba_flags = (ba_type & BA_TYPE_MASK) |
+ ((ba_direction << BA_DIRECTION_SHIFT) & BA_DIRECTION_MASK);
+ pcmd->ba_info.create_params.flags = cpu_to_le32(ba_flags);
+ pcmd->ba_info.create_params.queue_id = stream->idx;
+ pcmd->ba_info.create_params.param_info =
+ (stream->sta->ht_cap.ampdu_factor &
+ IEEE80211_HT_AMPDU_PARM_FACTOR) |
+ ((stream->sta->ht_cap.ampdu_density << 2) &
+ IEEE80211_HT_AMPDU_PARM_DENSITY);
+ pcmd->ba_info.create_params.reset_seq_no = 1;
+ pcmd->ba_info.create_params.current_seq = cpu_to_le16(0);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BASTREAM)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "create ba failed execution\n");
+ return -EIO;
+ }
+
+ if (pcmd->cmd_hdr.result != 0) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "create ba result error %d\n",
+ le16_to_cpu(pcmd->cmd_hdr.result));
+ return -EINVAL;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_destroy_ba(struct ieee80211_hw *hw,
+ u8 idx)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_bastream *pcmd;
+ u32 ba_flags, ba_type, ba_direction;
+
+ pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BASTREAM);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+ pcmd->action_type = cpu_to_le32(BA_DESTROY_STREAM);
+ ba_type = BASTREAM_FLAG_IMMEDIATE_TYPE;
+ ba_direction = BASTREAM_FLAG_DIRECTION_UPSTREAM;
+ ba_flags = (ba_type & BA_TYPE_MASK) |
+ ((ba_direction << BA_DIRECTION_SHIFT) & BA_DIRECTION_MASK);
+ pcmd->ba_info.destroy_params.flags = cpu_to_le32(ba_flags);
+ pcmd->ba_info.destroy_params.fw_ba_context.context = cpu_to_le32(idx);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BASTREAM)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "destroy ba failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+/* caller must hold priv->stream_lock when calling the stream functions */
+struct mwl_ampdu_stream *mwl_fwcmd_add_stream(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ u8 tid)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_ampdu_stream *stream;
+ int i;
+
+ for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) {
+ stream = &priv->ampdu[i];
+
+ if (stream->state == AMPDU_NO_STREAM) {
+ stream->sta = sta;
+ stream->state = AMPDU_STREAM_NEW;
+ stream->tid = tid;
+ stream->idx = i;
+ return stream;
+ }
+ }
+
+ return NULL;
+}
+
+void mwl_fwcmd_del_sta_streams(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_ampdu_stream *stream;
+ int i;
+
+ for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) {
+ stream = &priv->ampdu[i];
+
+ if (stream->sta == sta) {
+ mwl_fwcmd_destroy_ba(hw, stream->idx);
+ mwl_fwcmd_remove_stream(hw, stream);
+ }
+ }
+}
+
+int mwl_fwcmd_start_stream(struct ieee80211_hw *hw,
+ struct mwl_ampdu_stream *stream)
+{
+ /* if the stream has already been started, don't start it again */
+ if (stream->state != AMPDU_STREAM_NEW)
+ return 0;
+
+ return ieee80211_start_tx_ba_session(stream->sta, stream->tid, 0);
+}
+
+void mwl_fwcmd_remove_stream(struct ieee80211_hw *hw,
+ struct mwl_ampdu_stream *stream)
+{
+ memset(stream, 0, sizeof(*stream));
+}
+
+struct mwl_ampdu_stream *mwl_fwcmd_lookup_stream(struct ieee80211_hw *hw,
+ u8 *addr, u8 tid)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_ampdu_stream *stream;
+ int i;
+
+ for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) {
+ stream = &priv->ampdu[i];
+
+ if (stream->state == AMPDU_NO_STREAM)
+ continue;
+
+ if (ether_addr_equal(stream->sta->addr, addr) &&
+ stream->tid == tid)
+ return stream;
+ }
+
+ return NULL;
+}
+
+bool mwl_fwcmd_ampdu_allowed(struct ieee80211_sta *sta, u8 tid)
+{
+ struct mwl_sta *sta_info;
+ struct mwl_tx_info *tx_stats;
+
+ if (WARN_ON(tid >= SYSADPT_MAX_TID))
+ return false;
+
+ sta_info = mwl_dev_get_sta(sta);
+
+ tx_stats = &sta_info->tx_stats[tid];
+
+ return (sta_info->is_ampdu_allowed &&
+ tx_stats->pkts > SYSADPT_AMPDU_PACKET_THRESHOLD);
+}
+
+int mwl_fwcmd_set_optimization_level(struct ieee80211_hw *hw, u8 opt_level)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_set_optimization_level *pcmd;
+
+ pcmd = (struct hostcmd_cmd_set_optimization_level *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->opt_level = opt_level;
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_wsc_ie(struct ieee80211_hw *hw, u8 len, u8 *data)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_set_wsc_ie *pcmd;
+
+ pcmd = (struct hostcmd_cmd_set_wsc_ie *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_WSC_IE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->len = cpu_to_le16(len);
+ memcpy(pcmd->data, data, len);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_WSC_IE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ pcmd->ie_type = cpu_to_le16(WSC_IE_SET_PROBE_RESPONSE);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_WSC_IE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_dwds_stamode(struct ieee80211_hw *hw, bool enable)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_dwds_enable *pcmd;
+
+ pcmd = (struct hostcmd_cmd_dwds_enable *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_DWDS_ENABLE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->enable = cpu_to_le32(enable);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_DWDS_ENABLE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_fw_flush_timer(struct ieee80211_hw *hw, u32 value)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_fw_flush_timer *pcmd;
+
+ pcmd = (struct hostcmd_cmd_fw_flush_timer *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_FW_FLUSH_TIMER);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->value = cpu_to_le32(value);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_FW_FLUSH_TIMER)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_set_cdd *pcmd;
+
+ pcmd = (struct hostcmd_cmd_set_cdd *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_CDD);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->enable = cpu_to_le32(priv->cdd);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_CDD)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_get_temp(struct ieee80211_hw *hw, u32 *temp)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_get_temp *pcmd;
+
+ pcmd = (struct hostcmd_cmd_get_temp *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_TEMP);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_TEMP)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ *temp = le32_to_cpu(pcmd->celcius);
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_get_fw_region_code(struct ieee80211_hw *hw,
+ u32 *fw_region_code)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_get_fw_region_code *pcmd;
+ int status;
+
+ pcmd = (struct hostcmd_cmd_get_fw_region_code *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_FW_REGION_CODE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_FW_REGION_CODE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ if (pcmd->cmd_hdr.result != 0) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ return -EINVAL;
+ }
+
+ status = le32_to_cpu(pcmd->status);
+
+ if (!status)
+ *fw_region_code = le32_to_cpu(pcmd->fw_region_code);
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_get_device_pwr_tbl(struct ieee80211_hw *hw,
+ struct mwl_device_pwr_tbl *device_ch_pwrtbl,
+ u8 *region_code,
+ u8 *number_of_channels,
+ u32 channel_index)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_get_device_pwr_tbl *pcmd;
+ int status;
+
+ pcmd = (struct hostcmd_cmd_get_device_pwr_tbl *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_DEVICE_PWR_TBL);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->status = cpu_to_le16(HOSTCMD_CMD_GET_DEVICE_PWR_TBL);
+ pcmd->current_channel_index = cpu_to_le32(channel_index);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_DEVICE_PWR_TBL)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ device_ch_pwrtbl->channel = pcmd->channel_pwr_tbl.channel;
+ memcpy(device_ch_pwrtbl->tx_pwr, pcmd->channel_pwr_tbl.tx_pwr,
+ SYSADPT_TX_POWER_LEVEL_TOTAL);
+ device_ch_pwrtbl->dfs_capable = pcmd->channel_pwr_tbl.dfs_capable;
+ device_ch_pwrtbl->ax_ant = pcmd->channel_pwr_tbl.ax_ant;
+ device_ch_pwrtbl->cdd = pcmd->channel_pwr_tbl.cdd;
+ *region_code = pcmd->region_code;
+ *number_of_channels = pcmd->number_of_channels;
+ status = le16_to_cpu(pcmd->status);
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return status;
+}
+
+int mwl_fwcmd_quiet_mode(struct ieee80211_hw *hw, bool enable, u32 period,
+ u32 duration, u32 next_offset)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_quiet_mode *pcmd;
+
+ pcmd = (struct hostcmd_cmd_quiet_mode *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_QUIET_MODE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le16(WL_SET);
+ pcmd->enable = cpu_to_le32(enable);
+ if (enable) {
+ pcmd->period = cpu_to_le32(period);
+ pcmd->duration = cpu_to_le32(duration);
+ pcmd->next_offset = cpu_to_le32(next_offset);
+ }
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_QUIET_MODE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_send_mfg_cmd(struct mwl_priv *priv, char *mfgcmd)
+{
+ struct hostcmd_header *pcmd;
+ struct cmd_header *cmd_hd = (struct cmd_header *)(mfgcmd + 4);
+ u16 len;
+ u16 cmd;
+
+ pcmd = (struct hostcmd_header *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ len = le16_to_cpu(cmd_hd->len);
+ memset(pcmd, 0x00, len + 4);
+ memcpy((char *)pcmd, mfgcmd, len + 4);
+ cmd = le16_to_cpu(cmd_hd->command);
+ if (mwl_fwcmd_exec_cmd(priv, cmd)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution");
+ return -EIO;
+ }
+ cmd_hd = (struct cmd_header *)&priv->pcmd_buf[2];
+ len = le16_to_cpu(cmd_hd->len);
+ memcpy(mfgcmd, (char *)&priv->pcmd_buf[2], len);
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/marvell/mwlwifi/fwcmd.h b/drivers/net/wireless/marvell/mwlwifi/fwcmd.h
new file mode 100644
index 0000000..74a21d3
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/fwcmd.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file defines firmware host command related
+ * functions.
+ */
+
+#ifndef _FWCMD_H_
+#define _FWCMD_H_
+
+/* Define OpMode for SoftAP/Station mode
+ *
+ * The following mode signature has to be written to PCI scratch register#0
+ * right after successfully downloading the last block of firmware and
+ * before waiting for firmware ready signature
+ */
+
+#define HOSTCMD_STA_MODE 0x5A
+#define HOSTCMD_SOFTAP_MODE 0xA5
+
+#define HOSTCMD_STA_FWRDY_SIGNATURE 0xF0F1F2F4
+#define HOSTCMD_SOFTAP_FWRDY_SIGNATURE 0xF1F2F4A5
+
+#define GUARD_INTERVAL_STANDARD 1
+#define GUARD_INTERVAL_SHORT 2
+#define GUARD_INTERVAL_AUTO 3
+
+#define LINK_CS_STATE_CONSERV 0
+#define LINK_CS_STATE_AGGR 1
+#define LINK_CS_STATE_AUTO 2
+#define LINK_CS_STATE_AUTO_DISABLED 3
+
+#define STOP_DETECT_RADAR 0
+#define CAC_START 1
+#define MONITOR_START 3
+
+enum {
+ WL_ANTENNATYPE_RX = 1,
+ WL_ANTENNATYPE_TX = 2,
+};
+
+enum encr_type {
+ ENCR_TYPE_WEP = 0,
+ ENCR_TYPE_DISABLE = 1,
+ ENCR_TYPE_TKIP = 4,
+ ENCR_TYPE_AES = 6,
+ ENCR_TYPE_MIX = 7,
+};
+
+void mwl_fwcmd_reset(struct ieee80211_hw *hw);
+
+void mwl_fwcmd_int_enable(struct ieee80211_hw *hw);
+
+void mwl_fwcmd_int_disable(struct ieee80211_hw *hw);
+
+int mwl_fwcmd_get_hw_specs(struct ieee80211_hw *hw);
+
+int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw);
+
+int mwl_fwcmd_get_stat(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats);
+
+int mwl_fwcmd_radio_enable(struct ieee80211_hw *hw);
+
+int mwl_fwcmd_radio_disable(struct ieee80211_hw *hw);
+
+int mwl_fwcmd_set_radio_preamble(struct ieee80211_hw *hw,
+ bool short_preamble);
+
+int mwl_fwcmd_get_addr_value(struct ieee80211_hw *hw, u32 addr, u32 len,
+ u32 *val, u16 set);
+
+int mwl_fwcmd_max_tx_power(struct ieee80211_hw *hw,
+ struct ieee80211_conf *conf, u8 fraction);
+
+int mwl_fwcmd_tx_power(struct ieee80211_hw *hw,
+ struct ieee80211_conf *conf, u8 fraction);
+
+int mwl_fwcmd_rf_antenna(struct ieee80211_hw *hw, int dir, int antenna);
+
+int mwl_fwcmd_broadcast_ssid_enable(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, bool enable);
+
+int mwl_fwcmd_set_rf_channel(struct ieee80211_hw *hw,
+ struct ieee80211_conf *conf);
+
+int mwl_fwcmd_set_aid(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *bssid, u16 aid);
+
+int mwl_fwcmd_set_infra_mode(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+int mwl_fwcmd_set_rts_threshold(struct ieee80211_hw *hw,
+ int threshold);
+
+int mwl_fwcmd_set_edca_params(struct ieee80211_hw *hw, u8 index,
+ u16 cw_min, u16 cw_max, u8 aifs, u16 txop);
+
+int mwl_fwcmd_set_radar_detect(struct ieee80211_hw *hw, u16 action);
+
+int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable);
+
+int mwl_fwcmd_ht_guard_interval(struct ieee80211_hw *hw, u32 gi_type);
+
+int mwl_fwcmd_use_fixed_rate(struct ieee80211_hw *hw,
+ int mcast, int mgmt);
+
+int mwl_fwcmd_set_linkadapt_cs_mode(struct ieee80211_hw *hw,
+ u16 cs_mode);
+
+int mwl_fwcmd_set_rate_adapt_mode(struct ieee80211_hw *hw,
+ u16 mode);
+
+int mwl_fwcmd_set_mac_addr_client(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *mac_addr);
+
+int mwl_fwcmd_get_watchdog_bitmap(struct ieee80211_hw *hw,
+ u8 *bitmap);
+
+int mwl_fwcmd_remove_mac_addr(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *mac_addr);
+
+int mwl_fwcmd_bss_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, bool enable);
+
+int mwl_fwcmd_set_beacon(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *beacon, int len);
+
+int mwl_fwcmd_set_new_stn_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+
+int mwl_fwcmd_set_new_stn_add_self(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+int mwl_fwcmd_set_new_stn_del(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *addr);
+
+int mwl_fwcmd_set_apmode(struct ieee80211_hw *hw, u8 apmode);
+
+int mwl_fwcmd_set_switch_channel(struct mwl_priv *priv,
+ struct ieee80211_channel_switch *ch_switch);
+
+int mwl_fwcmd_update_encryption_enable(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u8 *addr, u8 encr_type);
+
+int mwl_fwcmd_encryption_set_key(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *addr,
+ struct ieee80211_key_conf *key);
+
+int mwl_fwcmd_encryption_remove_key(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *addr,
+ struct ieee80211_key_conf *key);
+
+int mwl_fwcmd_check_ba(struct ieee80211_hw *hw,
+ struct mwl_ampdu_stream *stream,
+ struct ieee80211_vif *vif);
+
+int mwl_fwcmd_create_ba(struct ieee80211_hw *hw,
+ struct mwl_ampdu_stream *stream,
+ u8 buf_size, struct ieee80211_vif *vif);
+
+int mwl_fwcmd_destroy_ba(struct ieee80211_hw *hw,
+ u8 idx);
+
+struct mwl_ampdu_stream *mwl_fwcmd_add_stream(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ u8 tid);
+
+void mwl_fwcmd_del_sta_streams(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta);
+
+int mwl_fwcmd_start_stream(struct ieee80211_hw *hw,
+ struct mwl_ampdu_stream *stream);
+
+void mwl_fwcmd_remove_stream(struct ieee80211_hw *hw,
+ struct mwl_ampdu_stream *stream);
+
+struct mwl_ampdu_stream *mwl_fwcmd_lookup_stream(struct ieee80211_hw *hw,
+ u8 *addr, u8 tid);
+
+bool mwl_fwcmd_ampdu_allowed(struct ieee80211_sta *sta, u8 tid);
+
+int mwl_fwcmd_set_optimization_level(struct ieee80211_hw *hw, u8 opt_level);
+
+int mwl_fwcmd_set_wsc_ie(struct ieee80211_hw *hw, u8 len, u8 *data);
+
+int mwl_fwcmd_set_dwds_stamode(struct ieee80211_hw *hw, bool enable);
+
+int mwl_fwcmd_set_fw_flush_timer(struct ieee80211_hw *hw, u32 value);
+
+int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw);
+
+int mwl_fwcmd_get_temp(struct ieee80211_hw *hw, u32 *temp);
+
+int mwl_fwcmd_get_fw_region_code(struct ieee80211_hw *hw,
+ u32 *fw_region_code);
+
+int mwl_fwcmd_get_device_pwr_tbl(struct ieee80211_hw *hw,
+ struct mwl_device_pwr_tbl *device_ch_pwrtbl,
+ u8 *region_code,
+ u8 *number_of_channels,
+ u32 channel_index);
+
+int mwl_fwcmd_quiet_mode(struct ieee80211_hw *hw, bool enable, u32 period,
+ u32 duration, u32 next_offset);
+
+int mwl_fwcmd_send_mfg_cmd(struct mwl_priv *priv, char *mfgcmd);
+
+#endif /* _FWCMD_H_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/fwdl.c b/drivers/net/wireless/marvell/mwlwifi/fwdl.c
new file mode 100644
index 0000000..f4d5fa1
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/fwdl.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file implements firmware download related
+ * functions.
+ */
+
+#include <linux/io.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwcmd.h"
+#include "fwdl.h"
+
+#define FW_DOWNLOAD_BLOCK_SIZE 256
+#define FW_CHECK_MSECS 3
+
+#define FW_MAX_NUM_CHECKS 0xffff
+
+static void mwl_fwdl_trig_pcicmd(struct mwl_priv *priv)
+{
+ writel(priv->pphys_cmd_buf, priv->iobase1 + MACREG_REG_GEN_PTR);
+
+ writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE);
+
+ writel(MACREG_H2ARIC_BIT_DOOR_BELL,
+ priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
+}
+
+static void mwl_fwdl_trig_pcicmd_bootcode(struct mwl_priv *priv)
+{
+ writel(priv->pphys_cmd_buf, priv->iobase1 + MACREG_REG_GEN_PTR);
+
+ writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE);
+
+ writel(MACREG_H2ARIC_BIT_DOOR_BELL,
+ priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
+}
+
+int mwl_fwdl_download_firmware(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+ const struct firmware *fw;
+ u32 curr_iteration = 0;
+ u32 size_fw_downloaded = 0;
+ u32 int_code = 0;
+ u32 len = 0;
+ u32 fwreadysignature = HOSTCMD_SOFTAP_FWRDY_SIGNATURE;
+
+ fw = priv->fw_ucode;
+
+ mwl_fwcmd_reset(hw);
+
+ /* FW before jumping to boot rom, it will enable PCIe transaction retry,
+ * wait for boot code to stop it.
+ */
+ usleep_range(FW_CHECK_MSECS * 1000, FW_CHECK_MSECS * 2000);
+
+ writel(MACREG_A2HRIC_BIT_MASK,
+ priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CLEAR_SEL);
+ writel(0x00, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE);
+ writel(0x00, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);
+ writel(MACREG_A2HRIC_BIT_MASK,
+ priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+
+ /* this routine interacts with SC2 bootrom to download firmware binary
+ * to the device. After DMA'd to SC2, the firmware could be deflated to
+ * reside on its respective blocks such as ITCM, DTCM, SQRAM,
+ * (or even DDR, AFTER DDR is init'd before fw download
+ */
+ wiphy_debug(hw->wiphy, "fw download start\n");
+
+ /* Disable PFU before FWDL */
+ writel(0x100, priv->iobase1 + 0xE0E4);
+
+ /* make sure SCRATCH2 C40 is clear, in case we are too quick */
+ while (readl(priv->iobase1 + 0xc40) == 0)
+ cond_resched();
+
+ while (size_fw_downloaded < fw->size) {
+ len = readl(priv->iobase1 + 0xc40);
+
+ if (!len)
+ break;
+
+ /* this copies the next chunk of fw binary to be delivered */
+ memcpy((char *)&priv->pcmd_buf[0],
+ (fw->data + size_fw_downloaded), len);
+
+ /* this function writes pdata to c10, then write 2 to c18 */
+ mwl_fwdl_trig_pcicmd_bootcode(priv);
+
+ /* this is arbitrary per your platform; we use 0xffff */
+ curr_iteration = FW_MAX_NUM_CHECKS;
+
+ /* NOTE: the following back to back checks on C1C is time
+ * sensitive, hence may need to be tweaked dependent on host
+ * processor. Time for SC2 to go from the write of event 2 to
+ * C1C == 2 is ~1300 nSec. Hence the checkings on host has to
+ * consider how efficient your code can be to meet this timing,
+ * or you can alternatively tweak this routines to fit your
+ * platform
+ */
+ do {
+ int_code = readl(priv->iobase1 + 0xc1c);
+ if (int_code != 0)
+ break;
+ cond_resched();
+ curr_iteration--;
+ } while (curr_iteration);
+
+ do {
+ int_code = readl(priv->iobase1 + 0xc1c);
+ if ((int_code & MACREG_H2ARIC_BIT_DOOR_BELL) !=
+ MACREG_H2ARIC_BIT_DOOR_BELL)
+ break;
+ cond_resched();
+ curr_iteration--;
+ } while (curr_iteration);
+
+ if (curr_iteration == 0) {
+ /* This limited loop check allows you to exit gracefully
+ * without locking up your entire system just because fw
+ * download failed
+ */
+ wiphy_err(hw->wiphy,
+ "Exhausted curr_iteration for fw download\n");
+ goto err_download;
+ }
+
+ size_fw_downloaded += len;
+ }
+
+ wiphy_debug(hw->wiphy,
+ "FwSize = %d downloaded Size = %d curr_iteration %d\n",
+ (int)fw->size, size_fw_downloaded, curr_iteration);
+
+ /* Now firware is downloaded successfully, so this part is to check
+ * whether fw can properly execute to an extent that write back
+ * signature to indicate its readiness to the host. NOTE: if your
+ * downloaded fw crashes, this signature checking will fail. This
+ * part is similar as SC1
+ */
+ *((u32 *)&priv->pcmd_buf[1]) = 0;
+ mwl_fwdl_trig_pcicmd(priv);
+ curr_iteration = FW_MAX_NUM_CHECKS;
+ do {
+ curr_iteration--;
+ writel(HOSTCMD_SOFTAP_MODE,
+ priv->iobase1 + MACREG_REG_GEN_PTR);
+ usleep_range(FW_CHECK_MSECS * 1000, FW_CHECK_MSECS * 2000);
+ int_code = readl(priv->iobase1 + MACREG_REG_INT_CODE);
+ if (!(curr_iteration % 0xff) && (int_code != 0))
+ wiphy_err(hw->wiphy, "%x;", int_code);
+ } while ((curr_iteration) &&
+ (int_code != fwreadysignature));
+
+ if (curr_iteration == 0) {
+ wiphy_err(hw->wiphy,
+ "Exhausted curr_iteration for fw signature\n");
+ goto err_download;
+ }
+
+ wiphy_debug(hw->wiphy, "fw download complete\n");
+ writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE);
+
+ return 0;
+
+err_download:
+
+ mwl_fwcmd_reset(hw);
+
+ return -EIO;
+}
diff --git a/drivers/net/wireless/marvell/mwlwifi/fwdl.h b/drivers/net/wireless/marvell/mwlwifi/fwdl.h
new file mode 100644
index 0000000..eedf4dd
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/fwdl.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file defines firmware download related
+ * functions.
+ */
+
+#ifndef _FWDL_H_
+#define _FWDL_H_
+
+int mwl_fwdl_download_firmware(struct ieee80211_hw *hw);
+
+#endif /* _FWDL_H_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/hostcmd.h b/drivers/net/wireless/marvell/mwlwifi/hostcmd.h
new file mode 100644
index 0000000..b163a94
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/hostcmd.h
@@ -0,0 +1,913 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file defines firmware host command related
+ * structure.
+ */
+
+#ifndef _HOSTCMD_H_
+#define _HOSTCMD_H_
+
+/* 16 bit host command code */
+#define HOSTCMD_CMD_GET_HW_SPEC 0x0003
+#define HOSTCMD_CMD_SET_HW_SPEC 0x0004
+#define HOSTCMD_CMD_802_11_GET_STAT 0x0014
+#define HOSTCMD_CMD_802_11_RADIO_CONTROL 0x001c
+#define HOSTCMD_CMD_MEM_ADDR_ACCESS 0x001d
+#define HOSTCMD_CMD_802_11_TX_POWER 0x001f
+#define HOSTCMD_CMD_802_11_RF_ANTENNA 0x0020
+#define HOSTCMD_CMD_BROADCAST_SSID_ENABLE 0x0050 /* per-vif */
+#define HOSTCMD_CMD_SET_RF_CHANNEL 0x010a
+#define HOSTCMD_CMD_SET_AID 0x010d /* per-vif */
+#define HOSTCMD_CMD_SET_INFRA_MODE 0x010e /* per-vif */
+#define HOSTCMD_CMD_802_11_RTS_THSD 0x0113
+#define HOSTCMD_CMD_SET_EDCA_PARAMS 0x0115
+#define HOSTCMD_CMD_802_11H_DETECT_RADAR 0x0120
+#define HOSTCMD_CMD_SET_WMM_MODE 0x0123
+#define HOSTCMD_CMD_HT_GUARD_INTERVAL 0x0124
+#define HOSTCMD_CMD_SET_FIXED_RATE 0x0126
+#define HOSTCMD_CMD_SET_IES 0x0127
+#define HOSTCMD_CMD_SET_LINKADAPT_CS_MODE 0x0129
+#define HOSTCMD_CMD_SET_MAC_ADDR 0x0202 /* per-vif */
+#define HOSTCMD_CMD_SET_RATE_ADAPT_MODE 0x0203
+#define HOSTCMD_CMD_GET_WATCHDOG_BITMAP 0x0205
+#define HOSTCMD_CMD_DEL_MAC_ADDR 0x0206 /* per-vif */
+#define HOSTCMD_CMD_BSS_START 0x1100 /* per-vif */
+#define HOSTCMD_CMD_AP_BEACON 0x1101 /* per-vif */
+#define HOSTCMD_CMD_SET_NEW_STN 0x1111 /* per-vif */
+#define HOSTCMD_CMD_SET_APMODE 0x1114
+#define HOSTCMD_CMD_SET_SWITCH_CHANNEL 0x1121
+#define HOSTCMD_CMD_UPDATE_ENCRYPTION 0x1122 /* per-vif */
+#define HOSTCMD_CMD_BASTREAM 0x1125
+#define HOSTCMD_CMD_SET_SPECTRUM_MGMT 0x1128
+#define HOSTCMD_CMD_SET_POWER_CONSTRAINT 0x1129
+#define HOSTCMD_CMD_SET_COUNTRY_CODE 0x1130
+#define HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL 0x1133
+#define HOSTCMD_CMD_SET_WSC_IE 0x1136 /* per-vif */
+#define HOSTCMD_CMD_DWDS_ENABLE 0x1144
+#define HOSTCMD_CMD_FW_FLUSH_TIMER 0x1148
+#define HOSTCMD_CMD_SET_CDD 0x1150
+#define HOSTCMD_CMD_GET_TEMP 0x1159
+#define HOSTCMD_CMD_GET_FW_REGION_CODE 0x116A
+#define HOSTCMD_CMD_GET_DEVICE_PWR_TBL 0x116B
+#define HOSTCMD_CMD_QUIET_MODE 0x1201
+
+/* Define general result code for each command */
+#define HOSTCMD_RESULT_OK 0x0000
+/* General error */
+#define HOSTCMD_RESULT_ERROR 0x0001
+/* Command is not valid */
+#define HOSTCMD_RESULT_NOT_SUPPORT 0x0002
+/* Command is pending (will be processed) */
+#define HOSTCMD_RESULT_PENDING 0x0003
+/* System is busy (command ignored) */
+#define HOSTCMD_RESULT_BUSY 0x0004
+/* Data buffer is not big enough */
+#define HOSTCMD_RESULT_PARTIAL_DATA 0x0005
+
+/* Define channel related constants */
+#define FREQ_BAND_2DOT4GHZ 0x1
+#define FREQ_BAND_4DOT9GHZ 0x2
+#define FREQ_BAND_5GHZ 0x4
+#define FREQ_BAND_5DOT2GHZ 0x8
+#define CH_AUTO_WIDTH 0
+#define CH_10_MHZ_WIDTH 0x1
+#define CH_20_MHZ_WIDTH 0x2
+#define CH_40_MHZ_WIDTH 0x4
+#define CH_80_MHZ_WIDTH 0x5
+#define EXT_CH_ABOVE_CTRL_CH 0x1
+#define EXT_CH_AUTO 0x2
+#define EXT_CH_BELOW_CTRL_CH 0x3
+#define NO_EXT_CHANNEL 0x0
+
+#define ACT_PRIMARY_CHAN_0 0
+#define ACT_PRIMARY_CHAN_1 1
+#define ACT_PRIMARY_CHAN_2 2
+#define ACT_PRIMARY_CHAN_3 3
+
+/* Define rate related constants */
+#define HOSTCMD_ACT_NOT_USE_FIXED_RATE 0x0002
+
+/* Define station related constants */
+#define HOSTCMD_ACT_STA_ACTION_ADD 0
+#define HOSTCMD_ACT_STA_ACTION_REMOVE 2
+
+/* Define key related constants */
+#define MAX_ENCR_KEY_LENGTH 16
+#define MIC_KEY_LENGTH 8
+
+#define KEY_TYPE_ID_WEP 0x00
+#define KEY_TYPE_ID_TKIP 0x01
+#define KEY_TYPE_ID_AES 0x02
+
+/* Group key for RX only */
+#define ENCR_KEY_FLAG_RXGROUPKEY 0x00000002
+#define ENCR_KEY_FLAG_TXGROUPKEY 0x00000004
+#define ENCR_KEY_FLAG_PAIRWISE 0x00000008
+#define ENCR_KEY_FLAG_TSC_VALID 0x00000040
+#define ENCR_KEY_FLAG_WEP_TXKEY 0x01000000
+#define ENCR_KEY_FLAG_MICKEY_VALID 0x02000000
+
+/* Define block ack related constants */
+#define BASTREAM_FLAG_IMMEDIATE_TYPE 1
+#define BASTREAM_FLAG_DIRECTION_UPSTREAM 0
+
+/* Define general purpose action */
+#define HOSTCMD_ACT_GEN_SET 0x0001
+#define HOSTCMD_ACT_GEN_SET_LIST 0x0002
+#define HOSTCMD_ACT_GEN_GET_LIST 0x0003
+
+/* Misc */
+#define WSC_IE_MAX_LENGTH 251
+#define WSC_IE_SET_BEACON 0
+#define WSC_IE_SET_PROBE_RESPONSE 1
+
+enum {
+ WL_DISABLE = 0,
+ WL_ENABLE = 1,
+ WL_DISABLE_VMAC = 0x80,
+};
+
+enum {
+ WL_GET = 0,
+ WL_SET = 1,
+ WL_RESET = 2,
+};
+
+enum {
+ WL_LONG_PREAMBLE = 1,
+ WL_SHORT_PREAMBLE = 3,
+ WL_AUTO_PREAMBLE = 5,
+};
+
+enum encr_action_type {
+ /* request to enable/disable HW encryption */
+ ENCR_ACTION_ENABLE_HW_ENCR,
+ /* request to set encryption key */
+ ENCR_ACTION_TYPE_SET_KEY,
+ /* request to remove one or more keys */
+ ENCR_ACTION_TYPE_REMOVE_KEY,
+ ENCR_ACTION_TYPE_SET_GROUP_KEY,
+};
+
+enum ba_action_type {
+ BA_CREATE_STREAM,
+ BA_UPDATE_STREAM,
+ BA_DESTROY_STREAM,
+ BA_FLUSH_STREAM,
+ BA_CHECK_STREAM,
+};
+
+enum mac_type {
+ WL_MAC_TYPE_PRIMARY_CLIENT,
+ WL_MAC_TYPE_SECONDARY_CLIENT,
+ WL_MAC_TYPE_PRIMARY_AP,
+ WL_MAC_TYPE_SECONDARY_AP,
+};
+
+/* General host command header */
+struct hostcmd_header {
+ __le16 cmd;
+ __le16 len;
+ u8 seq_num;
+ u8 macid;
+ __le16 result;
+} __packed;
+
+/* HOSTCMD_CMD_GET_HW_SPEC */
+struct hostcmd_cmd_get_hw_spec {
+ struct hostcmd_header cmd_hdr;
+ u8 version; /* version of the HW */
+ u8 host_if; /* host interface */
+ __le16 num_wcb; /* Max. number of WCB FW can handle */
+ __le16 num_mcast_addr; /* MaxNbr of MC addresses FW can handle */
+ u8 permanent_addr[ETH_ALEN]; /* MAC address programmed in HW */
+ __le16 region_code;
+ __le16 num_antenna; /* Number of antenna used */
+ __le32 fw_release_num; /* 4 byte of FW release number */
+ __le32 wcb_base0;
+ __le32 rxpd_wr_ptr;
+ __le32 rxpd_rd_ptr;
+ __le32 fw_awake_cookie;
+ __le32 wcb_base[SYSADPT_TOTAL_TX_QUEUES - 1];
+} __packed;
+
+/* HOSTCMD_CMD_SET_HW_SPEC */
+struct hostcmd_cmd_set_hw_spec {
+ struct hostcmd_header cmd_hdr;
+ /* HW revision */
+ u8 version;
+ /* Host interface */
+ u8 host_if;
+ /* Max. number of Multicast address FW can handle */
+ __le16 num_mcast_addr;
+ /* MAC address */
+ u8 permanent_addr[ETH_ALEN];
+ /* Region Code */
+ __le16 region_code;
+ /* 4 byte of FW release number, example 0x1234=1.2.3.4 */
+ __le32 fw_release_num;
+ /* Firmware awake cookie - used to ensure that the device
+ * is not in sleep mode
+ */
+ __le32 fw_awake_cookie;
+ /* Device capabilities (see above) */
+ __le32 device_caps;
+ /* Rx shared memory queue */
+ __le32 rxpd_wr_ptr;
+ /* Actual number of TX queues in WcbBase array */
+ __le32 num_tx_queues;
+ /* TX WCB Rings */
+ __le32 wcb_base[SYSADPT_NUM_OF_DESC_DATA];
+ /* Max AMSDU size (00 - AMSDU Disabled,
+ * 01 - 4K, 10 - 8K, 11 - not defined)
+ */
+ __le32 features;
+ __le32 tx_wcb_num_per_queue;
+ __le32 total_rx_wcb;
+} __packed;
+
+/* HOSTCMD_CMD_802_11_GET_STAT */
+struct hostcmd_cmd_802_11_get_stat {
+ struct hostcmd_header cmd_hdr;
+ __le32 tx_retry_successes;
+ __le32 tx_multiple_retry_successes;
+ __le32 tx_failures;
+ __le32 rts_successes;
+ __le32 rts_failures;
+ __le32 ack_failures;
+ __le32 rx_duplicate_frames;
+ __le32 rx_fcs_errors;
+ __le32 tx_watchdog_timeouts;
+ __le32 rx_overflows;
+ __le32 rx_frag_errors;
+ __le32 rx_mem_errors;
+ __le32 pointer_errors;
+ __le32 tx_underflows;
+ __le32 tx_done;
+ __le32 tx_done_buf_try_put;
+ __le32 tx_done_buf_put;
+ /* Put size of requested buffer in here */
+ __le32 wait_for_tx_buf;
+ __le32 tx_attempts;
+ __le32 tx_successes;
+ __le32 tx_fragments;
+ __le32 tx_multicasts;
+ __le32 rx_non_ctl_pkts;
+ __le32 rx_multicasts;
+ __le32 rx_undecryptable_frames;
+ __le32 rx_icv_errors;
+ __le32 rx_excluded_frames;
+ __le32 rx_weak_iv_count;
+ __le32 rx_unicasts;
+ __le32 rx_bytes;
+ __le32 rx_errors;
+ __le32 rx_rts_count;
+ __le32 tx_cts_count;
+} __packed;
+
+/* HOSTCMD_CMD_802_11_RADIO_CONTROL */
+struct hostcmd_cmd_802_11_radio_control {
+ struct hostcmd_header cmd_hdr;
+ __le16 action;
+ /* @bit0: 1/0,on/off, @bit1: 1/0, long/short @bit2: 1/0,auto/fix */
+ __le16 control;
+ __le16 radio_on;
+} __packed;
+
+/* HOSTCMD_CMD_MEM_ADDR_ACCESS */
+struct hostcmd_cmd_mem_addr_access {
+ struct hostcmd_header cmd_hdr;
+ __le32 address;
+ __le16 length;
+ __le16 reserved;
+ __le32 value[64];
+} __packed;
+
+/* HOSTCMD_CMD_802_11_TX_POWER */
+struct hostcmd_cmd_802_11_tx_power {
+ struct hostcmd_header cmd_hdr;
+ __le16 action;
+ __le16 band;
+ __le16 ch;
+ __le16 bw;
+ __le16 sub_ch;
+ __le16 power_level_list[SYSADPT_TX_POWER_LEVEL_TOTAL];
+} __packed;
+
+/* HOSTCMD_CMD_802_11_RF_ANTENNA */
+struct hostcmd_cmd_802_11_rf_antenna {
+ struct hostcmd_header cmd_hdr;
+ __le16 action;
+ __le16 antenna_mode; /* Number of antennas or 0xffff(diversity) */
+} __packed;
+
+/* HOSTCMD_CMD_BROADCAST_SSID_ENABLE */
+struct hostcmd_cmd_broadcast_ssid_enable {
+ struct hostcmd_header cmd_hdr;
+ __le32 enable;
+} __packed;
+
+/* HOSTCMD_CMD_SET_RF_CHANNEL */
+#define FREQ_BAND_MASK 0x0000003f
+#define CHNL_WIDTH_MASK 0x000007c0
+#define CHNL_WIDTH_SHIFT 6
+#define ACT_PRIMARY_MASK 0x00003800
+#define ACT_PRIMARY_SHIFT 11
+
+struct hostcmd_cmd_set_rf_channel {
+ struct hostcmd_header cmd_hdr;
+ __le16 action;
+ u8 curr_chnl;
+ __le32 chnl_flags;
+} __packed;
+
+/* HOSTCMD_CMD_SET_AID */
+struct hostcmd_cmd_set_aid {
+ struct hostcmd_header cmd_hdr;
+ __le16 aid;
+ u8 mac_addr[ETH_ALEN]; /* AP's Mac Address(BSSID) */
+ __le32 gprotect;
+ u8 ap_rates[SYSADPT_MAX_DATA_RATES_G];
+} __packed;
+
+/* HOSTCMD_CMD_SET_INFRA_MODE */
+struct hostcmd_cmd_set_infra_mode {
+ struct hostcmd_header cmd_hdr;
+} __packed;
+
+/* HOSTCMD_CMD_802_11_RTS_THSD */
+struct hostcmd_cmd_802_11_rts_thsd {
+ struct hostcmd_header cmd_hdr;
+ __le16 action;
+ __le16 threshold;
+} __packed;
+
+/* HOSTCMD_CMD_SET_EDCA_PARAMS */
+struct hostcmd_cmd_set_edca_params {
+ struct hostcmd_header cmd_hdr;
+ /* 0 = get all, 0x1 =set CWMin/Max, 0x2 = set TXOP , 0x4 =set AIFSN */
+ __le16 action;
+ __le16 txop; /* in unit of 32 us */
+ __le32 cw_max; /* 0~15 */
+ __le32 cw_min; /* 0~15 */
+ u8 aifsn;
+ u8 txq_num; /* Tx Queue number. */
+} __packed;
+
+/* HOSTCMD_CMD_802_11H_DETECT_RADAR */
+#define RADAR_TYPE_CODE_0 0
+#define RADAR_TYPE_CODE_53 53
+#define RADAR_TYPE_CODE_56 56
+#define RADAR_TYPE_CODE_ETSI 151
+
+struct hostcmd_cmd_802_11h_detect_radar {
+ struct hostcmd_header cmd_hdr;
+ __le16 action;
+ __le16 radar_type_code;
+ __le16 min_chirp_cnt;
+ __le16 chirp_time_intvl;
+ __le16 pw_filter;
+ __le16 min_num_radar;
+ __le16 pri_min_num;
+} __packed;
+
+/* HOSTCMD_CMD_SET_WMM_MODE */
+struct hostcmd_cmd_set_wmm_mode {
+ struct hostcmd_header cmd_hdr;
+ __le16 action; /* 0->unset, 1->set */
+} __packed;
+
+/* HOSTCMD_CMD_HT_GUARD_INTERVAL */
+struct hostcmd_cmd_ht_guard_interval {
+ struct hostcmd_header cmd_hdr;
+ __le32 action;
+ __le32 gi_type;
+} __packed;
+
+/* HOSTCMD_CMD_SET_FIXED_RATE */
+struct fix_rate_flag { /* lower rate after the retry count */
+ /* 0: legacy, 1: HT */
+ __le32 fix_rate_type;
+ /* 0: retry count is not valid, 1: use retry count specified */
+ __le32 retry_count_valid;
+} __packed;
+
+struct fix_rate_entry {
+ struct fix_rate_flag fix_rate_type_flags;
+ /* depending on the flags above, this can be either a legacy
+ * rate(not index) or an MCS code.
+ */
+ __le32 fixed_rate;
+ __le32 retry_count;
+} __packed;
+
+struct hostcmd_cmd_set_fixed_rate {
+ struct hostcmd_header cmd_hdr;
+ /* HOSTCMD_ACT_NOT_USE_FIXED_RATE 0x0002 */
+ __le32 action;
+ /* use fixed rate specified but firmware can drop to */
+ __le32 allow_rate_drop;
+ __le32 entry_count;
+ struct fix_rate_entry fixed_rate_table[4];
+ u8 multicast_rate;
+ u8 multi_rate_tx_type;
+ u8 management_rate;
+} __packed;
+
+/* HOSTCMD_CMD_SET_IES */
+struct hostcmd_cmd_set_ies {
+ struct hostcmd_header cmd_hdr;
+ __le16 action; /* 0->unset, 1->set */
+ __le16 ie_list_len_ht;
+ __le16 ie_list_len_vht;
+ __le16 ie_list_len_proprietary;
+ /*Buffer size same as Generic_Beacon*/
+ u8 ie_list_ht[148];
+ u8 ie_list_vht[24];
+ u8 ie_list_proprietary[112];
+} __packed;
+
+/* HOSTCMD_CMD_SET_LINKADAPT_CS_MODE */
+struct hostcmd_cmd_set_linkadapt_cs_mode {
+ struct hostcmd_header cmd_hdr;
+ __le16 action;
+ __le16 cs_mode;
+} __packed;
+
+/* HOSTCMD_CMD_SET_MAC_ADDR, HOSTCMD_CMD_DEL_MAC_ADDR */
+struct hostcmd_cmd_set_mac_addr {
+ struct hostcmd_header cmd_hdr;
+ __le16 mac_type;
+ u8 mac_addr[ETH_ALEN];
+} __packed;
+
+/* HOSTCMD_CMD_SET_RATE_ADAPT_MODE */
+struct hostcmd_cmd_set_rate_adapt_mode {
+ struct hostcmd_header cmd_hdr;
+ __le16 action;
+ __le16 rate_adapt_mode; /* 0:Indoor, 1:Outdoor */
+} __packed;
+
+/* HOSTCMD_CMD_GET_WATCHDOG_BITMAP */
+struct hostcmd_cmd_get_watchdog_bitmap {
+ struct hostcmd_header cmd_hdr;
+ u8 watchdog_bitmap; /* for SW/BA */
+} __packed;
+
+/* HOSTCMD_CMD_BSS_START */
+struct hostcmd_cmd_bss_start {
+ struct hostcmd_header cmd_hdr;
+ __le32 enable; /* FALSE: Disable or TRUE: Enable */
+} __packed;
+
+/* HOSTCMD_CMD_AP_BEACON */
+struct cf_params {
+ u8 elem_id;
+ u8 len;
+ u8 cfp_cnt;
+ u8 cfp_period;
+ __le16 cfp_max_duration;
+ __le16 cfp_duration_remaining;
+} __packed;
+
+struct ibss_params {
+ u8 elem_id;
+ u8 len;
+ __le16 atim_window;
+} __packed;
+
+union ss_params {
+ struct cf_params cf_param_set;
+ struct ibss_params ibss_param_set;
+} __packed;
+
+struct fh_params {
+ u8 elem_id;
+ u8 len;
+ __le16 dwell_time;
+ u8 hop_set;
+ u8 hop_pattern;
+ u8 hop_index;
+} __packed;
+
+struct ds_params {
+ u8 elem_id;
+ u8 len;
+ u8 current_chnl;
+} __packed;
+
+union phy_params {
+ struct fh_params fh_param_set;
+ struct ds_params ds_param_set;
+} __packed;
+
+struct rsn_ie {
+ u8 elem_id;
+ u8 len;
+ u8 oui_type[4]; /* 00:50:f2:01 */
+ u8 ver[2];
+ u8 grp_key_cipher[4];
+ u8 pws_key_cnt[2];
+ u8 pws_key_cipher_list[4];
+ u8 auth_key_cnt[2];
+ u8 auth_key_list[4];
+} __packed;
+
+struct rsn48_ie {
+ u8 elem_id;
+ u8 len;
+ u8 ver[2];
+ u8 grp_key_cipher[4];
+ u8 pws_key_cnt[2];
+ u8 pws_key_cipher_list[4];
+ u8 auth_key_cnt[2];
+ u8 auth_key_list[4];
+ u8 rsn_cap[2];
+ u8 pmk_id_cnt[2];
+ u8 pmk_id_list[16]; /* Should modify to 16 * S */
+ u8 reserved[8];
+} __packed;
+
+struct ac_param_rcd {
+ u8 aci_aifsn;
+ u8 ecw_min_max;
+ __le16 txop_lim;
+} __packed;
+
+struct wmm_param_elem {
+ u8 elem_id;
+ u8 len;
+ u8 oui[3];
+ u8 type;
+ u8 sub_type;
+ u8 version;
+ u8 rsvd;
+ struct ac_param_rcd ac_be;
+ struct ac_param_rcd ac_bk;
+ struct ac_param_rcd ac_vi;
+ struct ac_param_rcd ac_vo;
+} __packed;
+
+struct channel_info {
+ u8 first_channel_num;
+ u8 num_channels;
+ u8 max_tx_pwr_level;
+} __packed;
+
+struct country {
+ u8 elem_id;
+ u8 len;
+ u8 country_str[3];
+ struct channel_info channel_info[40];
+} __packed;
+
+struct start_cmd {
+ u8 sta_mac_addr[ETH_ALEN];
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 bss_type;
+ __le16 bcn_period;
+ u8 dtim_period;
+ union ss_params ss_param_set;
+ union phy_params phy_param_set;
+ __le16 probe_delay;
+ __le16 cap_info;
+ u8 b_rate_set[SYSADPT_MAX_DATA_RATES_G];
+ u8 op_rate_set[SYSADPT_MAX_DATA_RATES_G];
+ struct rsn_ie rsn_ie;
+ struct rsn48_ie rsn48_ie;
+ struct wmm_param_elem wmm_param;
+ struct country country;
+ __le32 ap_rf_type; /* 0->B, 1->G, 2->Mixed, 3->A, 4->11J */
+} __packed;
+
+struct hostcmd_cmd_ap_beacon {
+ struct hostcmd_header cmd_hdr;
+ struct start_cmd start_cmd;
+} __packed;
+
+/* HOSTCMD_CMD_SET_NEW_STN */
+struct add_ht_info {
+ u8 control_chnl;
+ u8 add_chnl;
+ __le16 op_mode;
+ __le16 stbc;
+} __packed;
+
+struct peer_info {
+ __le32 legacy_rate_bitmap;
+ u8 ht_rates[4];
+ __le16 cap_info;
+ __le16 ht_cap_info;
+ u8 mac_ht_param_info;
+ u8 mrvl_sta;
+ struct add_ht_info add_ht_info;
+ __le32 tx_bf_capabilities; /* EXBF_SUPPORT */
+ __le32 vht_max_rx_mcs;
+ __le32 vht_cap;
+ /* 0:20Mhz, 1:40Mhz, 2:80Mhz, 3:160 or 80+80Mhz */
+ u8 vht_rx_channel_width;
+} __packed;
+
+struct hostcmd_cmd_set_new_stn {
+ struct hostcmd_header cmd_hdr;
+ __le16 aid;
+ u8 mac_addr[ETH_ALEN];
+ __le16 stn_id;
+ __le16 action;
+ __le16 reserved;
+ struct peer_info peer_info;
+ /* UAPSD_SUPPORT */
+ u8 qos_info;
+ u8 is_qos_sta;
+ __le32 fw_sta_ptr;
+} __packed;
+
+/* HOSTCMD_CMD_SET_APMODE */
+struct hostcmd_cmd_set_apmode {
+ struct hostcmd_header cmd_hdr;
+ u8 apmode;
+} __packed;
+
+/* HOSTCMD_CMD_SET_SWITCH_CHANNEL */
+struct hostcmd_cmd_set_switch_channel {
+ struct hostcmd_header cmd_hdr;
+ __le32 next_11h_chnl;
+ __le32 mode;
+ __le32 init_count;
+ __le32 chnl_flags;
+ __le32 next_ht_extchnl_offset;
+ __le32 dfs_test_mode;
+} __packed;
+
+/* HOSTCMD_CMD_UPDATE_ENCRYPTION */
+struct hostcmd_cmd_update_encryption {
+ struct hostcmd_header cmd_hdr;
+ /* Action type - see encr_action_type */
+ __le32 action_type; /* encr_action_type */
+ /* size of the data buffer attached. */
+ __le32 data_length;
+ u8 mac_addr[ETH_ALEN];
+ u8 action_data[1];
+} __packed;
+
+struct wep_type_key {
+ /* WEP key material (max 128bit) */
+ u8 key_material[MAX_ENCR_KEY_LENGTH];
+} __packed;
+
+struct encr_tkip_seqcnt {
+ __le16 low;
+ __le32 high;
+} __packed;
+
+struct tkip_type_key {
+ /* TKIP Key material. Key type (group or pairwise key) is
+ * determined by flags
+ */
+ /* in KEY_PARAM_SET structure. */
+ u8 key_material[MAX_ENCR_KEY_LENGTH];
+ /* MIC keys */
+ u8 tkip_tx_mic_key[MIC_KEY_LENGTH];
+ u8 tkip_rx_mic_key[MIC_KEY_LENGTH];
+ struct encr_tkip_seqcnt tkip_rsc;
+ struct encr_tkip_seqcnt tkip_tsc;
+} __packed;
+
+struct aes_type_key {
+ /* AES Key material */
+ u8 key_material[MAX_ENCR_KEY_LENGTH];
+} __packed;
+
+union mwl_key_type {
+ struct wep_type_key wep_key;
+ struct tkip_type_key tkip_key;
+ struct aes_type_key aes_key;
+} __packed;
+
+struct key_param_set {
+ /* Total length of this structure (Key is variable size array) */
+ __le16 length;
+ /* Key type - WEP, TKIP or AES-CCMP. */
+ /* See definitions above */
+ __le16 key_type_id;
+ /* key flags (ENCR_KEY_FLAG_XXX_ */
+ __le32 key_info;
+ /* For WEP only - actual key index */
+ __le32 key_index;
+ /* Size of the key */
+ __le16 key_len;
+ /* Key material (variable size array) */
+ union mwl_key_type key;
+ u8 mac_addr[ETH_ALEN];
+} __packed;
+
+struct hostcmd_cmd_set_key {
+ struct hostcmd_header cmd_hdr;
+ /* Action type - see encr_action_type */
+ __le32 action_type; /* encr_action_type */
+ /* size of the data buffer attached. */
+ __le32 data_length;
+ /* data buffer - maps to one KEY_PARAM_SET structure */
+ struct key_param_set key_param;
+} __packed;
+
+/* HOSTCMD_CMD_BASTREAM */
+#define BA_TYPE_MASK 0x00000001
+#define BA_DIRECTION_MASK 0x00000006
+#define BA_DIRECTION_SHIFT 1
+
+struct ba_context {
+ __le32 context;
+} __packed;
+
+/* parameters for block ack creation */
+struct create_ba_params {
+ /* BA Creation flags - see above */
+ __le32 flags;
+ /* idle threshold */
+ __le32 idle_thrs;
+ /* block ack transmit threshold (after how many pkts should we
+ * send BAR?)
+ */
+ __le32 bar_thrs;
+ /* receiver window size */
+ __le32 window_size;
+ /* MAC Address of the BA partner */
+ u8 peer_mac_addr[ETH_ALEN];
+ /* Dialog Token */
+ u8 dialog_token;
+ /* TID for the traffic stream in this BA */
+ u8 tid;
+ /* shared memory queue ID (not sure if this is required) */
+ u8 queue_id;
+ u8 param_info;
+ /* returned by firmware - firmware context pointer. */
+ /* this context pointer will be passed to firmware for all
+ * future commands.
+ */
+ struct ba_context fw_ba_context;
+ u8 reset_seq_no; /** 0 or 1**/
+ __le16 current_seq;
+ /* This is for virtual station in Sta proxy mode for V6FW */
+ u8 sta_src_mac_addr[ETH_ALEN];
+} __packed;
+
+/* new transmit sequence number information */
+struct ba_update_seq_num {
+ /* BA flags - see above */
+ __le32 flags;
+ /* returned by firmware in the create ba stream response */
+ struct ba_context fw_ba_context;
+ /* new sequence number for this block ack stream */
+ __le16 ba_seq_num;
+} __packed;
+
+struct ba_stream_context {
+ /* BA Stream flags */
+ __le32 flags;
+ /* returned by firmware in the create ba stream response */
+ struct ba_context fw_ba_context;
+} __packed;
+
+union ba_info {
+ /* information required to create BA Stream... */
+ struct create_ba_params create_params;
+ /* update starting/new sequence number etc. */
+ struct ba_update_seq_num updt_seq_num;
+ /* destroy an existing stream... */
+ struct ba_stream_context destroy_params;
+ /* destroy an existing stream... */
+ struct ba_stream_context flush_params;
+} __packed;
+
+struct hostcmd_cmd_bastream {
+ struct hostcmd_header cmd_hdr;
+ __le32 action_type;
+ union ba_info ba_info;
+} __packed;
+
+/* HOSTCMD_CMD_SET_SPECTRUM_MGMT */
+struct hostcmd_cmd_set_spectrum_mgmt {
+ struct hostcmd_header cmd_hdr;
+ __le32 spectrum_mgmt;
+} __packed;
+
+/* HOSTCMD_CMD_SET_POWER_CONSTRAINT */
+struct hostcmd_cmd_set_power_constraint {
+ struct hostcmd_header cmd_hdr;
+ __le32 power_constraint;
+} __packed;
+
+/* HOSTCMD_CMD_SET_COUNTRY_CODE */
+struct domain_chnl_entry {
+ u8 first_chnl_num;
+ u8 chnl_num;
+ u8 max_transmit_pw;
+} __packed;
+
+struct domain_country_info {
+ u8 country_string[3];
+ u8 g_chnl_len;
+ struct domain_chnl_entry domain_entry_g[1];
+ u8 a_chnl_len;
+ struct domain_chnl_entry domain_entry_a[20];
+} __packed;
+
+struct hostcmd_cmd_set_country_code {
+ struct hostcmd_header cmd_hdr;
+ __le32 action ; /* 0 -> unset, 1 ->set */
+ struct domain_country_info domain_info;
+} __packed;
+
+/* HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL */
+struct hostcmd_cmd_set_optimization_level {
+ struct hostcmd_header cmd_hdr;
+ u8 opt_level;
+} __packed;
+
+/* HOSTCMD_CMD_SET_WSC_IE */
+struct hostcmd_cmd_set_wsc_ie {
+ struct hostcmd_header cmd_hdr;
+ __le16 ie_type; /* 0 -- beacon. or 1 -- probe response. */
+ __le16 len;
+ u8 data[WSC_IE_MAX_LENGTH];
+} __packed;
+
+/* HOSTCMD_CMD_DWDS_ENABLE */
+struct hostcmd_cmd_dwds_enable {
+ struct hostcmd_header cmd_hdr;
+ __le32 enable; /* 0 -- Disable. or 1 -- Enable. */
+} __packed;
+
+/* HOSTCMD_CMD_FW_FLUSH_TIMER */
+struct hostcmd_cmd_fw_flush_timer {
+ struct hostcmd_header cmd_hdr;
+ /* 0 -- Disable. > 0 -- holds time value in usecs. */
+ __le32 value;
+} __packed;
+
+/* HOSTCMD_CMD_SET_CDD */
+struct hostcmd_cmd_set_cdd {
+ struct hostcmd_header cmd_hdr;
+ __le32 enable;
+} __packed;
+
+/* HOSTCMD_CMD_GET_TEMP */
+struct hostcmd_cmd_get_temp {
+ struct hostcmd_header cmd_hdr;
+ __le32 celcius;
+ __le32 raw_data;
+} __packed;
+
+/* HOSTCMD_CMD_GET_FW_REGION_CODE */
+struct hostcmd_cmd_get_fw_region_code {
+ struct hostcmd_header cmd_hdr;
+ __le32 status; /* 0 = Found, 1 = Error */
+ __le32 fw_region_code;
+} __packed;
+
+/* HOSTCMD_CMD_GET_DEVICE_PWR_TBL */
+#define HAL_TRPC_ID_MAX 16
+
+struct channel_power_tbl {
+ u8 channel;
+ u8 tx_pwr[HAL_TRPC_ID_MAX];
+ u8 dfs_capable;
+ u8 ax_ant;
+ u8 cdd;
+} __packed;
+
+struct hostcmd_cmd_get_device_pwr_tbl {
+ struct hostcmd_header cmd_hdr;
+ __le16 status; /* 0 = Found, 1 = Error */
+ u8 region_code;
+ u8 number_of_channels;
+ __le32 current_channel_index;
+ /* Only for 1 channel, so, 1 channel at a time */
+ struct channel_power_tbl channel_pwr_tbl;
+} __packed;
+
+/* HOSTCMD_CMD_QUIET_MODE */
+struct hostcmd_cmd_quiet_mode {
+ struct hostcmd_header cmd_hdr;
+ __le16 action;
+ __le32 enable;
+ __le32 period;
+ __le32 duration;
+ __le32 next_offset;
+} __packed;
+
+#endif /* _HOSTCMD_H_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/isr.c b/drivers/net/wireless/marvell/mwlwifi/isr.c
new file mode 100644
index 0000000..881cea8
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/isr.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file implements interrupt related functions. */
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwcmd.h"
+#include "isr.h"
+
+#define INVALID_WATCHDOG 0xAA
+
+irqreturn_t mwl_isr(int irq, void *dev_id)
+{
+ struct ieee80211_hw *hw = dev_id;
+ struct mwl_priv *priv = hw->priv;
+ void __iomem *int_status_mask;
+ u32 int_status;
+ u32 status;
+
+ int_status_mask = priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK;
+
+ int_status = readl(priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE);
+
+ if (int_status == 0x00000000)
+ return IRQ_NONE;
+
+ if (int_status == 0xffffffff) {
+ wiphy_warn(hw->wiphy, "card unplugged?\n");
+ } else {
+ writel(~int_status,
+ priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE);
+
+ if (int_status & MACREG_A2HRIC_BIT_TX_DONE) {
+ if (!priv->is_tx_done_schedule) {
+ status = readl(int_status_mask);
+ writel((status & ~MACREG_A2HRIC_BIT_TX_DONE),
+ int_status_mask);
+ tasklet_schedule(&priv->tx_done_task);
+ priv->is_tx_done_schedule = true;
+ }
+ }
+
+ if (int_status & MACREG_A2HRIC_BIT_RX_RDY) {
+ if (!priv->is_rx_schedule) {
+ status = readl(int_status_mask);
+ writel((status & ~MACREG_A2HRIC_BIT_RX_RDY),
+ int_status_mask);
+ tasklet_schedule(&priv->rx_task);
+ priv->is_rx_schedule = true;
+ }
+ }
+
+ if (int_status & MACREG_A2HRIC_BIT_RADAR_DETECT) {
+ wiphy_info(hw->wiphy, "radar detected by firmware\n");
+ ieee80211_radar_detected(hw);
+ }
+
+ if (int_status & MACREG_A2HRIC_BIT_QUE_EMPTY) {
+ if (!priv->is_qe_schedule) {
+ if (time_after(jiffies,
+ (priv->qe_trigger_time + 1))) {
+ status = readl(int_status_mask);
+ writel((status &
+ ~MACREG_A2HRIC_BIT_QUE_EMPTY),
+ int_status_mask);
+ tasklet_schedule(&priv->qe_task);
+ priv->qe_trigger_num++;
+ priv->is_qe_schedule = true;
+ priv->qe_trigger_time = jiffies;
+ }
+ }
+ }
+
+ if (int_status & MACREG_A2HRIC_BIT_CHAN_SWITCH)
+ ieee80211_queue_work(hw, &priv->chnl_switch_handle);
+
+ if (int_status & MACREG_A2HRIC_BA_WATCHDOG)
+ ieee80211_queue_work(hw, &priv->watchdog_ba_handle);
+ }
+
+ return IRQ_HANDLED;
+}
+
+void mwl_chnl_switch_event(struct work_struct *work)
+{
+ struct mwl_priv *priv =
+ container_of(work, struct mwl_priv, chnl_switch_handle);
+ struct mwl_vif *mwl_vif;
+ struct ieee80211_vif *vif;
+
+ if (!priv->csa_active) {
+ wiphy_err(priv->hw->wiphy,
+ "csa is not active (got channel switch event)\n");
+ return;
+ }
+
+ spin_lock_bh(&priv->vif_lock);
+ list_for_each_entry(mwl_vif, &priv->vif_list, list) {
+ vif = container_of((char *)mwl_vif, struct ieee80211_vif,
+ drv_priv[0]);
+
+ if (vif->csa_active)
+ ieee80211_csa_finish(vif);
+ }
+ spin_unlock_bh(&priv->vif_lock);
+
+ wiphy_info(priv->hw->wiphy, "channel switch is done\n");
+
+ priv->csa_active = false;
+}
+
+void mwl_watchdog_ba_events(struct work_struct *work)
+{
+ int rc;
+ u8 bitmap = 0, stream_index;
+ struct mwl_ampdu_stream *streams;
+ struct mwl_priv *priv =
+ container_of(work, struct mwl_priv, watchdog_ba_handle);
+
+ rc = mwl_fwcmd_get_watchdog_bitmap(priv->hw, &bitmap);
+
+ if (rc)
+ return;
+
+ spin_lock_bh(&priv->stream_lock);
+
+ /* the bitmap is the hw queue number. Map it to the ampdu queue. */
+ if (bitmap != INVALID_WATCHDOG) {
+ if (bitmap == SYSADPT_TX_AMPDU_QUEUES)
+ stream_index = 0;
+ else if (bitmap > SYSADPT_TX_AMPDU_QUEUES)
+ stream_index = bitmap - SYSADPT_TX_AMPDU_QUEUES;
+ else
+ stream_index = bitmap + 3; /** queue 0 is stream 3*/
+
+ if (bitmap != 0xFF) {
+ /* Check if the stream is in use before disabling it */
+ streams = &priv->ampdu[stream_index];
+
+ if (streams->state == AMPDU_STREAM_ACTIVE)
+ ieee80211_stop_tx_ba_session(streams->sta,
+ streams->tid);
+ } else {
+ for (stream_index = 0;
+ stream_index < SYSADPT_TX_AMPDU_QUEUES;
+ stream_index++) {
+ streams = &priv->ampdu[stream_index];
+
+ if (streams->state != AMPDU_STREAM_ACTIVE)
+ continue;
+
+ ieee80211_stop_tx_ba_session(streams->sta,
+ streams->tid);
+ }
+ }
+ }
+
+ spin_unlock_bh(&priv->stream_lock);
+}
diff --git a/drivers/net/wireless/marvell/mwlwifi/isr.h b/drivers/net/wireless/marvell/mwlwifi/isr.h
new file mode 100644
index 0000000..adcc67f
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/isr.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file defines interrupt related functions. */
+
+#ifndef _ISR_H_
+#define _ISR_H_
+
+#include <linux/interrupt.h>
+
+irqreturn_t mwl_isr(int irq, void *dev_id);
+void mwl_chnl_switch_event(struct work_struct *work);
+void mwl_watchdog_ba_events(struct work_struct *work);
+
+#endif /* _ISR_H_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/mac80211.c b/drivers/net/wireless/marvell/mwlwifi/mac80211.c
new file mode 100644
index 0000000..a600eda
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/mac80211.c
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file implements mac80211 related functions. */
+
+#include <linux/etherdevice.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwcmd.h"
+#include "tx.h"
+
+#define MWL_DRV_NAME KBUILD_MODNAME
+
+#define MAX_AMPDU_ATTEMPTS 5
+
+static const struct ieee80211_rate mwl_rates_24[] = {
+ { .bitrate = 10, .hw_value = 2, },
+ { .bitrate = 20, .hw_value = 4, },
+ { .bitrate = 55, .hw_value = 11, },
+ { .bitrate = 110, .hw_value = 22, },
+ { .bitrate = 220, .hw_value = 44, },
+ { .bitrate = 60, .hw_value = 12, },
+ { .bitrate = 90, .hw_value = 18, },
+ { .bitrate = 120, .hw_value = 24, },
+ { .bitrate = 180, .hw_value = 36, },
+ { .bitrate = 240, .hw_value = 48, },
+ { .bitrate = 360, .hw_value = 72, },
+ { .bitrate = 480, .hw_value = 96, },
+ { .bitrate = 540, .hw_value = 108, },
+};
+
+static const struct ieee80211_rate mwl_rates_50[] = {
+ { .bitrate = 60, .hw_value = 12, },
+ { .bitrate = 90, .hw_value = 18, },
+ { .bitrate = 120, .hw_value = 24, },
+ { .bitrate = 180, .hw_value = 36, },
+ { .bitrate = 240, .hw_value = 48, },
+ { .bitrate = 360, .hw_value = 72, },
+ { .bitrate = 480, .hw_value = 96, },
+ { .bitrate = 540, .hw_value = 108, },
+};
+
+static void mwl_mac80211_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct mwl_priv *priv = hw->priv;
+
+ if (!priv->radio_on) {
+ wiphy_warn(hw->wiphy,
+ "dropped TX frame since radio is disabled\n");
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ mwl_tx_xmit(hw, control, skb);
+}
+
+static int mwl_mac80211_start(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+ int rc;
+
+ /* Enable TX and RX tasklets. */
+ tasklet_enable(&priv->tx_task);
+ tasklet_enable(&priv->tx_done_task);
+ tasklet_enable(&priv->rx_task);
+ tasklet_enable(&priv->qe_task);
+
+ /* Enable interrupts */
+ mwl_fwcmd_int_enable(hw);
+
+ rc = mwl_fwcmd_radio_enable(hw);
+ if (rc)
+ goto fwcmd_fail;
+ rc = mwl_fwcmd_set_rate_adapt_mode(hw, 0);
+ if (rc)
+ goto fwcmd_fail;
+ rc = mwl_fwcmd_set_wmm_mode(hw, true);
+ if (rc)
+ goto fwcmd_fail;
+ rc = mwl_fwcmd_ht_guard_interval(hw, GUARD_INTERVAL_AUTO);
+ if (rc)
+ goto fwcmd_fail;
+ rc = mwl_fwcmd_set_dwds_stamode(hw, true);
+ if (rc)
+ goto fwcmd_fail;
+ rc = mwl_fwcmd_set_fw_flush_timer(hw, SYSADPT_AMSDU_FLUSH_TIME);
+ if (rc)
+ goto fwcmd_fail;
+ rc = mwl_fwcmd_set_optimization_level(hw, 1);
+ if (rc)
+ goto fwcmd_fail;
+
+ ieee80211_wake_queues(hw);
+ return 0;
+
+fwcmd_fail:
+ mwl_fwcmd_int_disable(hw);
+ tasklet_disable(&priv->tx_task);
+ tasklet_disable(&priv->tx_done_task);
+ tasklet_disable(&priv->rx_task);
+ tasklet_disable(&priv->qe_task);
+
+ return rc;
+}
+
+static void mwl_mac80211_stop(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+
+ mwl_fwcmd_radio_disable(hw);
+
+ ieee80211_stop_queues(hw);
+
+ /* Disable interrupts */
+ mwl_fwcmd_int_disable(hw);
+
+ /* Disable TX reclaim and RX tasklets. */
+ tasklet_disable(&priv->tx_task);
+ tasklet_disable(&priv->tx_done_task);
+ tasklet_disable(&priv->rx_task);
+ tasklet_disable(&priv->qe_task);
+
+ /* Return all skbs to mac80211 */
+ mwl_tx_done((unsigned long)hw);
+}
+
+static int mwl_mac80211_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ u32 macids_supported;
+ int macid;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ macids_supported = priv->ap_macids_supported;
+ break;
+ case NL80211_IFTYPE_STATION:
+ macids_supported = priv->sta_macids_supported;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ macid = ffs(macids_supported & ~priv->macids_used);
+
+ if (!macid) {
+ wiphy_warn(hw->wiphy, "no macid can be allocated\n");
+ return -EBUSY;
+ }
+ macid--;
+
+ /* Setup driver private area. */
+ mwl_vif = mwl_dev_get_vif(vif);
+ memset(mwl_vif, 0, sizeof(*mwl_vif));
+ mwl_vif->macid = macid;
+ mwl_vif->seqno = 0;
+ mwl_vif->is_hw_crypto_enabled = false;
+ mwl_vif->beacon_info.valid = false;
+ mwl_vif->iv16 = 1;
+ mwl_vif->iv32 = 0;
+ mwl_vif->keyidx = 0;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ ether_addr_copy(mwl_vif->bssid, vif->addr);
+ mwl_fwcmd_set_new_stn_add_self(hw, vif);
+ break;
+ case NL80211_IFTYPE_STATION:
+ ether_addr_copy(mwl_vif->sta_mac, vif->addr);
+ mwl_fwcmd_bss_start(hw, vif, true);
+ mwl_fwcmd_set_infra_mode(hw, vif);
+ mwl_fwcmd_set_mac_addr_client(hw, vif, vif->addr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ priv->macids_used |= 1 << mwl_vif->macid;
+ spin_lock_bh(&priv->vif_lock);
+ list_add_tail(&mwl_vif->list, &priv->vif_list);
+ spin_unlock_bh(&priv->vif_lock);
+
+ return 0;
+}
+
+static void mwl_mac80211_remove_vif(struct mwl_priv *priv,
+ struct ieee80211_vif *vif)
+{
+ struct mwl_vif *mwl_vif = mwl_dev_get_vif(vif);
+
+ if (!priv->macids_used)
+ return;
+
+ mwl_tx_del_pkts_via_vif(priv->hw, vif);
+
+ priv->macids_used &= ~(1 << mwl_vif->macid);
+ spin_lock_bh(&priv->vif_lock);
+ list_del(&mwl_vif->list);
+ spin_unlock_bh(&priv->vif_lock);
+}
+
+static void mwl_mac80211_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct mwl_priv *priv = hw->priv;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ mwl_fwcmd_set_new_stn_del(hw, vif, vif->addr);
+ break;
+ case NL80211_IFTYPE_STATION:
+ mwl_fwcmd_remove_mac_addr(hw, vif, vif->addr);
+ break;
+ default:
+ break;
+ }
+
+ mwl_mac80211_remove_vif(priv, vif);
+}
+
+static int mwl_mac80211_config(struct ieee80211_hw *hw,
+ u32 changed)
+{
+ struct ieee80211_conf *conf = &hw->conf;
+ int rc;
+
+ wiphy_debug(hw->wiphy, "change: 0x%x\n", changed);
+
+ if (conf->flags & IEEE80211_CONF_IDLE)
+ rc = mwl_fwcmd_radio_disable(hw);
+ else
+ rc = mwl_fwcmd_radio_enable(hw);
+
+ if (rc)
+ goto out;
+
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ int rate = 0;
+
+ if (conf->chandef.chan->band == NL80211_BAND_2GHZ) {
+ mwl_fwcmd_set_apmode(hw, AP_MODE_2_4GHZ_11AC_MIXED);
+ mwl_fwcmd_set_linkadapt_cs_mode(hw,
+ LINK_CS_STATE_CONSERV);
+ rate = mwl_rates_24[0].hw_value;
+ } else if (conf->chandef.chan->band == NL80211_BAND_5GHZ) {
+ mwl_fwcmd_set_apmode(hw, AP_MODE_11AC);
+ mwl_fwcmd_set_linkadapt_cs_mode(hw,
+ LINK_CS_STATE_AUTO);
+ rate = mwl_rates_50[0].hw_value;
+
+ if (conf->radar_enabled)
+ mwl_fwcmd_set_radar_detect(hw, MONITOR_START);
+ else
+ mwl_fwcmd_set_radar_detect(hw,
+ STOP_DETECT_RADAR);
+ }
+
+ rc = mwl_fwcmd_set_rf_channel(hw, conf);
+ if (rc)
+ goto out;
+ rc = mwl_fwcmd_use_fixed_rate(hw, rate, rate);
+ if (rc)
+ goto out;
+ rc = mwl_fwcmd_max_tx_power(hw, conf, 0);
+ if (rc)
+ goto out;
+ rc = mwl_fwcmd_tx_power(hw, conf, 0);
+ if (rc)
+ goto out;
+ rc = mwl_fwcmd_set_cdd(hw);
+ }
+
+out:
+
+ return rc;
+}
+
+static void mwl_mac80211_bss_info_changed_sta(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed)
+{
+ if (changed & BSS_CHANGED_ERP_PREAMBLE)
+ mwl_fwcmd_set_radio_preamble(hw,
+ vif->bss_conf.use_short_preamble);
+
+ if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc)
+ mwl_fwcmd_set_aid(hw, vif, (u8 *)vif->bss_conf.bssid,
+ vif->bss_conf.aid);
+}
+
+static void mwl_mac80211_bss_info_changed_ap(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed)
+{
+ if (changed & BSS_CHANGED_ERP_PREAMBLE)
+ mwl_fwcmd_set_radio_preamble(hw,
+ vif->bss_conf.use_short_preamble);
+
+ if (changed & BSS_CHANGED_BASIC_RATES) {
+ int idx;
+ int rate;
+
+ /* Use lowest supported basic rate for multicasts
+ * and management frames (such as probe responses --
+ * beacons will always go out at 1 Mb/s).
+ */
+ idx = ffs(vif->bss_conf.basic_rates);
+ if (idx)
+ idx--;
+
+ if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
+ rate = mwl_rates_24[idx].hw_value;
+ else
+ rate = mwl_rates_50[idx].hw_value;
+
+ mwl_fwcmd_use_fixed_rate(hw, rate, rate);
+ }
+
+ if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) {
+ struct sk_buff *skb;
+
+ if ((info->ssid[0] != '\0') &&
+ (info->ssid_len != 0) &&
+ (!info->hidden_ssid))
+ mwl_fwcmd_broadcast_ssid_enable(hw, vif, true);
+ else
+ mwl_fwcmd_broadcast_ssid_enable(hw, vif, false);
+
+ skb = ieee80211_beacon_get(hw, vif);
+
+ if (skb) {
+ mwl_fwcmd_set_beacon(hw, vif, skb->data, skb->len);
+ dev_kfree_skb_any(skb);
+ }
+ }
+
+ if (changed & BSS_CHANGED_BEACON_ENABLED)
+ mwl_fwcmd_bss_start(hw, vif, info->enable_beacon);
+}
+
+static void mwl_mac80211_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed)
+{
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ mwl_mac80211_bss_info_changed_ap(hw, vif, info, changed);
+ break;
+ case NL80211_IFTYPE_STATION:
+ mwl_mac80211_bss_info_changed_sta(hw, vif, info, changed);
+ break;
+ default:
+ break;
+ }
+}
+
+static void mwl_mac80211_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast)
+{
+ /* AP firmware doesn't allow fine-grained control over
+ * the receive filter.
+ */
+ *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
+}
+
+static int mwl_mac80211_set_key(struct ieee80211_hw *hw,
+ enum set_key_cmd cmd_param,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct mwl_vif *mwl_vif;
+ int rc = 0;
+ u8 encr_type;
+ u8 *addr;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+
+ if (!sta) {
+ addr = vif->addr;
+ } else {
+ addr = sta->addr;
+ if (vif->type == NL80211_IFTYPE_STATION)
+ ether_addr_copy(mwl_vif->bssid, addr);
+ }
+
+ if (cmd_param == SET_KEY) {
+ rc = mwl_fwcmd_encryption_set_key(hw, vif, addr, key);
+
+ if (rc)
+ goto out;
+
+ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40) ||
+ (key->cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ encr_type = ENCR_TYPE_WEP;
+ } else if (key->cipher == WLAN_CIPHER_SUITE_CCMP) {
+ encr_type = ENCR_TYPE_AES;
+ if ((key->flags & IEEE80211_KEY_FLAG_PAIRWISE) == 0) {
+ if (vif->type != NL80211_IFTYPE_STATION)
+ mwl_vif->keyidx = key->keyidx;
+ }
+ } else if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ encr_type = ENCR_TYPE_TKIP;
+ } else {
+ encr_type = ENCR_TYPE_DISABLE;
+ }
+
+ rc = mwl_fwcmd_update_encryption_enable(hw, vif, addr,
+ encr_type);
+ if (rc)
+ goto out;
+
+ mwl_vif->is_hw_crypto_enabled = true;
+ } else {
+ rc = mwl_fwcmd_encryption_remove_key(hw, vif, addr, key);
+ if (rc)
+ goto out;
+ }
+
+out:
+
+ return rc;
+}
+
+static int mwl_mac80211_set_rts_threshold(struct ieee80211_hw *hw,
+ u32 value)
+{
+ return mwl_fwcmd_set_rts_threshold(hw, value);
+}
+
+static int mwl_mac80211_sta_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+ struct mwl_sta *sta_info;
+ struct ieee80211_key_conf *key;
+ int rc;
+ int i;
+
+ mwl_vif = mwl_dev_get_vif(vif);
+ sta_info = mwl_dev_get_sta(sta);
+
+ memset(sta_info, 0, sizeof(*sta_info));
+
+ if (sta->ht_cap.ht_supported) {
+ sta_info->is_ampdu_allowed = true;
+ sta_info->is_amsdu_allowed = false;
+ if (sta->ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU)
+ sta_info->amsdu_ctrl.cap = MWL_AMSDU_SIZE_8K;
+ else
+ sta_info->amsdu_ctrl.cap = MWL_AMSDU_SIZE_4K;
+ if ((sta->tdls) && (!sta->wme))
+ sta->wme = true;
+ }
+ sta_info->iv16 = 1;
+ sta_info->iv32 = 0;
+ spin_lock_init(&sta_info->amsdu_lock);
+ spin_lock_bh(&priv->sta_lock);
+ list_add_tail(&sta_info->list, &priv->sta_list);
+ spin_unlock_bh(&priv->sta_lock);
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ mwl_fwcmd_set_new_stn_del(hw, vif, sta->addr);
+
+ rc = mwl_fwcmd_set_new_stn_add(hw, vif, sta);
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ key = (struct ieee80211_key_conf *)mwl_vif->wep_key_conf[i].key;
+
+ if (mwl_vif->wep_key_conf[i].enabled)
+ mwl_mac80211_set_key(hw, SET_KEY, vif, sta, key);
+ }
+
+ return rc;
+}
+
+static int mwl_mac80211_sta_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct mwl_priv *priv = hw->priv;
+ int rc;
+ struct mwl_sta *sta_info = mwl_dev_get_sta(sta);
+
+ mwl_tx_del_sta_amsdu_pkts(sta);
+
+ spin_lock_bh(&priv->stream_lock);
+ mwl_fwcmd_del_sta_streams(hw, sta);
+ spin_unlock_bh(&priv->stream_lock);
+
+ mwl_tx_del_pkts_via_sta(hw, sta);
+
+ rc = mwl_fwcmd_set_new_stn_del(hw, vif, sta->addr);
+
+ spin_lock_bh(&priv->sta_lock);
+ list_del(&sta_info->list);
+ spin_unlock_bh(&priv->sta_lock);
+
+ return rc;
+}
+
+static int mwl_mac80211_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct mwl_priv *priv = hw->priv;
+ int rc = 0;
+
+ if (WARN_ON(queue > SYSADPT_TX_WMM_QUEUES - 1))
+ return -EINVAL;
+
+ memcpy(&priv->wmm_params[queue], params, sizeof(*params));
+
+ if (!priv->wmm_enabled) {
+ rc = mwl_fwcmd_set_wmm_mode(hw, true);
+ priv->wmm_enabled = true;
+ }
+
+ if (!rc) {
+ int q = SYSADPT_TX_WMM_QUEUES - 1 - queue;
+
+ rc = mwl_fwcmd_set_edca_params(hw, q,
+ params->cw_min, params->cw_max,
+ params->aifs, params->txop);
+ }
+
+ return rc;
+}
+
+static int mwl_mac80211_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ return mwl_fwcmd_get_stat(hw, stats);
+}
+
+static int mwl_mac80211_get_survey(struct ieee80211_hw *hw,
+ int idx,
+ struct survey_info *survey)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
+
+ if (idx != 0)
+ return -ENOENT;
+
+ survey->channel = conf->chandef.chan;
+ survey->filled = SURVEY_INFO_NOISE_DBM;
+ survey->noise = priv->noise;
+
+ return 0;
+}
+
+static int mwl_mac80211_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_ampdu_params *params)
+{
+ int rc = 0;
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_ampdu_stream *stream;
+ enum ieee80211_ampdu_mlme_action action = params->action;
+ struct ieee80211_sta *sta = params->sta;
+ u16 tid = params->tid;
+ u8 buf_size = params->buf_size;
+ u8 *addr = sta->addr, idx;
+ struct mwl_sta *sta_info;
+
+ sta_info = mwl_dev_get_sta(sta);
+
+ spin_lock_bh(&priv->stream_lock);
+
+ stream = mwl_fwcmd_lookup_stream(hw, addr, tid);
+
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+ case IEEE80211_AMPDU_RX_STOP:
+ break;
+ case IEEE80211_AMPDU_TX_START:
+ if (!sta_info->is_ampdu_allowed) {
+ wiphy_warn(hw->wiphy, "ampdu not allowed\n");
+ rc = -EPERM;
+ break;
+ }
+
+ if (!stream) {
+ stream = mwl_fwcmd_add_stream(hw, sta, tid);
+ if (!stream) {
+ wiphy_warn(hw->wiphy, "no stream found\n");
+ rc = -EPERM;
+ break;
+ }
+ }
+
+ spin_unlock_bh(&priv->stream_lock);
+ rc = mwl_fwcmd_check_ba(hw, stream, vif);
+ spin_lock_bh(&priv->stream_lock);
+ if (rc) {
+ mwl_fwcmd_remove_stream(hw, stream);
+ sta_info->check_ba_failed[tid]++;
+ rc = -EPERM;
+ break;
+ }
+ stream->state = AMPDU_STREAM_IN_PROGRESS;
+ params->ssn = 0;
+ ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
+ break;
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+ if (stream) {
+ if (stream->state == AMPDU_STREAM_ACTIVE) {
+ mwl_tx_del_ampdu_pkts(hw, sta, tid);
+ idx = stream->idx;
+ spin_unlock_bh(&priv->stream_lock);
+ mwl_fwcmd_destroy_ba(hw, idx);
+ spin_lock_bh(&priv->stream_lock);
+ sta_info->is_amsdu_allowed = false;
+ }
+
+ mwl_fwcmd_remove_stream(hw, stream);
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid);
+ } else {
+ rc = -EPERM;
+ }
+ break;
+ case IEEE80211_AMPDU_TX_OPERATIONAL:
+ if (stream) {
+ if (WARN_ON(stream->state !=
+ AMPDU_STREAM_IN_PROGRESS)) {
+ rc = -EPERM;
+ break;
+ }
+ spin_unlock_bh(&priv->stream_lock);
+ rc = mwl_fwcmd_create_ba(hw, stream, buf_size, vif);
+ spin_lock_bh(&priv->stream_lock);
+
+ if (!rc) {
+ stream->state = AMPDU_STREAM_ACTIVE;
+ sta_info->check_ba_failed[tid] = 0;
+ sta_info->is_amsdu_allowed = params->amsdu;
+ } else {
+ idx = stream->idx;
+ spin_unlock_bh(&priv->stream_lock);
+ mwl_fwcmd_destroy_ba(hw, idx);
+ spin_lock_bh(&priv->stream_lock);
+ mwl_fwcmd_remove_stream(hw, stream);
+ wiphy_err(hw->wiphy,
+ "ampdu operation error code: %d\n",
+ rc);
+ }
+ } else {
+ rc = -EPERM;
+ }
+ break;
+ default:
+ rc = -ENOTSUPP;
+ break;
+ }
+
+ spin_unlock_bh(&priv->stream_lock);
+
+ return rc;
+}
+
+static int mwl_mac80211_chnl_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *ch_switch)
+{
+ struct mwl_priv *priv = hw->priv;
+ int rc = 0;
+
+ rc = mwl_fwcmd_set_switch_channel(priv, ch_switch);
+
+ return rc;
+}
+
+const struct ieee80211_ops mwl_mac80211_ops = {
+ .tx = mwl_mac80211_tx,
+ .start = mwl_mac80211_start,
+ .stop = mwl_mac80211_stop,
+ .add_interface = mwl_mac80211_add_interface,
+ .remove_interface = mwl_mac80211_remove_interface,
+ .config = mwl_mac80211_config,
+ .bss_info_changed = mwl_mac80211_bss_info_changed,
+ .configure_filter = mwl_mac80211_configure_filter,
+ .set_key = mwl_mac80211_set_key,
+ .set_rts_threshold = mwl_mac80211_set_rts_threshold,
+ .sta_add = mwl_mac80211_sta_add,
+ .sta_remove = mwl_mac80211_sta_remove,
+ .conf_tx = mwl_mac80211_conf_tx,
+ .get_stats = mwl_mac80211_get_stats,
+ .get_survey = mwl_mac80211_get_survey,
+ .ampdu_action = mwl_mac80211_ampdu_action,
+ .pre_channel_switch = mwl_mac80211_chnl_switch,
+};
diff --git a/drivers/net/wireless/marvell/mwlwifi/main.c b/drivers/net/wireless/marvell/mwlwifi/main.c
new file mode 100644
index 0000000..5be867d
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/main.c
@@ -0,0 +1,840 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file implements main functions of this module. */
+
+#include <linux/module.h>
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#endif
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwdl.h"
+#include "fwcmd.h"
+#include "tx.h"
+#include "rx.h"
+#include "isr.h"
+#include "thermal.h"
+#ifdef CONFIG_DEBUG_FS
+#include "debugfs.h"
+#endif
+
+#define MWL_DESC "Marvell 802.11ac Wireless Network Driver"
+#define MWL_DEV_NAME "Marvell 802.11ac Adapter"
+
+#define FILE_PATH_LEN 64
+#define CMD_BUF_SIZE 0x4000
+
+static struct pci_device_id mwl_pci_id_tbl[] = {
+ { PCI_VDEVICE(MARVELL, 0x2a55), .driver_data = MWL8864, },
+ { PCI_VDEVICE(MARVELL, 0x2b38), .driver_data = MWL8897, },
+ { },
+};
+
+static struct mwl_chip_info mwl_chip_tbl[] = {
+ [MWL8864] = {
+ .part_name = "88W8864",
+ .fw_image = "mwlwifi/88W8864.bin",
+ .antenna_tx = ANTENNA_TX_4_AUTO,
+ .antenna_rx = ANTENNA_RX_4_AUTO,
+ },
+ [MWL8897] = {
+ .part_name = "88W8897",
+ .fw_image = "mwlwifi/88W8897.bin",
+ .antenna_tx = ANTENNA_TX_2,
+ .antenna_rx = ANTENNA_RX_2,
+ },
+};
+
+static const struct ieee80211_channel mwl_channels_24[] = {
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2412, .hw_value = 1, },
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2417, .hw_value = 2, },
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2422, .hw_value = 3, },
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2427, .hw_value = 4, },
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2432, .hw_value = 5, },
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2437, .hw_value = 6, },
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2442, .hw_value = 7, },
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2447, .hw_value = 8, },
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2452, .hw_value = 9, },
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2457, .hw_value = 10, },
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2462, .hw_value = 11, },
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2467, .hw_value = 12, },
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2472, .hw_value = 13, },
+ { .band = NL80211_BAND_2GHZ, .center_freq = 2484, .hw_value = 14, },
+};
+
+static const struct ieee80211_rate mwl_rates_24[] = {
+ { .bitrate = 10, .hw_value = 2, },
+ { .bitrate = 20, .hw_value = 4, },
+ { .bitrate = 55, .hw_value = 11, },
+ { .bitrate = 110, .hw_value = 22, },
+ { .bitrate = 220, .hw_value = 44, },
+ { .bitrate = 60, .hw_value = 12, },
+ { .bitrate = 90, .hw_value = 18, },
+ { .bitrate = 120, .hw_value = 24, },
+ { .bitrate = 180, .hw_value = 36, },
+ { .bitrate = 240, .hw_value = 48, },
+ { .bitrate = 360, .hw_value = 72, },
+ { .bitrate = 480, .hw_value = 96, },
+ { .bitrate = 540, .hw_value = 108, },
+};
+
+static const struct ieee80211_channel mwl_channels_50[] = {
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5180, .hw_value = 36, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5200, .hw_value = 40, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5220, .hw_value = 44, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5240, .hw_value = 48, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5260, .hw_value = 52, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5280, .hw_value = 56, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5300, .hw_value = 60, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5320, .hw_value = 64, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5500, .hw_value = 100, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5520, .hw_value = 104, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5540, .hw_value = 108, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5560, .hw_value = 112, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5580, .hw_value = 116, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5600, .hw_value = 120, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5620, .hw_value = 124, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5640, .hw_value = 128, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5660, .hw_value = 132, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5680, .hw_value = 136, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5700, .hw_value = 140, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5720, .hw_value = 144, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5745, .hw_value = 149, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5765, .hw_value = 153, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5785, .hw_value = 157, },
+ { .band = NL80211_BAND_5GHZ, .center_freq = 5805, .hw_value = 161, },
+};
+
+static const struct ieee80211_rate mwl_rates_50[] = {
+ { .bitrate = 60, .hw_value = 12, },
+ { .bitrate = 90, .hw_value = 18, },
+ { .bitrate = 120, .hw_value = 24, },
+ { .bitrate = 180, .hw_value = 36, },
+ { .bitrate = 240, .hw_value = 48, },
+ { .bitrate = 360, .hw_value = 72, },
+ { .bitrate = 480, .hw_value = 96, },
+ { .bitrate = 540, .hw_value = 108, },
+};
+
+static const struct ieee80211_iface_limit ap_if_limits[] = {
+ { .max = SYSADPT_NUM_OF_AP, .types = BIT(NL80211_IFTYPE_AP) },
+ { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) },
+};
+
+static const struct ieee80211_iface_combination ap_if_comb = {
+ .limits = ap_if_limits,
+ .n_limits = ARRAY_SIZE(ap_if_limits),
+ .max_interfaces = SYSADPT_NUM_OF_AP,
+ .num_different_channels = 1,
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40) |
+ BIT(NL80211_CHAN_WIDTH_80),
+};
+
+struct region_code_mapping {
+ const char *alpha2;
+ u32 region_code;
+};
+
+static const struct region_code_mapping regmap[] = {
+ {"US", 0x10}, /* US FCC */
+ {"CA", 0x20}, /* Canada */
+ {"EU", 0x30}, /* ETSI */
+ {"ES", 0x31}, /* Spain */
+ {"FR", 0x32}, /* France */
+ {"JP", 0x40}, /* Japan */
+ {"TW", 0x80}, /* Taiwan */
+ {"AU", 0x81}, /* Australia */
+ {"CN", 0x90}, /* China (Asia) */
+};
+
+static int mwl_alloc_pci_resource(struct mwl_priv *priv)
+{
+ struct pci_dev *pdev = priv->pdev;
+ void __iomem *addr;
+
+ priv->next_bar_num = 1; /* 32-bit */
+ if (pci_resource_flags(pdev, 0) & 0x04)
+ priv->next_bar_num = 2; /* 64-bit */
+
+ addr = devm_ioremap_resource(priv->dev, &pdev->resource[0]);
+ if (IS_ERR(addr)) {
+ wiphy_err(priv->hw->wiphy,
+ "%s: cannot reserve PCI memory region 0\n",
+ MWL_DRV_NAME);
+ goto err;
+ }
+ priv->iobase0 = addr;
+ wiphy_debug(priv->hw->wiphy, "priv->iobase0 = %p\n", priv->iobase0);
+
+ addr = devm_ioremap_resource(priv->dev,
+ &pdev->resource[priv->next_bar_num]);
+ if (IS_ERR(addr)) {
+ wiphy_err(priv->hw->wiphy,
+ "%s: cannot reserve PCI memory region 1\n",
+ MWL_DRV_NAME);
+ goto err;
+ }
+ priv->iobase1 = addr;
+ wiphy_debug(priv->hw->wiphy, "priv->iobase1 = %p\n", priv->iobase1);
+
+ priv->pcmd_buf =
+ (unsigned short *)dmam_alloc_coherent(priv->dev,
+ CMD_BUF_SIZE,
+ &priv->pphys_cmd_buf,
+ GFP_KERNEL);
+ if (!priv->pcmd_buf) {
+ wiphy_err(priv->hw->wiphy,
+ "%s: cannot alloc memory for command buffer\n",
+ MWL_DRV_NAME);
+ goto err;
+ }
+ wiphy_debug(priv->hw->wiphy,
+ "priv->pcmd_buf = %p priv->pphys_cmd_buf = %p\n",
+ priv->pcmd_buf,
+ (void *)priv->pphys_cmd_buf);
+ memset(priv->pcmd_buf, 0x00, CMD_BUF_SIZE);
+
+ return 0;
+
+err:
+ wiphy_err(priv->hw->wiphy, "pci alloc fail\n");
+
+ return -EIO;
+}
+
+static int mwl_init_firmware(struct mwl_priv *priv, const char *fw_name)
+{
+ int rc = 0;
+
+ rc = request_firmware((const struct firmware **)&priv->fw_ucode,
+ fw_name, priv->dev);
+
+ if (rc) {
+ wiphy_err(priv->hw->wiphy,
+ "%s: cannot find firmware image <%s>\n",
+ MWL_DRV_NAME, fw_name);
+ goto err_load_fw;
+ }
+
+ rc = mwl_fwdl_download_firmware(priv->hw);
+ if (rc) {
+ wiphy_err(priv->hw->wiphy,
+ "%s: cannot download firmware image <%s>\n",
+ MWL_DRV_NAME, fw_name);
+ goto err_download_fw;
+ }
+
+ return rc;
+
+err_download_fw:
+
+ release_firmware(priv->fw_ucode);
+
+err_load_fw:
+
+ wiphy_err(priv->hw->wiphy, "firmware init fail\n");
+
+ return rc;
+}
+
+static void mwl_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct ieee80211_hw *hw;
+ struct mwl_priv *priv;
+
+ hw = (struct ieee80211_hw *)wiphy_priv(wiphy);
+ priv = hw->priv;
+
+ if (priv->forbidden_setting) {
+ if (!priv->regulatory_set) {
+ regulatory_hint(wiphy, priv->fw_alpha2);
+ priv->regulatory_set = true;
+ } else {
+ if (memcmp(priv->fw_alpha2, request->alpha2, 2))
+ regulatory_hint(wiphy, priv->fw_alpha2);
+ }
+ return;
+ }
+
+ priv->dfs_region = request->dfs_region;
+}
+
+static void mwl_process_of_dts(struct mwl_priv *priv)
+{
+#ifdef CONFIG_OF
+ struct property *prop;
+ u32 prop_value;
+
+ priv->dt_node =
+ of_find_node_by_name(pci_bus_to_OF_node(priv->pdev->bus),
+ "mwlwifi");
+ if (!priv->dt_node)
+ return;
+
+ /* look for all matching property names */
+ for_each_property_of_node(priv->dt_node, prop) {
+ if (strcmp(prop->name, "marvell,2ghz") == 0)
+ priv->disable_2g = true;
+ if (strcmp(prop->name, "marvell,5ghz") == 0)
+ priv->disable_5g = true;
+ if (strcmp(prop->name, "marvell,chainmask") == 0) {
+ prop_value = be32_to_cpu(*((__be32 *)prop->value));
+ if (prop_value == 2)
+ priv->antenna_tx = ANTENNA_TX_2;
+
+ prop_value = be32_to_cpu(*((__be32 *)
+ (prop->value + 4)));
+ if (prop_value == 2)
+ priv->antenna_rx = ANTENNA_RX_2;
+ }
+ }
+
+ priv->pwr_node = of_find_node_by_name(priv->dt_node,
+ "marvell,powertable");
+#endif
+}
+
+static void mwl_set_ht_caps(struct mwl_priv *priv,
+ struct ieee80211_supported_band *band)
+{
+ struct ieee80211_hw *hw;
+
+ hw = priv->hw;
+
+ band->ht_cap.ht_supported = 1;
+
+ band->ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
+ band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ band->ht_cap.cap |= IEEE80211_HT_CAP_SM_PS;
+ band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+ band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+
+ ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+ ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
+ band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+ band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
+
+ band->ht_cap.mcs.rx_mask[0] = 0xff;
+ band->ht_cap.mcs.rx_mask[1] = 0xff;
+ if (priv->antenna_rx == ANTENNA_RX_4_AUTO)
+ band->ht_cap.mcs.rx_mask[2] = 0xff;
+ band->ht_cap.mcs.rx_mask[4] = 0x01;
+
+ band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+}
+
+static void mwl_set_vht_caps(struct mwl_priv *priv,
+ struct ieee80211_supported_band *band)
+{
+ band->vht_cap.vht_supported = 1;
+
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC;
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN;
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
+
+ if (priv->antenna_rx == ANTENNA_RX_2)
+ band->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0xfffa);
+ else
+ band->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0xffea);
+
+ if (priv->antenna_tx == ANTENNA_TX_2)
+ band->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0xfffa);
+ else
+ band->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0xffea);
+}
+
+static void mwl_set_caps(struct mwl_priv *priv)
+{
+ struct ieee80211_hw *hw;
+
+ hw = priv->hw;
+
+ /* set up band information for 2.4G */
+ if (!priv->disable_2g) {
+ BUILD_BUG_ON(sizeof(priv->channels_24) !=
+ sizeof(mwl_channels_24));
+ memcpy(priv->channels_24, mwl_channels_24,
+ sizeof(mwl_channels_24));
+
+ BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl_rates_24));
+ memcpy(priv->rates_24, mwl_rates_24, sizeof(mwl_rates_24));
+
+ priv->band_24.band = NL80211_BAND_2GHZ;
+ priv->band_24.channels = priv->channels_24;
+ priv->band_24.n_channels = ARRAY_SIZE(mwl_channels_24);
+ priv->band_24.bitrates = priv->rates_24;
+ priv->band_24.n_bitrates = ARRAY_SIZE(mwl_rates_24);
+
+ mwl_set_ht_caps(priv, &priv->band_24);
+ mwl_set_vht_caps(priv, &priv->band_24);
+
+ hw->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band_24;
+ }
+
+ /* set up band information for 5G */
+ if (!priv->disable_5g) {
+ BUILD_BUG_ON(sizeof(priv->channels_50) !=
+ sizeof(mwl_channels_50));
+ memcpy(priv->channels_50, mwl_channels_50,
+ sizeof(mwl_channels_50));
+
+ BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl_rates_50));
+ memcpy(priv->rates_50, mwl_rates_50, sizeof(mwl_rates_50));
+
+ priv->band_50.band = NL80211_BAND_5GHZ;
+ priv->band_50.channels = priv->channels_50;
+ priv->band_50.n_channels = ARRAY_SIZE(mwl_channels_50);
+ priv->band_50.bitrates = priv->rates_50;
+ priv->band_50.n_bitrates = ARRAY_SIZE(mwl_rates_50);
+
+ mwl_set_ht_caps(priv, &priv->band_50);
+ mwl_set_vht_caps(priv, &priv->band_50);
+
+ hw->wiphy->bands[NL80211_BAND_5GHZ] = &priv->band_50;
+ }
+}
+
+static void mwl_regd_init(struct mwl_priv *priv)
+{
+ u8 region_code;
+ int i;
+
+ /* hook regulatory domain change notification */
+ priv->hw->wiphy->reg_notifier = mwl_reg_notifier;
+
+ if (mwl_fwcmd_get_device_pwr_tbl(priv->hw,
+ &priv->device_pwr_tbl[0],
+ ®ion_code,
+ &priv->number_of_channels,
+ 0))
+ return;
+
+ priv->forbidden_setting = true;
+
+ for (i = 0; i < priv->number_of_channels; i++)
+ mwl_fwcmd_get_device_pwr_tbl(priv->hw,
+ &priv->device_pwr_tbl[i],
+ ®ion_code,
+ &priv->number_of_channels,
+ i);
+
+ for (i = 0; i < ARRAY_SIZE(regmap); i++)
+ if (regmap[i].region_code == priv->fw_region_code) {
+ memcpy(priv->fw_alpha2, regmap[i].alpha2, 2);
+ break;
+ }
+}
+
+static void timer_routine(unsigned long data)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)data;
+ struct mwl_ampdu_stream *stream;
+ struct mwl_sta *sta_info;
+ struct mwl_tx_info *tx_stats;
+ int i;
+
+ spin_lock_bh(&priv->stream_lock);
+ for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) {
+ stream = &priv->ampdu[i];
+
+ if (stream->state == AMPDU_STREAM_ACTIVE) {
+ sta_info = mwl_dev_get_sta(stream->sta);
+ tx_stats = &sta_info->tx_stats[stream->tid];
+
+ if ((jiffies - tx_stats->start_time > HZ) &&
+ (tx_stats->pkts < SYSADPT_AMPDU_PACKET_THRESHOLD)) {
+ ieee80211_stop_tx_ba_session(stream->sta,
+ stream->tid);
+ }
+
+ if (jiffies - tx_stats->start_time > HZ) {
+ tx_stats->pkts = 0;
+ tx_stats->start_time = jiffies;
+ }
+ }
+ }
+ spin_unlock_bh(&priv->stream_lock);
+
+ mod_timer(&priv->period_timer, jiffies +
+ msecs_to_jiffies(SYSADPT_TIMER_WAKEUP_TIME));
+}
+
+static int mwl_wl_init(struct mwl_priv *priv)
+{
+ struct ieee80211_hw *hw;
+ int rc;
+ int i;
+
+ hw = priv->hw;
+
+ hw->extra_tx_headroom = SYSADPT_MIN_BYTES_HEADROOM;
+ hw->queues = SYSADPT_TX_WMM_QUEUES;
+
+ /* Set rssi values to dBm */
+ ieee80211_hw_set(hw, SIGNAL_DBM);
+ ieee80211_hw_set(hw, HAS_RATE_CONTROL);
+
+ /* Ask mac80211 not to trigger PS mode
+ * based on PM bit of incoming frames.
+ */
+ ieee80211_hw_set(hw, AP_LINK_PS);
+
+ ieee80211_hw_set(hw, SUPPORTS_PER_STA_GTK);
+ ieee80211_hw_set(hw, MFP_CAPABLE);
+
+ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+ hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+
+ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+
+ hw->vif_data_size = sizeof(struct mwl_vif);
+ hw->sta_data_size = sizeof(struct mwl_sta);
+
+ priv->ap_macids_supported = 0x0000ffff;
+ priv->sta_macids_supported = 0x00010000;
+ priv->macids_used = 0;
+ INIT_LIST_HEAD(&priv->vif_list);
+ INIT_LIST_HEAD(&priv->sta_list);
+
+ /* Set default radio state, preamble and wmm */
+ priv->radio_on = false;
+ priv->radio_short_preamble = false;
+ priv->wmm_enabled = false;
+
+ priv->powinited = 0;
+
+ priv->csa_active = false;
+ priv->dfs_chirp_count_min = 5;
+ priv->dfs_chirp_time_interval = 1000;
+ priv->dfs_pw_filter = 0;
+ priv->dfs_min_num_radar = 5;
+ priv->dfs_min_pri_count = 4;
+
+ /* Handle watchdog ba events */
+ INIT_WORK(&priv->watchdog_ba_handle, mwl_watchdog_ba_events);
+ INIT_WORK(&priv->chnl_switch_handle, mwl_chnl_switch_event);
+
+ tasklet_init(&priv->tx_task, (void *)mwl_tx_skbs, (unsigned long)hw);
+ tasklet_disable(&priv->tx_task);
+ tasklet_init(&priv->tx_done_task,
+ (void *)mwl_tx_done, (unsigned long)hw);
+ tasklet_disable(&priv->tx_done_task);
+ tasklet_init(&priv->rx_task, (void *)mwl_rx_recv, (unsigned long)hw);
+ tasklet_disable(&priv->rx_task);
+ tasklet_init(&priv->qe_task,
+ (void *)mwl_tx_flush_amsdu, (unsigned long)hw);
+ tasklet_disable(&priv->qe_task);
+ priv->txq_limit = SYSADPT_TX_QUEUE_LIMIT;
+ priv->is_tx_done_schedule = false;
+ priv->recv_limit = SYSADPT_RECEIVE_LIMIT;
+ priv->is_rx_schedule = false;
+ priv->is_qe_schedule = false;
+ priv->qe_trigger_num = 0;
+ priv->qe_trigger_time = jiffies;
+
+ mutex_init(&priv->fwcmd_mutex);
+ spin_lock_init(&priv->tx_desc_lock);
+ spin_lock_init(&priv->vif_lock);
+ spin_lock_init(&priv->sta_lock);
+ spin_lock_init(&priv->stream_lock);
+
+ rc = mwl_thermal_register(priv);
+ if (rc) {
+ wiphy_err(hw->wiphy, "%s: fail to register thermal framework\n",
+ MWL_DRV_NAME);
+ goto err_thermal_register;
+ }
+
+ rc = mwl_tx_init(hw);
+ if (rc) {
+ wiphy_err(hw->wiphy, "%s: fail to initialize TX\n",
+ MWL_DRV_NAME);
+ goto err_mwl_tx_init;
+ }
+
+ rc = mwl_rx_init(hw);
+ if (rc) {
+ wiphy_err(hw->wiphy, "%s: fail to initialize RX\n",
+ MWL_DRV_NAME);
+ goto err_mwl_rx_init;
+ }
+
+ rc = mwl_fwcmd_get_hw_specs(hw);
+ if (rc) {
+ wiphy_err(hw->wiphy, "%s: fail to get HW specifications\n",
+ MWL_DRV_NAME);
+ goto err_get_hw_specs;
+ }
+
+ SET_IEEE80211_PERM_ADDR(hw, priv->hw_data.mac_addr);
+
+ writel(priv->desc_data[0].pphys_tx_ring,
+ priv->iobase0 + priv->desc_data[0].wcb_base);
+ for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)
+ writel(priv->desc_data[i].pphys_tx_ring,
+ priv->iobase0 + priv->desc_data[i].wcb_base);
+ writel(priv->desc_data[0].pphys_rx_ring,
+ priv->iobase0 + priv->desc_data[0].rx_desc_read);
+ writel(priv->desc_data[0].pphys_rx_ring,
+ priv->iobase0 + priv->desc_data[0].rx_desc_write);
+
+ rc = mwl_fwcmd_set_hw_specs(hw);
+ if (rc) {
+ wiphy_err(hw->wiphy, "%s: fail to set HW specifications\n",
+ MWL_DRV_NAME);
+ goto err_set_hw_specs;
+ }
+
+ wiphy_info(hw->wiphy,
+ "firmware version: 0x%x\n", priv->hw_data.fw_release_num);
+
+ if (!mwl_fwcmd_get_fw_region_code(hw, &priv->fw_region_code)) {
+ priv->fw_device_pwrtbl = true;
+ mwl_regd_init(priv);
+ wiphy_info(hw->wiphy,
+ "firmware region code: %x\n", priv->fw_region_code);
+ }
+
+ mwl_fwcmd_radio_disable(hw);
+
+ mwl_fwcmd_rf_antenna(hw, WL_ANTENNATYPE_TX, priv->antenna_tx);
+
+ mwl_fwcmd_rf_antenna(hw, WL_ANTENNATYPE_RX, priv->antenna_rx);
+
+ hw->wiphy->interface_modes = 0;
+ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
+ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
+ hw->wiphy->iface_combinations = &ap_if_comb;
+ hw->wiphy->n_iface_combinations = 1;
+
+ mwl_set_caps(priv);
+
+ rc = ieee80211_register_hw(hw);
+ if (rc) {
+ wiphy_err(hw->wiphy, "%s: fail to register device\n",
+ MWL_DRV_NAME);
+ goto err_register_hw;
+ }
+
+ rc = request_irq(priv->pdev->irq, mwl_isr,
+ IRQF_SHARED, MWL_DRV_NAME, hw);
+ if (rc) {
+ priv->irq = -1;
+ wiphy_err(hw->wiphy, "fail to register IRQ handler\n");
+ goto err_register_irq;
+ }
+ priv->irq = priv->pdev->irq;
+
+ setup_timer(&priv->period_timer, timer_routine, (unsigned long)priv);
+ mod_timer(&priv->period_timer, jiffies +
+ msecs_to_jiffies(SYSADPT_TIMER_WAKEUP_TIME));
+
+ return rc;
+
+err_register_irq:
+err_register_hw:
+err_set_hw_specs:
+err_get_hw_specs:
+
+ mwl_rx_deinit(hw);
+
+err_mwl_rx_init:
+
+ mwl_tx_deinit(hw);
+
+err_mwl_tx_init:
+err_thermal_register:
+
+ wiphy_err(hw->wiphy, "init fail\n");
+
+ return rc;
+}
+
+static void mwl_wl_deinit(struct mwl_priv *priv)
+{
+ struct ieee80211_hw *hw = priv->hw;
+
+ del_timer_sync(&priv->period_timer);
+
+ if (priv->irq != -1) {
+ free_irq(priv->pdev->irq, hw);
+ priv->irq = -1;
+ }
+
+ ieee80211_unregister_hw(hw);
+ mwl_thermal_unregister(priv);
+ mwl_rx_deinit(hw);
+ mwl_tx_deinit(hw);
+ tasklet_kill(&priv->qe_task);
+ tasklet_kill(&priv->rx_task);
+ tasklet_kill(&priv->tx_done_task);
+ tasklet_kill(&priv->tx_task);
+ cancel_work_sync(&priv->watchdog_ba_handle);
+ mwl_fwcmd_reset(hw);
+}
+
+static int mwl_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct ieee80211_hw *hw;
+ struct mwl_priv *priv;
+ const char *fw_name;
+ int rc = 0;
+
+ if (id->driver_data >= MWLUNKNOWN)
+ return -ENODEV;
+
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ pr_err("%s: cannot enable new PCI device",
+ MWL_DRV_NAME);
+ return rc;
+ }
+
+ rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (rc) {
+ pr_err("%s: 32-bit PCI DMA not supported",
+ MWL_DRV_NAME);
+ goto err_pci_disable_device;
+ }
+
+ pci_set_master(pdev);
+
+ hw = ieee80211_alloc_hw(sizeof(*priv), &mwl_mac80211_ops);
+ if (!hw) {
+ pr_err("%s: ieee80211 alloc failed",
+ MWL_DRV_NAME);
+ rc = -ENOMEM;
+ goto err_pci_disable_device;
+ }
+
+ pci_set_drvdata(pdev, hw);
+
+ priv = hw->priv;
+ priv->hw = hw;
+ priv->pdev = pdev;
+ priv->dev = &pdev->dev;
+ priv->chip_type = id->driver_data;
+ priv->fw_device_pwrtbl = false;
+ priv->forbidden_setting = false;
+ priv->regulatory_set = false;
+ priv->disable_2g = false;
+ priv->disable_5g = false;
+ priv->antenna_tx = mwl_chip_tbl[priv->chip_type].antenna_tx;
+ priv->antenna_rx = mwl_chip_tbl[priv->chip_type].antenna_rx;
+
+ SET_IEEE80211_DEV(hw, priv->dev);
+
+ rc = mwl_alloc_pci_resource(priv);
+ if (rc)
+ goto err_alloc_pci_resource;
+
+ fw_name = mwl_chip_tbl[priv->chip_type].fw_image;
+
+ rc = mwl_init_firmware(priv, fw_name);
+
+ if (rc) {
+ wiphy_err(hw->wiphy, "%s: fail to initialize firmware\n",
+ MWL_DRV_NAME);
+ goto err_init_firmware;
+ }
+
+ /* firmware is loaded to H/W, it can be released now */
+ release_firmware(priv->fw_ucode);
+
+ mwl_process_of_dts(priv);
+
+ rc = mwl_wl_init(priv);
+ if (rc) {
+ wiphy_err(hw->wiphy, "%s: fail to initialize wireless lan\n",
+ MWL_DRV_NAME);
+ goto err_wl_init;
+ }
+
+ wiphy_info(priv->hw->wiphy, "2G %s, 5G %s\n",
+ priv->disable_2g ? "disabled" : "enabled",
+ priv->disable_5g ? "disabled" : "enabled");
+
+ wiphy_info(priv->hw->wiphy, "%s TX antennas, %s RX antennas\n",
+ (priv->antenna_tx == ANTENNA_TX_4_AUTO) ? "4" : "2",
+ (priv->antenna_rx == ANTENNA_RX_4_AUTO) ? "4" : "2");
+
+#ifdef CONFIG_DEBUG_FS
+ mwl_debugfs_init(hw);
+#endif
+
+ return rc;
+
+err_wl_init:
+err_init_firmware:
+
+ mwl_fwcmd_reset(hw);
+
+err_alloc_pci_resource:
+
+ pci_set_drvdata(pdev, NULL);
+ ieee80211_free_hw(hw);
+
+err_pci_disable_device:
+
+ pci_disable_device(pdev);
+
+ return rc;
+}
+
+static void mwl_remove(struct pci_dev *pdev)
+{
+ struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+ struct mwl_priv *priv;
+
+ if (!hw)
+ return;
+
+ priv = hw->priv;
+
+ mwl_wl_deinit(priv);
+ pci_set_drvdata(pdev, NULL);
+ ieee80211_free_hw(hw);
+ pci_disable_device(pdev);
+
+#ifdef CONFIG_DEBUG_FS
+ mwl_debugfs_remove(hw);
+#endif
+}
+
+static struct pci_driver mwl_pci_driver = {
+ .name = MWL_DRV_NAME,
+ .id_table = mwl_pci_id_tbl,
+ .probe = mwl_probe,
+ .remove = mwl_remove
+};
+
+module_pci_driver(mwl_pci_driver);
+
+MODULE_DESCRIPTION(MWL_DESC);
+MODULE_AUTHOR("Marvell Semiconductor, Inc.");
+MODULE_LICENSE("GPL v2");
+MODULE_SUPPORTED_DEVICE(MWL_DEV_NAME);
+MODULE_DEVICE_TABLE(pci, mwl_pci_id_tbl);
diff --git a/drivers/net/wireless/marvell/mwlwifi/rx.c b/drivers/net/wireless/marvell/mwlwifi/rx.c
new file mode 100644
index 0000000..1b8b385
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/rx.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file implements receive related functions. */
+
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "rx.h"
+
+#define MAX_NUM_RX_RING_BYTES (SYSADPT_MAX_NUM_RX_DESC * \
+ sizeof(struct mwl_rx_desc))
+
+#define MAX_NUM_RX_HNDL_BYTES (SYSADPT_MAX_NUM_RX_DESC * \
+ sizeof(struct mwl_rx_hndl))
+
+#define DECRYPT_ERR_MASK 0x80
+#define GENERAL_DECRYPT_ERR 0xFF
+#define TKIP_DECRYPT_MIC_ERR 0x02
+#define WEP_DECRYPT_ICV_ERR 0x04
+#define TKIP_DECRYPT_ICV_ERR 0x08
+
+#define W836X_RSSI_OFFSET 8
+
+/* Receive rate information constants */
+#define RX_RATE_INFO_FORMAT_11A 0
+#define RX_RATE_INFO_FORMAT_11B 1
+#define RX_RATE_INFO_FORMAT_11N 2
+#define RX_RATE_INFO_FORMAT_11AC 4
+
+#define RX_RATE_INFO_HT20 0
+#define RX_RATE_INFO_HT40 1
+#define RX_RATE_INFO_HT80 2
+
+#define RX_RATE_INFO_LONG_INTERVAL 0
+#define RX_RATE_INFO_SHORT_INTERVAL 1
+
+static int mwl_rx_ring_alloc(struct mwl_priv *priv)
+{
+ struct mwl_desc_data *desc;
+
+ desc = &priv->desc_data[0];
+
+ desc->prx_ring = (struct mwl_rx_desc *)
+ dma_alloc_coherent(priv->dev,
+ MAX_NUM_RX_RING_BYTES,
+ &desc->pphys_rx_ring,
+ GFP_KERNEL);
+
+ if (!desc->prx_ring) {
+ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n");
+ return -ENOMEM;
+ }
+
+ memset(desc->prx_ring, 0x00, MAX_NUM_RX_RING_BYTES);
+
+ desc->rx_hndl = kmalloc(MAX_NUM_RX_HNDL_BYTES, GFP_KERNEL);
+
+ if (!desc->rx_hndl) {
+ dma_free_coherent(priv->dev,
+ MAX_NUM_RX_RING_BYTES,
+ desc->prx_ring,
+ desc->pphys_rx_ring);
+ return -ENOMEM;
+ }
+
+ memset(desc->rx_hndl, 0x00, MAX_NUM_RX_HNDL_BYTES);
+
+ return 0;
+}
+
+static int mwl_rx_ring_init(struct mwl_priv *priv)
+{
+ struct mwl_desc_data *desc;
+ int i;
+ struct mwl_rx_hndl *rx_hndl;
+ dma_addr_t dma;
+ u32 val;
+
+ desc = &priv->desc_data[0];
+
+ if (desc->prx_ring) {
+ desc->rx_buf_size = SYSADPT_MAX_AGGR_SIZE;
+
+ for (i = 0; i < SYSADPT_MAX_NUM_RX_DESC; i++) {
+ rx_hndl = &desc->rx_hndl[i];
+ rx_hndl->psk_buff =
+ dev_alloc_skb(desc->rx_buf_size);
+
+ if (!rx_hndl->psk_buff) {
+ wiphy_err(priv->hw->wiphy,
+ "rxdesc %i: no skbuff available\n",
+ i);
+ return -ENOMEM;
+ }
+
+ skb_reserve(rx_hndl->psk_buff,
+ SYSADPT_MIN_BYTES_HEADROOM);
+ desc->prx_ring[i].rx_control =
+ EAGLE_RXD_CTRL_DRIVER_OWN;
+ desc->prx_ring[i].status = EAGLE_RXD_STATUS_OK;
+ desc->prx_ring[i].qos_ctrl = 0x0000;
+ desc->prx_ring[i].channel = 0x00;
+ desc->prx_ring[i].rssi = 0x00;
+ desc->prx_ring[i].pkt_len =
+ cpu_to_le16(SYSADPT_MAX_AGGR_SIZE);
+ dma = pci_map_single(priv->pdev,
+ rx_hndl->psk_buff->data,
+ desc->rx_buf_size,
+ PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(priv->pdev, dma)) {
+ wiphy_err(priv->hw->wiphy,
+ "failed to map pci memory!\n");
+ return -ENOMEM;
+ }
+ desc->prx_ring[i].pphys_buff_data = cpu_to_le32(dma);
+ val = (u32)desc->pphys_rx_ring +
+ ((i + 1) * sizeof(struct mwl_rx_desc));
+ desc->prx_ring[i].pphys_next = cpu_to_le32(val);
+ rx_hndl->pdesc = &desc->prx_ring[i];
+ if (i < (SYSADPT_MAX_NUM_RX_DESC - 1))
+ rx_hndl->pnext = &desc->rx_hndl[i + 1];
+ }
+ desc->prx_ring[SYSADPT_MAX_NUM_RX_DESC - 1].pphys_next =
+ cpu_to_le32((u32)desc->pphys_rx_ring);
+ desc->rx_hndl[SYSADPT_MAX_NUM_RX_DESC - 1].pnext =
+ &desc->rx_hndl[0];
+ desc->pnext_rx_hndl = &desc->rx_hndl[0];
+
+ return 0;
+ }
+
+ wiphy_err(priv->hw->wiphy, "no valid RX mem\n");
+
+ return -ENOMEM;
+}
+
+static void mwl_rx_ring_cleanup(struct mwl_priv *priv)
+{
+ struct mwl_desc_data *desc;
+ int i;
+ struct mwl_rx_hndl *rx_hndl;
+
+ desc = &priv->desc_data[0];
+
+ if (desc->prx_ring) {
+ for (i = 0; i < SYSADPT_MAX_NUM_RX_DESC; i++) {
+ rx_hndl = &desc->rx_hndl[i];
+ if (!rx_hndl->psk_buff)
+ continue;
+
+ pci_unmap_single(priv->pdev,
+ le32_to_cpu
+ (rx_hndl->pdesc->pphys_buff_data),
+ desc->rx_buf_size,
+ PCI_DMA_FROMDEVICE);
+
+ dev_kfree_skb_any(rx_hndl->psk_buff);
+
+ wiphy_info(priv->hw->wiphy,
+ "unmapped+free'd %i 0x%p 0x%x %i\n",
+ i, rx_hndl->psk_buff->data,
+ le32_to_cpu(rx_hndl->pdesc->pphys_buff_data),
+ desc->rx_buf_size);
+
+ rx_hndl->psk_buff = NULL;
+ }
+ }
+}
+
+static void mwl_rx_ring_free(struct mwl_priv *priv)
+{
+ struct mwl_desc_data *desc;
+
+ desc = &priv->desc_data[0];
+
+ if (desc->prx_ring) {
+ mwl_rx_ring_cleanup(priv);
+
+ dma_free_coherent(priv->dev,
+ MAX_NUM_RX_RING_BYTES,
+ desc->prx_ring,
+ desc->pphys_rx_ring);
+
+ desc->prx_ring = NULL;
+ }
+
+ kfree(desc->rx_hndl);
+
+ desc->pnext_rx_hndl = NULL;
+}
+
+static inline void mwl_rx_prepare_status(struct mwl_rx_desc *pdesc,
+ struct ieee80211_rx_status *status)
+{
+ u16 rate, format, nss, bw, gi, rt;
+
+ memset(status, 0, sizeof(*status));
+
+ status->signal = -(pdesc->rssi + W836X_RSSI_OFFSET);
+
+ rate = le16_to_cpu(pdesc->rate);
+ format = rate & MWL_RX_RATE_FORMAT_MASK;
+ nss = (rate & MWL_RX_RATE_NSS_MASK) >> MWL_RX_RATE_NSS_SHIFT;
+ bw = (rate & MWL_RX_RATE_BW_MASK) >> MWL_RX_RATE_BW_SHIFT;
+ gi = (rate & MWL_RX_RATE_GI_MASK) >> MWL_RX_RATE_GI_SHIFT;
+ rt = (rate & MWL_RX_RATE_RT_MASK) >> MWL_RX_RATE_RT_SHIFT;
+
+ switch (format) {
+ case RX_RATE_INFO_FORMAT_11N:
+ status->flag |= RX_FLAG_HT;
+ if (bw == RX_RATE_INFO_HT40)
+ status->flag |= RX_FLAG_40MHZ;
+ if (gi == RX_RATE_INFO_SHORT_INTERVAL)
+ status->flag |= RX_FLAG_SHORT_GI;
+ break;
+ case RX_RATE_INFO_FORMAT_11AC:
+ status->flag |= RX_FLAG_VHT;
+ if (bw == RX_RATE_INFO_HT40)
+ status->flag |= RX_FLAG_40MHZ;
+ if (bw == RX_RATE_INFO_HT80)
+ status->vht_flag |= RX_VHT_FLAG_80MHZ;
+ if (gi == RX_RATE_INFO_SHORT_INTERVAL)
+ status->flag |= RX_FLAG_SHORT_GI;
+ status->vht_nss = (nss + 1);
+ break;
+ }
+
+ status->rate_idx = rt;
+
+ if (pdesc->channel > BAND_24_CHANNEL_NUM) {
+ status->band = NL80211_BAND_5GHZ;
+ if ((!(status->flag & RX_FLAG_HT)) &&
+ (!(status->flag & RX_FLAG_VHT))) {
+ status->rate_idx -= 5;
+ if (status->rate_idx >= BAND_50_RATE_NUM)
+ status->rate_idx = BAND_50_RATE_NUM - 1;
+ }
+ } else {
+ status->band = NL80211_BAND_2GHZ;
+ if ((!(status->flag & RX_FLAG_HT)) &&
+ (!(status->flag & RX_FLAG_VHT))) {
+ if (status->rate_idx >= BAND_24_RATE_NUM)
+ status->rate_idx = BAND_24_RATE_NUM - 1;
+ }
+ }
+
+ status->freq = ieee80211_channel_to_frequency(pdesc->channel,
+ status->band);
+
+ /* check if status has a specific error bit (bit 7) set or indicates
+ * a general decrypt error
+ */
+ if ((pdesc->status == GENERAL_DECRYPT_ERR) ||
+ (pdesc->status & DECRYPT_ERR_MASK)) {
+ /* check if status is not equal to 0xFF
+ * the 0xFF check is for backward compatibility
+ */
+ if (pdesc->status != GENERAL_DECRYPT_ERR) {
+ if (((pdesc->status & (~DECRYPT_ERR_MASK)) &
+ TKIP_DECRYPT_MIC_ERR) && !((pdesc->status &
+ (WEP_DECRYPT_ICV_ERR | TKIP_DECRYPT_ICV_ERR)))) {
+ status->flag |= RX_FLAG_MMIC_ERROR;
+ }
+ }
+ }
+}
+
+static inline struct mwl_vif *mwl_rx_find_vif_bss(struct mwl_priv *priv,
+ u8 *bssid)
+{
+ struct mwl_vif *mwl_vif;
+
+ spin_lock_bh(&priv->vif_lock);
+ list_for_each_entry(mwl_vif, &priv->vif_list, list) {
+ if (ether_addr_equal(bssid, mwl_vif->bssid)) {
+ spin_unlock_bh(&priv->vif_lock);
+ return mwl_vif;
+ }
+ }
+ spin_unlock_bh(&priv->vif_lock);
+
+ return NULL;
+}
+
+static inline void mwl_rx_remove_dma_header(struct sk_buff *skb, __le16 qos)
+{
+ struct mwl_dma_data *tr;
+ int hdrlen;
+
+ tr = (struct mwl_dma_data *)skb->data;
+ hdrlen = ieee80211_hdrlen(tr->wh.frame_control);
+
+ if (hdrlen != sizeof(tr->wh)) {
+ if (ieee80211_is_data_qos(tr->wh.frame_control)) {
+ memmove(tr->data - hdrlen, &tr->wh, hdrlen - 2);
+ *((__le16 *)(tr->data - 2)) = qos;
+ } else {
+ memmove(tr->data - hdrlen, &tr->wh, hdrlen);
+ }
+ }
+
+ if (hdrlen != sizeof(*tr))
+ skb_pull(skb, sizeof(*tr) - hdrlen);
+}
+
+static int mwl_rx_refill(struct mwl_priv *priv, struct mwl_rx_hndl *rx_hndl)
+{
+ struct mwl_desc_data *desc;
+ dma_addr_t dma;
+
+ desc = &priv->desc_data[0];
+
+ rx_hndl->psk_buff = dev_alloc_skb(desc->rx_buf_size);
+
+ if (!rx_hndl->psk_buff)
+ return -ENOMEM;
+
+ skb_reserve(rx_hndl->psk_buff, SYSADPT_MIN_BYTES_HEADROOM);
+
+ rx_hndl->pdesc->status = EAGLE_RXD_STATUS_OK;
+ rx_hndl->pdesc->qos_ctrl = 0x0000;
+ rx_hndl->pdesc->channel = 0x00;
+ rx_hndl->pdesc->rssi = 0x00;
+ rx_hndl->pdesc->pkt_len = cpu_to_le16(desc->rx_buf_size);
+
+ dma = pci_map_single(priv->pdev,
+ rx_hndl->psk_buff->data,
+ desc->rx_buf_size,
+ PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(priv->pdev, dma)) {
+ dev_kfree_skb_any(rx_hndl->psk_buff);
+ wiphy_err(priv->hw->wiphy,
+ "failed to map pci memory!\n");
+ return -ENOMEM;
+ }
+
+ rx_hndl->pdesc->pphys_buff_data = cpu_to_le32(dma);
+
+ return 0;
+}
+
+int mwl_rx_init(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+ int rc;
+
+ rc = mwl_rx_ring_alloc(priv);
+ if (rc) {
+ wiphy_err(hw->wiphy, "allocating RX ring failed\n");
+ return rc;
+ }
+
+ rc = mwl_rx_ring_init(priv);
+ if (rc) {
+ mwl_rx_ring_free(priv);
+ wiphy_err(hw->wiphy,
+ "initializing RX ring failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+void mwl_rx_deinit(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+
+ mwl_rx_ring_cleanup(priv);
+ mwl_rx_ring_free(priv);
+}
+
+void mwl_rx_recv(unsigned long data)
+{
+ struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_desc_data *desc;
+ struct mwl_rx_hndl *curr_hndl;
+ int work_done = 0;
+ struct sk_buff *prx_skb = NULL;
+ int pkt_len;
+ struct ieee80211_rx_status status;
+ struct mwl_vif *mwl_vif = NULL;
+ struct ieee80211_hdr *wh;
+ u32 status_mask;
+
+ desc = &priv->desc_data[0];
+ curr_hndl = desc->pnext_rx_hndl;
+
+ if (!curr_hndl) {
+ status_mask = readl(priv->iobase1 +
+ MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+ writel(status_mask | MACREG_A2HRIC_BIT_RX_RDY,
+ priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+
+ priv->is_rx_schedule = false;
+ wiphy_warn(hw->wiphy, "busy or no receiving packets\n");
+ return;
+ }
+
+ while ((curr_hndl->pdesc->rx_control == EAGLE_RXD_CTRL_DMA_OWN) &&
+ (work_done < priv->recv_limit)) {
+ prx_skb = curr_hndl->psk_buff;
+ if (!prx_skb)
+ goto out;
+ pci_unmap_single(priv->pdev,
+ le32_to_cpu(curr_hndl->pdesc->pphys_buff_data),
+ desc->rx_buf_size,
+ PCI_DMA_FROMDEVICE);
+ pkt_len = le16_to_cpu(curr_hndl->pdesc->pkt_len);
+
+ if (skb_tailroom(prx_skb) < pkt_len) {
+ dev_kfree_skb_any(prx_skb);
+ goto out;
+ }
+
+ if (curr_hndl->pdesc->channel !=
+ hw->conf.chandef.chan->hw_value) {
+ dev_kfree_skb_any(prx_skb);
+ goto out;
+ }
+
+ mwl_rx_prepare_status(curr_hndl->pdesc, &status);
+
+ priv->noise = -curr_hndl->pdesc->noise_floor;
+
+ wh = &((struct mwl_dma_data *)prx_skb->data)->wh;
+
+ if (ieee80211_has_protected(wh->frame_control)) {
+ /* Check if hw crypto has been enabled for
+ * this bss. If yes, set the status flags
+ * accordingly
+ */
+ if (ieee80211_has_tods(wh->frame_control)) {
+ mwl_vif = mwl_rx_find_vif_bss(priv, wh->addr1);
+ if (!mwl_vif &&
+ ieee80211_has_a4(wh->frame_control))
+ mwl_vif =
+ mwl_rx_find_vif_bss(priv,
+ wh->addr2);
+ } else {
+ mwl_vif = mwl_rx_find_vif_bss(priv, wh->addr2);
+ }
+
+ if ((mwl_vif && mwl_vif->is_hw_crypto_enabled) ||
+ is_multicast_ether_addr(wh->addr1) ||
+ (ieee80211_is_mgmt(wh->frame_control) &&
+ ieee80211_has_protected(wh->frame_control) &&
+ !is_multicast_ether_addr(wh->addr1))) {
+ /* When MMIC ERROR is encountered
+ * by the firmware, payload is
+ * dropped and only 32 bytes of
+ * mwlwifi Firmware header is sent
+ * to the host.
+ *
+ * We need to add four bytes of
+ * key information. In it
+ * MAC80211 expects keyidx set to
+ * 0 for triggering Counter
+ * Measure of MMIC failure.
+ */
+ if (status.flag & RX_FLAG_MMIC_ERROR) {
+ struct mwl_dma_data *tr;
+
+ tr = (struct mwl_dma_data *)
+ prx_skb->data;
+ memset((void *)&tr->data, 0, 4);
+ pkt_len += 4;
+ }
+
+ if (!ieee80211_is_auth(wh->frame_control))
+ status.flag |= RX_FLAG_IV_STRIPPED |
+ RX_FLAG_DECRYPTED |
+ RX_FLAG_MMIC_STRIPPED;
+ }
+ }
+
+ skb_put(prx_skb, pkt_len);
+ mwl_rx_remove_dma_header(prx_skb, curr_hndl->pdesc->qos_ctrl);
+
+ memcpy(IEEE80211_SKB_RXCB(prx_skb), &status, sizeof(status));
+ ieee80211_rx(hw, prx_skb);
+out:
+ mwl_rx_refill(priv, curr_hndl);
+ curr_hndl->pdesc->rx_control = EAGLE_RXD_CTRL_DRIVER_OWN;
+ curr_hndl->pdesc->qos_ctrl = 0;
+ curr_hndl = curr_hndl->pnext;
+ work_done++;
+ }
+
+ desc->pnext_rx_hndl = curr_hndl;
+
+ status_mask = readl(priv->iobase1 +
+ MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+ writel(status_mask | MACREG_A2HRIC_BIT_RX_RDY,
+ priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+
+ priv->is_rx_schedule = false;
+}
diff --git a/drivers/net/wireless/marvell/mwlwifi/rx.h b/drivers/net/wireless/marvell/mwlwifi/rx.h
new file mode 100644
index 0000000..f19e692
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/rx.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file defines receive related functions. */
+
+#ifndef _RX_H_
+#define _RX_H_
+
+int mwl_rx_init(struct ieee80211_hw *hw);
+void mwl_rx_deinit(struct ieee80211_hw *hw);
+void mwl_rx_recv(unsigned long data);
+
+#endif /* _RX_H_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/sysadpt.h b/drivers/net/wireless/marvell/mwlwifi/sysadpt.h
new file mode 100644
index 0000000..8535a43
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/sysadpt.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file defines system adaptation related information. */
+
+#ifndef _SYSADPT_H_
+#define _SYSADPT_H_
+
+#define SYSADPT_MAX_NUM_CHANNELS 64
+
+#define SYSADPT_MAX_DATA_RATES_G 14
+
+#define SYSADPT_TX_POWER_LEVEL_TOTAL 16
+
+#define SYSADPT_TX_WMM_QUEUES 4
+
+#define SYSADPT_TX_AMPDU_QUEUES 4
+
+#define SYSADPT_NUM_OF_AP 16
+
+#define SYSADPT_TOTAL_TX_QUEUES (SYSADPT_TX_WMM_QUEUES + \
+ SYSADPT_NUM_OF_AP)
+
+#define SYSADPT_TOTAL_HW_QUEUES (SYSADPT_TX_WMM_QUEUES + \
+ SYSADPT_TX_AMPDU_QUEUES)
+
+#define SYSADPT_NUM_OF_DESC_DATA (4 + SYSADPT_NUM_OF_AP)
+
+#define SYSADPT_MAX_NUM_TX_DESC 256
+
+#define SYSADPT_TX_QUEUE_LIMIT (3 * SYSADPT_MAX_NUM_TX_DESC)
+
+#define SYSADPT_TX_WAKE_Q_THRESHOLD (2 * SYSADPT_MAX_NUM_TX_DESC)
+
+#define SYSADPT_DELAY_FREE_Q_LIMIT SYSADPT_MAX_NUM_TX_DESC
+
+#define SYSADPT_MAX_NUM_RX_DESC 256
+
+#define SYSADPT_RECEIVE_LIMIT 64
+
+#define SYSADPT_MAX_AGGR_SIZE 8192
+
+#define SYSADPT_MIN_BYTES_HEADROOM 64
+
+#define SYSADPT_AMPDU_PACKET_THRESHOLD 64
+
+#define SYSADPT_AMSDU_FW_MAX_SIZE 3300
+
+#define SYSADPT_AMSDU_4K_MAX_SIZE SYSADPT_AMSDU_FW_MAX_SIZE
+
+#define SYSADPT_AMSDU_8K_MAX_SIZE SYSADPT_AMSDU_FW_MAX_SIZE
+
+#define SYSADPT_AMSDU_ALLOW_SIZE 1600
+
+#define SYSADPT_AMSDU_FLUSH_TIME 500
+
+#define SYSADPT_AMSDU_PACKET_THRESHOLD 10
+
+#define SYSADPT_MAX_TID 8
+
+#define SYSADPT_QUIET_PERIOD_DEFAULT 100
+
+#define SYSADPT_QUIET_PERIOD_MIN 25
+
+#define SYSADPT_QUIET_START_OFFSET 10
+
+#define SYSADPT_THERMAL_THROTTLE_MAX 100
+
+#define SYSADPT_TIMER_WAKEUP_TIME 300 /* ms */
+
+#endif /* _SYSADPT_H_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/thermal.c b/drivers/net/wireless/marvell/mwlwifi/thermal.c
new file mode 100644
index 0000000..ada9056
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/thermal.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file implements thermal framework related functions. */
+
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwcmd.h"
+#include "thermal.h"
+
+static int
+mwl_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = SYSADPT_THERMAL_THROTTLE_MAX;
+
+ return 0;
+}
+
+static int
+mwl_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct mwl_priv *priv = cdev->devdata;
+
+ *state = priv->throttle_state;
+
+ return 0;
+}
+
+static int
+mwl_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
+ unsigned long throttle_state)
+{
+ struct mwl_priv *priv = cdev->devdata;
+
+ if (throttle_state > SYSADPT_THERMAL_THROTTLE_MAX) {
+ wiphy_warn(priv->hw->wiphy,
+ "throttle state %ld is exceeding the limit %d\n",
+ throttle_state, SYSADPT_THERMAL_THROTTLE_MAX);
+ return -EINVAL;
+ }
+ priv->throttle_state = throttle_state;
+ mwl_thermal_set_throttling(priv);
+
+ return 0;
+}
+
+static struct thermal_cooling_device_ops mwl_thermal_ops = {
+ .get_max_state = mwl_thermal_get_max_throttle_state,
+ .get_cur_state = mwl_thermal_get_cur_throttle_state,
+ .set_cur_state = mwl_thermal_set_cur_throttle_state,
+};
+
+static ssize_t mwl_thermal_show_temp(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mwl_priv *priv = dev_get_drvdata(dev);
+ int ret, temperature;
+
+ ret = mwl_fwcmd_get_temp(priv->hw, &priv->temperature);
+ if (ret) {
+ wiphy_warn(priv->hw->wiphy, "failed: can't get temperature\n");
+ goto out;
+ }
+
+ temperature = priv->temperature;
+
+ /* display in millidegree celcius */
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000);
+out:
+ return ret;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, 0444, mwl_thermal_show_temp,
+ NULL, 0);
+
+static struct attribute *mwl_hwmon_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(mwl_hwmon);
+
+void mwl_thermal_set_throttling(struct mwl_priv *priv)
+{
+ u32 period, duration, enabled;
+ int ret;
+
+ period = priv->quiet_period;
+ duration = (period * priv->throttle_state) / 100;
+ enabled = duration ? 1 : 0;
+
+ ret = mwl_fwcmd_quiet_mode(priv->hw, enabled, period,
+ duration, SYSADPT_QUIET_START_OFFSET);
+ if (ret) {
+ wiphy_warn(priv->hw->wiphy,
+ "failed: period %u duarion %u enabled %u ret %d\n",
+ period, duration, enabled, ret);
+ }
+}
+
+int mwl_thermal_register(struct mwl_priv *priv)
+{
+ struct thermal_cooling_device *cdev;
+ struct device *hwmon_dev;
+ int ret;
+
+ if (priv->chip_type != MWL8897)
+ return 0;
+
+ cdev = thermal_cooling_device_register("mwlwifi_thermal", priv,
+ &mwl_thermal_ops);
+ if (IS_ERR(cdev)) {
+ wiphy_err(priv->hw->wiphy,
+ "failed to setup thermal device result: %ld\n",
+ PTR_ERR(cdev));
+ return -EINVAL;
+ }
+
+ ret = sysfs_create_link(&priv->dev->kobj, &cdev->device.kobj,
+ "cooling_device");
+ if (ret) {
+ wiphy_err(priv->hw->wiphy,
+ "failed to create cooling device symlink\n");
+ goto err_cooling_destroy;
+ }
+
+ priv->cdev = cdev;
+ priv->quiet_period = SYSADPT_QUIET_PERIOD_DEFAULT;
+
+ if (!IS_ENABLED(CONFIG_HWMON))
+ return 0;
+
+ hwmon_dev =
+ devm_hwmon_device_register_with_groups(priv->dev,
+ "mwlwifi_hwmon", priv,
+ mwl_hwmon_groups);
+ if (IS_ERR(hwmon_dev)) {
+ wiphy_err(priv->hw->wiphy,
+ "failed to register hwmon device: %ld\n",
+ PTR_ERR(hwmon_dev));
+ ret = -EINVAL;
+ goto err_remove_link;
+ }
+
+ return 0;
+
+err_remove_link:
+ sysfs_remove_link(&priv->dev->kobj, "cooling_device");
+err_cooling_destroy:
+ thermal_cooling_device_unregister(cdev);
+
+ return ret;
+}
+
+void mwl_thermal_unregister(struct mwl_priv *priv)
+{
+ if (priv->chip_type != MWL8897)
+ return;
+
+ sysfs_remove_link(&priv->dev->kobj, "cooling_device");
+ thermal_cooling_device_unregister(priv->cdev);
+}
diff --git a/drivers/net/wireless/marvell/mwlwifi/thermal.h b/drivers/net/wireless/marvell/mwlwifi/thermal.h
new file mode 100644
index 0000000..67d601c
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/thermal.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file defines Linux thermal framework related functions. */
+
+#ifndef _THERMAL_H_
+#define _THERMAL_H_
+
+#include <linux/kconfig.h>
+
+#if IS_ENABLED(CONFIG_THERMAL)
+int mwl_thermal_register(struct mwl_priv *priv);
+void mwl_thermal_unregister(struct mwl_priv *priv);
+void mwl_thermal_set_throttling(struct mwl_priv *priv);
+#else
+static inline int mwl_thermal_register(struct mwl_priv *priv)
+{
+ return 0;
+}
+
+static inline void mwl_thermal_unregister(struct mwl_priv *priv)
+{
+}
+
+static inline void mwl_thermal_set_throttling(struct mwl_priv *priv)
+{
+}
+#endif
+
+#endif /* _THERMAL_H_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/tx.c b/drivers/net/wireless/marvell/mwlwifi/tx.c
new file mode 100644
index 0000000..ee3d58a
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/tx.c
@@ -0,0 +1,1250 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file implements transmit related functions. */
+
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwcmd.h"
+#include "tx.h"
+
+#define MAX_NUM_TX_RING_BYTES (SYSADPT_MAX_NUM_TX_DESC * \
+ sizeof(struct mwl_tx_desc))
+
+#define MAX_NUM_TX_HNDL_BYTES (SYSADPT_MAX_NUM_TX_DESC * \
+ sizeof(struct mwl_tx_hndl))
+
+#define EAGLE_TXD_XMITCTRL_USE_MC_RATE 0x8 /* Use multicast data rate */
+
+#define MWL_QOS_ACK_POLICY_MASK 0x0060
+#define MWL_QOS_ACK_POLICY_NORMAL 0x0000
+#define MWL_QOS_ACK_POLICY_BLOCKACK 0x0060
+
+#define EXT_IV 0x20
+#define INCREASE_IV(iv16, iv32) \
+{ \
+ (iv16)++; \
+ if ((iv16) == 0) \
+ (iv32)++; \
+}
+
+/* Transmit rate information constants */
+#define TX_RATE_FORMAT_LEGACY 0
+#define TX_RATE_FORMAT_11N 1
+#define TX_RATE_FORMAT_11AC 2
+
+#define TX_RATE_BANDWIDTH_20 0
+#define TX_RATE_BANDWIDTH_40 1
+#define TX_RATE_BANDWIDTH_80 2
+
+#define TX_RATE_INFO_STD_GI 0
+#define TX_RATE_INFO_SHORT_GI 1
+
+enum {
+ IEEE_TYPE_MANAGEMENT = 0,
+ IEEE_TYPE_CONTROL,
+ IEEE_TYPE_DATA
+};
+
+struct ccmp_hdr {
+ __le16 iv16;
+ u8 rsvd;
+ u8 key_id;
+ __le32 iv32;
+} __packed;
+
+static int mwl_tx_ring_alloc(struct mwl_priv *priv)
+{
+ struct mwl_desc_data *desc;
+ int num;
+ u8 *mem;
+
+ desc = &priv->desc_data[0];
+
+ mem = dma_alloc_coherent(priv->dev,
+ MAX_NUM_TX_RING_BYTES *
+ SYSADPT_NUM_OF_DESC_DATA,
+ &desc->pphys_tx_ring,
+ GFP_KERNEL);
+
+ if (!mem) {
+ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n");
+ return -ENOMEM;
+ }
+
+ for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+ desc = &priv->desc_data[num];
+
+ desc->ptx_ring = (struct mwl_tx_desc *)
+ (mem + num * MAX_NUM_TX_RING_BYTES);
+
+ desc->pphys_tx_ring = (dma_addr_t)
+ ((u32)priv->desc_data[0].pphys_tx_ring +
+ num * MAX_NUM_TX_RING_BYTES);
+
+ memset(desc->ptx_ring, 0x00,
+ MAX_NUM_TX_RING_BYTES);
+ }
+
+ mem = kmalloc(MAX_NUM_TX_HNDL_BYTES * SYSADPT_NUM_OF_DESC_DATA,
+ GFP_KERNEL);
+
+ if (!mem) {
+ wiphy_err(priv->hw->wiphy, "cannot alloc mem\n");
+ dma_free_coherent(priv->dev,
+ MAX_NUM_TX_RING_BYTES *
+ SYSADPT_NUM_OF_DESC_DATA,
+ priv->desc_data[0].ptx_ring,
+ priv->desc_data[0].pphys_tx_ring);
+ return -ENOMEM;
+ }
+
+ for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+ desc = &priv->desc_data[num];
+
+ desc->tx_hndl = (struct mwl_tx_hndl *)
+ (mem + num * MAX_NUM_TX_HNDL_BYTES);
+
+ memset(desc->tx_hndl, 0x00,
+ MAX_NUM_TX_HNDL_BYTES);
+ }
+
+ return 0;
+}
+
+static int mwl_tx_ring_init(struct mwl_priv *priv)
+{
+ int num, i;
+ struct mwl_desc_data *desc;
+
+ for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+ skb_queue_head_init(&priv->txq[num]);
+ priv->fw_desc_cnt[num] = 0;
+
+ desc = &priv->desc_data[num];
+
+ if (desc->ptx_ring) {
+ for (i = 0; i < SYSADPT_MAX_NUM_TX_DESC; i++) {
+ desc->ptx_ring[i].status =
+ cpu_to_le32(EAGLE_TXD_STATUS_IDLE);
+ desc->ptx_ring[i].pphys_next =
+ cpu_to_le32((u32)desc->pphys_tx_ring +
+ ((i + 1) * sizeof(struct mwl_tx_desc)));
+ desc->tx_hndl[i].pdesc =
+ &desc->ptx_ring[i];
+ if (i < SYSADPT_MAX_NUM_TX_DESC - 1)
+ desc->tx_hndl[i].pnext =
+ &desc->tx_hndl[i + 1];
+ }
+ desc->ptx_ring[SYSADPT_MAX_NUM_TX_DESC - 1].pphys_next =
+ cpu_to_le32((u32)desc->pphys_tx_ring);
+ desc->tx_hndl[SYSADPT_MAX_NUM_TX_DESC - 1].pnext =
+ &desc->tx_hndl[0];
+
+ desc->pstale_tx_hndl = &desc->tx_hndl[0];
+ desc->pnext_tx_hndl = &desc->tx_hndl[0];
+ } else {
+ wiphy_err(priv->hw->wiphy, "no valid TX mem\n");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static void mwl_tx_ring_cleanup(struct mwl_priv *priv)
+{
+ int cleaned_tx_desc = 0;
+ int num, i;
+ struct mwl_desc_data *desc;
+
+ for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+ skb_queue_purge(&priv->txq[num]);
+ priv->fw_desc_cnt[num] = 0;
+
+ desc = &priv->desc_data[num];
+
+ if (desc->ptx_ring) {
+ for (i = 0; i < SYSADPT_MAX_NUM_TX_DESC; i++) {
+ if (!desc->tx_hndl[i].psk_buff)
+ continue;
+
+ wiphy_info(priv->hw->wiphy,
+ "unmapped and free'd %i 0x%p 0x%x\n",
+ i,
+ desc->tx_hndl[i].psk_buff->data,
+ le32_to_cpu(
+ desc->ptx_ring[i].pkt_ptr));
+ pci_unmap_single(priv->pdev,
+ le32_to_cpu(
+ desc->ptx_ring[i].pkt_ptr),
+ desc->tx_hndl[i].psk_buff->len,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(desc->tx_hndl[i].psk_buff);
+ desc->ptx_ring[i].status =
+ cpu_to_le32(EAGLE_TXD_STATUS_IDLE);
+ desc->ptx_ring[i].pkt_ptr = 0;
+ desc->ptx_ring[i].pkt_len = 0;
+ desc->tx_hndl[i].psk_buff = NULL;
+ cleaned_tx_desc++;
+ }
+ }
+ }
+
+ wiphy_info(priv->hw->wiphy, "cleaned %i TX descr\n", cleaned_tx_desc);
+}
+
+static void mwl_tx_ring_free(struct mwl_priv *priv)
+{
+ int num;
+
+ if (priv->desc_data[0].ptx_ring) {
+ dma_free_coherent(priv->dev,
+ MAX_NUM_TX_RING_BYTES *
+ SYSADPT_NUM_OF_DESC_DATA,
+ priv->desc_data[0].ptx_ring,
+ priv->desc_data[0].pphys_tx_ring);
+ }
+
+ for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+ if (priv->desc_data[num].ptx_ring)
+ priv->desc_data[num].ptx_ring = NULL;
+ priv->desc_data[num].pstale_tx_hndl = NULL;
+ priv->desc_data[num].pnext_tx_hndl = NULL;
+ }
+
+ kfree(priv->desc_data[0].tx_hndl);
+}
+
+static inline void mwl_tx_add_dma_header(struct mwl_priv *priv,
+ struct sk_buff *skb,
+ int head_pad,
+ int tail_pad)
+{
+ struct ieee80211_hdr *wh;
+ int hdrlen;
+ int reqd_hdrlen;
+ struct mwl_dma_data *tr;
+
+ /* Add a firmware DMA header; the firmware requires that we
+ * present a 2-byte payload length followed by a 4-address
+ * header (without QoS field), followed (optionally) by any
+ * WEP/ExtIV header (but only filled in for CCMP).
+ */
+ wh = (struct ieee80211_hdr *)skb->data;
+
+ hdrlen = ieee80211_hdrlen(wh->frame_control);
+
+ reqd_hdrlen = sizeof(*tr) + head_pad;
+
+ if (hdrlen != reqd_hdrlen)
+ skb_push(skb, reqd_hdrlen - hdrlen);
+
+ if (ieee80211_is_data_qos(wh->frame_control))
+ hdrlen -= IEEE80211_QOS_CTL_LEN;
+
+ tr = (struct mwl_dma_data *)skb->data;
+
+ if (wh != &tr->wh)
+ memmove(&tr->wh, wh, hdrlen);
+
+ if (hdrlen != sizeof(tr->wh))
+ memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen);
+
+ /* Firmware length is the length of the fully formed "802.11
+ * payload". That is, everything except for the 802.11 header.
+ * This includes all crypto material including the MIC.
+ */
+ tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr) + tail_pad);
+}
+
+static inline void mwl_tx_encapsulate_frame(struct mwl_priv *priv,
+ struct sk_buff *skb,
+ struct ieee80211_key_conf *k_conf,
+ bool *ccmp)
+{
+ int head_pad = 0;
+ int data_pad = 0;
+
+ /* Make sure the packet header is in the DMA header format (4-address
+ * without QoS), and add head & tail padding when HW crypto is enabled.
+ *
+ * We have the following trailer padding requirements:
+ * - WEP: 4 trailer bytes (ICV)
+ * - TKIP: 12 trailer bytes (8 MIC + 4 ICV)
+ * - CCMP: 8 trailer bytes (MIC)
+ */
+
+ if (k_conf) {
+ head_pad = k_conf->iv_len;
+
+ switch (k_conf->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ data_pad = 4;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ data_pad = 12;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ data_pad = 8;
+ *ccmp = true;
+ break;
+ }
+ }
+
+ mwl_tx_add_dma_header(priv, skb, head_pad, data_pad);
+}
+
+static inline void mwl_tx_insert_ccmp_hdr(u8 *pccmp_hdr,
+ u8 key_id, u16 iv16, u32 iv32)
+{
+ struct ccmp_hdr *ccmp_h = (struct ccmp_hdr *)pccmp_hdr;
+
+ ccmp_h->iv16 = cpu_to_le16(iv16);
+ ccmp_h->rsvd = 0;
+ ccmp_h->key_id = EXT_IV | (key_id << 6);
+ ccmp_h->iv32 = cpu_to_le32(iv32);
+}
+
+static inline int mwl_tx_tid_queue_mapping(u8 tid)
+{
+ switch (tid) {
+ case 0:
+ case 3:
+ return IEEE80211_AC_BE;
+ case 1:
+ case 2:
+ return IEEE80211_AC_BK;
+ case 4:
+ case 5:
+ return IEEE80211_AC_VI;
+ case 6:
+ case 7:
+ return IEEE80211_AC_VO;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+static inline void mwl_tx_count_packet(struct ieee80211_sta *sta, u8 tid)
+{
+ struct mwl_sta *sta_info;
+ struct mwl_tx_info *tx_stats;
+
+ if (WARN_ON(tid >= SYSADPT_MAX_TID))
+ return;
+
+ sta_info = mwl_dev_get_sta(sta);
+
+ tx_stats = &sta_info->tx_stats[tid];
+
+ if (tx_stats->start_time == 0)
+ tx_stats->start_time = jiffies;
+
+ /* reset the packet count after each second elapses. If the number of
+ * packets ever exceeds the ampdu_min_traffic threshold, we will allow
+ * an ampdu stream to be started.
+ */
+ if (jiffies - tx_stats->start_time > HZ) {
+ tx_stats->pkts = 0;
+ tx_stats->start_time = jiffies;
+ } else {
+ tx_stats->pkts++;
+ }
+}
+
+static inline bool mwl_tx_available(struct mwl_priv *priv, int desc_num)
+{
+ struct mwl_tx_hndl *tx_hndl;
+
+ tx_hndl = priv->desc_data[desc_num].pnext_tx_hndl;
+
+ if (!tx_hndl->pdesc)
+ return false;
+
+ if (tx_hndl->pdesc->status != EAGLE_TXD_STATUS_IDLE) {
+ /* Interrupt F/W anyway */
+ if (tx_hndl->pdesc->status &
+ cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED))
+ writel(MACREG_H2ARIC_BIT_PPA_READY,
+ priv->iobase1 +
+ MACREG_REG_H2A_INTERRUPT_EVENTS);
+ return false;
+ }
+
+ return true;
+}
+
+static inline void mwl_tx_skb(struct mwl_priv *priv, int desc_num,
+ struct sk_buff *tx_skb)
+{
+ struct ieee80211_tx_info *tx_info;
+ struct mwl_tx_ctrl *tx_ctrl;
+ struct mwl_tx_hndl *tx_hndl;
+ struct mwl_tx_desc *tx_desc;
+ struct ieee80211_sta *sta;
+ struct ieee80211_vif *vif;
+ struct mwl_vif *mwl_vif;
+ struct ieee80211_key_conf *k_conf;
+ bool ccmp = false;
+ struct mwl_dma_data *dma_data;
+ struct ieee80211_hdr *wh;
+ dma_addr_t dma;
+
+ if (WARN_ON(!tx_skb))
+ return;
+
+ tx_info = IEEE80211_SKB_CB(tx_skb);
+ tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+ sta = (struct ieee80211_sta *)tx_ctrl->sta;
+ vif = (struct ieee80211_vif *)tx_ctrl->vif;
+ mwl_vif = mwl_dev_get_vif(vif);
+ k_conf = (struct ieee80211_key_conf *)tx_ctrl->k_conf;
+
+ mwl_tx_encapsulate_frame(priv, tx_skb, k_conf, &ccmp);
+
+ dma_data = (struct mwl_dma_data *)tx_skb->data;
+ wh = &dma_data->wh;
+
+ if (ieee80211_is_data(wh->frame_control) ||
+ (ieee80211_is_mgmt(wh->frame_control) &&
+ ieee80211_has_protected(wh->frame_control) &&
+ !is_multicast_ether_addr(wh->addr1))) {
+ if (is_multicast_ether_addr(wh->addr1)) {
+ if (ccmp) {
+ mwl_tx_insert_ccmp_hdr(dma_data->data,
+ mwl_vif->keyidx,
+ mwl_vif->iv16,
+ mwl_vif->iv32);
+ INCREASE_IV(mwl_vif->iv16, mwl_vif->iv32);
+ }
+ } else {
+ if (ccmp) {
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ mwl_tx_insert_ccmp_hdr(dma_data->data,
+ mwl_vif->keyidx,
+ mwl_vif->iv16,
+ mwl_vif->iv32);
+ INCREASE_IV(mwl_vif->iv16,
+ mwl_vif->iv32);
+ } else {
+ struct mwl_sta *sta_info;
+
+ sta_info = mwl_dev_get_sta(sta);
+
+ mwl_tx_insert_ccmp_hdr(dma_data->data,
+ 0,
+ sta_info->iv16,
+ sta_info->iv32);
+ INCREASE_IV(sta_info->iv16,
+ sta_info->iv32);
+ }
+ }
+ }
+ }
+
+ tx_hndl = priv->desc_data[desc_num].pnext_tx_hndl;
+ tx_hndl->psk_buff = tx_skb;
+ tx_desc = tx_hndl->pdesc;
+ tx_desc->tx_priority = tx_ctrl->tx_priority;
+ tx_desc->qos_ctrl = cpu_to_le16(tx_ctrl->qos_ctrl);
+ tx_desc->pkt_len = cpu_to_le16(tx_skb->len);
+ tx_desc->packet_info = 0;
+ tx_desc->data_rate = 0;
+ tx_desc->type = tx_ctrl->type;
+ tx_desc->xmit_control = tx_ctrl->xmit_control;
+ tx_desc->sap_pkt_info = 0;
+ dma = pci_map_single(priv->pdev, tx_skb->data,
+ tx_skb->len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(priv->pdev, dma)) {
+ dev_kfree_skb_any(tx_skb);
+ wiphy_err(priv->hw->wiphy,
+ "failed to map pci memory!\n");
+ return;
+ }
+ tx_desc->pkt_ptr = cpu_to_le32(dma);
+ tx_desc->status = cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED);
+ /* make sure all the memory transactions done by cpu were completed */
+ wmb(); /*Data Memory Barrier*/
+ writel(MACREG_H2ARIC_BIT_PPA_READY,
+ priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
+ priv->desc_data[desc_num].pnext_tx_hndl = tx_hndl->pnext;
+ priv->fw_desc_cnt[desc_num]++;
+}
+
+static inline struct sk_buff *mwl_tx_do_amsdu(struct mwl_priv *priv,
+ int desc_num,
+ struct sk_buff *tx_skb,
+ struct ieee80211_tx_info *tx_info)
+{
+ struct ieee80211_sta *sta;
+ struct mwl_sta *sta_info;
+ struct mwl_tx_ctrl *tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+ struct ieee80211_tx_info *amsdu_info;
+ struct sk_buff_head *amsdu_pkts;
+ struct mwl_amsdu_frag *amsdu;
+ int amsdu_allow_size;
+ struct ieee80211_hdr *wh;
+ int wh_len;
+ u16 len;
+ u8 *data;
+
+ sta = (struct ieee80211_sta *)tx_ctrl->sta;
+ sta_info = mwl_dev_get_sta(sta);
+
+ if (!sta_info->is_amsdu_allowed)
+ return tx_skb;
+
+ wh = (struct ieee80211_hdr *)tx_skb->data;
+ if (sta_info->is_mesh_node && is_multicast_ether_addr(wh->addr3))
+ return tx_skb;
+
+ if (sta_info->amsdu_ctrl.cap == MWL_AMSDU_SIZE_4K)
+ amsdu_allow_size = SYSADPT_AMSDU_4K_MAX_SIZE;
+ else if (sta_info->amsdu_ctrl.cap == MWL_AMSDU_SIZE_8K)
+ amsdu_allow_size = SYSADPT_AMSDU_8K_MAX_SIZE;
+ else
+ return tx_skb;
+
+ spin_lock_bh(&sta_info->amsdu_lock);
+ amsdu = &sta_info->amsdu_ctrl.frag[desc_num];
+
+ if (tx_skb->len > SYSADPT_AMSDU_ALLOW_SIZE) {
+ if (amsdu->num) {
+ mwl_tx_skb(priv, desc_num, amsdu->skb);
+ amsdu->num = 0;
+ amsdu->cur_pos = NULL;
+ }
+ spin_unlock_bh(&sta_info->amsdu_lock);
+ return tx_skb;
+ }
+
+ /* potential amsdu size, should add amsdu header 14 bytes +
+ * maximum padding 3.
+ */
+ wh_len = ieee80211_hdrlen(wh->frame_control);
+ len = tx_skb->len - wh_len + 17;
+
+ if (amsdu->num) {
+ if ((amsdu->skb->len + len) > amsdu_allow_size) {
+ mwl_tx_skb(priv, desc_num, amsdu->skb);
+ amsdu->num = 0;
+ amsdu->cur_pos = NULL;
+ }
+ }
+
+ amsdu->jiffies = jiffies;
+ len = tx_skb->len - wh_len;
+
+ if (amsdu->num == 0) {
+ struct sk_buff *newskb;
+
+ amsdu_pkts = (struct sk_buff_head *)
+ kmalloc(sizeof(*amsdu_pkts), GFP_ATOMIC);
+ if (!amsdu_pkts) {
+ spin_unlock_bh(&sta_info->amsdu_lock);
+ return tx_skb;
+ }
+ newskb = dev_alloc_skb(amsdu_allow_size +
+ SYSADPT_MIN_BYTES_HEADROOM);
+ if (!newskb) {
+ spin_unlock_bh(&sta_info->amsdu_lock);
+ kfree(amsdu_pkts);
+ return tx_skb;
+ }
+
+ data = newskb->data;
+ memcpy(data, tx_skb->data, wh_len);
+ if (sta_info->is_mesh_node) {
+ ether_addr_copy(data + wh_len, wh->addr3);
+ ether_addr_copy(data + wh_len + ETH_ALEN, wh->addr4);
+ } else {
+ ether_addr_copy(data + wh_len,
+ ieee80211_get_DA(wh));
+ ether_addr_copy(data + wh_len + ETH_ALEN,
+ ieee80211_get_SA(wh));
+ }
+ *(u8 *)(data + wh_len + ETH_HLEN - 1) = len & 0xff;
+ *(u8 *)(data + wh_len + ETH_HLEN - 2) = (len >> 8) & 0xff;
+ memcpy(data + wh_len + ETH_HLEN, tx_skb->data + wh_len, len);
+
+ skb_put(newskb, tx_skb->len + ETH_HLEN);
+ tx_ctrl->qos_ctrl |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+ amsdu_info = IEEE80211_SKB_CB(newskb);
+ memcpy(amsdu_info, tx_info, sizeof(*tx_info));
+ skb_queue_head_init(amsdu_pkts);
+ ((struct mwl_tx_ctrl *)&amsdu_info->status)->amsdu_pkts =
+ (void *)amsdu_pkts;
+ amsdu->skb = newskb;
+ } else {
+ amsdu->cur_pos += amsdu->pad;
+ data = amsdu->cur_pos;
+
+ if (sta_info->is_mesh_node) {
+ ether_addr_copy(data, wh->addr3);
+ ether_addr_copy(data + ETH_ALEN, wh->addr4);
+ } else {
+ ether_addr_copy(data, ieee80211_get_DA(wh));
+ ether_addr_copy(data + ETH_ALEN, ieee80211_get_SA(wh));
+ }
+ *(u8 *)(data + ETH_HLEN - 1) = len & 0xff;
+ *(u8 *)(data + ETH_HLEN - 2) = (len >> 8) & 0xff;
+ memcpy(data + ETH_HLEN, tx_skb->data + wh_len, len);
+
+ skb_put(amsdu->skb, len + ETH_HLEN + amsdu->pad);
+ amsdu_info = IEEE80211_SKB_CB(amsdu->skb);
+ amsdu_pkts = (struct sk_buff_head *)
+ ((struct mwl_tx_ctrl *)&amsdu_info->status)->amsdu_pkts;
+ }
+
+ amsdu->num++;
+ amsdu->pad = ((len + ETH_HLEN) % 4) ? (4 - (len + ETH_HLEN) % 4) : 0;
+ amsdu->cur_pos = amsdu->skb->data + amsdu->skb->len;
+ skb_queue_tail(amsdu_pkts, tx_skb);
+
+ if (amsdu->num > SYSADPT_AMSDU_PACKET_THRESHOLD) {
+ amsdu->num = 0;
+ amsdu->cur_pos = NULL;
+ spin_unlock_bh(&sta_info->amsdu_lock);
+ return amsdu->skb;
+ }
+
+ spin_unlock_bh(&sta_info->amsdu_lock);
+ return NULL;
+}
+
+static inline void mwl_tx_prepare_info(struct ieee80211_hw *hw, u32 rate,
+ struct ieee80211_tx_info *info)
+{
+ u32 format, bandwidth, short_gi, rate_id;
+
+ ieee80211_tx_info_clear_status(info);
+
+ info->status.rates[0].idx = -1;
+ info->status.rates[0].count = 0;
+ info->status.rates[0].flags = 0;
+
+ if (rate) {
+ /* Prepare rate information */
+ format = rate & MWL_TX_RATE_FORMAT_MASK;
+ bandwidth =
+ (rate & MWL_TX_RATE_BANDWIDTH_MASK) >>
+ MWL_TX_RATE_BANDWIDTH_SHIFT;
+ short_gi = (rate & MWL_TX_RATE_SHORTGI_MASK) >>
+ MWL_TX_RATE_SHORTGI_SHIFT;
+ rate_id = (rate & MWL_TX_RATE_RATEIDMCS_MASK) >>
+ MWL_TX_RATE_RATEIDMCS_SHIFT;
+
+ info->status.rates[0].idx = rate_id;
+ if (format == TX_RATE_FORMAT_LEGACY) {
+ if (hw->conf.chandef.chan->hw_value >
+ BAND_24_CHANNEL_NUM) {
+ info->status.rates[0].idx -= 5;
+ }
+ }
+ if (format == TX_RATE_FORMAT_11N)
+ info->status.rates[0].flags |=
+ IEEE80211_TX_RC_MCS;
+ if (format == TX_RATE_FORMAT_11AC)
+ info->status.rates[0].flags |=
+ IEEE80211_TX_RC_VHT_MCS;
+ if (bandwidth == TX_RATE_BANDWIDTH_40)
+ info->status.rates[0].flags |=
+ IEEE80211_TX_RC_40_MHZ_WIDTH;
+ if (bandwidth == TX_RATE_BANDWIDTH_80)
+ info->status.rates[0].flags |=
+ IEEE80211_TX_RC_80_MHZ_WIDTH;
+ if (short_gi == TX_RATE_INFO_SHORT_GI)
+ info->status.rates[0].flags |=
+ IEEE80211_TX_RC_SHORT_GI;
+ info->status.rates[0].count = 1;
+ info->status.rates[1].idx = -1;
+ }
+}
+
+static inline void mwl_tx_ack_amsdu_pkts(struct ieee80211_hw *hw, u32 rate,
+ struct sk_buff_head *amsdu_pkts)
+{
+ struct sk_buff *amsdu_pkt;
+ struct ieee80211_tx_info *info;
+
+ while (skb_queue_len(amsdu_pkts) > 0) {
+ amsdu_pkt = skb_dequeue(amsdu_pkts);
+ info = IEEE80211_SKB_CB(amsdu_pkt);
+ mwl_tx_prepare_info(hw, rate, info);
+ info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ ieee80211_tx_status(hw, amsdu_pkt);
+ }
+
+ kfree(amsdu_pkts);
+}
+
+int mwl_tx_init(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+ int rc;
+
+ skb_queue_head_init(&priv->delay_q);
+
+ rc = mwl_tx_ring_alloc(priv);
+ if (rc) {
+ wiphy_err(hw->wiphy, "allocating TX ring failed\n");
+ return rc;
+ }
+
+ rc = mwl_tx_ring_init(priv);
+ if (rc) {
+ mwl_tx_ring_free(priv);
+ wiphy_err(hw->wiphy, "initializing TX ring failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+void mwl_tx_deinit(struct ieee80211_hw *hw)
+{
+ struct mwl_priv *priv = hw->priv;
+
+ skb_queue_purge(&priv->delay_q);
+
+ mwl_tx_ring_cleanup(priv);
+ mwl_tx_ring_free(priv);
+}
+
+void mwl_tx_xmit(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct mwl_priv *priv = hw->priv;
+ int index;
+ struct ieee80211_sta *sta;
+ struct ieee80211_tx_info *tx_info;
+ struct mwl_vif *mwl_vif;
+ struct ieee80211_hdr *wh;
+ u8 xmitcontrol;
+ u16 qos;
+ int txpriority;
+ u8 tid = 0;
+ struct mwl_ampdu_stream *stream = NULL;
+ bool start_ba_session = false;
+ bool mgmtframe = false;
+ struct ieee80211_mgmt *mgmt;
+ bool eapol_frame = false;
+ struct mwl_tx_ctrl *tx_ctrl;
+ struct ieee80211_key_conf *k_conf = NULL;
+
+ index = skb_get_queue_mapping(skb);
+ sta = control->sta;
+
+ wh = (struct ieee80211_hdr *)skb->data;
+
+ if (ieee80211_is_data_qos(wh->frame_control))
+ qos = *((u16 *)ieee80211_get_qos_ctl(wh));
+ else
+ qos = 0;
+
+ if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
+ index = IEEE80211_AC_VO;
+ eapol_frame = true;
+ }
+
+ if (ieee80211_is_mgmt(wh->frame_control)) {
+ mgmtframe = true;
+ mgmt = (struct ieee80211_mgmt *)skb->data;
+ }
+
+ tx_info = IEEE80211_SKB_CB(skb);
+ mwl_vif = mwl_dev_get_vif(tx_info->control.vif);
+
+ if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+ wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+ wh->seq_ctrl |= cpu_to_le16(mwl_vif->seqno);
+ mwl_vif->seqno += 0x10;
+ }
+
+ /* Setup firmware control bit fields for each frame type. */
+ xmitcontrol = 0;
+
+ if (mgmtframe || ieee80211_is_ctl(wh->frame_control)) {
+ qos = 0;
+ } else if (ieee80211_is_data(wh->frame_control)) {
+ qos &= ~MWL_QOS_ACK_POLICY_MASK;
+
+ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+ xmitcontrol &= 0xfb;
+ qos |= MWL_QOS_ACK_POLICY_BLOCKACK;
+ } else {
+ xmitcontrol |= 0x4;
+ qos |= MWL_QOS_ACK_POLICY_NORMAL;
+ }
+
+ if (is_multicast_ether_addr(wh->addr1))
+ xmitcontrol |= EAGLE_TXD_XMITCTRL_USE_MC_RATE;
+ }
+
+ k_conf = tx_info->control.hw_key;
+
+ /* Queue ADDBA request in the respective data queue. While setting up
+ * the ampdu stream, mac80211 queues further packets for that
+ * particular ra/tid pair. However, packets piled up in the hardware
+ * for that ra/tid pair will still go out. ADDBA request and the
+ * related data packets going out from different queues asynchronously
+ * will cause a shift in the receiver window which might result in
+ * ampdu packets getting dropped at the receiver after the stream has
+ * been setup.
+ */
+ if (mgmtframe) {
+ u16 capab;
+
+ if (unlikely(ieee80211_is_action(wh->frame_control) &&
+ mgmt->u.action.category == WLAN_CATEGORY_BACK &&
+ mgmt->u.action.u.addba_req.action_code ==
+ WLAN_ACTION_ADDBA_REQ)) {
+ capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
+ tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+ index = mwl_tx_tid_queue_mapping(tid);
+ }
+ }
+
+ index = SYSADPT_TX_WMM_QUEUES - index - 1;
+ txpriority = index;
+
+ if (sta && sta->ht_cap.ht_supported && !eapol_frame &&
+ ieee80211_is_data_qos(wh->frame_control)) {
+ tid = qos & 0xf;
+ mwl_tx_count_packet(sta, tid);
+
+ spin_lock_bh(&priv->stream_lock);
+ stream = mwl_fwcmd_lookup_stream(hw, sta->addr, tid);
+
+ if (stream) {
+ if (stream->state == AMPDU_STREAM_ACTIVE) {
+ if (WARN_ON(!(qos &
+ MWL_QOS_ACK_POLICY_BLOCKACK))) {
+ spin_unlock_bh(&priv->stream_lock);
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ txpriority =
+ (SYSADPT_TX_WMM_QUEUES + stream->idx) %
+ SYSADPT_TOTAL_HW_QUEUES;
+ } else if (stream->state == AMPDU_STREAM_NEW) {
+ /* We get here if the driver sends us packets
+ * after we've initiated a stream, but before
+ * our ampdu_action routine has been called
+ * with IEEE80211_AMPDU_TX_START to get the SSN
+ * for the ADDBA request. So this packet can
+ * go out with no risk of sequence number
+ * mismatch. No special handling is required.
+ */
+ } else {
+ /* Drop packets that would go out after the
+ * ADDBA request was sent but before the ADDBA
+ * response is received. If we don't do this,
+ * the recipient would probably receive it
+ * after the ADDBA request with SSN 0. This
+ * will cause the recipient's BA receive window
+ * to shift, which would cause the subsequent
+ * packets in the BA stream to be discarded.
+ * mac80211 queues our packets for us in this
+ * case, so this is really just a safety check.
+ */
+ wiphy_warn(hw->wiphy,
+ "can't send packet during ADDBA\n");
+ spin_unlock_bh(&priv->stream_lock);
+ dev_kfree_skb_any(skb);
+ return;
+ }
+ } else {
+ if (mwl_fwcmd_ampdu_allowed(sta, tid)) {
+ stream = mwl_fwcmd_add_stream(hw, sta, tid);
+
+ if (stream)
+ start_ba_session = true;
+ }
+ }
+
+ spin_unlock_bh(&priv->stream_lock);
+ } else {
+ qos &= ~MWL_QOS_ACK_POLICY_MASK;
+ qos |= MWL_QOS_ACK_POLICY_NORMAL;
+ }
+
+ tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+ tx_ctrl->vif = (void *)tx_info->control.vif;
+ tx_ctrl->sta = (void *)sta;
+ tx_ctrl->k_conf = (void *)k_conf;
+ tx_ctrl->amsdu_pkts = NULL;
+ tx_ctrl->tx_priority = txpriority;
+ tx_ctrl->type = (mgmtframe ? IEEE_TYPE_MANAGEMENT : IEEE_TYPE_DATA);
+ tx_ctrl->qos_ctrl = qos;
+ tx_ctrl->xmit_control = xmitcontrol;
+
+ if (skb_queue_len(&priv->txq[index]) > priv->txq_limit)
+ ieee80211_stop_queue(hw, SYSADPT_TX_WMM_QUEUES - index - 1);
+
+ skb_queue_tail(&priv->txq[index], skb);
+
+ tasklet_schedule(&priv->tx_task);
+
+ /* Initiate the ampdu session here */
+ if (start_ba_session) {
+ spin_lock_bh(&priv->stream_lock);
+ if (mwl_fwcmd_start_stream(hw, stream))
+ mwl_fwcmd_remove_stream(hw, stream);
+ spin_unlock_bh(&priv->stream_lock);
+ }
+}
+
+void mwl_tx_del_pkts_via_vif(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct mwl_priv *priv = hw->priv;
+ int num;
+ struct sk_buff *skb, *tmp;
+ struct ieee80211_tx_info *tx_info;
+ struct mwl_tx_ctrl *tx_ctrl;
+ struct sk_buff_head *amsdu_pkts;
+
+ for (num = 1; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+ spin_lock_bh(&priv->txq[num].lock);
+ skb_queue_walk_safe(&priv->txq[num], skb, tmp) {
+ tx_info = IEEE80211_SKB_CB(skb);
+ tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+ if (tx_ctrl->vif == vif) {
+ amsdu_pkts = (struct sk_buff_head *)
+ tx_ctrl->amsdu_pkts;
+ if (amsdu_pkts) {
+ skb_queue_purge(amsdu_pkts);
+ kfree(amsdu_pkts);
+ }
+ __skb_unlink(skb, &priv->txq[num]);
+ dev_kfree_skb_any(skb);
+ }
+ }
+ spin_unlock_bh(&priv->txq[num].lock);
+ }
+}
+
+void mwl_tx_del_pkts_via_sta(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta)
+{
+ struct mwl_priv *priv = hw->priv;
+ int num;
+ struct sk_buff *skb, *tmp;
+ struct ieee80211_tx_info *tx_info;
+ struct mwl_tx_ctrl *tx_ctrl;
+ struct sk_buff_head *amsdu_pkts;
+
+ for (num = 1; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+ spin_lock_bh(&priv->txq[num].lock);
+ skb_queue_walk_safe(&priv->txq[num], skb, tmp) {
+ tx_info = IEEE80211_SKB_CB(skb);
+ tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+ if (tx_ctrl->sta == sta) {
+ amsdu_pkts = (struct sk_buff_head *)
+ tx_ctrl->amsdu_pkts;
+ if (amsdu_pkts) {
+ skb_queue_purge(amsdu_pkts);
+ kfree(amsdu_pkts);
+ }
+ __skb_unlink(skb, &priv->txq[num]);
+ dev_kfree_skb_any(skb);
+ }
+ }
+ spin_unlock_bh(&priv->txq[num].lock);
+ }
+}
+
+void mwl_tx_del_ampdu_pkts(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta, u8 tid)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_sta *sta_info = mwl_dev_get_sta(sta);
+ int ac, desc_num;
+ struct mwl_amsdu_frag *amsdu_frag;
+ struct sk_buff *skb, *tmp;
+ struct ieee80211_tx_info *tx_info;
+ struct mwl_tx_ctrl *tx_ctrl;
+ struct sk_buff_head *amsdu_pkts;
+
+ ac = mwl_tx_tid_queue_mapping(tid);
+ desc_num = SYSADPT_TX_WMM_QUEUES - ac - 1;
+ spin_lock_bh(&priv->txq[desc_num].lock);
+ skb_queue_walk_safe(&priv->txq[desc_num], skb, tmp) {
+ tx_info = IEEE80211_SKB_CB(skb);
+ tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+ if (tx_ctrl->sta == sta) {
+ amsdu_pkts = (struct sk_buff_head *)
+ tx_ctrl->amsdu_pkts;
+ if (amsdu_pkts) {
+ skb_queue_purge(amsdu_pkts);
+ kfree(amsdu_pkts);
+ }
+ __skb_unlink(skb, &priv->txq[desc_num]);
+ dev_kfree_skb_any(skb);
+ }
+ }
+ spin_unlock_bh(&priv->txq[desc_num].lock);
+
+ spin_lock_bh(&sta_info->amsdu_lock);
+ amsdu_frag = &sta_info->amsdu_ctrl.frag[desc_num];
+ if (amsdu_frag->num) {
+ amsdu_frag->num = 0;
+ amsdu_frag->cur_pos = NULL;
+ if (amsdu_frag->skb) {
+ tx_info = IEEE80211_SKB_CB(amsdu_frag->skb);
+ tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+ amsdu_pkts = (struct sk_buff_head *)
+ tx_ctrl->amsdu_pkts;
+ if (amsdu_pkts) {
+ skb_queue_purge(amsdu_pkts);
+ kfree(amsdu_pkts);
+ }
+ dev_kfree_skb_any(amsdu_frag->skb);
+ }
+ }
+ spin_unlock_bh(&sta_info->amsdu_lock);
+}
+
+void mwl_tx_skbs(unsigned long data)
+{
+ struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
+ struct mwl_priv *priv = hw->priv;
+ int num = SYSADPT_TX_WMM_QUEUES;
+ struct sk_buff *tx_skb;
+
+ spin_lock_bh(&priv->tx_desc_lock);
+ while (num--) {
+ while (skb_queue_len(&priv->txq[num]) > 0) {
+ struct ieee80211_tx_info *tx_info;
+ struct mwl_tx_ctrl *tx_ctrl;
+
+ if (!mwl_tx_available(priv, num))
+ break;
+
+ tx_skb = skb_dequeue(&priv->txq[num]);
+ tx_info = IEEE80211_SKB_CB(tx_skb);
+ tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+
+ if ((tx_skb->protocol != cpu_to_be16(ETH_P_PAE)) &&
+ (tx_ctrl->tx_priority >= SYSADPT_TX_WMM_QUEUES)) {
+ tx_skb = mwl_tx_do_amsdu(priv, num,
+ tx_skb, tx_info);
+ }
+
+ if (tx_skb) {
+ if (mwl_tx_available(priv, num))
+ mwl_tx_skb(priv, num, tx_skb);
+ else
+ skb_queue_head(&priv->txq[num], tx_skb);
+ }
+ }
+
+ if (skb_queue_len(&priv->txq[num]) <
+ SYSADPT_TX_WAKE_Q_THRESHOLD) {
+ int queue;
+
+ queue = SYSADPT_TX_WMM_QUEUES - num - 1;
+ if (ieee80211_queue_stopped(hw, queue))
+ ieee80211_wake_queue(hw, queue);
+ }
+ }
+ spin_unlock_bh(&priv->tx_desc_lock);
+}
+
+void mwl_tx_done(unsigned long data)
+{
+ struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
+ struct mwl_priv *priv = hw->priv;
+ int num;
+ struct mwl_desc_data *desc;
+ struct mwl_tx_hndl *tx_hndl;
+ struct mwl_tx_desc *tx_desc;
+ struct sk_buff *done_skb;
+ u32 rate;
+ struct mwl_dma_data *tr;
+ struct ieee80211_tx_info *info;
+ struct mwl_tx_ctrl *tx_ctrl;
+ struct sk_buff_head *amsdu_pkts;
+ int hdrlen;
+
+ spin_lock_bh(&priv->tx_desc_lock);
+ for (num = 0; num < SYSADPT_TX_WMM_QUEUES; num++) {
+ desc = &priv->desc_data[num];
+ tx_hndl = desc->pstale_tx_hndl;
+ tx_desc = tx_hndl->pdesc;
+
+ if ((tx_desc->status &
+ cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED)) &&
+ (tx_hndl->pnext->pdesc->status &
+ cpu_to_le32(EAGLE_TXD_STATUS_OK)))
+ tx_desc->status = cpu_to_le32(EAGLE_TXD_STATUS_OK);
+
+ while (tx_hndl &&
+ (tx_desc->status & cpu_to_le32(EAGLE_TXD_STATUS_OK)) &&
+ (!(tx_desc->status &
+ cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED)))) {
+ pci_unmap_single(priv->pdev,
+ le32_to_cpu(tx_desc->pkt_ptr),
+ le16_to_cpu(tx_desc->pkt_len),
+ PCI_DMA_TODEVICE);
+ done_skb = tx_hndl->psk_buff;
+ rate = le32_to_cpu(tx_desc->rate_info);
+ tx_desc->pkt_ptr = 0;
+ tx_desc->pkt_len = 0;
+ tx_desc->status =
+ cpu_to_le32(EAGLE_TXD_STATUS_IDLE);
+ tx_hndl->psk_buff = NULL;
+ wmb(); /* memory barrier */
+
+ skb_get(done_skb);
+ skb_queue_tail(&priv->delay_q, done_skb);
+ if (skb_queue_len(&priv->delay_q) >
+ SYSADPT_DELAY_FREE_Q_LIMIT)
+ dev_kfree_skb_any(skb_dequeue(&priv->delay_q));
+
+ tr = (struct mwl_dma_data *)done_skb->data;
+ info = IEEE80211_SKB_CB(done_skb);
+
+ if (ieee80211_is_data(tr->wh.frame_control) ||
+ ieee80211_is_data_qos(tr->wh.frame_control)) {
+ tx_ctrl = (struct mwl_tx_ctrl *)&info->status;
+ amsdu_pkts = (struct sk_buff_head *)
+ tx_ctrl->amsdu_pkts;
+ if (amsdu_pkts) {
+ mwl_tx_ack_amsdu_pkts(hw, rate,
+ amsdu_pkts);
+ dev_kfree_skb_any(done_skb);
+ done_skb = NULL;
+ } else {
+ mwl_tx_prepare_info(hw, rate, info);
+ }
+ } else {
+ mwl_tx_prepare_info(hw, 0, info);
+ }
+
+ if (done_skb) {
+ /* Remove H/W dma header */
+ hdrlen = ieee80211_hdrlen(tr->wh.frame_control);
+ memmove(tr->data - hdrlen, &tr->wh, hdrlen);
+ skb_pull(done_skb, sizeof(*tr) - hdrlen);
+ info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ ieee80211_tx_status(hw, done_skb);
+ }
+
+ tx_hndl = tx_hndl->pnext;
+ tx_desc = tx_hndl->pdesc;
+ priv->fw_desc_cnt[num]--;
+ }
+
+ desc->pstale_tx_hndl = tx_hndl;
+ }
+ spin_unlock_bh(&priv->tx_desc_lock);
+
+ if (priv->is_tx_done_schedule) {
+ u32 status_mask;
+
+ status_mask = readl(priv->iobase1 +
+ MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+ writel(status_mask | MACREG_A2HRIC_BIT_TX_DONE,
+ priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+
+ tasklet_schedule(&priv->tx_task);
+ priv->is_tx_done_schedule = false;
+ }
+}
+
+void mwl_tx_flush_amsdu(unsigned long data)
+{
+ struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
+ struct mwl_priv *priv = hw->priv;
+ u32 status_mask;
+ struct mwl_sta *sta_info;
+ int i;
+ struct mwl_amsdu_frag *amsdu_frag;
+
+ spin_lock(&priv->sta_lock);
+ list_for_each_entry(sta_info, &priv->sta_list, list) {
+ spin_lock(&priv->tx_desc_lock);
+ spin_lock(&sta_info->amsdu_lock);
+ for (i = 0; i < SYSADPT_TX_WMM_QUEUES; i++) {
+ amsdu_frag = &sta_info->amsdu_ctrl.frag[i];
+ if (amsdu_frag->num) {
+ if (time_after(jiffies,
+ (amsdu_frag->jiffies + 1))) {
+ if (mwl_tx_available(priv, i)) {
+ mwl_tx_skb(priv, i,
+ amsdu_frag->skb);
+ amsdu_frag->num = 0;
+ amsdu_frag->cur_pos = NULL;
+ }
+ }
+ }
+ }
+ spin_unlock(&sta_info->amsdu_lock);
+ spin_unlock(&priv->tx_desc_lock);
+ }
+ spin_unlock(&priv->sta_lock);
+
+ status_mask = readl(priv->iobase1 +
+ MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+ writel(status_mask | MACREG_A2HRIC_BIT_QUE_EMPTY,
+ priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+
+ priv->is_qe_schedule = false;
+}
+
+void mwl_tx_del_sta_amsdu_pkts(struct ieee80211_sta *sta)
+{
+ struct mwl_sta *sta_info = mwl_dev_get_sta(sta);
+ int num;
+ struct mwl_amsdu_frag *amsdu_frag;
+ struct ieee80211_tx_info *tx_info;
+ struct mwl_tx_ctrl *tx_ctrl;
+ struct sk_buff_head *amsdu_pkts;
+
+ spin_lock_bh(&sta_info->amsdu_lock);
+ for (num = 0; num < SYSADPT_TX_WMM_QUEUES; num++) {
+ amsdu_frag = &sta_info->amsdu_ctrl.frag[num];
+ if (amsdu_frag->num) {
+ amsdu_frag->num = 0;
+ amsdu_frag->cur_pos = NULL;
+ if (amsdu_frag->skb) {
+ tx_info = IEEE80211_SKB_CB(amsdu_frag->skb);
+ tx_ctrl = (struct mwl_tx_ctrl *)
+ &tx_info->status;
+ amsdu_pkts = (struct sk_buff_head *)
+ tx_ctrl->amsdu_pkts;
+ if (amsdu_pkts) {
+ skb_queue_purge(amsdu_pkts);
+ kfree(amsdu_pkts);
+ }
+ dev_kfree_skb_any(amsdu_frag->skb);
+ }
+ }
+ }
+ spin_unlock_bh(&sta_info->amsdu_lock);
+}
diff --git a/drivers/net/wireless/marvell/mwlwifi/tx.h b/drivers/net/wireless/marvell/mwlwifi/tx.h
new file mode 100644
index 0000000..eaacc3b
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/tx.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file defines transmit related functions. */
+
+#ifndef _TX_H_
+#define _TX_H_
+
+int mwl_tx_init(struct ieee80211_hw *hw);
+void mwl_tx_deinit(struct ieee80211_hw *hw);
+void mwl_tx_xmit(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb);
+void mwl_tx_del_pkts_via_vif(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+void mwl_tx_del_pkts_via_sta(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta);
+void mwl_tx_del_ampdu_pkts(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta, u8 tid);
+void mwl_tx_skbs(unsigned long data);
+void mwl_tx_done(unsigned long data);
+void mwl_tx_flush_amsdu(unsigned long data);
+void mwl_tx_del_sta_amsdu_pkts(struct ieee80211_sta *sta);
+
+#endif /* _TX_H_ */
--
1.9.3
On Wed, 2016-12-21 at 04:11 +0000, David Lin wrote:
> This patch provides the mwlwifi driver for Marvell 8863, 8864 and
> 8897
> chipsets.
> This driver was developed as part of the openwrt.org project to
> support
> Linksys WRT1900AC and is maintained on https://github.com/kaloz/mwlwi
> fi.
I found some minor things:
* using kmalloc/memset instead of kzalloc
* mwl_fwcmd_get_80m_pri_chnl() could use some arithmetic or at least
switch case consolidation - if it shouldn't even go away entirely
since mac80211 doesn't really limit you to the primary channel as
the spec requires it - so the whole purpose of the function seems a
bit questionable
* wiphy_err(hw->wiphy, "failed execution\n"); seems like a bit too
generic (you have that many times - how are you going to tell which
happened?)
* rates = sta->supp_rates[NL80211_BAND_5GHZ] << 5;
is a bit questionable - that 5 should probably be defined (also,
does that mean you support 1,2, 5.5, 11 and *22MBps*? curious,
almost nobody does that afaict)
* a lot of initializations like
struct mwl_vif *mwl_vif;
[...]
mwl_vif = mwl_dev_get_vif(vif);
could be rolled into a single line of code (which you sometimes do,
apparently always for "priv" but never for "mwl_vif" or "pcmd"?)
* "celcius" isn't really spelled that way - and you probably mean
"temperature" anyway since Celsius is a unit :)
* typo: firware
* a bunch of your IE handling structs seem duplicate with ieee80211.h
* and partially wrong - something with a vendor OUI can't be RSN
(maybe you meant WPA? though it's not quite clear to me why you need
this at all!)
* +#ifdef CONFIG_OF
+#include <linux/of.h>
+#endif
I don't think you have to guard that include ...
* +#ifdef CONFIG_DEBUG_FS
+#include "debugfs.h"
+#endif
likewise
* git am also complained about a blank line at EOF
The only thing that really seems questionable to me is the whole beacon
parsing (and apparent rebuilding in firmware?). It's very odd that you
could do that, with a softmac device where all the authentication and
association is handled by hostapd anyway, and you can't possibly
pretend to handle everything hostapd might throw at you - this will
mean that you'll have features hostapd and every other mac80211
supports that you will silently drop afaict - which is rather
unexpected.
First, you're parsing the data obtained from hostapd, in
mwl_fwcmd_parse_beacon(), and then you send them all to the firmware in
mwl_fwcmd_set_ies(), but only the things you actually understood. New
higher-level features you don't understand, or vendor beacon IEs that
aren't WMM, would be completely dropped.
I'm not very happy with that behaviour.
Why does the firmware require this? Why not just pack all IEs into the
pcmd->ie_list_len_proprietary, and leave everything else 0? Or - if
perhaps firmware for some reason requires HT/VHT to be treated
specially, only parse out the ones that are really needed specially and
leave everything else in the ie_list_len_proprietary?
Also, this is dropping all the encryption stuff - even those are
extensible btw, and hostapd might do so. Having the firmware rebuild
those out of out-of-band information is very unexpected for a mac80211
driver.
johannes
On Tue, 2017-01-10 at 01:32 +0000, David Lin wrote:
> > The only thing that really seems questionable to me is the whole
> > beacon parsing (and apparent rebuilding in firmware?). It's very
> > odd that you could do that, with a softmac device where all the
> > authentication and association is handled by hostapd anyway, and
> > you can't possibly pretend to handle everything hostapd might throw
> > at you - this will mean that you'll have features hostapd and every
> > other mac80211 supports that you will silently drop afaict - which
> > is rather unexpected.
> >
> > First, you're parsing the data obtained from hostapd, in
> > mwl_fwcmd_parse_beacon(), and then you send them all to the
> > firmware in mwl_fwcmd_set_ies(), but only the things you actually
> > understood. New higher-level features you don't understand, or
> > vendor beacon IEs that aren't WMM, would be completely dropped.
> >
> > I'm not very happy with that behaviour.
> >
> > Why does the firmware require this? Why not just pack all IEs into
> > the pcmd->ie_list_len_proprietary, and leave everything else 0? Or
> > - if perhaps firmware for some reason requires HT/VHT to be treated
> > specially, only parse out the ones that are really needed specially
> > and leave everything else in the ie_list_len_proprietary?
> >
> > Also, this is dropping all the encryption stuff - even those are
> > extensible btw, and hostapd might do so. Having the firmware
> > rebuild those out of out-of-band information is very unexpected for
> > a mac80211 driver.
> >
>
> This driver just extracts needed IEs which is used for the host
> command of firmware. I think we will not support other vendor IEs.
> And if needed, we can add them to pcmd->ie_list_proprietary.
Sure, I see that you're doing this. It still makes no sense though,
since all management frames are handled by hostapd anyway. It would
make some sense if you actually were going to handle association in the
firmware, but it makes no sense here, as far as I can tell.
I'm not sure how hard a line I should draw here, but I'll warn you now
that I won't take this into consideration when adding new features to
mac80211, and certainly Jouni won't take it into account when adding
new features to hostapd, so that such new features will then SILENTLY
and UNEXPECTEDLY (due to mac80211) not work with your driver.
I strongly advise you to reconsider this and try to pass through all
the IEs so that newly added features that shouldn't require firmware
interaction (since hostapd is handling all the association handshake
and IEs to advertise the feature) will automatically and cleanly work.
If you really can't do that, for some reason, then for my benefit as
the mac80211 maintainer and future maintainers of your driver, I'd like
to have documentation in the driver as to why the firmware needs the
driver to split up all those IEs and what it does with them. After all,
a common-sense analysis would suggest that the firmware has no need for
it, since all configuration should come through other places and all
IEs are just for going out on the air.
johannes
PiBKb2hhbm5lcyBCZXJnIFttYWlsdG86am9oYW5uZXNAc2lwc29sdXRpb25zLm5ldF0gd3JvdGU6
DQo+IE9uIFR1ZSwgMjAxNy0wMS0xMCBhdCAwMTozMiArMDAwMCwgRGF2aWQgTGluIHdyb3RlOg0K
PiANCj4gPiA+IFRoZSBvbmx5IHRoaW5nIHRoYXQgcmVhbGx5IHNlZW1zIHF1ZXN0aW9uYWJsZSB0
byBtZSBpcyB0aGUgd2hvbGUNCj4gPiA+IGJlYWNvbiBwYXJzaW5nIChhbmQgYXBwYXJlbnQgcmVi
dWlsZGluZyBpbiBmaXJtd2FyZT8pLiBJdCdzIHZlcnkgb2RkDQo+ID4gPiB0aGF0IHlvdSBjb3Vs
ZCBkbyB0aGF0LCB3aXRoIGEgc29mdG1hYyBkZXZpY2Ugd2hlcmUgYWxsIHRoZQ0KPiA+ID4gYXV0
aGVudGljYXRpb24gYW5kIGFzc29jaWF0aW9uIGlzIGhhbmRsZWQgYnkgaG9zdGFwZCBhbnl3YXks
IGFuZCB5b3UNCj4gPiA+IGNhbid0IHBvc3NpYmx5IHByZXRlbmQgdG8gaGFuZGxlIGV2ZXJ5dGhp
bmcgaG9zdGFwZCBtaWdodCB0aHJvdyBhdA0KPiA+ID4geW91IC0gdGhpcyB3aWxsIG1lYW4gdGhh
dCB5b3UnbGwgaGF2ZSBmZWF0dXJlcyBob3N0YXBkIGFuZCBldmVyeQ0KPiA+ID4gb3RoZXIgbWFj
ODAyMTEgc3VwcG9ydHMgdGhhdCB5b3Ugd2lsbCBzaWxlbnRseSBkcm9wIGFmYWljdCAtIHdoaWNo
DQo+ID4gPiBpcyByYXRoZXIgdW5leHBlY3RlZC4NCj4gPiA+DQo+ID4gPiBGaXJzdCwgeW91J3Jl
IHBhcnNpbmcgdGhlIGRhdGEgb2J0YWluZWQgZnJvbSBob3N0YXBkLCBpbg0KPiA+ID4gbXdsX2Z3
Y21kX3BhcnNlX2JlYWNvbigpLCBhbmQgdGhlbiB5b3Ugc2VuZCB0aGVtIGFsbCB0byB0aGUgZmly
bXdhcmUNCj4gPiA+IGluIG13bF9md2NtZF9zZXRfaWVzKCksIGJ1dCBvbmx5IHRoZSB0aGluZ3Mg
eW91IGFjdHVhbGx5IHVuZGVyc3Rvb2QuDQo+ID4gPiBOZXcgaGlnaGVyLWxldmVsIGZlYXR1cmVz
IHlvdSBkb24ndCB1bmRlcnN0YW5kLCBvciB2ZW5kb3IgYmVhY29uIElFcw0KPiA+ID4gdGhhdCBh
cmVuJ3QgV01NLCB3b3VsZCBiZSBjb21wbGV0ZWx5IGRyb3BwZWQuDQo+ID4gPg0KPiA+ID4gSSdt
IG5vdCB2ZXJ5IGhhcHB5IHdpdGggdGhhdCBiZWhhdmlvdXIuDQo+ID4gPg0KPiA+ID4gV2h5IGRv
ZXMgdGhlIGZpcm13YXJlIHJlcXVpcmUgdGhpcz8gV2h5IG5vdCBqdXN0IHBhY2sgYWxsIElFcyBp
bnRvDQo+ID4gPiB0aGUgcGNtZC0+aWVfbGlzdF9sZW5fcHJvcHJpZXRhcnksIGFuZCBsZWF2ZSBl
dmVyeXRoaW5nIGVsc2UgMD8gT3INCj4gPiA+IC0gaWYgcGVyaGFwcyBmaXJtd2FyZSBmb3Igc29t
ZSByZWFzb24gcmVxdWlyZXMgSFQvVkhUIHRvIGJlIHRyZWF0ZWQNCj4gPiA+IHNwZWNpYWxseSwg
b25seSBwYXJzZSBvdXQgdGhlIG9uZXMgdGhhdCBhcmUgcmVhbGx5IG5lZWRlZCBzcGVjaWFsbHkN
Cj4gPiA+IGFuZCBsZWF2ZSBldmVyeXRoaW5nIGVsc2UgaW4gdGhlIGllX2xpc3RfbGVuX3Byb3By
aWV0YXJ5Pw0KPiA+ID4NCj4gPiA+IEFsc28sIHRoaXMgaXMgZHJvcHBpbmcgYWxsIHRoZSBlbmNy
eXB0aW9uIHN0dWZmIC0gZXZlbiB0aG9zZSBhcmUNCj4gPiA+IGV4dGVuc2libGUgYnR3LCBhbmQg
aG9zdGFwZCBtaWdodCBkbyBzby4gSGF2aW5nIHRoZSBmaXJtd2FyZSByZWJ1aWxkDQo+ID4gPiB0
aG9zZSBvdXQgb2Ygb3V0LW9mLWJhbmQgaW5mb3JtYXRpb24gaXMgdmVyeSB1bmV4cGVjdGVkIGZv
ciBhDQo+ID4gPiBtYWM4MDIxMSBkcml2ZXIuDQo+ID4gPg0KPiA+DQo+ID4gVGhpcyBkcml2ZXIg
anVzdCBleHRyYWN0cyBuZWVkZWQgSUVzIHdoaWNoIGlzIHVzZWQgZm9yIHRoZSBob3N0DQo+ID4g
Y29tbWFuZCBvZiBmaXJtd2FyZS4gSSB0aGluayB3ZSB3aWxsIG5vdCBzdXBwb3J0IG90aGVyIHZl
bmRvciBJRXMuDQo+ID4gQW5kIGlmIG5lZWRlZCwgd2UgY2FuIGFkZCB0aGVtIHRvIHBjbWQtPmll
X2xpc3RfcHJvcHJpZXRhcnkuDQo+IA0KPiBTdXJlLCBJIHNlZSB0aGF0IHlvdSdyZSBkb2luZyB0
aGlzLiBJdCBzdGlsbCBtYWtlcyBubyBzZW5zZSB0aG91Z2gsIHNpbmNlIGFsbA0KPiBtYW5hZ2Vt
ZW50IGZyYW1lcyBhcmUgaGFuZGxlZCBieSBob3N0YXBkIGFueXdheS4gSXQgd291bGQgbWFrZSBz
b21lDQo+IHNlbnNlIGlmIHlvdSBhY3R1YWxseSB3ZXJlIGdvaW5nIHRvIGhhbmRsZSBhc3NvY2lh
dGlvbiBpbiB0aGUgZmlybXdhcmUsIGJ1dCBpdA0KPiBtYWtlcyBubyBzZW5zZSBoZXJlLCBhcyBm
YXIgYXMgSSBjYW4gdGVsbC4NCj4gDQo+IEknbSBub3Qgc3VyZSBob3cgaGFyZCBhIGxpbmUgSSBz
aG91bGQgZHJhdyBoZXJlLCBidXQgSSdsbCB3YXJuIHlvdSBub3cgdGhhdCBJDQo+IHdvbid0IHRh
a2UgdGhpcyBpbnRvIGNvbnNpZGVyYXRpb24gd2hlbiBhZGRpbmcgbmV3IGZlYXR1cmVzIHRvIG1h
YzgwMjExLCBhbmQNCj4gY2VydGFpbmx5IEpvdW5pIHdvbid0IHRha2UgaXQgaW50byBhY2NvdW50
IHdoZW4gYWRkaW5nIG5ldyBmZWF0dXJlcyB0byBob3N0YXBkLA0KPiBzbyB0aGF0IHN1Y2ggbmV3
IGZlYXR1cmVzIHdpbGwgdGhlbiBTSUxFTlRMWSBhbmQgVU5FWFBFQ1RFRExZIChkdWUgdG8NCj4g
bWFjODAyMTEpIG5vdCB3b3JrIHdpdGggeW91ciBkcml2ZXIuDQo+IA0KPiBJIHN0cm9uZ2x5IGFk
dmlzZSB5b3UgdG8gcmVjb25zaWRlciB0aGlzIGFuZCB0cnkgdG8gcGFzcyB0aHJvdWdoIGFsbCB0
aGUgSUVzIHNvDQo+IHRoYXQgbmV3bHkgYWRkZWQgZmVhdHVyZXMgdGhhdCBzaG91bGRuJ3QgcmVx
dWlyZSBmaXJtd2FyZSBpbnRlcmFjdGlvbiAoc2luY2UNCj4gaG9zdGFwZCBpcyBoYW5kbGluZyBh
bGwgdGhlIGFzc29jaWF0aW9uIGhhbmRzaGFrZSBhbmQgSUVzIHRvIGFkdmVydGlzZSB0aGUNCj4g
ZmVhdHVyZSkgd2lsbCBhdXRvbWF0aWNhbGx5IGFuZCBjbGVhbmx5IHdvcmsuDQo+IA0KPiBJZiB5
b3UgcmVhbGx5IGNhbid0IGRvIHRoYXQsIGZvciBzb21lIHJlYXNvbiwgdGhlbiBmb3IgbXkgYmVu
ZWZpdCBhcyB0aGUNCj4gbWFjODAyMTEgbWFpbnRhaW5lciBhbmQgZnV0dXJlIG1haW50YWluZXJz
IG9mIHlvdXIgZHJpdmVyLCBJJ2QgbGlrZSB0byBoYXZlDQo+IGRvY3VtZW50YXRpb24gaW4gdGhl
IGRyaXZlciBhcyB0byB3aHkgdGhlIGZpcm13YXJlIG5lZWRzIHRoZSBkcml2ZXIgdG8gc3BsaXQN
Cj4gdXAgYWxsIHRob3NlIElFcyBhbmQgd2hhdCBpdCBkb2VzIHdpdGggdGhlbS4gQWZ0ZXIgYWxs
LCBhIGNvbW1vbi1zZW5zZSBhbmFseXNpcw0KPiB3b3VsZCBzdWdnZXN0IHRoYXQgdGhlIGZpcm13
YXJlIGhhcyBubyBuZWVkIGZvciBpdCwgc2luY2UgYWxsIGNvbmZpZ3VyYXRpb24NCj4gc2hvdWxk
IGNvbWUgdGhyb3VnaCBvdGhlciBwbGFjZXMgYW5kIGFsbCBJRXMgYXJlIGp1c3QgZm9yIGdvaW5n
IG91dCBvbiB0aGUgYWlyLg0KPiANCg0KLSBUaGUgb2JqZWN0aXZlIGlzIHRvIHVzZSB0aGUgc2Ft
ZSBwcm9kdWN0aW9uIGZpcm13YXJlIGJpbmFyeSBmb3IgYm90aCB0aGUgb3BlbiBzb3VyY2UgYW5k
IHByb3ByaWV0YXJ5IGRyaXZlci4gU2FtZSBpbnRlcmZhY2UgaXMgY3VycmVudGx5IHVzZWQgYnkg
cHJvcHJpZXRhcnkgZHJpdmVyIGZvciBoaXN0b3JpY2FsbHkgcmVhc29uLCB3aGlsZSB0aGUgb3Bl
biBzb3VyY2UgSEFMIGlzIGFkYXB0aW5nIHRvIGl0IGZvciB0aGUgZXhpc3Rpbmcgc2hpcHBpbmcg
cHJvZHVjdC4NCi0gV2Ugd2lsbCBtYWtlIGNoYW5nZXMgYW5kIGNsZWFuIHRoaW5ncyB1cCBpbiBm
dXR1cmUuIEkgd2lsbCBzcGVuZCBlZmZvcnQgdG8gY29udGludWUgaXRzIG1haW50ZW5hbmNlIGFu
ZCBjbGVhbi11cC4NCg0KVGhhbmtzLA0KRGF2aWQNCg0KPiBqb2hhbm5lcw0K
PiBKb2hhbm5lcyBCZXJnIFttYWlsdG86am9oYW5uZXNAc2lwc29sdXRpb25zLm5ldF0gd3JvdGU6
DQo+ID4gT24gV2VkLCAyMDE2LTEyLTIxIGF0IDA0OjExICswMDAwLCBEYXZpZCBMaW4gd3JvdGU6
DQo+ID4gVGhpcyBwYXRjaCBwcm92aWRlcyB0aGUgbXdsd2lmaSBkcml2ZXIgZm9yIE1hcnZlbGwg
ODg2MywgODg2NCBhbmQNCj4gPiA4ODk3DQo+ID4gY2hpcHNldHMuDQo+ID4gVGhpcyBkcml2ZXIg
d2FzIGRldmVsb3BlZCBhcyBwYXJ0IG9mIHRoZSBvcGVud3J0Lm9yZyBwcm9qZWN0IHRvDQo+ID4g
c3VwcG9ydCBMaW5rc3lzIFdSVDE5MDBBQyBhbmQgaXMgbWFpbnRhaW5lZCBvbg0KPiA+IGh0dHBz
Oi8vZ2l0aHViLmNvbS9rYWxvei9td2x3aSBmaS4NCj4gDQo+IEkgZm91bmQgc29tZSBtaW5vciB0
aGluZ3M6DQo+IA0KPiAgKiB1c2luZyBrbWFsbG9jL21lbXNldCBpbnN0ZWFkIG9mIGt6YWxsb2MN
Cg0KSSB3aWxsIGNoYW5nZSBpdC4NCg0KPiAgKiBtd2xfZndjbWRfZ2V0XzgwbV9wcmlfY2hubCgp
IGNvdWxkIHVzZSBzb21lIGFyaXRobWV0aWMgb3IgYXQgbGVhc3QNCj4gICAgc3dpdGNoIGNhc2Ug
Y29uc29saWRhdGlvbiAtIGlmIGl0IHNob3VsZG4ndCBldmVuIGdvIGF3YXkgZW50aXJlbHkNCj4g
ICAgc2luY2UgbWFjODAyMTEgZG9lc24ndCByZWFsbHkgbGltaXQgeW91IHRvIHRoZSBwcmltYXJ5
IGNoYW5uZWwgYXMNCj4gICAgdGhlIHNwZWMgcmVxdWlyZXMgaXQgLSBzbyB0aGUgd2hvbGUgcHVy
cG9zZSBvZiB0aGUgZnVuY3Rpb24gc2VlbXMgYQ0KPiAgICBiaXQgcXVlc3Rpb25hYmxlDQoNClRo
aXMgZnVuY3Rpb24gaXMgdXNlZCBmb3IgdGhlIGhvc3QgY29tbWFuZCBvZiBmaXJtd2FyZS4NCg0K
PiAgKiB3aXBoeV9lcnIoaHctPndpcGh5LCAiZmFpbGVkIGV4ZWN1dGlvblxuIik7IHNlZW1zIGxp
a2UgYSBiaXQgdG9vDQo+ICAgIGdlbmVyaWMgKHlvdSBoYXZlIHRoYXQgbWFueSB0aW1lcyAtIGhv
dyBhcmUgeW91IGdvaW5nIHRvIHRlbGwgd2hpY2gNCj4gICAgaGFwcGVuZWQ/KQ0KDQpJIHdpbGwg
bW9kaWZ5IGl0Lg0KDQo+ICAqIHJhdGVzID0gc3RhLT5zdXBwX3JhdGVzW05MODAyMTFfQkFORF81
R0haXSA8PCA1Ow0KPiAgICBpcyBhIGJpdCBxdWVzdGlvbmFibGUgLSB0aGF0IDUgc2hvdWxkIHBy
b2JhYmx5IGJlIGRlZmluZWQgKGFsc28sDQo+ICAgIGRvZXMgdGhhdCBtZWFuIHlvdSBzdXBwb3J0
IDEsMiwgNS41LCAxMSBhbmQgKjIyTUJwcyo/IGN1cmlvdXMsDQo+ICAgIGFsbW9zdCBub2JvZHkg
ZG9lcyB0aGF0IGFmYWljdCkNCg0KVGhpcyBpcyB1c2VkIHRvIGNvbXBseSB3aXRoIHRoZSBwYXJh
bWV0ZXJzIG9mIGhvc3QgY29tbWFuZCBvZiBmaXJtd2FyZS4NCg0KPiAgKiBhIGxvdCBvZiBpbml0
aWFsaXphdGlvbnMgbGlrZQ0KPiDCoCDCoHN0cnVjdCBtd2xfdmlmICptd2xfdmlmOw0KPiAgICBb
Li4uXQ0KPiDCoCDCoG13bF92aWYgPSBtd2xfZGV2X2dldF92aWYodmlmKTsNCj4gDQo+ICAgIGNv
dWxkIGJlIHJvbGxlZCBpbnRvIGEgc2luZ2xlIGxpbmUgb2YgY29kZSAod2hpY2ggeW91IHNvbWV0
aW1lcyBkbywNCj4gICAgYXBwYXJlbnRseSBhbHdheXMgZm9yICJwcml2IiBidXQgbmV2ZXIgZm9y
ICJtd2xfdmlmIiBvciAicGNtZCI/KQ0KDQpJIHdpbGwgbW9kaWZ5IGl0Lg0KDQo+ICAqICJjZWxj
aXVzIiBpc24ndCByZWFsbHkgc3BlbGxlZCB0aGF0IHdheSAtIGFuZCB5b3UgcHJvYmFibHkgbWVh
bg0KPiAgICAidGVtcGVyYXR1cmUiIGFueXdheSBzaW5jZSBDZWxzaXVzIGlzIGEgdW5pdCA6KQ0K
DQpJIHdpbGwgY2hhbmdlIGl0Lg0KDQo+ICAqIHR5cG86IGZpcndhcmUNCg0KSSB3aWxsIGZpeCBp
dC4NCg0KPiAgKiBhIGJ1bmNoIG9mIHlvdXIgSUUgaGFuZGxpbmcgc3RydWN0cyBzZWVtIGR1cGxp
Y2F0ZSB3aXRoIGllZWU4MDIxMS5oDQoNCk5vLCBvbmx5IHBvaW50ZXIgaXMgZGVmaW5lZCBmb3Ig
ZXh0cmFjdGluZyBjb250ZW50IG9mIGJlYWNvbi4gVGhpcyBkcml2ZXIgZG9lcyBub3QgcmVkZWZp
bmUgSUUgc3RydWN0dXJlIGlmIGl0IGlzIGFscmVhZHkgZGVmaW5lZCBpbiBpZWVlODAyMTEuaC4N
Cg0KPiAgKiBhbmQgcGFydGlhbGx5IHdyb25nIC0gc29tZXRoaW5nIHdpdGggYSB2ZW5kb3IgT1VJ
IGNhbid0IGJlIFJTTg0KPiAgICAobWF5YmUgeW91IG1lYW50IFdQQT8gdGhvdWdoIGl0J3Mgbm90
IHF1aXRlIGNsZWFyIHRvIG1lIHdoeSB5b3UgbmVlZA0KPiAgICB0aGlzIGF0IGFsbCEpDQoNClll
cy4gSXQgbWVhbnMgV1BBLiBUaGlzIGRyaXZlciBkb2VzIG5vdCBoYW5kbGUgY29udGVudCBvZiBp
dC4gSXQgb25seSBuZWVkcyB0aGUgSUUgZm9yIGhvc3QgY29tbWFuZCBvZiBmaXJtd2FyZS4NCg0K
PiAgKiArI2lmZGVmIENPTkZJR19PRg0KPiAgICArI2luY2x1ZGUgPGxpbnV4L29mLmg+DQo+ICAg
ICsjZW5kaWYNCj4gICAgSSBkb24ndCB0aGluayB5b3UgaGF2ZSB0byBndWFyZCB0aGF0IGluY2x1
ZGUgLi4uDQo+ICAqICsjaWZkZWYgQ09ORklHX0RFQlVHX0ZTDQo+ICAgICsjaW5jbHVkZSAiZGVi
dWdmcy5oIg0KPiAgICArI2VuZGlmDQo+ICAgIGxpa2V3aXNlDQo+ICAqIGdpdCBhbSBhbHNvIGNv
bXBsYWluZWQgYWJvdXQgYSBibGFuayBsaW5lIGF0IEVPRg0KPiANCg0KSSB3aWxsIHJlbW92ZSB0
aGVtIGFuZCBmaXggYmxhbmsgbGluZS4NCg0KPiANCj4gVGhlIG9ubHkgdGhpbmcgdGhhdCByZWFs
bHkgc2VlbXMgcXVlc3Rpb25hYmxlIHRvIG1lIGlzIHRoZSB3aG9sZSBiZWFjb24NCj4gcGFyc2lu
ZyAoYW5kIGFwcGFyZW50IHJlYnVpbGRpbmcgaW4gZmlybXdhcmU/KS4gSXQncyB2ZXJ5IG9kZCB0
aGF0IHlvdSBjb3VsZCBkbw0KPiB0aGF0LCB3aXRoIGEgc29mdG1hYyBkZXZpY2Ugd2hlcmUgYWxs
IHRoZSBhdXRoZW50aWNhdGlvbiBhbmQgYXNzb2NpYXRpb24gaXMNCj4gaGFuZGxlZCBieSBob3N0
YXBkIGFueXdheSwgYW5kIHlvdSBjYW4ndCBwb3NzaWJseSBwcmV0ZW5kIHRvIGhhbmRsZQ0KPiBl
dmVyeXRoaW5nIGhvc3RhcGQgbWlnaHQgdGhyb3cgYXQgeW91IC0gdGhpcyB3aWxsIG1lYW4gdGhh
dCB5b3UnbGwgaGF2ZQ0KPiBmZWF0dXJlcyBob3N0YXBkIGFuZCBldmVyeSBvdGhlciBtYWM4MDIx
MSBzdXBwb3J0cyB0aGF0IHlvdSB3aWxsIHNpbGVudGx5DQo+IGRyb3AgYWZhaWN0IC0gd2hpY2gg
aXMgcmF0aGVyIHVuZXhwZWN0ZWQuDQo+IA0KPiBGaXJzdCwgeW91J3JlIHBhcnNpbmcgdGhlIGRh
dGEgb2J0YWluZWQgZnJvbSBob3N0YXBkLCBpbg0KPiBtd2xfZndjbWRfcGFyc2VfYmVhY29uKCks
IGFuZCB0aGVuIHlvdSBzZW5kIHRoZW0gYWxsIHRvIHRoZSBmaXJtd2FyZSBpbg0KPiBtd2xfZndj
bWRfc2V0X2llcygpLCBidXQgb25seSB0aGUgdGhpbmdzIHlvdSBhY3R1YWxseSB1bmRlcnN0b29k
LiBOZXcNCj4gaGlnaGVyLWxldmVsIGZlYXR1cmVzIHlvdSBkb24ndCB1bmRlcnN0YW5kLCBvciB2
ZW5kb3IgYmVhY29uIElFcyB0aGF0IGFyZW4ndA0KPiBXTU0sIHdvdWxkIGJlIGNvbXBsZXRlbHkg
ZHJvcHBlZC4NCj4gDQo+IEknbSBub3QgdmVyeSBoYXBweSB3aXRoIHRoYXQgYmVoYXZpb3VyLg0K
PiANCj4gV2h5IGRvZXMgdGhlIGZpcm13YXJlIHJlcXVpcmUgdGhpcz8gV2h5IG5vdCBqdXN0IHBh
Y2sgYWxsIElFcyBpbnRvIHRoZQ0KPiBwY21kLT5pZV9saXN0X2xlbl9wcm9wcmlldGFyeSwgYW5k
IGxlYXZlIGV2ZXJ5dGhpbmcgZWxzZSAwPyBPciAtIGlmDQo+IHBlcmhhcHMgZmlybXdhcmUgZm9y
IHNvbWUgcmVhc29uIHJlcXVpcmVzIEhUL1ZIVCB0byBiZSB0cmVhdGVkIHNwZWNpYWxseSwNCj4g
b25seSBwYXJzZSBvdXQgdGhlIG9uZXMgdGhhdCBhcmUgcmVhbGx5IG5lZWRlZCBzcGVjaWFsbHkg
YW5kIGxlYXZlIGV2ZXJ5dGhpbmcNCj4gZWxzZSBpbiB0aGUgaWVfbGlzdF9sZW5fcHJvcHJpZXRh
cnk/DQo+IA0KPiBBbHNvLCB0aGlzIGlzIGRyb3BwaW5nIGFsbCB0aGUgZW5jcnlwdGlvbiBzdHVm
ZiAtIGV2ZW4gdGhvc2UgYXJlIGV4dGVuc2libGUgYnR3LA0KPiBhbmQgaG9zdGFwZCBtaWdodCBk
byBzby4gSGF2aW5nIHRoZSBmaXJtd2FyZSByZWJ1aWxkIHRob3NlIG91dCBvZiBvdXQtb2YtYmFu
ZA0KPiBpbmZvcm1hdGlvbiBpcyB2ZXJ5IHVuZXhwZWN0ZWQgZm9yIGEgbWFjODAyMTEgZHJpdmVy
Lg0KPiANCg0KVGhpcyBkcml2ZXIganVzdCBleHRyYWN0cyBuZWVkZWQgSUVzIHdoaWNoIGlzIHVz
ZWQgZm9yIHRoZSBob3N0IGNvbW1hbmQgb2YgZmlybXdhcmUuIEkgdGhpbmsgd2Ugd2lsbCBub3Qg
c3VwcG9ydCBvdGhlciB2ZW5kb3IgSUVzLiBBbmQgaWYgbmVlZGVkLCB3ZSBjYW4gYWRkIHRoZW0g
dG8gcGNtZC0+aWVfbGlzdF9wcm9wcmlldGFyeS4NCg0KPiBqb2hhbm5lcw0K
PiBGcm9tOiBSYWZhxYIgTWnFgmVja2kgW21haWx0bzp6YWplYzVAZ21haWwuY29tXSB3b3J0ZToN
Cj4gU2VudDogV2VkbmVzZGF5LCBGZWJydWFyeSAwOCwgMjAxNyAzOjI0IFBNDQo+IE9uIDggRmVi
cnVhcnkgMjAxNyBhdCAwNzozMCwgRGF2aWQgTGluIDxkbGluQG1hcnZlbGwuY29tPiB3cm90ZToN
Cj4gPiBzdGV2ZS5kZXJvc2llckBnbWFpbC5jb20gW21haWx0bzpzdGV2ZS5kZXJvc2llckBnbWFp
bC5jb21dIHdyb3RlOg0KPiA+PiBPbiBUdWUsIEZlYiA3LCAyMDE3IGF0IDEwOjE1IFBNLCBEYXZp
ZCBMaW4gPGRsaW5AbWFydmVsbC5jb20+IHdyb3RlOg0KPiA+PiA+PiBSYWZhxYIgTWnFgmVja2kg
W21haWx0bzp6YWplYzVAZ21haWwuY29tXSB3cm90ZToNCj4gPj4gPj4gUGxlYXNlIHVzZSBpZWVl
ODAyMTEtZnJlcS1saW1pdDoNCj4gPj4gPj4gaHR0cHM6Ly9naXQua2VybmVsLm9yZy9jZ2l0L2xp
bnV4L2tlcm5lbC9naXQvZGF2ZW0vbmV0LW5leHQuZ2l0L2NvDQo+ID4+ID4+IG1taQ0KPiA+PiA+
PiB0Lz9pZD1iMw0KPiA+PiA+PiAzMGIyNWVhYWJkYTAwZDc0ZTQ3NTY2ZDkyMDA5MDdkYTM4MTg5
Ng0KPiA+PiA+Pg0KPiA+PiA+PiBNb3N0IGxpa2VseSB3aXRoIHdpcGh5X3JlYWRfb2ZfZnJlcV9s
aW1pdHMgaGVscGVyOg0KPiA+PiA+PiBodHRwczovL2dpdC5rZXJuZWwub3JnL2NnaXQvbGludXgv
a2VybmVsL2dpdC9kYXZlbS9uZXQtbmV4dC5naXQvY28NCj4gPj4gPj4gbW1pDQo+ID4+ID4+IHQv
P2lkPWU2DQo+ID4+ID4+IDkxYWMyZjc1YjY5YmVlNzQzZjAzNzBkNzk0NTRiYTQ0MjliMTc1DQo+
ID4+ID4NCj4gPj4gPiBJIGFscmVhZHkgcmVwbGllZCBtZWFuaW5nIG9mIHRoZXNlIHBhcmFtZXRl
cnM6DQo+ID4+ID4gPG1hcnZlbGwsMmdoej4gaXMgdXNlZCB0byBkaXNhYmxlIDJnIGJhbmQuDQo+
ID4+ID4gPG1hcnZlbGwsNWdoej4gaXMgdXNlZCB0byBkaXNhYmxlIDVnIGJhbmQuDQo+ID4+ID4g
PG1hcnZlbGwsIGNoYWlubWFzaz4gaXMgdXNlZCB0byBzcGVjaWZ5IGFudGVubmEgbnVtYmVyIChp
ZiBkZWZhdWx0DQo+ID4+ID4gbnVtYmVyDQo+ID4+IGlzIHN1aXRhYmxlIGZvciB5b3UsIHRoZXJl
IGlzIG5vIG5lZWQgdG8gdXNlIHRoaXMgcGFyYW1ldGVyKS4NCj4gPj4gPiA8bWFydmVsbCxwb3dl
cnRhYmxlPiBzaG91bGQgbm90IGJlIHVzZWQgZm9yIGNoaXAgd2l0aCBkZXZpY2UgcG93ZXINCj4g
dGFibGUuDQo+ID4+IEluIGZhY3QsIHRoaXMgcGFyYW1ldGVyIHNob3VsZCBub3QgYmUgdXNlZCBh
bnkgbW9yZS4NCj4gPj4gPg0KPiA+Pg0KPiA+PiBEYXZpZCwgSSB0aGluayB5b3UncmUgbm90IHVu
ZGVyc3RhbmRpbmcgdGhlIGNvbW1lbnQsIG9yIGF0IGxlYXN0DQo+ID4+IHRoYXQncyB3aGF0IGl0
IGxvb2tzIGxpa2UgdG8gbWUuIFllcywgeW91IGRpZCByZXBseSBhcyB0byB0aGUgbWVhbmluZy4N
Cj4gPj4gQW5kLCB5b3VyIHJlcGx5LCB3aGlsZSBpbmZvcm1hdGl2ZSwgZGlkbid0IHRlbGwgdXMg
eW91IHVuZGVyc3Rvb2QgYW5kDQo+ID4+IHdlcmUgd2lsbGluZyB0byBmaXggdGhlIHByb2JsZW0u
IEkgZG91YnQgeW91IG1lYW50IGl0IHRoaXMgd2F5LCBidXQNCj4gPj4gaXQgZmVlbHMgZGVmZW5z
aXZlIGFuZCBsaWtlIGEgYnJ1c2gtb2ZmLg0KPiA+Pg0KPiA+PiBGaXJzdCBvZmYsIHlvdSB3aWxs
IHN0aWxsIGhhdmUgdG8gZG9jdW1lbnQgYW55IERUIGJpbmRpbmdzIHlvdSdyZQ0KPiA+PiBhZGRp
bmcuIEp1c3QgYmVjYXVzZSB5b3UgYW5zd2VyIHRoZSBxdWVzdGlvbiBpbiB0aGUgcmV2aWV3IGRv
ZXNuJ3QNCj4gPj4gbWVhbiB5b3UncmUgbm90IHJlc3BvbnNpYmxlIGZvciBkb2luZyBzby4NCj4g
Pj4NCj4gPj4gQW5kIHNlY29uZCBvZmYsIEkgdGhpbmsgdGhhdCBSYWZhbCAoYW5kIHNvcnJ5IGFi
b3V0IG15IHNwZWxsaW5nLA0KPiA+PiBsb29rcyBsaWtlIHRoZXJlJ3Mgc29tZSBzb3J0IG9mIGFj
Y2VudCBvbiB0aGUgbCB0aGF0IEkgZG9uJ3Qga25vdyBob3cNCj4gPj4gdG8gbWFrZSBteSBrZXli
b2FyZCBkbykgaXMgc2F5aW5nOiB0aGVyZSdzIGFscmVhZHkgc29tZSBnZW5lcmljDQo+ID4+IGJp
bmRpbmdzIHRoYXQgY2FuIGJlIHVzZWQgdG8gZGlzYWJsZSB0aGUgMmcgb3IgNWcgYmFuZHMuIEdy
YW50ZWQNCj4gPj4gdGhleSdyZSBldmVuIG5ld2VyIHRoYW4geW91ciBwYXRjaCwgYnV0IEkgZG8g
dGhpbmsgaWYgc2FpZCBiaW5kaW5ncyBleGlzdCBhbmQNCj4gYXJlIGFwcHJvcHJpYXRlLCB5b3Ug
c2hvdWxkIHVzZSB0aGVtLg0KPiA+Pg0KPiA+PiAtIFN0ZXZlDQo+ID4NCj4gPiBUaGVzZSBwYXJh
bWV0ZXJzIGFyZSBtYXJ2ZWxsIHByb3ByaWV0YXJ5IHBhcmFtZXRlcnMsIEkgZG9uJ3QgdGhpbmsg
aXQgc2hvdWxkDQo+IGJlIGFkZGVkIHRvIERUIGJpbmRpbmdzLg0KPiANCj4gU3RldmUgaXMgY29y
cmVjdC4NCj4gDQo+IFlvdSBoYXZlIHRvIGRvY3VtZW50IG5ldyBwcm9wZXJ0aWVzLCBqdXN0IGJl
Y2F1c2UgdGhleSBhcmUgTWFydmVsbCBzcGVjaWZpYw0KPiBkb2Vzbid0IGNoYW5nZSBhbnl0aGlu
Zy4gWW91IGp1c3QgZG9jdW1lbnQgdGhlbSBpbiBhIHByb3BlciBwbGFjZS4NCj4gDQoNCkFsbCBy
aWdodC4gSSB3aWxsIGRvIHRoYXQuDQoNCj4gDQo+ID4gQlRXLCA8bWFydmVsbCwyZ2h6PiBhbmQg
PG1hcnZlbGwsNWdoej4gYXJlIG9ubHkgdXNlZCBmb3IgbXdsd2lmaSB0byByZXBvcnQNCj4gc3Vw
cG9ydGVkIGJhbmRzLCBpdCBpcyBub3QgcmVsYXRlZCB0byBsaW1pdGF0aW9uIG9mIGZyZXF1ZW5j
eS4NCj4gDQo+IEhvdyByZXBvcnRpbmcgYSBzaW5nbGUgYmFuZCBkb2Vzbid0IGxpbWl0IHJlcG9y
dGVkIGZyZXF1ZW5jaWVzPyBZb3UgY2FuDQo+IGFjaGlldmUgZXhhY3RseSB0aGUgc2FtZSB1c2lu
ZyBnZW5lcmljIHByb3BlcnR5LCBzbyB0aGVyZSBpcyBubyBwbGFjZSBmb3INCj4gTWFydmVsbCBz
cGVjaWZpYyBvbmVzLg0KPiANCj4gSW4gZmFjdCB0aGVyZSB3ZXJlIGRyaXZlcnMgb2YgMyB2ZW5k
b3JzIHJlcXVpcmluZyBiYW5kL2ZyZXEtcmVsYXRlZCBkZXNjcmlwdGlvbg0KPiBpbiBEVDogQnJv
YWRjb20sIE1hcnZlbGwgJiBNZWRpYXRlay4gVGhpcyBwcm9wZXJ0eSB3YXMgZGlzY3Vzc2VkICYg
ZGVzaWduZWQNCj4gdG8gc3VwcG9ydCBhbGwgbGltaXRhdGlvbiBjYXNlcyB3ZSBmb3VuZCBwb3Nz
aWJsZSB0byBtYWtlIGl0IHVzYWJsZSB3aXRoDQo+IChob3BlZnVsbHkpIGFsbCBkcml2ZXJzLg0K
PiANCg0KSSBvbmx5IG5lZWQgc2ltcGxlIHdheSB0byBkaXNhYmxlIDJnIG9yIDVnIGJhbmQuIEkg
d2lsbCBmb2xsb3cgeW91ciBzdWdnZXN0aW9uIHRvIGRvY3VtZW50IHRoZXNlIG1hcnZlbGwgcHJv
cHJpZXRhcnkgcGFyYW1ldGVycy4NCg0KVGhhbmtzLA0KRGF2aWQNCg0KPiAtLQ0KPiBSYWZhxYIN
Cg==
c3RldmUuZGVyb3NpZXJAZ21haWwuY29tIFttYWlsdG86c3RldmUuZGVyb3NpZXJAZ21haWwuY29t
XSB3cm90ZToNCj4gT24gVHVlLCBGZWIgNywgMjAxNyBhdCAxMDoxNSBQTSwgRGF2aWQgTGluIDxk
bGluQG1hcnZlbGwuY29tPiB3cm90ZToNCj4gPj4gUmFmYcWCIE1pxYJlY2tpIFttYWlsdG86emFq
ZWM1QGdtYWlsLmNvbV0gd3JvdGU6DQo+ID4+IE9uIDcgRmVicnVhcnkgMjAxNyBhdCAyMDoxMiwg
U3RldmUgZGVSb3NpZXIgPGRlcm9zaWVyQGdtYWlsLmNvbT4gd3JvdGU6DQo+ID4+ID4+ICsgLyog
bG9vayBmb3IgYWxsIG1hdGNoaW5nIHByb3BlcnR5IG5hbWVzICovDQo+ID4+ID4+ICsgZm9yX2Vh
Y2hfcHJvcGVydHlfb2Zfbm9kZShwcml2LT5kdF9ub2RlLCBwcm9wKSB7IGlmDQo+ID4+ID4+ICsg
KHN0cmNtcChwcm9wLT5uYW1lLCAibWFydmVsbCwyZ2h6IikgPT0gMCkNCj4gPj4gPj4gKyBwcml2
LT5kaXNhYmxlXzJnID0gdHJ1ZTsNCj4gPj4gPj4gKyBpZiAoc3RyY21wKHByb3AtPm5hbWUsICJt
YXJ2ZWxsLDVnaHoiKSA9PSAwKQ0KPiA+PiA+PiArIHByaXYtPmRpc2FibGVfNWcgPSB0cnVlOw0K
PiA+PiA+PiArIGlmIChzdHJjbXAocHJvcC0+bmFtZSwgIm1hcnZlbGwsY2hhaW5tYXNrIikgPT0g
MCkgeyBwcm9wX3ZhbHVlID0NCj4gPj4gPj4gKyBiZTMyX3RvX2NwdSgqKChfX2JlMzIgKilwcm9w
LT52YWx1ZSkpOyBpZiAocHJvcF92YWx1ZSA9PSAyKQ0KPiA+PiA+PiArIHByaXYtPmFudGVubmFf
dHggPSBBTlRFTk5BX1RYXzI7DQo+ID4+ID4+ICsNCj4gPj4gPj4gKyBwcm9wX3ZhbHVlID0gYmUz
Ml90b19jcHUoKigoX19iZTMyICopIChwcm9wLT52YWx1ZSArIDQpKSk7IGlmDQo+ID4+ID4+ICsg
KHByb3BfdmFsdWUgPT0gMikNCj4gPj4gPj4gKyBwcml2LT5hbnRlbm5hX3J4ID0gQU5URU5OQV9S
WF8yOw0KPiA+PiA+PiArIH0NCj4gPj4gPj4gKyB9DQo+ID4+ID4+ICsNCj4gPj4gPj4gKyBwcml2
LT5wd3Jfbm9kZSA9IG9mX2ZpbmRfbm9kZV9ieV9uYW1lKHByaXYtPmR0X25vZGUsDQo+ID4+ID4+
ICsgICAgICAibWFydmVsbCxwb3dlcnRhYmxlIik7DQo+ID4+ID4+ICsjZW5kaWYNCj4gPj4gPj4g
K30NCj4gPj4gPg0KPiA+PiA+IEFGQUlDVCwgdGhlcmUncyBubyBkb2N1bWVudGF0aW9uIGZvciB0
aGVzZSBEVCBiaW5kaW5ncy4gVGhlIG13bHdpZmkNCj4gPj4gPiBub2RlIGFuZCB0aGUgbWFydmVs
bCxwb3dlcnRhYmxlIGJvdGggbmVlZCBmdWxsIGRvY3VtZW50YXRpb24gaW4NCj4gPj4gPiBEb2N1
bWVudGF0aW9uL2RldmljZXRyZWUvYmluZGluZ3MvLi4uIC4NCj4gPj4gPg0KPiA+PiA+IEZyYW5r
bHkgSSBoYXZlIGEgZmVlbGluZyBJJ20gZ29pbmcgdG8gbmVlZCB0aGVzZSBEVCBub2RlcyBhbmQg
SSdkDQo+ID4+ID4gbGlrZSB0byBub3QgaGF2ZSB0byBndWVzcy1hbmQtY2hlY2sgYmFzZWQgb24g
dGhlIGNvZGUuDQo+ID4+DQo+ID4+IFBsZWFzZSB1c2UgaWVlZTgwMjExLWZyZXEtbGltaXQ6DQo+
ID4+IGh0dHBzOi8vZ2l0Lmtlcm5lbC5vcmcvY2dpdC9saW51eC9rZXJuZWwvZ2l0L2RhdmVtL25l
dC1uZXh0LmdpdC9jb21taQ0KPiA+PiB0Lz9pZD1iMw0KPiA+PiAzMGIyNWVhYWJkYTAwZDc0ZTQ3
NTY2ZDkyMDA5MDdkYTM4MTg5Ng0KPiA+Pg0KPiA+PiBNb3N0IGxpa2VseSB3aXRoIHdpcGh5X3Jl
YWRfb2ZfZnJlcV9saW1pdHMgaGVscGVyOg0KPiA+PiBodHRwczovL2dpdC5rZXJuZWwub3JnL2Nn
aXQvbGludXgva2VybmVsL2dpdC9kYXZlbS9uZXQtbmV4dC5naXQvY29tbWkNCj4gPj4gdC8/aWQ9
ZTYNCj4gPj4gOTFhYzJmNzViNjliZWU3NDNmMDM3MGQ3OTQ1NGJhNDQyOWIxNzUNCj4gPg0KPiA+
IEkgYWxyZWFkeSByZXBsaWVkIG1lYW5pbmcgb2YgdGhlc2UgcGFyYW1ldGVyczoNCj4gPiA8bWFy
dmVsbCwyZ2h6PiBpcyB1c2VkIHRvIGRpc2FibGUgMmcgYmFuZC4NCj4gPiA8bWFydmVsbCw1Z2h6
PiBpcyB1c2VkIHRvIGRpc2FibGUgNWcgYmFuZC4NCj4gPiA8bWFydmVsbCwgY2hhaW5tYXNrPiBp
cyB1c2VkIHRvIHNwZWNpZnkgYW50ZW5uYSBudW1iZXIgKGlmIGRlZmF1bHQgbnVtYmVyDQo+IGlz
IHN1aXRhYmxlIGZvciB5b3UsIHRoZXJlIGlzIG5vIG5lZWQgdG8gdXNlIHRoaXMgcGFyYW1ldGVy
KS4NCj4gPiA8bWFydmVsbCxwb3dlcnRhYmxlPiBzaG91bGQgbm90IGJlIHVzZWQgZm9yIGNoaXAg
d2l0aCBkZXZpY2UgcG93ZXIgdGFibGUuDQo+IEluIGZhY3QsIHRoaXMgcGFyYW1ldGVyIHNob3Vs
ZCBub3QgYmUgdXNlZCBhbnkgbW9yZS4NCj4gPg0KPiANCj4gRGF2aWQsIEkgdGhpbmsgeW91J3Jl
IG5vdCB1bmRlcnN0YW5kaW5nIHRoZSBjb21tZW50LCBvciBhdCBsZWFzdCB0aGF0J3Mgd2hhdCBp
dA0KPiBsb29rcyBsaWtlIHRvIG1lLiBZZXMsIHlvdSBkaWQgcmVwbHkgYXMgdG8gdGhlIG1lYW5p
bmcuDQo+IEFuZCwgeW91ciByZXBseSwgd2hpbGUgaW5mb3JtYXRpdmUsIGRpZG4ndCB0ZWxsIHVz
IHlvdSB1bmRlcnN0b29kIGFuZCB3ZXJlDQo+IHdpbGxpbmcgdG8gZml4IHRoZSBwcm9ibGVtLiBJ
IGRvdWJ0IHlvdSBtZWFudCBpdCB0aGlzIHdheSwgYnV0IGl0IGZlZWxzIGRlZmVuc2l2ZQ0KPiBh
bmQgbGlrZSBhIGJydXNoLW9mZi4NCj4gDQo+IEZpcnN0IG9mZiwgeW91IHdpbGwgc3RpbGwgaGF2
ZSB0byBkb2N1bWVudCBhbnkgRFQgYmluZGluZ3MgeW91J3JlIGFkZGluZy4gSnVzdA0KPiBiZWNh
dXNlIHlvdSBhbnN3ZXIgdGhlIHF1ZXN0aW9uIGluIHRoZSByZXZpZXcgZG9lc24ndCBtZWFuIHlv
dSdyZSBub3QNCj4gcmVzcG9uc2libGUgZm9yIGRvaW5nIHNvLg0KPiANCj4gQW5kIHNlY29uZCBv
ZmYsIEkgdGhpbmsgdGhhdCBSYWZhbCAoYW5kIHNvcnJ5IGFib3V0IG15IHNwZWxsaW5nLCBsb29r
cyBsaWtlDQo+IHRoZXJlJ3Mgc29tZSBzb3J0IG9mIGFjY2VudCBvbiB0aGUgbCB0aGF0IEkgZG9u
J3Qga25vdyBob3cgdG8gbWFrZSBteQ0KPiBrZXlib2FyZCBkbykgaXMgc2F5aW5nOiB0aGVyZSdz
IGFscmVhZHkgc29tZSBnZW5lcmljIGJpbmRpbmdzIHRoYXQgY2FuIGJlIHVzZWQNCj4gdG8gZGlz
YWJsZSB0aGUgMmcgb3IgNWcgYmFuZHMuIEdyYW50ZWQgdGhleSdyZSBldmVuIG5ld2VyIHRoYW4g
eW91ciBwYXRjaCwNCj4gYnV0IEkgZG8gdGhpbmsgaWYgc2FpZCBiaW5kaW5ncyBleGlzdCBhbmQg
YXJlIGFwcHJvcHJpYXRlLCB5b3Ugc2hvdWxkIHVzZSB0aGVtLg0KPiANCj4gLSBTdGV2ZQ0KDQpU
aGVzZSBwYXJhbWV0ZXJzIGFyZSBtYXJ2ZWxsIHByb3ByaWV0YXJ5IHBhcmFtZXRlcnMsIEkgZG9u
J3QgdGhpbmsgaXQgc2hvdWxkIGJlIGFkZGVkIHRvIERUIGJpbmRpbmdzLg0KDQpCVFcsIDxtYXJ2
ZWxsLDJnaHo+IGFuZCA8bWFydmVsbCw1Z2h6PiBhcmUgb25seSB1c2VkIGZvciBtd2x3aWZpIHRv
IHJlcG9ydCBzdXBwb3J0ZWQgYmFuZHMsIGl0IGlzIG5vdCByZWxhdGVkIHRvIGxpbWl0YXRpb24g
b2YgZnJlcXVlbmN5Lg0K
On 7 February 2017 at 20:12, Steve deRosier <[email protected]> wrote:
>> + /* look for all matching property names */
>> + for_each_property_of_node(priv->dt_node, prop) {
>> + if (strcmp(prop->name, "marvell,2ghz") == 0)
>> + priv->disable_2g = true;
>> + if (strcmp(prop->name, "marvell,5ghz") == 0)
>> + priv->disable_5g = true;
>> + if (strcmp(prop->name, "marvell,chainmask") == 0) {
>> + prop_value = be32_to_cpu(*((__be32 *)prop->value));
>> + if (prop_value == 2)
>> + priv->antenna_tx = ANTENNA_TX_2;
>> +
>> + prop_value = be32_to_cpu(*((__be32 *)
>> + (prop->value + 4)));
>> + if (prop_value == 2)
>> + priv->antenna_rx = ANTENNA_RX_2;
>> + }
>> + }
>> +
>> + priv->pwr_node = of_find_node_by_name(priv->dt_node,
>> + "marvell,powertable");
>> +#endif
>> +}
>
> AFAICT, there's no documentation for these DT bindings. The mwlwifi node
> and the marvell,powertable both need full documentation in
> Documentation/devicetree/bindings/... .
>
> Frankly I have a feeling I'm going to need these DT nodes and I'd like to not
> have to guess-and-check based on the code.
Please use ieee80211-freq-limit:
https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=b330b25eaabda00d74e47566d9200907da381896
Most likely with wiphy_read_of_freq_limits helper:
https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=e691ac2f75b69bee743f0370d79454ba4429b175
Hi David,
First off, I wanted to say thank-you for your work and effort in trying
to get mwlwifi upstream. My comments are in-line with my general notes
afterwards.
On Tue, Dec 20, 2016 at 8:11 PM, David Lin <[email protected]> wrote:
> diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.h b/drivers/net/wireless/marvell/mwlwifi/debugfs.h
> new file mode 100644
> index 0000000..b4c3eb3
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.h
> @@ -0,0 +1,24 @@
> +/*
> + * Copyright (C) 2006-2016, Marvell International Ltd.
> + *
> + * This software file (the "File") is distributed by Marvell International
> + * Ltd. under the terms of the GNU General Public License Version 2, June 1991
> + * (the "License"). You may use, redistribute and/or modify this File in
> + * accordance with the terms and conditions of the License, a copy of which
> + * is available by writing to the Free Software Foundation, Inc.
> + *
> + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
> + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
> + * this warranty disclaimer.
> + */
> +
> +/* Description: This file defines debug fs related functions. */
> +
> +#ifndef _MWL_DEBUGFS_H_
> +#define _MWL_DEBUGFS_H_
> +
> +void mwl_debugfs_init(struct ieee80211_hw *hw);
> +void mwl_debugfs_remove(struct ieee80211_hw *hw);
> +
You should guard these so they define to empty functions if CONFIG_DEBUG_FS
isn't enabled so they can be used by a caller without explicit guards.
> diff --git a/drivers/net/wireless/marvell/mwlwifi/dev.h b/drivers/net/wireless/marvell/mwlwifi/dev.h
> new file mode 100644
> index 0000000..c7b10ac
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/mwlwifi/dev.h
...
> +struct mwl_ampdu_stream {
> + struct ieee80211_sta *sta;
> + u8 tid;
> + u8 state;
> + u8 idx;
> +};
> +
> +#ifdef CONFIG_DEBUG_FS
> +#define MAC_REG_ADDR_PCI(offset) ((priv->iobase1 + 0xA000) + (offset))
> +
> +#define MWL_ACCESS_MAC 1
> +#define MWL_ACCESS_RF 2
> +#define MWL_ACCESS_BBP 3
> +#define MWL_ACCESS_CAU 4
> +#define MWL_ACCESS_ADDR0 5
> +#define MWL_ACCESS_ADDR1 6
> +#define MWL_ACCESS_ADDR 7
> +#endif
OK, I get that you're only using the above in the debugfs functions, but
as for generic register access functions, these would be valid no matter if
CONFIG_DEBUG_FS is defined. Having the guard here just seems to complicate
things.
> +
> +struct mwl_priv {
> + struct ieee80211_hw *hw;
> + struct firmware *fw_ucode;
> + bool fw_device_pwrtbl;
> + bool forbidden_setting;
> + bool regulatory_set;
> + u32 fw_region_code;
> + char fw_alpha2[2];
> + u8 number_of_channels;
> + struct mwl_device_pwr_tbl device_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS];
> + int chip_type;
> +
> + struct device_node *dt_node;
> + struct device_node *pwr_node;
> + bool disable_2g;
> + bool disable_5g;
> + int antenna_tx;
> + int antenna_rx;
> +
> + struct mwl_tx_pwr_tbl tx_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS];
> + bool cdd;
> + u16 txantenna2;
> + u8 powinited;
> + u16 max_tx_pow[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* max tx power (dBm) */
> + u16 target_powers[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* target powers */
> +
> + struct pci_dev *pdev;
> + struct device *dev;
> + void __iomem *iobase0; /* MEM Base Address Register 0 */
> + void __iomem *iobase1; /* MEM Base Address Register 1 */
> + u32 next_bar_num;
> +
> + struct mutex fwcmd_mutex; /* for firmware command */
> + unsigned short *pcmd_buf; /* pointer to CmdBuf (virtual) */
> + dma_addr_t pphys_cmd_buf; /* pointer to CmdBuf (physical) */
> + bool in_send_cmd;
> +
> + int irq;
> + struct mwl_hw_data hw_data; /* Adapter HW specific info */
> +
> + /* various descriptor data */
> + /* for tx descriptor data */
> + spinlock_t tx_desc_lock ____cacheline_aligned_in_smp;
> + struct mwl_desc_data desc_data[SYSADPT_NUM_OF_DESC_DATA];
> + struct sk_buff_head txq[SYSADPT_NUM_OF_DESC_DATA];
> + struct sk_buff_head delay_q;
> + /* number of descriptors owned by fw at any one time */
> + int fw_desc_cnt[SYSADPT_NUM_OF_DESC_DATA];
> +
> + struct tasklet_struct tx_task;
> + struct tasklet_struct tx_done_task;
> + struct tasklet_struct rx_task;
> + struct tasklet_struct qe_task;
> + int txq_limit;
> + bool is_tx_done_schedule;
> + int recv_limit;
> + bool is_rx_schedule;
> + bool is_qe_schedule;
> + u32 qe_trigger_num;
> + unsigned long qe_trigger_time;
> +
> + struct timer_list period_timer;
> +
> + s8 noise; /* Most recently reported noise in dBm */
> + struct ieee80211_supported_band band_24;
> + struct ieee80211_channel channels_24[BAND_24_CHANNEL_NUM];
> + struct ieee80211_rate rates_24[BAND_24_RATE_NUM];
> + struct ieee80211_supported_band band_50;
> + struct ieee80211_channel channels_50[BAND_50_CHANNEL_NUM];
> + struct ieee80211_rate rates_50[BAND_50_RATE_NUM];
> +
> + u32 ap_macids_supported;
> + u32 sta_macids_supported;
> + u32 macids_used;
> + u32 running_bsses; /* bitmap of running BSSes */
> +
> + struct {
> + spinlock_t vif_lock; /* for private interface info */
> + struct list_head vif_list; /* List of interfaces. */
> + } ____cacheline_aligned_in_smp;
> +
> + struct {
> + spinlock_t sta_lock; /* for private sta info */
> + struct list_head sta_list; /* List of stations */
> + } ____cacheline_aligned_in_smp;
> +
> + bool radio_on;
> + bool radio_short_preamble;
> + bool wmm_enabled;
> + struct ieee80211_tx_queue_params wmm_params[SYSADPT_TX_WMM_QUEUES];
> +
> + /* ampdu stream information */
> + /* for ampdu stream */
> + struct {
> + spinlock_t stream_lock; /* for BA stream */
> + struct mwl_ampdu_stream ampdu[SYSADPT_TX_AMPDU_QUEUES];
> + } ____cacheline_aligned_in_smp;
> + struct work_struct watchdog_ba_handle;
> +
> + bool csa_active;
> + struct work_struct chnl_switch_handle;
> + enum nl80211_dfs_regions dfs_region;
> + u16 dfs_chirp_count_min;
> + u16 dfs_chirp_time_interval;
> + u16 dfs_pw_filter;
> + u16 dfs_min_num_radar;
> + u16 dfs_min_pri_count;
> +
> + struct thermal_cooling_device *cdev;
> + u32 throttle_state;
> + u32 quiet_period;
> + int temperature;
> +
> +#ifdef CONFIG_DEBUG_FS
> + struct dentry *debugfs_phy;
> + u32 reg_type;
> + u32 reg_offset;
> + u32 reg_value;
> + int tx_desc_num;
> +#endif
We're saving a few bytes here at the cost of some complexity. I could go
either way, but I hate when priv structures mutate.
> +/* Defined in mac80211.c. */
> +extern const struct ieee80211_ops mwl_mac80211_ops;
Does this need to be in dev.h?
> diff --git a/drivers/net/wireless/marvell/mwlwifi/fwcmd.c b/drivers/net/wireless/marvell/mwlwifi/fwcmd.c
> new file mode 100644
> index 0000000..9c3ccf9
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/mwlwifi/fwcmd.c
> @@ -0,0 +1,2837 @@
> +/*
> + * Copyright (C) 2006-2016, Marvell International Ltd.
> + *
> + * This software file (the "File") is distributed by Marvell International
> + * Ltd. under the terms of the GNU General Public License Version 2, June 1991
> + * (the "License"). You may use, redistribute and/or modify this File in
> + * accordance with the terms and conditions of the License, a copy of which
> + * is available by writing to the Free Software Foundation, Inc.
> + *
> + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
> + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
> + * this warranty disclaimer.
> + */
> +
> +/* Description: This file implements firmware host command related
> + * functions.
> + */
> +
> +#include <linux/etherdevice.h>
> +
> +#include "sysadpt.h"
> +#include "dev.h"
> +#include "fwcmd.h"
> +#include "hostcmd.h"
> +
> +#define MAX_WAIT_FW_COMPLETE_ITERATIONS 2000
> +#define MAX_WAIT_GET_HW_SPECS_ITERATONS 3
> +
> +struct cmd_header {
> + __le16 command;
> + __le16 len;
> +} __packed;
> +
> +static bool mwl_fwcmd_chk_adapter(struct mwl_priv *priv)
> +{
> + u32 regval;
> +
> + regval = readl(priv->iobase1 + MACREG_REG_INT_CODE);
Couldn't the above be one line?
> +
> +static int mwl_fwcmd_get_tx_powers(struct mwl_priv *priv, u16 *powlist, u16 ch,
> + u16 band, u16 width, u16 sub_ch)
> +{
> + struct hostcmd_cmd_802_11_tx_power *pcmd;
> + int i;
> +
> + pcmd = (struct hostcmd_cmd_802_11_tx_power *)&priv->pcmd_buf[0];
Often happens, so I probably won't catch them all, but this could likewise
be done in one line. Then again... line length issues, so 50/50 on that.
> +
> +static u8 mwl_fwcmd_get_80m_pri_chnl(u8 channel)
> +{
> + u8 act_primary = ACT_PRIMARY_CHAN_0;
> +
> + switch (channel) {
> + case 36:
> + act_primary = ACT_PRIMARY_CHAN_0;
> + break;
> + case 40:
> + act_primary = ACT_PRIMARY_CHAN_1;
> + break;
> + case 44:
> + act_primary = ACT_PRIMARY_CHAN_2;
> + break;
> + case 48:
> + act_primary = ACT_PRIMARY_CHAN_3;
> + break;
> + case 52:
> + act_primary = ACT_PRIMARY_CHAN_0;
> + break;
> + case 56:
> + act_primary = ACT_PRIMARY_CHAN_1;
> + break;
> + case 60:
> + act_primary = ACT_PRIMARY_CHAN_2;
> + break;
> + case 64:
> + act_primary = ACT_PRIMARY_CHAN_3;
> + break;
> + case 100:
> + act_primary = ACT_PRIMARY_CHAN_0;
> + break;
> + case 104:
> + act_primary = ACT_PRIMARY_CHAN_1;
> + break;
> + case 108:
> + act_primary = ACT_PRIMARY_CHAN_2;
> + break;
> + case 112:
> + act_primary = ACT_PRIMARY_CHAN_3;
> + break;
> + case 116:
> + act_primary = ACT_PRIMARY_CHAN_0;
> + break;
> + case 120:
> + act_primary = ACT_PRIMARY_CHAN_1;
> + break;
> + case 124:
> + act_primary = ACT_PRIMARY_CHAN_2;
> + break;
> + case 128:
> + act_primary = ACT_PRIMARY_CHAN_3;
> + break;
> + case 132:
> + act_primary = ACT_PRIMARY_CHAN_0;
> + break;
> + case 136:
> + act_primary = ACT_PRIMARY_CHAN_1;
> + break;
> + case 140:
> + act_primary = ACT_PRIMARY_CHAN_2;
> + break;
> + case 144:
> + act_primary = ACT_PRIMARY_CHAN_3;
> + break;
> + case 149:
> + act_primary = ACT_PRIMARY_CHAN_0;
> + break;
> + case 153:
> + act_primary = ACT_PRIMARY_CHAN_1;
> + break;
> + case 157:
> + act_primary = ACT_PRIMARY_CHAN_2;
> + break;
> + case 161:
> + act_primary = ACT_PRIMARY_CHAN_3;
> + break;
> + }
> +
> + return act_primary;
> +}
> +
Ignorance speaking here perhaps, but the above looks like something that
nearly every driver would need to deal with. Isn't there a helper function
in mac80211 or some better way to do this than explicitly doing a switch
logic lookup?
> +int mwl_fwcmd_set_new_stn_add(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct ieee80211_sta *sta)
> +{
> + struct mwl_priv *priv = hw->priv;
> + struct mwl_vif *mwl_vif;
> + struct hostcmd_cmd_set_new_stn *pcmd;
> + u32 rates;
> +
> + mwl_vif = mwl_dev_get_vif(vif);
> +
> + pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];
> +
> + mutex_lock(&priv->fwcmd_mutex);
> +
> + memset(pcmd, 0x00, sizeof(*pcmd));
> + pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN);
> + pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
> + pcmd->cmd_hdr.macid = mwl_vif->macid;
> +
> + pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_ADD);
> + if (vif->type == NL80211_IFTYPE_STATION) {
> + pcmd->aid = 0;
> + pcmd->stn_id = 0;
> + } else {
> + pcmd->aid = cpu_to_le16(sta->aid);
> + pcmd->stn_id = cpu_to_le16(sta->aid);
> + }
> + ether_addr_copy(pcmd->mac_addr, sta->addr);
> +
> + if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
> + rates = sta->supp_rates[NL80211_BAND_2GHZ];
> + else
> + rates = sta->supp_rates[NL80211_BAND_5GHZ] << 5;
> + pcmd->peer_info.legacy_rate_bitmap = cpu_to_le32(rates);
> +
> + if (sta->ht_cap.ht_supported) {
> + pcmd->peer_info.ht_rates[0] = sta->ht_cap.mcs.rx_mask[0];
> + pcmd->peer_info.ht_rates[1] = sta->ht_cap.mcs.rx_mask[1];
> + pcmd->peer_info.ht_rates[2] = sta->ht_cap.mcs.rx_mask[2];
> + pcmd->peer_info.ht_rates[3] = sta->ht_cap.mcs.rx_mask[3];
> + pcmd->peer_info.ht_cap_info = cpu_to_le16(sta->ht_cap.cap);
> + pcmd->peer_info.mac_ht_param_info =
> + (sta->ht_cap.ampdu_factor & 3) |
> + ((sta->ht_cap.ampdu_density & 7) << 2);
> + }
> +
> + if (sta->vht_cap.vht_supported) {
> + pcmd->peer_info.vht_max_rx_mcs =
> + cpu_to_le32(*((u32 *)
> + &sta->vht_cap.vht_mcs.rx_mcs_map));
> + pcmd->peer_info.vht_cap = cpu_to_le32(sta->vht_cap.cap);
> + pcmd->peer_info.vht_rx_channel_width = sta->bandwidth;
> + }
> +
> + pcmd->is_qos_sta = sta->wme;
> + pcmd->qos_info = ((sta->uapsd_queues << 4) | (sta->max_sp << 1));
> +
> + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
> + mutex_unlock(&priv->fwcmd_mutex);
> + wiphy_err(hw->wiphy, "failed execution\n");
> + return -EIO;
> + }
> +
> + if (vif->type == NL80211_IFTYPE_STATION) {
> + ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac);
> +
> + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
> + mutex_unlock(&priv->fwcmd_mutex);
> + wiphy_err(hw->wiphy, "failed execution\n");
> + return -EIO;
> + }
> + }
OK, time to ask. Why are we telling the firmware that we're connected to
ourselves? I would presume the firmware already knows our MAC address.
I noticed this originally because there's a nasty bug with
recycling the command buffer (for sdio, it's not relevant for this driver)
and in doing experiments I noticed that throughput significantly increases
in STA mode if we just leave out the entire clause.
In briefly examining the firmware source I see no reason to do this, but
there's a hidden chunk and I don't know what the hardware itself does with
the MAC table.
So, why is it necessary?
> +int mwl_fwcmd_set_new_stn_del(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif, u8 *addr)
> +{
> + struct mwl_priv *priv = hw->priv;
> + struct mwl_vif *mwl_vif;
> + struct hostcmd_cmd_set_new_stn *pcmd;
> +
> + mwl_vif = mwl_dev_get_vif(vif);
> +
> + pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];
> +
> + mutex_lock(&priv->fwcmd_mutex);
> +
> + memset(pcmd, 0x00, sizeof(*pcmd));
> + pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN);
> + pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
> + pcmd->cmd_hdr.macid = mwl_vif->macid;
> +
> + pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_REMOVE);
> + ether_addr_copy(pcmd->mac_addr, addr);
> +
> + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
> + mutex_unlock(&priv->fwcmd_mutex);
> + wiphy_err(hw->wiphy, "failed execution\n");
> + return -EIO;
> + }
> +
> + if (vif->type == NL80211_IFTYPE_STATION) {
> + ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac);
> +
> + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
> + mutex_unlock(&priv->fwcmd_mutex);
> + wiphy_err(hw->wiphy, "failed execution\n");
> + return -EIO;
> + }
> + }
> +
Ditto.
> diff --git a/drivers/net/wireless/marvell/mwlwifi/fwdl.c b/drivers/net/wireless/marvell/mwlwifi/fwdl.c
> new file mode 100644
> index 0000000..f4d5fa1
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/mwlwifi/fwdl.c
...
> +
> +static void mwl_fwdl_trig_pcicmd(struct mwl_priv *priv)
> +{
> + writel(priv->pphys_cmd_buf, priv->iobase1 + MACREG_REG_GEN_PTR);
> +
> + writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE);
> +
> + writel(MACREG_H2ARIC_BIT_DOOR_BELL,
> + priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
> +}
> +
> +static void mwl_fwdl_trig_pcicmd_bootcode(struct mwl_priv *priv)
> +{
> + writel(priv->pphys_cmd_buf, priv->iobase1 + MACREG_REG_GEN_PTR);
> +
> + writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE);
> +
> + writel(MACREG_H2ARIC_BIT_DOOR_BELL,
> + priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
> +}
> +
Unless I'm mistaken the above two functions are 100% identical. In my version
I collapsed them to a single one and it works fine.
> +int mwl_fwdl_download_firmware(struct ieee80211_hw *hw)
> +{
...
> +
> + while (size_fw_downloaded < fw->size) {
> + len = readl(priv->iobase1 + 0xc40);
> +
> + if (!len)
> + break;
> +
> + /* this copies the next chunk of fw binary to be delivered */
> + memcpy((char *)&priv->pcmd_buf[0],
> + (fw->data + size_fw_downloaded), len);
> +
> + /* this function writes pdata to c10, then write 2 to c18 */
> + mwl_fwdl_trig_pcicmd_bootcode(priv);
> +
> + /* this is arbitrary per your platform; we use 0xffff */
> + curr_iteration = FW_MAX_NUM_CHECKS;
> +
> + /* NOTE: the following back to back checks on C1C is time
> + * sensitive, hence may need to be tweaked dependent on host
> + * processor. Time for SC2 to go from the write of event 2 to
> + * C1C == 2 is ~1300 nSec. Hence the checkings on host has to
> + * consider how efficient your code can be to meet this timing,
> + * or you can alternatively tweak this routines to fit your
> + * platform
> + */
> + do {
> + int_code = readl(priv->iobase1 + 0xc1c);
> + if (int_code != 0)
> + break;
> + cond_resched();
> + curr_iteration--;
> + } while (curr_iteration);
> +
There's something fishy with the above. Having to "tweak" driver timing based
on platform is a huge red flag. There's got to be something better than the
above.
> diff --git a/drivers/net/wireless/marvell/mwlwifi/mac80211.c b/drivers/net/wireless/marvell/mwlwifi/mac80211.c
...
> +static int mwl_mac80211_config(struct ieee80211_hw *hw,
> + u32 changed)
> +{
> + struct ieee80211_conf *conf = &hw->conf;
> + int rc;
> +
> + wiphy_debug(hw->wiphy, "change: 0x%x\n", changed);
> +
> + if (conf->flags & IEEE80211_CONF_IDLE)
> + rc = mwl_fwcmd_radio_disable(hw);
> + else
> + rc = mwl_fwcmd_radio_enable(hw);
> +
> + if (rc)
> + goto out;
> +
> + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
> + int rate = 0;
> +
> + if (conf->chandef.chan->band == NL80211_BAND_2GHZ) {
Causes compile warning. Should be IEEE80211_BAND_2GHZ.
> + mwl_fwcmd_set_apmode(hw, AP_MODE_2_4GHZ_11AC_MIXED);
> + mwl_fwcmd_set_linkadapt_cs_mode(hw,
> + LINK_CS_STATE_CONSERV);
> + rate = mwl_rates_24[0].hw_value;
> + } else if (conf->chandef.chan->band == NL80211_BAND_5GHZ) {
Causes compile warning. Should be IEEE80211_BAND_5GHZ.
> +static void mwl_mac80211_bss_info_changed_ap(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct ieee80211_bss_conf *info,
> + u32 changed)
> +{
> + if (changed & BSS_CHANGED_ERP_PREAMBLE)
> + mwl_fwcmd_set_radio_preamble(hw,
> + vif->bss_conf.use_short_preamble);
> +
> + if (changed & BSS_CHANGED_BASIC_RATES) {
> + int idx;
> + int rate;
> +
> + /* Use lowest supported basic rate for multicasts
> + * and management frames (such as probe responses --
> + * beacons will always go out at 1 Mb/s).
> + */
> + idx = ffs(vif->bss_conf.basic_rates);
> + if (idx)
> + idx--;
> +
> + if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
Causes compile warning. Should be IEEE80211_BAND_2GHZ.
> diff --git a/drivers/net/wireless/marvell/mwlwifi/main.c b/drivers/net/wireless/marvell/mwlwifi/main.c
> new file mode 100644
...
> +#include <linux/module.h>
> +#ifdef CONFIG_OF
> +#include <linux/of.h>
> +#endif
Isn't of.h internally guarded?
> +
> +#include "sysadpt.h"
> +#include "dev.h"
> +#include "fwdl.h"
> +#include "fwcmd.h"
> +#include "tx.h"
> +#include "rx.h"
> +#include "isr.h"
> +#include "thermal.h"
> +#ifdef CONFIG_DEBUG_FS
> +#include "debugfs.h"
> +#endif
Your debugfs.h should be internally guarded. More later...
> +
> +#define MWL_DESC "Marvell 802.11ac Wireless Network Driver"
> +#define MWL_DEV_NAME "Marvell 802.11ac Adapter"
> +
> +#define FILE_PATH_LEN 64
> +#define CMD_BUF_SIZE 0x4000
> +
> +static struct pci_device_id mwl_pci_id_tbl[] = {
> + { PCI_VDEVICE(MARVELL, 0x2a55), .driver_data = MWL8864, },
> + { PCI_VDEVICE(MARVELL, 0x2b38), .driver_data = MWL8897, },
> + { },
> +};
> +
> +static struct mwl_chip_info mwl_chip_tbl[] = {
> + [MWL8864] = {
> + .part_name = "88W8864",
> + .fw_image = "mwlwifi/88W8864.bin",
> + .antenna_tx = ANTENNA_TX_4_AUTO,
> + .antenna_rx = ANTENNA_RX_4_AUTO,
> + },
> + [MWL8897] = {
> + .part_name = "88W8897",
> + .fw_image = "mwlwifi/88W8897.bin",
> + .antenna_tx = ANTENNA_TX_2,
> + .antenna_rx = ANTENNA_RX_2,
> + },
> +};
> +
> +static const struct ieee80211_channel mwl_channels_24[] = {
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2412, .hw_value = 1, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2417, .hw_value = 2, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2422, .hw_value = 3, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2427, .hw_value = 4, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2432, .hw_value = 5, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2437, .hw_value = 6, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2442, .hw_value = 7, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2447, .hw_value = 8, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2452, .hw_value = 9, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2457, .hw_value = 10, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2462, .hw_value = 11, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2467, .hw_value = 12, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2472, .hw_value = 13, },
> + { .band = NL80211_BAND_2GHZ, .center_freq = 2484, .hw_value = 14, },
> +};
> +
So, interesting thing... there's 62 uses of NL80211_BAND_x in this driver, but
only the few spots I mentioned cause warnings. I notice in the most recent
internal drop you've changed the above to IEEE80211_BAND_2GHZ. I wonder if that
is what should be done everywhere?
> +static const struct ieee80211_rate mwl_rates_24[] = {
> + { .bitrate = 10, .hw_value = 2, },
> + { .bitrate = 20, .hw_value = 4, },
> + { .bitrate = 55, .hw_value = 11, },
> + { .bitrate = 110, .hw_value = 22, },
> + { .bitrate = 220, .hw_value = 44, },
> + { .bitrate = 60, .hw_value = 12, },
> + { .bitrate = 90, .hw_value = 18, },
> + { .bitrate = 120, .hw_value = 24, },
> + { .bitrate = 180, .hw_value = 36, },
> + { .bitrate = 240, .hw_value = 48, },
> + { .bitrate = 360, .hw_value = 72, },
> + { .bitrate = 480, .hw_value = 96, },
> + { .bitrate = 540, .hw_value = 108, },
> +};
> +
> +static const struct ieee80211_channel mwl_channels_50[] = {
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5180, .hw_value = 36, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5200, .hw_value = 40, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5220, .hw_value = 44, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5240, .hw_value = 48, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5260, .hw_value = 52, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5280, .hw_value = 56, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5300, .hw_value = 60, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5320, .hw_value = 64, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5500, .hw_value = 100, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5520, .hw_value = 104, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5540, .hw_value = 108, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5560, .hw_value = 112, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5580, .hw_value = 116, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5600, .hw_value = 120, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5620, .hw_value = 124, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5640, .hw_value = 128, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5660, .hw_value = 132, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5680, .hw_value = 136, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5700, .hw_value = 140, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5720, .hw_value = 144, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5745, .hw_value = 149, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5765, .hw_value = 153, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5785, .hw_value = 157, },
> + { .band = NL80211_BAND_5GHZ, .center_freq = 5805, .hw_value = 161, },
> +};
Ditto.
...
> +
> +static void mwl_process_of_dts(struct mwl_priv *priv)
> +{
> +#ifdef CONFIG_OF
So a more common idiom in the drivers is:
#ifdef CONFIG_OF
define process of_dts function {}
#else
define EMPTY of_dts function {}
#endif
I guess it's the same effect either way. I don't much care as long as
mwl_process_of_dts() can be called without guards, which it can. But I
thought I should mention it incase anyone else cares. And this is not
consistent with how you have done the same thing for CONFIG_DEBUG_FS
> + struct property *prop;
> + u32 prop_value;
> +
> + priv->dt_node =
> + of_find_node_by_name(pci_bus_to_OF_node(priv->pdev->bus),
> + "mwlwifi");
> + if (!priv->dt_node)
> + return;
> +
> + /* look for all matching property names */
> + for_each_property_of_node(priv->dt_node, prop) {
> + if (strcmp(prop->name, "marvell,2ghz") == 0)
> + priv->disable_2g = true;
> + if (strcmp(prop->name, "marvell,5ghz") == 0)
> + priv->disable_5g = true;
> + if (strcmp(prop->name, "marvell,chainmask") == 0) {
> + prop_value = be32_to_cpu(*((__be32 *)prop->value));
> + if (prop_value == 2)
> + priv->antenna_tx = ANTENNA_TX_2;
> +
> + prop_value = be32_to_cpu(*((__be32 *)
> + (prop->value + 4)));
> + if (prop_value == 2)
> + priv->antenna_rx = ANTENNA_RX_2;
> + }
> + }
> +
> + priv->pwr_node = of_find_node_by_name(priv->dt_node,
> + "marvell,powertable");
> +#endif
> +}
AFAICT, there's no documentation for these DT bindings. The mwlwifi node
and the marvell,powertable both need full documentation in
Documentation/devicetree/bindings/... .
Frankly I have a feeling I'm going to need these DT nodes and I'd like to not
have to guess-and-check based on the code.
...
> +static int mwl_wl_init(struct mwl_priv *priv)
> +{
> + struct ieee80211_hw *hw;
> + int rc;
> + int i;
> +
> + hw = priv->hw;
> +
> + hw->extra_tx_headroom = SYSADPT_MIN_BYTES_HEADROOM;
> + hw->queues = SYSADPT_TX_WMM_QUEUES;
> +
> + /* Set rssi values to dBm */
> + ieee80211_hw_set(hw, SIGNAL_DBM);
> + ieee80211_hw_set(hw, HAS_RATE_CONTROL);
> +
> + /* Ask mac80211 not to trigger PS mode
> + * based on PM bit of incoming frames.
> + */
> + ieee80211_hw_set(hw, AP_LINK_PS);
> +
> + ieee80211_hw_set(hw, SUPPORTS_PER_STA_GTK);
> + ieee80211_hw_set(hw, MFP_CAPABLE);
> +
> + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
> + hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
> +
> + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
> +
> + hw->vif_data_size = sizeof(struct mwl_vif);
> + hw->sta_data_size = sizeof(struct mwl_sta);
> +
> + priv->ap_macids_supported = 0x0000ffff;
> + priv->sta_macids_supported = 0x00010000;
How about we document what these magic numbers are? A nice named constant
at least would be nice.
> +static int mwl_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> +{
...
> + wiphy_info(priv->hw->wiphy, "%s TX antennas, %s RX antennas\n",
> + (priv->antenna_tx == ANTENNA_TX_4_AUTO) ? "4" : "2",
> + (priv->antenna_rx == ANTENNA_RX_4_AUTO) ? "4" : "2");
> +
> +#ifdef CONFIG_DEBUG_FS
> + mwl_debugfs_init(hw);
The guards should be internal to mwl_debugfs_init() so we don't have to
guard it when we call it. Much like mwl_process_of_dts() is able to be called
and compiles out if CONFIG_OF isn't defined, mwl_debugfs_init() should have
the guards internal to debugfs.h/debugfs.c and we shouldn't need to worry
about it when we call it.
> +static void mwl_remove(struct pci_dev *pdev)
> +{
> + struct ieee80211_hw *hw = pci_get_drvdata(pdev);
> + struct mwl_priv *priv;
> +
> + if (!hw)
> + return;
> +
> + priv = hw->priv;
> +
> + mwl_wl_deinit(priv);
> + pci_set_drvdata(pdev, NULL);
> + ieee80211_free_hw(hw);
> + pci_disable_device(pdev);
> +
> +#ifdef CONFIG_DEBUG_FS
> + mwl_debugfs_remove(hw);
> +#endif
As previously commented on.
> +++ b/drivers/net/wireless/marvell/mwlwifi/thermal.c
...
> +static SENSOR_DEVICE_ATTR(temp1_input, 0444, mwl_thermal_show_temp,
> + NULL, 0);
> +
Should use S_IRUGO instead of numeric 0444.
OK, that's it for specifics. I know a number of them are just nits.
A few general comments:
* I saw it it quite a bit, but didn't comment on it every time: there's
many places where a variable declaration can be combined with its
initial assignment.
* I happen to concur with Johannes' comments regarding the IEs and
your beacon processing. This is a significant issue, with potential for big
bugs down the road. At the very least, it's a maintenance headache.
>From my perspective, I'd consider it a firmware bug if there's no way
around it. Is the firmware going to strip the IEs that hostapd happens
to add to the beacons? Is there some "passthrough" or some other way
that it can be reconciled?
I strongly suspect there's better ways to handle it, even without
changing the firmware, but I haven't yet taken a look to see if there is.
In any case, while there's stuff I wouldn't mind seeing changed, I rather
see it go in sooner rather than later so I and others can contribute
on top of it, instead of waiting to see it "perfect" first.
Please add my reviewed-by. If we're waiting on a v10,
do you have an ETA?
Thanks,
- Steve
PiBGcm9tOiBSYWZhxYIgTWnFgmVja2kgW21haWx0bzp6YWplYzVAZ21haWwuY29tXSB3b3J0ZTog
DQo+IE9uIDggRmVicnVhcnkgMjAxNyBhdCAwODoyOCwgRGF2aWQgTGluIDxkbGluQG1hcnZlbGwu
Y29tPiB3cm90ZToNCj4gPj4gRnJvbTogUmFmYcWCIE1pxYJlY2tpIFttYWlsdG86emFqZWM1QGdt
YWlsLmNvbV0gd29ydGU6DQo+ID4+IFNlbnQ6IFdlZG5lc2RheSwgRmVicnVhcnkgMDgsIDIwMTcg
MzoyNCBQTSBPbiA4IEZlYnJ1YXJ5IDIwMTcgYXQNCj4gPj4gMDc6MzAsIERhdmlkIExpbiA8ZGxp
bkBtYXJ2ZWxsLmNvbT4gd3JvdGU6DQo+ID4+ID4gc3RldmUuZGVyb3NpZXJAZ21haWwuY29tIFtt
YWlsdG86c3RldmUuZGVyb3NpZXJAZ21haWwuY29tXSB3cm90ZToNCj4gPj4gPj4gT24gVHVlLCBG
ZWIgNywgMjAxNyBhdCAxMDoxNSBQTSwgRGF2aWQgTGluIDxkbGluQG1hcnZlbGwuY29tPiB3cm90
ZToNCj4gPj4gPj4gPj4gUmFmYcWCIE1pxYJlY2tpIFttYWlsdG86emFqZWM1QGdtYWlsLmNvbV0g
d3JvdGU6DQo+ID4+ID4+ID4+IFBsZWFzZSB1c2UgaWVlZTgwMjExLWZyZXEtbGltaXQ6DQo+ID4+
ID4+ID4+IGh0dHBzOi8vZ2l0Lmtlcm5lbC5vcmcvY2dpdC9saW51eC9rZXJuZWwvZ2l0L2RhdmVt
L25ldC1uZXh0LmdpdA0KPiA+PiA+PiA+PiAvY28NCj4gPj4gPj4gPj4gbW1pDQo+ID4+ID4+ID4+
IHQvP2lkPWIzDQo+ID4+ID4+ID4+IDMwYjI1ZWFhYmRhMDBkNzRlNDc1NjZkOTIwMDkwN2RhMzgx
ODk2DQo+ID4+ID4+ID4+DQo+ID4+ID4+ID4+IE1vc3QgbGlrZWx5IHdpdGggd2lwaHlfcmVhZF9v
Zl9mcmVxX2xpbWl0cyBoZWxwZXI6DQo+ID4+ID4+ID4+IGh0dHBzOi8vZ2l0Lmtlcm5lbC5vcmcv
Y2dpdC9saW51eC9rZXJuZWwvZ2l0L2RhdmVtL25ldC1uZXh0LmdpdA0KPiA+PiA+PiA+PiAvY28N
Cj4gPj4gPj4gPj4gbW1pDQo+ID4+ID4+ID4+IHQvP2lkPWU2DQo+ID4+ID4+ID4+IDkxYWMyZjc1
YjY5YmVlNzQzZjAzNzBkNzk0NTRiYTQ0MjliMTc1DQo+ID4+ID4+ID4NCj4gPj4gPj4gPiBJIGFs
cmVhZHkgcmVwbGllZCBtZWFuaW5nIG9mIHRoZXNlIHBhcmFtZXRlcnM6DQo+ID4+ID4+ID4gPG1h
cnZlbGwsMmdoej4gaXMgdXNlZCB0byBkaXNhYmxlIDJnIGJhbmQuDQo+ID4+ID4+ID4gPG1hcnZl
bGwsNWdoej4gaXMgdXNlZCB0byBkaXNhYmxlIDVnIGJhbmQuDQo+ID4+ID4+ID4gPG1hcnZlbGws
IGNoYWlubWFzaz4gaXMgdXNlZCB0byBzcGVjaWZ5IGFudGVubmEgbnVtYmVyIChpZg0KPiA+PiA+
PiA+IGRlZmF1bHQgbnVtYmVyDQo+ID4+ID4+IGlzIHN1aXRhYmxlIGZvciB5b3UsIHRoZXJlIGlz
IG5vIG5lZWQgdG8gdXNlIHRoaXMgcGFyYW1ldGVyKS4NCj4gPj4gPj4gPiA8bWFydmVsbCxwb3dl
cnRhYmxlPiBzaG91bGQgbm90IGJlIHVzZWQgZm9yIGNoaXAgd2l0aCBkZXZpY2UNCj4gPj4gPj4g
PiBwb3dlcg0KPiA+PiB0YWJsZS4NCj4gPj4gPj4gSW4gZmFjdCwgdGhpcyBwYXJhbWV0ZXIgc2hv
dWxkIG5vdCBiZSB1c2VkIGFueSBtb3JlLg0KPiA+PiA+PiA+DQo+ID4+ID4+DQo+ID4+ID4+IERh
dmlkLCBJIHRoaW5rIHlvdSdyZSBub3QgdW5kZXJzdGFuZGluZyB0aGUgY29tbWVudCwgb3IgYXQg
bGVhc3QNCj4gPj4gPj4gdGhhdCdzIHdoYXQgaXQgbG9va3MgbGlrZSB0byBtZS4gWWVzLCB5b3Ug
ZGlkIHJlcGx5IGFzIHRvIHRoZSBtZWFuaW5nLg0KPiA+PiA+PiBBbmQsIHlvdXIgcmVwbHksIHdo
aWxlIGluZm9ybWF0aXZlLCBkaWRuJ3QgdGVsbCB1cyB5b3UgdW5kZXJzdG9vZA0KPiA+PiA+PiBh
bmQgd2VyZSB3aWxsaW5nIHRvIGZpeCB0aGUgcHJvYmxlbS4gSSBkb3VidCB5b3UgbWVhbnQgaXQg
dGhpcw0KPiA+PiA+PiB3YXksIGJ1dCBpdCBmZWVscyBkZWZlbnNpdmUgYW5kIGxpa2UgYSBicnVz
aC1vZmYuDQo+ID4+ID4+DQo+ID4+ID4+IEZpcnN0IG9mZiwgeW91IHdpbGwgc3RpbGwgaGF2ZSB0
byBkb2N1bWVudCBhbnkgRFQgYmluZGluZ3MgeW91J3JlDQo+ID4+ID4+IGFkZGluZy4gSnVzdCBi
ZWNhdXNlIHlvdSBhbnN3ZXIgdGhlIHF1ZXN0aW9uIGluIHRoZSByZXZpZXcgZG9lc24ndA0KPiA+
PiA+PiBtZWFuIHlvdSdyZSBub3QgcmVzcG9uc2libGUgZm9yIGRvaW5nIHNvLg0KPiA+PiA+Pg0K
PiA+PiA+PiBBbmQgc2Vjb25kIG9mZiwgSSB0aGluayB0aGF0IFJhZmFsIChhbmQgc29ycnkgYWJv
dXQgbXkgc3BlbGxpbmcsDQo+ID4+ID4+IGxvb2tzIGxpa2UgdGhlcmUncyBzb21lIHNvcnQgb2Yg
YWNjZW50IG9uIHRoZSBsIHRoYXQgSSBkb24ndCBrbm93DQo+ID4+ID4+IGhvdyB0byBtYWtlIG15
IGtleWJvYXJkIGRvKSBpcyBzYXlpbmc6IHRoZXJlJ3MgYWxyZWFkeSBzb21lDQo+ID4+ID4+IGdl
bmVyaWMgYmluZGluZ3MgdGhhdCBjYW4gYmUgdXNlZCB0byBkaXNhYmxlIHRoZSAyZyBvciA1ZyBi
YW5kcy4NCj4gPj4gPj4gR3JhbnRlZCB0aGV5J3JlIGV2ZW4gbmV3ZXIgdGhhbiB5b3VyIHBhdGNo
LCBidXQgSSBkbyB0aGluayBpZiBzYWlkDQo+ID4+ID4+IGJpbmRpbmdzIGV4aXN0IGFuZA0KPiA+
PiBhcmUgYXBwcm9wcmlhdGUsIHlvdSBzaG91bGQgdXNlIHRoZW0uDQo+ID4+ID4+DQo+ID4+ID4+
IC0gU3RldmUNCj4gPj4gPg0KPiA+PiA+IFRoZXNlIHBhcmFtZXRlcnMgYXJlIG1hcnZlbGwgcHJv
cHJpZXRhcnkgcGFyYW1ldGVycywgSSBkb24ndCB0aGluaw0KPiA+PiA+IGl0IHNob3VsZA0KPiA+
PiBiZSBhZGRlZCB0byBEVCBiaW5kaW5ncy4NCj4gPj4NCj4gPj4gU3RldmUgaXMgY29ycmVjdC4N
Cj4gPj4NCj4gPj4gWW91IGhhdmUgdG8gZG9jdW1lbnQgbmV3IHByb3BlcnRpZXMsIGp1c3QgYmVj
YXVzZSB0aGV5IGFyZSBNYXJ2ZWxsDQo+ID4+IHNwZWNpZmljIGRvZXNuJ3QgY2hhbmdlIGFueXRo
aW5nLiBZb3UganVzdCBkb2N1bWVudCB0aGVtIGluIGEgcHJvcGVyDQo+IHBsYWNlLg0KPiA+Pg0K
PiA+DQo+ID4gQWxsIHJpZ2h0LiBJIHdpbGwgZG8gdGhhdC4NCj4gPg0KPiA+Pg0KPiA+PiA+IEJU
VywgPG1hcnZlbGwsMmdoej4gYW5kIDxtYXJ2ZWxsLDVnaHo+IGFyZSBvbmx5IHVzZWQgZm9yIG13
bHdpZmkgdG8NCj4gPj4gPiByZXBvcnQNCj4gPj4gc3VwcG9ydGVkIGJhbmRzLCBpdCBpcyBub3Qg
cmVsYXRlZCB0byBsaW1pdGF0aW9uIG9mIGZyZXF1ZW5jeS4NCj4gPj4NCj4gPj4gSG93IHJlcG9y
dGluZyBhIHNpbmdsZSBiYW5kIGRvZXNuJ3QgbGltaXQgcmVwb3J0ZWQgZnJlcXVlbmNpZXM/IFlv
dQ0KPiA+PiBjYW4gYWNoaWV2ZSBleGFjdGx5IHRoZSBzYW1lIHVzaW5nIGdlbmVyaWMgcHJvcGVy
dHksIHNvIHRoZXJlIGlzIG5vDQo+ID4+IHBsYWNlIGZvciBNYXJ2ZWxsIHNwZWNpZmljIG9uZXMu
DQo+ID4+DQo+ID4+IEluIGZhY3QgdGhlcmUgd2VyZSBkcml2ZXJzIG9mIDMgdmVuZG9ycyByZXF1
aXJpbmcgYmFuZC9mcmVxLXJlbGF0ZWQNCj4gPj4gZGVzY3JpcHRpb24gaW4gRFQ6IEJyb2FkY29t
LCBNYXJ2ZWxsICYgTWVkaWF0ZWsuIFRoaXMgcHJvcGVydHkgd2FzDQo+ID4+IGRpc2N1c3NlZCAm
IGRlc2lnbmVkIHRvIHN1cHBvcnQgYWxsIGxpbWl0YXRpb24gY2FzZXMgd2UgZm91bmQNCj4gPj4g
cG9zc2libGUgdG8gbWFrZSBpdCB1c2FibGUgd2l0aA0KPiA+PiAoaG9wZWZ1bGx5KSBhbGwgZHJp
dmVycy4NCj4gPj4NCj4gPg0KPiA+IEkgb25seSBuZWVkIHNpbXBsZSB3YXkgdG8gZGlzYWJsZSAy
ZyBvciA1ZyBiYW5kLiBJIHdpbGwgZm9sbG93IHlvdXIgc3VnZ2VzdGlvbg0KPiB0byBkb2N1bWVu
dCB0aGVzZSBtYXJ2ZWxsIHByb3ByaWV0YXJ5IHBhcmFtZXRlcnMuDQo+IA0KPiBTZXJpb3VzbHk/
IFJlZnVzaW5nIHRvIHVzZSBnZW5lcmljIGJpbmRpbmcgYmVjYXVzZSB5b3UgdGhpbmsgbWFydmVs
bCw1Z2h6OyBpcw0KPiBzaW1wbGVyIHRoYW4gaWVlZTgwMjExLWZyZXEtbGltaXQgPSA8MjQwMjAw
MCAyNDgyMDAwPjsgKG5vdCB0byBtZW50aW9uIHlvdXINCj4gcHJvcGVydHkgc2VlbXMgcmV2ZXJz
ZWQhKT8NCj4gDQo+IEkgZG9uJ3Qga25vdyBob3cgZWxzZSB0byBleHBsYWluIHRoaXMgdG8geW91
LiBXZSBkb24ndCB3YW50IGR1cGxpY2F0ZWQNCj4gcHJvcGVydGllcyB3aGVyZSBvbmUgY2FuIHdv
cmsuIEp1c3QgdXNlIGV4aXN0aW5nIG9uZS4gRG9uJ3QgYWRkIG5ldyBvbmUgZXZlbg0KPiBpZiBk
b2N1bWVudGVkLg0KPiANCg0KQWxsIHJpZ2h0LiBJIHdpbGwgY2hlY2sgYW5kIGxldCBwYXRjaCB2
MTAgdG8gdXNlIGl0LiBGb3IgcHJldmlvdXMgcGFyYW1ldGVycywgdGhleSB3aWxsIG9ubHkgYmUg
dXNlZCBieSBwcmV2aW91cyBPcGVuV3J0IHByb2plY3RzLg0KDQpUaGFua3MsDQpEYXZpZA0KDQo+
IC0tDQo+IFJhZmHFgg0K
PiBSYWZhxYIgTWnFgmVja2kgW21haWx0bzp6YWplYzVAZ21haWwuY29tXSB3cm90ZToNCj4gT24g
NyBGZWJydWFyeSAyMDE3IGF0IDIwOjEyLCBTdGV2ZSBkZVJvc2llciA8ZGVyb3NpZXJAZ21haWwu
Y29tPiB3cm90ZToNCj4gPj4gKyAvKiBsb29rIGZvciBhbGwgbWF0Y2hpbmcgcHJvcGVydHkgbmFt
ZXMgKi8NCj4gPj4gKyBmb3JfZWFjaF9wcm9wZXJ0eV9vZl9ub2RlKHByaXYtPmR0X25vZGUsIHBy
b3ApIHsgaWYNCj4gPj4gKyAoc3RyY21wKHByb3AtPm5hbWUsICJtYXJ2ZWxsLDJnaHoiKSA9PSAw
KQ0KPiA+PiArIHByaXYtPmRpc2FibGVfMmcgPSB0cnVlOw0KPiA+PiArIGlmIChzdHJjbXAocHJv
cC0+bmFtZSwgIm1hcnZlbGwsNWdoeiIpID09IDApDQo+ID4+ICsgcHJpdi0+ZGlzYWJsZV81ZyA9
IHRydWU7DQo+ID4+ICsgaWYgKHN0cmNtcChwcm9wLT5uYW1lLCAibWFydmVsbCxjaGFpbm1hc2si
KSA9PSAwKSB7IHByb3BfdmFsdWUgPQ0KPiA+PiArIGJlMzJfdG9fY3B1KCooKF9fYmUzMiAqKXBy
b3AtPnZhbHVlKSk7IGlmIChwcm9wX3ZhbHVlID09IDIpDQo+ID4+ICsgcHJpdi0+YW50ZW5uYV90
eCA9IEFOVEVOTkFfVFhfMjsNCj4gPj4gKw0KPiA+PiArIHByb3BfdmFsdWUgPSBiZTMyX3RvX2Nw
dSgqKChfX2JlMzIgKikgKHByb3AtPnZhbHVlICsgNCkpKTsgaWYNCj4gPj4gKyAocHJvcF92YWx1
ZSA9PSAyKQ0KPiA+PiArIHByaXYtPmFudGVubmFfcnggPSBBTlRFTk5BX1JYXzI7DQo+ID4+ICsg
fQ0KPiA+PiArIH0NCj4gPj4gKw0KPiA+PiArIHByaXYtPnB3cl9ub2RlID0gb2ZfZmluZF9ub2Rl
X2J5X25hbWUocHJpdi0+ZHRfbm9kZSwNCj4gPj4gKyAgICAgICJtYXJ2ZWxsLHBvd2VydGFibGUi
KTsNCj4gPj4gKyNlbmRpZg0KPiA+PiArfQ0KPiA+DQo+ID4gQUZBSUNULCB0aGVyZSdzIG5vIGRv
Y3VtZW50YXRpb24gZm9yIHRoZXNlIERUIGJpbmRpbmdzLiBUaGUgbXdsd2lmaQ0KPiA+IG5vZGUg
YW5kIHRoZSBtYXJ2ZWxsLHBvd2VydGFibGUgYm90aCBuZWVkIGZ1bGwgZG9jdW1lbnRhdGlvbiBp
bg0KPiA+IERvY3VtZW50YXRpb24vZGV2aWNldHJlZS9iaW5kaW5ncy8uLi4gLg0KPiA+DQo+ID4g
RnJhbmtseSBJIGhhdmUgYSBmZWVsaW5nIEknbSBnb2luZyB0byBuZWVkIHRoZXNlIERUIG5vZGVz
IGFuZCBJJ2QgbGlrZQ0KPiA+IHRvIG5vdCBoYXZlIHRvIGd1ZXNzLWFuZC1jaGVjayBiYXNlZCBv
biB0aGUgY29kZS4NCj4gDQo+IFBsZWFzZSB1c2UgaWVlZTgwMjExLWZyZXEtbGltaXQ6DQo+IGh0
dHBzOi8vZ2l0Lmtlcm5lbC5vcmcvY2dpdC9saW51eC9rZXJuZWwvZ2l0L2RhdmVtL25ldC1uZXh0
LmdpdC9jb21taXQvP2lkPWIzDQo+IDMwYjI1ZWFhYmRhMDBkNzRlNDc1NjZkOTIwMDkwN2RhMzgx
ODk2DQo+IA0KPiBNb3N0IGxpa2VseSB3aXRoIHdpcGh5X3JlYWRfb2ZfZnJlcV9saW1pdHMgaGVs
cGVyOg0KPiBodHRwczovL2dpdC5rZXJuZWwub3JnL2NnaXQvbGludXgva2VybmVsL2dpdC9kYXZl
bS9uZXQtbmV4dC5naXQvY29tbWl0Lz9pZD1lNg0KPiA5MWFjMmY3NWI2OWJlZTc0M2YwMzcwZDc5
NDU0YmE0NDI5YjE3NQ0KDQpJIGFscmVhZHkgcmVwbGllZCBtZWFuaW5nIG9mIHRoZXNlIHBhcmFt
ZXRlcnM6DQo8bWFydmVsbCwyZ2h6PiBpcyB1c2VkIHRvIGRpc2FibGUgMmcgYmFuZC4NCjxtYXJ2
ZWxsLDVnaHo+IGlzIHVzZWQgdG8gZGlzYWJsZSA1ZyBiYW5kLg0KPG1hcnZlbGwsIGNoYWlubWFz
az4gaXMgdXNlZCB0byBzcGVjaWZ5IGFudGVubmEgbnVtYmVyIChpZiBkZWZhdWx0IG51bWJlciBp
cyBzdWl0YWJsZSBmb3IgeW91LCB0aGVyZSBpcyBubyBuZWVkIHRvIHVzZSB0aGlzIHBhcmFtZXRl
cikuDQo8bWFydmVsbCxwb3dlcnRhYmxlPiBzaG91bGQgbm90IGJlIHVzZWQgZm9yIGNoaXAgd2l0
aCBkZXZpY2UgcG93ZXIgdGFibGUuIEluIGZhY3QsIHRoaXMgcGFyYW1ldGVyIHNob3VsZCBub3Qg
YmUgdXNlZCBhbnkgbW9yZS4NCg0KVGhhbmtzLA0KRGF2aWQNCg==
On 8 February 2017 at 08:28, David Lin <[email protected]> wrote:
>> From: Rafa=C5=82 Mi=C5=82ecki [mailto:[email protected]] worte:
>> Sent: Wednesday, February 08, 2017 3:24 PM
>> On 8 February 2017 at 07:30, David Lin <[email protected]> wrote:
>> > [email protected] [mailto:[email protected]] wrote:
>> >> On Tue, Feb 7, 2017 at 10:15 PM, David Lin <[email protected]> wrote:
>> >> >> Rafa=C5=82 Mi=C5=82ecki [mailto:[email protected]] wrote:
>> >> >> Please use ieee80211-freq-limit:
>> >> >> https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/co
>> >> >> mmi
>> >> >> t/?id=3Db3
>> >> >> 30b25eaabda00d74e47566d9200907da381896
>> >> >>
>> >> >> Most likely with wiphy_read_of_freq_limits helper:
>> >> >> https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/co
>> >> >> mmi
>> >> >> t/?id=3De6
>> >> >> 91ac2f75b69bee743f0370d79454ba4429b175
>> >> >
>> >> > I already replied meaning of these parameters:
>> >> > <marvell,2ghz> is used to disable 2g band.
>> >> > <marvell,5ghz> is used to disable 5g band.
>> >> > <marvell, chainmask> is used to specify antenna number (if default
>> >> > number
>> >> is suitable for you, there is no need to use this parameter).
>> >> > <marvell,powertable> should not be used for chip with device power
>> table.
>> >> In fact, this parameter should not be used any more.
>> >> >
>> >>
>> >> David, I think you're not understanding the comment, or at least
>> >> that's what it looks like to me. Yes, you did reply as to the meaning=
.
>> >> And, your reply, while informative, didn't tell us you understood and
>> >> were willing to fix the problem. I doubt you meant it this way, but
>> >> it feels defensive and like a brush-off.
>> >>
>> >> First off, you will still have to document any DT bindings you're
>> >> adding. Just because you answer the question in the review doesn't
>> >> mean you're not responsible for doing so.
>> >>
>> >> And second off, I think that Rafal (and sorry about my spelling,
>> >> looks like there's some sort of accent on the l that I don't know how
>> >> to make my keyboard do) is saying: there's already some generic
>> >> bindings that can be used to disable the 2g or 5g bands. Granted
>> >> they're even newer than your patch, but I do think if said bindings e=
xist and
>> are appropriate, you should use them.
>> >>
>> >> - Steve
>> >
>> > These parameters are marvell proprietary parameters, I don't think it =
should
>> be added to DT bindings.
>>
>> Steve is correct.
>>
>> You have to document new properties, just because they are Marvell speci=
fic
>> doesn't change anything. You just document them in a proper place.
>>
>
> All right. I will do that.
>
>>
>> > BTW, <marvell,2ghz> and <marvell,5ghz> are only used for mwlwifi to re=
port
>> supported bands, it is not related to limitation of frequency.
>>
>> How reporting a single band doesn't limit reported frequencies? You can
>> achieve exactly the same using generic property, so there is no place fo=
r
>> Marvell specific ones.
>>
>> In fact there were drivers of 3 vendors requiring band/freq-related desc=
ription
>> in DT: Broadcom, Marvell & Mediatek. This property was discussed & desig=
ned
>> to support all limitation cases we found possible to make it usable with
>> (hopefully) all drivers.
>>
>
> I only need simple way to disable 2g or 5g band. I will follow your sugge=
stion to document these marvell proprietary parameters.
Seriously? Refusing to use generic binding because you think
marvell,5ghz;
is simpler than
ieee80211-freq-limit =3D <2402000 2482000>;
(not to mention your property seems reversed!)?
I don't know how else to explain this to you. We don't want duplicated
properties where one can work. Just use existing one. Don't add new
one even if documented.
--=20
Rafa=C5=82
PiBGcm9tOiBzdGV2ZS5kZXJvc2llckBnbWFpbC5jb20gV3JvdGU6DQo+IEhpIERhdmlkLA0KPiAN
Cj4gRmlyc3Qgb2ZmLCBJIHdhbnRlZCB0byBzYXkgdGhhbmsteW91IGZvciB5b3VyIHdvcmsgYW5k
IGVmZm9ydCBpbiB0cnlpbmcgdG8gZ2V0DQo+IG13bHdpZmkgdXBzdHJlYW0uIE15IGNvbW1lbnRz
IGFyZSBpbi1saW5lIHdpdGggbXkgZ2VuZXJhbCBub3Rlcw0KPiBhZnRlcndhcmRzLg0KPiANCj4g
T24gVHVlLCBEZWMgMjAsIDIwMTYgYXQgODoxMSBQTSwgRGF2aWQgTGluIDxkbGluQG1hcnZlbGwu
Y29tPiB3cm90ZToNCj4gDQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvbmV0L3dpcmVsZXNzL21h
cnZlbGwvbXdsd2lmaS9kZWJ1Z2ZzLmgNCj4gPiBiL2RyaXZlcnMvbmV0L3dpcmVsZXNzL21hcnZl
bGwvbXdsd2lmaS9kZWJ1Z2ZzLmgNCj4gPiBuZXcgZmlsZSBtb2RlIDEwMDY0NA0KPiA+IGluZGV4
IDAwMDAwMDAuLmI0YzNlYjMNCj4gPiArLyogRGVzY3JpcHRpb246ICBUaGlzIGZpbGUgZGVmaW5l
cyBkZWJ1ZyBmcyByZWxhdGVkIGZ1bmN0aW9ucy4gKi8NCj4gPiArDQo+ID4gKyNpZm5kZWYgX01X
TF9ERUJVR0ZTX0hfDQo+ID4gKyNkZWZpbmUgX01XTF9ERUJVR0ZTX0hfDQo+ID4gKw0KPiA+ICt2
b2lkIG13bF9kZWJ1Z2ZzX2luaXQoc3RydWN0IGllZWU4MDIxMV9odyAqaHcpOyB2b2lkDQo+ID4g
K213bF9kZWJ1Z2ZzX3JlbW92ZShzdHJ1Y3QgaWVlZTgwMjExX2h3ICpodyk7DQo+ID4gKw0KPiAN
Cj4gWW91IHNob3VsZCBndWFyZCB0aGVzZSBzbyB0aGV5IGRlZmluZSB0byBlbXB0eSBmdW5jdGlv
bnMgaWYNCj4gQ09ORklHX0RFQlVHX0ZTIGlzbid0IGVuYWJsZWQgc28gdGhleSBjYW4gYmUgdXNl
ZCBieSBhIGNhbGxlciB3aXRob3V0DQo+IGV4cGxpY2l0IGd1YXJkcy4NCj4gDQoNCkkgd2lsbCBt
b2RpZnkgaXQuDQoNCj4NCj4gPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9uZXQvd2lyZWxlc3MvbWFy
dmVsbC9td2x3aWZpL2Rldi5oDQo+ID4gYi9kcml2ZXJzL25ldC93aXJlbGVzcy9tYXJ2ZWxsL213
bHdpZmkvZGV2LmgNCj4gPiBuZXcgZmlsZSBtb2RlIDEwMDY0NA0KPiA+IGluZGV4IDAwMDAwMDAu
LmM3YjEwYWMNCj4gPiAtLS0gL2Rldi9udWxsDQo+ID4gKysrIGIvZHJpdmVycy9uZXQvd2lyZWxl
c3MvbWFydmVsbC9td2x3aWZpL2Rldi5oDQo+IC4uLg0KPiA+ICtzdHJ1Y3QgbXdsX2FtcGR1X3N0
cmVhbSB7DQo+ID4gKyBzdHJ1Y3QgaWVlZTgwMjExX3N0YSAqc3RhOw0KPiA+ICsgdTggdGlkOw0K
PiA+ICsgdTggc3RhdGU7DQo+ID4gKyB1OCBpZHg7DQo+ID4gK307DQo+ID4gKw0KPiA+ICsjaWZk
ZWYgQ09ORklHX0RFQlVHX0ZTDQo+ID4gKyNkZWZpbmUgTUFDX1JFR19BRERSX1BDSShvZmZzZXQp
ICAgICAgKChwcml2LT5pb2Jhc2UxICsgMHhBMDAwKSArDQo+IChvZmZzZXQpKQ0KPiA+ICsNCj4g
PiArI2RlZmluZSBNV0xfQUNDRVNTX01BQyAgICAgICAgICAgICAgICAxDQo+ID4gKyNkZWZpbmUg
TVdMX0FDQ0VTU19SRiAgICAgICAgICAgICAgICAgMg0KPiA+ICsjZGVmaW5lIE1XTF9BQ0NFU1Nf
QkJQICAgICAgICAgICAgICAgIDMNCj4gPiArI2RlZmluZSBNV0xfQUNDRVNTX0NBVSAgICAgICAg
ICAgICAgICA0DQo+ID4gKyNkZWZpbmUgTVdMX0FDQ0VTU19BRERSMCAgICAgICAgICAgICAgNQ0K
PiA+ICsjZGVmaW5lIE1XTF9BQ0NFU1NfQUREUjEgICAgICAgICAgICAgIDYNCj4gPiArI2RlZmlu
ZSBNV0xfQUNDRVNTX0FERFIgICAgICAgICAgICAgICA3DQo+ID4gKyNlbmRpZg0KPiANCj4gT0ss
IEkgZ2V0IHRoYXQgeW91J3JlIG9ubHkgdXNpbmcgdGhlIGFib3ZlIGluIHRoZSBkZWJ1Z2ZzIGZ1
bmN0aW9ucywgYnV0IGFzIGZvcg0KPiBnZW5lcmljIHJlZ2lzdGVyIGFjY2VzcyBmdW5jdGlvbnMs
IHRoZXNlIHdvdWxkIGJlIHZhbGlkIG5vIG1hdHRlciBpZg0KPiBDT05GSUdfREVCVUdfRlMgaXMg
ZGVmaW5lZC4gSGF2aW5nIHRoZSBndWFyZCBoZXJlIGp1c3Qgc2VlbXMgdG8NCj4gY29tcGxpY2F0
ZSB0aGluZ3MuDQo+IA0KDQpJIHdpbGwgcmVtb3ZlIGl0Lg0KDQo+IA0KPiA+ICsNCj4gPiArc3Ry
dWN0IG13bF9wcml2IHsNCj4gPiArIHN0cnVjdCBpZWVlODAyMTFfaHcgKmh3Ow0KPiA+ICsgc3Ry
dWN0IGZpcm13YXJlICpmd191Y29kZTsNCj4gPiArIGJvb2wgZndfZGV2aWNlX3B3cnRibDsNCj4g
PiArIGJvb2wgZm9yYmlkZGVuX3NldHRpbmc7DQo+ID4gKw0KPiA+ICsgc3RydWN0IHRoZXJtYWxf
Y29vbGluZ19kZXZpY2UgKmNkZXY7DQo+ID4gKyB1MzIgdGhyb3R0bGVfc3RhdGU7DQo+ID4gKyB1
MzIgcXVpZXRfcGVyaW9kOw0KPiA+ICsgaW50IHRlbXBlcmF0dXJlOw0KPiA+ICsNCj4gPiArI2lm
ZGVmIENPTkZJR19ERUJVR19GUw0KPiA+ICsgc3RydWN0IGRlbnRyeSAqZGVidWdmc19waHk7DQo+
ID4gKyB1MzIgcmVnX3R5cGU7DQo+ID4gKyB1MzIgcmVnX29mZnNldDsNCj4gPiArIHUzMiByZWdf
dmFsdWU7DQo+ID4gKyBpbnQgdHhfZGVzY19udW07DQo+ID4gKyNlbmRpZg0KPiANCj4gV2UncmUg
c2F2aW5nIGEgZmV3IGJ5dGVzIGhlcmUgYXQgdGhlIGNvc3Qgb2Ygc29tZSBjb21wbGV4aXR5LiBJ
IGNvdWxkIGdvIGVpdGhlcg0KPiB3YXksIGJ1dCBJIGhhdGUgd2hlbiBwcml2IHN0cnVjdHVyZXMg
bXV0YXRlLg0KPiANCg0KSSB3aWxsIHJlbW92ZSB0aGUgY29tcGlsZSBjb250cm9sLg0KDQo+IA0K
PiA+ICsvKiBEZWZpbmVkIGluIG1hYzgwMjExLmMuICovDQo+ID4gK2V4dGVybiBjb25zdCBzdHJ1
Y3QgaWVlZTgwMjExX29wcyBtd2xfbWFjODAyMTFfb3BzOw0KPiANCj4gRG9lcyB0aGlzIG5lZWQg
dG8gYmUgaW4gZGV2Lmg/DQo+IA0KPiANCg0KSXQgY2FuIGJlIG1vdmVkIHRvIG1haW4uYy4NCg0K
PiANCj4gPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9uZXQvd2lyZWxlc3MvbWFydmVsbC9td2x3aWZp
L2Z3Y21kLmMNCj4gPiBiL2RyaXZlcnMvbmV0L3dpcmVsZXNzL21hcnZlbGwvbXdsd2lmaS9md2Nt
ZC5jDQo+ID4gbmV3IGZpbGUgbW9kZSAxMDA2NDQNCj4gPiBpbmRleCAwMDAwMDAwLi45YzNjY2Y5
DQo+ID4gLS0tIC9kZXYvbnVsbA0KPiA+ICsrKyBiL2RyaXZlcnMvbmV0L3dpcmVsZXNzL21hcnZl
bGwvbXdsd2lmaS9md2NtZC5jDQo+ID4gQEAgLTAsMCArMSwyODM3IEBADQo+ID4gKy8qDQo+ID4g
KyAqIENvcHlyaWdodCAoQykgMjAwNi0yMDE2LCBNYXJ2ZWxsIEludGVybmF0aW9uYWwgTHRkLg0K
PiA+ICsgKg0KPiA+ICsgKiBUaGlzIHNvZnR3YXJlIGZpbGUgKHRoZSAiRmlsZSIpIGlzIGRpc3Ry
aWJ1dGVkIGJ5IE1hcnZlbGwNCj4gPiArSW50ZXJuYXRpb25hbA0KPiA+ICsgKiBMdGQuIHVuZGVy
IHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgVmVyc2lvbiAyLA0K
PiA+ICtKdW5lIDE5OTENCj4gPiArICogKHRoZSAiTGljZW5zZSIpLiAgWW91IG1heSB1c2UsIHJl
ZGlzdHJpYnV0ZSBhbmQvb3IgbW9kaWZ5IHRoaXMNCj4gPiArDQo+ID4gKyNkZWZpbmUgTUFYX1dB
SVRfRldfQ09NUExFVEVfSVRFUkFUSU9OUyAgICAgICAgIDIwMDANCj4gPiArI2RlZmluZSBNQVhf
V0FJVF9HRVRfSFdfU1BFQ1NfSVRFUkFUT05TICAgICAgICAgMw0KPiA+ICsNCj4gPiArc3RydWN0
IGNtZF9oZWFkZXIgew0KPiA+ICsgX19sZTE2IGNvbW1hbmQ7DQo+ID4gKyBfX2xlMTYgbGVuOw0K
PiA+ICt9IF9fcGFja2VkOw0KPiA+ICsNCj4gPiArc3RhdGljIGJvb2wgbXdsX2Z3Y21kX2Noa19h
ZGFwdGVyKHN0cnVjdCBtd2xfcHJpdiAqcHJpdikgew0KPiA+ICsgdTMyIHJlZ3ZhbDsNCj4gPiAr
DQo+ID4gKyByZWd2YWwgPSByZWFkbChwcml2LT5pb2Jhc2UxICsgTUFDUkVHX1JFR19JTlRfQ09E
RSk7DQo+IA0KPiBDb3VsZG4ndCB0aGUgYWJvdmUgYmUgb25lIGxpbmU/DQo+DQoNClllcy4gSSB3
aWxsIG1vZGlmeSBpdC4NCiANCj4gDQo+ID4gKw0KPiA+ICtzdGF0aWMgaW50IG13bF9md2NtZF9n
ZXRfdHhfcG93ZXJzKHN0cnVjdCBtd2xfcHJpdiAqcHJpdiwgdTE2ICpwb3dsaXN0LA0KPiB1MTYg
Y2gsDQo+ID4gKyAgIHUxNiBiYW5kLCB1MTYgd2lkdGgsIHUxNiBzdWJfY2gpDQo+ID4gK3sNCj4g
PiArIHN0cnVjdCBob3N0Y21kX2NtZF84MDJfMTFfdHhfcG93ZXIgKnBjbWQ7ICBpbnQgaTsNCj4g
PiArDQo+ID4gKyBwY21kID0gKHN0cnVjdCBob3N0Y21kX2NtZF84MDJfMTFfdHhfcG93ZXIgKikm
cHJpdi0+cGNtZF9idWZbMF07DQo+IA0KPiBPZnRlbiBoYXBwZW5zLCBzbyBJIHByb2JhYmx5IHdv
bid0IGNhdGNoIHRoZW0gYWxsLCBidXQgdGhpcyBjb3VsZCBsaWtld2lzZSBiZQ0KPiBkb25lIGlu
IG9uZSBsaW5lLiBUaGVuIGFnYWluLi4uIGxpbmUgbGVuZ3RoIGlzc3Vlcywgc28gNTAvNTAgb24g
dGhhdC4NCj4gDQo+DQoNCkkgd2lsbCBtb2RpZnkgaXQuDQoNCj4gPiArDQo+ID4gK3N0YXRpYyB1
OCBtd2xfZndjbWRfZ2V0XzgwbV9wcmlfY2hubCh1OCBjaGFubmVsKSB7DQo+ID4gKyB1OCBhY3Rf
cHJpbWFyeSA9IEFDVF9QUklNQVJZX0NIQU5fMDsNCj4gPiArDQo+ID4gKyBzd2l0Y2ggKGNoYW5u
ZWwpIHsNCj4gPiArIGNhc2UgMzY6DQo+ID4gKyBhY3RfcHJpbWFyeSA9IEFDVF9QUklNQVJZX0NI
QU5fMDsNCj4gPiArIGJyZWFrOw0KPiA+ICsgY2FzZSA0MDoNCj4gPiArIGFjdF9wcmltYXJ5ID0g
QUNUX1BSSU1BUllfQ0hBTl8xOw0KPiA+ICsgYnJlYWs7DQo+ID4gKyBjYXNlIDQ0Og0KPiA+ICsg
YWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFOXzI7DQo+ID4gKyBicmVhazsNCj4gPiArIGNh
c2UgNDg6DQo+ID4gKyBhY3RfcHJpbWFyeSA9IEFDVF9QUklNQVJZX0NIQU5fMzsNCj4gPiArIGJy
ZWFrOw0KPiA+ICsgY2FzZSA1MjoNCj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hB
Tl8wOw0KPiA+ICsgYnJlYWs7DQo+ID4gKyBjYXNlIDU2Og0KPiA+ICsgYWN0X3ByaW1hcnkgPSBB
Q1RfUFJJTUFSWV9DSEFOXzE7DQo+ID4gKyBicmVhazsNCj4gPiArIGNhc2UgNjA6DQo+ID4gKyBh
Y3RfcHJpbWFyeSA9IEFDVF9QUklNQVJZX0NIQU5fMjsNCj4gPiArIGJyZWFrOw0KPiA+ICsgY2Fz
ZSA2NDoNCj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8zOw0KPiA+ICsgYnJl
YWs7DQo+ID4gKyBjYXNlIDEwMDoNCj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hB
Tl8wOw0KPiA+ICsgYnJlYWs7DQo+ID4gKyBjYXNlIDEwNDoNCj4gPiArIGFjdF9wcmltYXJ5ID0g
QUNUX1BSSU1BUllfQ0hBTl8xOw0KPiA+ICsgYnJlYWs7DQo+ID4gKyBjYXNlIDEwODoNCj4gPiAr
IGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8yOw0KPiA+ICsgYnJlYWs7DQo+ID4gKyBj
YXNlIDExMjoNCj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8zOw0KPiA+ICsg
YnJlYWs7DQo+ID4gKyBjYXNlIDExNjoNCj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllf
Q0hBTl8wOw0KPiA+ICsgYnJlYWs7DQo+ID4gKyBjYXNlIDEyMDoNCj4gPiArIGFjdF9wcmltYXJ5
ID0gQUNUX1BSSU1BUllfQ0hBTl8xOw0KPiA+ICsgYnJlYWs7DQo+ID4gKyBjYXNlIDEyNDoNCj4g
PiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8yOw0KPiA+ICsgYnJlYWs7DQo+ID4g
KyBjYXNlIDEyODoNCj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8zOw0KPiA+
ICsgYnJlYWs7DQo+ID4gKyBjYXNlIDEzMjoNCj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1B
UllfQ0hBTl8wOw0KPiA+ICsgYnJlYWs7DQo+ID4gKyBjYXNlIDEzNjoNCj4gPiArIGFjdF9wcmlt
YXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8xOw0KPiA+ICsgYnJlYWs7DQo+ID4gKyBjYXNlIDE0MDoN
Cj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8yOw0KPiA+ICsgYnJlYWs7DQo+
ID4gKyBjYXNlIDE0NDoNCj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8zOw0K
PiA+ICsgYnJlYWs7DQo+ID4gKyBjYXNlIDE0OToNCj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BS
SU1BUllfQ0hBTl8wOw0KPiA+ICsgYnJlYWs7DQo+ID4gKyBjYXNlIDE1MzoNCj4gPiArIGFjdF9w
cmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8xOw0KPiA+ICsgYnJlYWs7DQo+ID4gKyBjYXNlIDE1
NzoNCj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8yOw0KPiA+ICsgYnJlYWs7
DQo+ID4gKyBjYXNlIDE2MToNCj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8z
Ow0KPiA+ICsgYnJlYWs7DQo+ID4gKyB9DQo+ID4gKw0KPiA+ICsgcmV0dXJuIGFjdF9wcmltYXJ5
Ow0KPiA+ICt9DQo+ID4gKw0KPiANCj4gSWdub3JhbmNlIHNwZWFraW5nIGhlcmUgcGVyaGFwcywg
YnV0IHRoZSBhYm92ZSBsb29rcyBsaWtlIHNvbWV0aGluZyB0aGF0DQo+IG5lYXJseSBldmVyeSBk
cml2ZXIgd291bGQgbmVlZCB0byBkZWFsIHdpdGguIElzbid0IHRoZXJlIGEgaGVscGVyIGZ1bmN0
aW9uIGluDQo+IG1hYzgwMjExIG9yIHNvbWUgYmV0dGVyIHdheSB0byBkbyB0aGlzIHRoYW4gZXhw
bGljaXRseSBkb2luZyBhIHN3aXRjaCBsb2dpYw0KPiBsb29rdXA/DQo+IA0KDQpUaGlzIGlzIHRo
ZSBmdW5jdGlvbiBuZWVkZWQgZm9yIG91ciBob3N0IGNvbW1hbmQuIFRoZXJlIGlzIG5vIGhlbHBl
ciBmdW5jdGlvbiBpbiBtYWM4MDIxMS4NCg0KPiA+ICtpbnQgbXdsX2Z3Y21kX3NldF9uZXdfc3Ru
X2FkZChzdHJ1Y3QgaWVlZTgwMjExX2h3ICpodywNCj4gPiArICAgICAgc3RydWN0IGllZWU4MDIx
MV92aWYgKnZpZiwNCj4gPiArICAgICAgc3RydWN0IGllZWU4MDIxMV9zdGEgKnN0YSkNCj4gPiAr
ew0KPiA+ICsgc3RydWN0IG13bF9wcml2ICpwcml2ID0gaHctPnByaXY7DQo+ID4gKyBzdHJ1Y3Qg
bXdsX3ZpZiAqbXdsX3ZpZjsNCj4gPiArIHN0cnVjdCBob3N0Y21kX2NtZF9zZXRfbmV3X3N0biAq
cGNtZDsNCj4gPiArIHUzMiByYXRlczsNCj4gPiArDQo+ID4gKyBtd2xfdmlmID0gbXdsX2Rldl9n
ZXRfdmlmKHZpZik7DQo+ID4gKw0KPiA+ICsgcGNtZCA9IChzdHJ1Y3QgaG9zdGNtZF9jbWRfc2V0
X25ld19zdG4gKikmcHJpdi0+cGNtZF9idWZbMF07DQo+ID4gKw0KPiA+ICsgbXV0ZXhfbG9jaygm
cHJpdi0+ZndjbWRfbXV0ZXgpOw0KPiA+ICsNCj4gPiArIG1lbXNldChwY21kLCAweDAwLCBzaXpl
b2YoKnBjbWQpKTsNCj4gPiArIHBjbWQtPmNtZF9oZHIuY21kID0gY3B1X3RvX2xlMTYoSE9TVENN
RF9DTURfU0VUX05FV19TVE4pOw0KPiA+ICsgcGNtZC0+Y21kX2hkci5sZW4gPSBjcHVfdG9fbGUx
NihzaXplb2YoKnBjbWQpKTsgY21kX2hkci5tYWNpZCA9DQo+ID4gKyBwY21kLT5td2xfdmlmLT5t
YWNpZDsNCj4gPiArDQo+ID4gKyBwY21kLT5hY3Rpb24gPSBjcHVfdG9fbGUxNihIT1NUQ01EX0FD
VF9TVEFfQUNUSU9OX0FERCk7DQo+ID4gKyBpZiAodmlmLT50eXBlID09IE5MODAyMTFfSUZUWVBF
X1NUQVRJT04pIHsNCj4gPiArIHBjbWQtPmFpZCA9IDA7DQo+ID4gKyBwY21kLT5zdG5faWQgPSAw
Ow0KPiA+ICsgfSBlbHNlIHsNCj4gPiArIHBjbWQtPmFpZCA9IGNwdV90b19sZTE2KHN0YS0+YWlk
KTsNCj4gPiArIHBjbWQtPnN0bl9pZCA9IGNwdV90b19sZTE2KHN0YS0+YWlkKTsNCj4gPiArIH0N
Cj4gPiArIGV0aGVyX2FkZHJfY29weShwY21kLT5tYWNfYWRkciwgc3RhLT5hZGRyKTsNCj4gPiAr
DQo+ID4gKyBpZiAoaHctPmNvbmYuY2hhbmRlZi5jaGFuLT5iYW5kID09IE5MODAyMTFfQkFORF8y
R0haKSByYXRlcyA9DQo+ID4gKyBzdGEtPnN1cHBfcmF0ZXNbTkw4MDIxMV9CQU5EXzJHSFpdOw0K
PiA+ICsgZWxzZQ0KPiA+ICsgcmF0ZXMgPSBzdGEtPnN1cHBfcmF0ZXNbTkw4MDIxMV9CQU5EXzVH
SFpdIDw8IDU7DQo+ID4gKyBwY21kLT5wZWVyX2luZm8ubGVnYWN5X3JhdGVfYml0bWFwID0gY3B1
X3RvX2xlMzIocmF0ZXMpOw0KPiA+ICsNCj4gPiArIGlmIChzdGEtPmh0X2NhcC5odF9zdXBwb3J0
ZWQpIHsNCj4gPiArIHBjbWQtPnBlZXJfaW5mby5odF9yYXRlc1swXSA9IHN0YS0+aHRfY2FwLm1j
cy5yeF9tYXNrWzBdOw0KPiA+ICsgcGNtZC0+cGVlcl9pbmZvLmh0X3JhdGVzWzFdID0gc3RhLT5o
dF9jYXAubWNzLnJ4X21hc2tbMV07DQo+ID4gKyBwY21kLT5wZWVyX2luZm8uaHRfcmF0ZXNbMl0g
PSBzdGEtPmh0X2NhcC5tY3MucnhfbWFza1syXTsNCj4gPiArIHBjbWQtPnBlZXJfaW5mby5odF9y
YXRlc1szXSA9IHN0YS0+aHRfY2FwLm1jcy5yeF9tYXNrWzNdOw0KPiA+ICsgcGNtZC0+cGVlcl9p
bmZvLmh0X2NhcF9pbmZvID0gY3B1X3RvX2xlMTYoc3RhLT5odF9jYXAuY2FwKTsNCj4gPiArIHBj
bWQtPnBlZXJfaW5mby5tYWNfaHRfcGFyYW1faW5mbyA9DQo+ID4gKyAoc3RhLT5odF9jYXAuYW1w
ZHVfZmFjdG9yICYgMykgfA0KPiA+ICsgKChzdGEtPmh0X2NhcC5hbXBkdV9kZW5zaXR5ICYgNykg
PDwgMik7IH0NCj4gPiArDQo+ID4gKyBpZiAoc3RhLT52aHRfY2FwLnZodF9zdXBwb3J0ZWQpIHsN
Cj4gPiArIHBjbWQtPnBlZXJfaW5mby52aHRfbWF4X3J4X21jcyA9DQo+ID4gKyBjcHVfdG9fbGUz
MigqKCh1MzIgKikNCj4gPiArICZzdGEtPnZodF9jYXAudmh0X21jcy5yeF9tY3NfbWFwKSk7DQo+
ID4gKyBwY21kLT5wZWVyX2luZm8udmh0X2NhcCA9IGNwdV90b19sZTMyKHN0YS0+dmh0X2NhcC5j
YXApOw0KPiA+ICsgcGNtZC0+cGVlcl9pbmZvLnZodF9yeF9jaGFubmVsX3dpZHRoID0gc3RhLT5i
YW5kd2lkdGg7DQo+ID4gKyB9DQo+ID4gKw0KPiA+ICsgcGNtZC0+aXNfcW9zX3N0YSA9IHN0YS0+
d21lOw0KPiA+ICsgcGNtZC0+cW9zX2luZm8gPSAoKHN0YS0+dWFwc2RfcXVldWVzIDw8IDQpIHwg
KHN0YS0+bWF4X3NwIDw8IDEpKTsNCj4gPiArDQo+ID4gKyBpZiAobXdsX2Z3Y21kX2V4ZWNfY21k
KHByaXYsIEhPU1RDTURfQ01EX1NFVF9ORVdfU1ROKSkgew0KPiA+ICsgbXV0ZXhfdW5sb2NrKCZw
cml2LT5md2NtZF9tdXRleCk7IHdpcGh5X2Vycihody0+d2lwaHksICJmYWlsZWQNCj4gPiArIGV4
ZWN1dGlvblxuIik7IHJldHVybiAtRUlPOyB9DQo+ID4gKw0KPiA+ICsgaWYgKHZpZi0+dHlwZSA9
PSBOTDgwMjExX0lGVFlQRV9TVEFUSU9OKSB7DQo+ID4gKyBldGhlcl9hZGRyX2NvcHkocGNtZC0+
bWFjX2FkZHIsIG13bF92aWYtPnN0YV9tYWMpOw0KPiA+ICsNCj4gPiArIGlmIChtd2xfZndjbWRf
ZXhlY19jbWQocHJpdiwgSE9TVENNRF9DTURfU0VUX05FV19TVE4pKSB7DQo+ID4gKyBtdXRleF91
bmxvY2soJnByaXYtPmZ3Y21kX211dGV4KTsgd2lwaHlfZXJyKGh3LT53aXBoeSwgImZhaWxlZA0K
PiA+ICsgZXhlY3V0aW9uXG4iKTsgcmV0dXJuIC1FSU87IH0gfQ0KPiANCj4gT0ssIHRpbWUgdG8g
YXNrLiBXaHkgYXJlIHdlIHRlbGxpbmcgdGhlIGZpcm13YXJlIHRoYXQgd2UncmUgY29ubmVjdGVk
IHRvDQo+IG91cnNlbHZlcz8gSSB3b3VsZCBwcmVzdW1lIHRoZSBmaXJtd2FyZSBhbHJlYWR5IGtu
b3dzIG91ciBNQUMgYWRkcmVzcy4NCj4gDQo+IEkgbm90aWNlZCB0aGlzIG9yaWdpbmFsbHkgYmVj
YXVzZSB0aGVyZSdzIGEgbmFzdHkgYnVnIHdpdGggcmVjeWNsaW5nIHRoZQ0KPiBjb21tYW5kIGJ1
ZmZlciAoZm9yIHNkaW8sIGl0J3Mgbm90IHJlbGV2YW50IGZvciB0aGlzIGRyaXZlcikgYW5kIGlu
IGRvaW5nDQo+IGV4cGVyaW1lbnRzIEkgbm90aWNlZCB0aGF0IHRocm91Z2hwdXQgc2lnbmlmaWNh
bnRseSBpbmNyZWFzZXMgaW4gU1RBIG1vZGUgaWYNCj4gd2UganVzdCBsZWF2ZSBvdXQgdGhlIGVu
dGlyZSBjbGF1c2UuDQo+IA0KPiBJbiBicmllZmx5IGV4YW1pbmluZyB0aGUgZmlybXdhcmUgc291
cmNlIEkgc2VlIG5vIHJlYXNvbiB0byBkbyB0aGlzLCBidXQgdGhlcmUncw0KPiBhIGhpZGRlbiBj
aHVuayBhbmQgSSBkb24ndCBrbm93IHdoYXQgdGhlIGhhcmR3YXJlIGl0c2VsZiBkb2VzIHdpdGgg
dGhlIE1BQw0KPiB0YWJsZS4NCj4gDQo+IFNvLCB3aHkgaXMgaXQgbmVjZXNzYXJ5Pw0KPiANCg0K
SSB3aWxsIGNoZWNrLg0KDQo+IA0KPiA+ICtpbnQgbXdsX2Z3Y21kX3NldF9uZXdfc3RuX2RlbChz
dHJ1Y3QgaWVlZTgwMjExX2h3ICpodywNCj4gPiArICAgICAgc3RydWN0IGllZWU4MDIxMV92aWYg
KnZpZiwgdTggKmFkZHIpIHsgIHN0cnVjdCBtd2xfcHJpdiAqcHJpdiA9DQo+ID4gK2h3LT5wcml2
OyAgc3RydWN0IG13bF92aWYgKm13bF92aWY7ICBzdHJ1Y3QgaG9zdGNtZF9jbWRfc2V0X25ld19z
dG4NCj4gPiArKnBjbWQ7DQo+ID4gKw0KPiA+ICsgbXdsX3ZpZiA9IG13bF9kZXZfZ2V0X3ZpZih2
aWYpOw0KPiA+ICsNCj4gPiArIHBjbWQgPSAoc3RydWN0IGhvc3RjbWRfY21kX3NldF9uZXdfc3Ru
ICopJnByaXYtPnBjbWRfYnVmWzBdOw0KPiA+ICsNCj4gPiArIG11dGV4X2xvY2soJnByaXYtPmZ3
Y21kX211dGV4KTsNCj4gPiArDQo+ID4gKyBtZW1zZXQocGNtZCwgMHgwMCwgc2l6ZW9mKCpwY21k
KSk7DQo+ID4gKyBwY21kLT5jbWRfaGRyLmNtZCA9IGNwdV90b19sZTE2KEhPU1RDTURfQ01EX1NF
VF9ORVdfU1ROKTsNCj4gPiArIHBjbWQtPmNtZF9oZHIubGVuID0gY3B1X3RvX2xlMTYoc2l6ZW9m
KCpwY21kKSk7IGNtZF9oZHIubWFjaWQgPQ0KPiA+ICsgcGNtZC0+bXdsX3ZpZi0+bWFjaWQ7DQo+
ID4gKw0KPiA+ICsgcGNtZC0+YWN0aW9uID0gY3B1X3RvX2xlMTYoSE9TVENNRF9BQ1RfU1RBX0FD
VElPTl9SRU1PVkUpOw0KPiA+ICsgZXRoZXJfYWRkcl9jb3B5KHBjbWQtPm1hY19hZGRyLCBhZGRy
KTsNCj4gPiArDQo+ID4gKyBpZiAobXdsX2Z3Y21kX2V4ZWNfY21kKHByaXYsIEhPU1RDTURfQ01E
X1NFVF9ORVdfU1ROKSkgew0KPiA+ICsgbXV0ZXhfdW5sb2NrKCZwcml2LT5md2NtZF9tdXRleCk7
IHdpcGh5X2Vycihody0+d2lwaHksICJmYWlsZWQNCj4gPiArIGV4ZWN1dGlvblxuIik7IHJldHVy
biAtRUlPOyB9DQo+ID4gKw0KPiA+ICsgaWYgKHZpZi0+dHlwZSA9PSBOTDgwMjExX0lGVFlQRV9T
VEFUSU9OKSB7DQo+ID4gKyBldGhlcl9hZGRyX2NvcHkocGNtZC0+bWFjX2FkZHIsIG13bF92aWYt
PnN0YV9tYWMpOw0KPiA+ICsNCj4gPiArIGlmIChtd2xfZndjbWRfZXhlY19jbWQocHJpdiwgSE9T
VENNRF9DTURfU0VUX05FV19TVE4pKSB7DQo+ID4gKyBtdXRleF91bmxvY2soJnByaXYtPmZ3Y21k
X211dGV4KTsgd2lwaHlfZXJyKGh3LT53aXBoeSwgImZhaWxlZA0KPiA+ICsgZXhlY3V0aW9uXG4i
KTsgcmV0dXJuIC1FSU87IH0gfQ0KPiA+ICsNCj4gDQo+IERpdHRvLg0KPiANCj4gPiBkaWZmIC0t
Z2l0IGEvZHJpdmVycy9uZXQvd2lyZWxlc3MvbWFydmVsbC9td2x3aWZpL2Z3ZGwuYw0KPiA+IGIv
ZHJpdmVycy9uZXQvd2lyZWxlc3MvbWFydmVsbC9td2x3aWZpL2Z3ZGwuYw0KPiA+IG5ldyBmaWxl
IG1vZGUgMTAwNjQ0DQo+ID4gaW5kZXggMDAwMDAwMC4uZjRkNWZhMQ0KPiA+IC0tLSAvZGV2L251
bGwNCj4gPiArKysgYi9kcml2ZXJzL25ldC93aXJlbGVzcy9tYXJ2ZWxsL213bHdpZmkvZndkbC5j
DQo+IC4uLg0KPiA+ICsNCj4gPiArc3RhdGljIHZvaWQgbXdsX2Z3ZGxfdHJpZ19wY2ljbWQoc3Ry
dWN0IG13bF9wcml2ICpwcml2KSB7DQo+ID4gK3dyaXRlbChwcml2LT5wcGh5c19jbWRfYnVmLCBw
cml2LT5pb2Jhc2UxICsgTUFDUkVHX1JFR19HRU5fUFRSKTsNCj4gPiArDQo+ID4gKyB3cml0ZWwo
MHgwMCwgcHJpdi0+aW9iYXNlMSArIE1BQ1JFR19SRUdfSU5UX0NPREUpOw0KPiA+ICsNCj4gPiAr
IHdyaXRlbChNQUNSRUdfSDJBUklDX0JJVF9ET09SX0JFTEwsDQo+ID4gKyAgICAgICBwcml2LT5p
b2Jhc2UxICsgTUFDUkVHX1JFR19IMkFfSU5URVJSVVBUX0VWRU5UUyk7IH0NCj4gPiArDQo+ID4g
K3N0YXRpYyB2b2lkIG13bF9md2RsX3RyaWdfcGNpY21kX2Jvb3Rjb2RlKHN0cnVjdCBtd2xfcHJp
diAqcHJpdikgew0KPiA+ICt3cml0ZWwocHJpdi0+cHBoeXNfY21kX2J1ZiwgcHJpdi0+aW9iYXNl
MSArIE1BQ1JFR19SRUdfR0VOX1BUUik7DQo+ID4gKw0KPiA+ICsgd3JpdGVsKDB4MDAsIHByaXYt
PmlvYmFzZTEgKyBNQUNSRUdfUkVHX0lOVF9DT0RFKTsNCj4gPiArDQo+ID4gKyB3cml0ZWwoTUFD
UkVHX0gyQVJJQ19CSVRfRE9PUl9CRUxMLA0KPiA+ICsgICAgICAgcHJpdi0+aW9iYXNlMSArIE1B
Q1JFR19SRUdfSDJBX0lOVEVSUlVQVF9FVkVOVFMpOyB9DQo+ID4gKw0KPiANCj4gVW5sZXNzIEkn
bSBtaXN0YWtlbiB0aGUgYWJvdmUgdHdvIGZ1bmN0aW9ucyBhcmUgMTAwJSBpZGVudGljYWwuIElu
IG15IHZlcnNpb24NCj4gSSBjb2xsYXBzZWQgdGhlbSB0byBhIHNpbmdsZSBvbmUgYW5kIGl0IHdv
cmtzIGZpbmUuDQo+IA0KDQpJIHdpbGwgY2hlY2sgYW5kIG1vZGlmeSBpdC4NCg0KPiA+ICtpbnQg
bXdsX2Z3ZGxfZG93bmxvYWRfZmlybXdhcmUoc3RydWN0IGllZWU4MDIxMV9odyAqaHcpIHsNCj4g
Li4uDQo+ID4gKw0KPiA+ICsgd2hpbGUgKHNpemVfZndfZG93bmxvYWRlZCA8IGZ3LT5zaXplKSB7
IGxlbiA9IHJlYWRsKHByaXYtPmlvYmFzZTEgKw0KPiA+ICsgMHhjNDApOw0KPiA+ICsNCj4gPiAr
IGlmICghbGVuKQ0KPiA+ICsgYnJlYWs7DQo+ID4gKw0KPiA+ICsgLyogdGhpcyBjb3BpZXMgdGhl
IG5leHQgY2h1bmsgb2YgZncgYmluYXJ5IHRvIGJlIGRlbGl2ZXJlZCAqLw0KPiA+ICsgbWVtY3B5
KChjaGFyICopJnByaXYtPnBjbWRfYnVmWzBdLA0KPiA+ICsgICAgICAgKGZ3LT5kYXRhICsgc2l6
ZV9md19kb3dubG9hZGVkKSwgbGVuKTsNCj4gPiArDQo+ID4gKyAvKiB0aGlzIGZ1bmN0aW9uIHdy
aXRlcyBwZGF0YSB0byBjMTAsIHRoZW4gd3JpdGUgMiB0byBjMTggKi8NCj4gPiArIG13bF9md2Rs
X3RyaWdfcGNpY21kX2Jvb3Rjb2RlKHByaXYpOw0KPiA+ICsNCj4gPiArIC8qIHRoaXMgaXMgYXJi
aXRyYXJ5IHBlciB5b3VyIHBsYXRmb3JtOyB3ZSB1c2UgMHhmZmZmICovDQo+ID4gKyBjdXJyX2l0
ZXJhdGlvbiA9IEZXX01BWF9OVU1fQ0hFQ0tTOw0KPiA+ICsNCj4gPiArIC8qIE5PVEU6IHRoZSBm
b2xsb3dpbmcgYmFjayB0byBiYWNrIGNoZWNrcyBvbiBDMUMgaXMgdGltZQ0KPiA+ICsgKiBzZW5z
aXRpdmUsIGhlbmNlIG1heSBuZWVkIHRvIGJlIHR3ZWFrZWQgZGVwZW5kZW50IG9uIGhvc3QNCj4g
PiArICogcHJvY2Vzc29yLiBUaW1lIGZvciBTQzIgdG8gZ28gZnJvbSB0aGUgd3JpdGUgb2YgZXZl
bnQgMiB0bw0KPiA+ICsgKiBDMUMgPT0gMiBpcyB+MTMwMCBuU2VjLiBIZW5jZSB0aGUgY2hlY2tp
bmdzIG9uIGhvc3QgaGFzIHRvDQo+ID4gKyAqIGNvbnNpZGVyIGhvdyBlZmZpY2llbnQgeW91ciBj
b2RlIGNhbiBiZSB0byBtZWV0IHRoaXMgdGltaW5nLA0KPiA+ICsgKiBvciB5b3UgY2FuIGFsdGVy
bmF0aXZlbHkgdHdlYWsgdGhpcyByb3V0aW5lcyB0byBmaXQgeW91cg0KPiA+ICsgKiBwbGF0Zm9y
bQ0KPiA+ICsgKi8NCj4gPiArIGRvIHsNCj4gPiArIGludF9jb2RlID0gcmVhZGwocHJpdi0+aW9i
YXNlMSArIDB4YzFjKTsgaWYgKGludF9jb2RlICE9IDApIGJyZWFrOw0KPiA+ICsgY29uZF9yZXNj
aGVkKCk7IGN1cnJfaXRlcmF0aW9uLS07IH0gd2hpbGUgKGN1cnJfaXRlcmF0aW9uKTsNCj4gPiAr
DQo+IA0KPiBUaGVyZSdzIHNvbWV0aGluZyBmaXNoeSB3aXRoIHRoZSBhYm92ZS4gSGF2aW5nIHRv
ICJ0d2VhayIgZHJpdmVyIHRpbWluZyBiYXNlZA0KPiBvbiBwbGF0Zm9ybSBpcyBhIGh1Z2UgcmVk
IGZsYWcuIFRoZXJlJ3MgZ290IHRvIGJlIHNvbWV0aGluZyBiZXR0ZXIgdGhhbiB0aGUNCj4gYWJv
dmUuDQo+IA0KDQpJdCB3aWxsIHJlbGVhc2UgQ1BVLg0KDQo+IA0KPiA+IGRpZmYgLS1naXQgYS9k
cml2ZXJzL25ldC93aXJlbGVzcy9tYXJ2ZWxsL213bHdpZmkvbWFjODAyMTEuYw0KPiA+IGIvZHJp
dmVycy9uZXQvd2lyZWxlc3MvbWFydmVsbC9td2x3aWZpL21hYzgwMjExLmMNCj4gLi4uDQo+ID4g
K3N0YXRpYyBpbnQgbXdsX21hYzgwMjExX2NvbmZpZyhzdHJ1Y3QgaWVlZTgwMjExX2h3ICpodywN
Cj4gPiArICAgICAgIHUzMiBjaGFuZ2VkKQ0KPiA+ICt7DQo+ID4gKyBzdHJ1Y3QgaWVlZTgwMjEx
X2NvbmYgKmNvbmYgPSAmaHctPmNvbmY7ICBpbnQgcmM7DQo+ID4gKw0KPiA+ICsgd2lwaHlfZGVi
dWcoaHctPndpcGh5LCAiY2hhbmdlOiAweCV4XG4iLCBjaGFuZ2VkKTsNCj4gPiArDQo+ID4gKyBp
ZiAoY29uZi0+ZmxhZ3MgJiBJRUVFODAyMTFfQ09ORl9JRExFKSByYyA9DQo+ID4gKyBtd2xfZndj
bWRfcmFkaW9fZGlzYWJsZShodyk7IGVsc2UgcmMgPSBtd2xfZndjbWRfcmFkaW9fZW5hYmxlKGh3
KTsNCj4gPiArDQo+ID4gKyBpZiAocmMpDQo+ID4gKyBnb3RvIG91dDsNCj4gPiArDQo+ID4gKyBp
ZiAoY2hhbmdlZCAmIElFRUU4MDIxMV9DT05GX0NIQU5HRV9DSEFOTkVMKSB7IGludCByYXRlID0g
MDsNCj4gPiArDQo+ID4gKyBpZiAoY29uZi0+Y2hhbmRlZi5jaGFuLT5iYW5kID09IE5MODAyMTFf
QkFORF8yR0haKSB7DQo+IA0KPiBDYXVzZXMgY29tcGlsZSB3YXJuaW5nLiBTaG91bGQgYmUgSUVF
RTgwMjExX0JBTkRfMkdIWi4NCj4gDQoNCk5vLCB5b3UgbmVlZCB0byB1c2UgdXBkYXRlZCBtYWM4
MDIxMS4NCg0KPiANCj4gPiArIG13bF9md2NtZF9zZXRfYXBtb2RlKGh3LCBBUF9NT0RFXzJfNEdI
Wl8xMUFDX01JWEVEKTsNCj4gPiArIG13bF9md2NtZF9zZXRfbGlua2FkYXB0X2NzX21vZGUoaHcs
DQo+ID4gKyBMSU5LX0NTX1NUQVRFX0NPTlNFUlYpOw0KPiA+ICsgcmF0ZSA9IG13bF9yYXRlc18y
NFswXS5od192YWx1ZTsNCj4gPiArIH0gZWxzZSBpZiAoY29uZi0+Y2hhbmRlZi5jaGFuLT5iYW5k
ID09IE5MODAyMTFfQkFORF81R0haKSB7DQo+IA0KPiBDYXVzZXMgY29tcGlsZSB3YXJuaW5nLiBT
aG91bGQgYmUgSUVFRTgwMjExX0JBTkRfNUdIWi4NCj4gDQo+ID4gK3N0YXRpYyB2b2lkIG13bF9t
YWM4MDIxMV9ic3NfaW5mb19jaGFuZ2VkX2FwKHN0cnVjdCBpZWVlODAyMTFfaHcNCj4gKmh3LA0K
PiA+ICsgICAgIHN0cnVjdCBpZWVlODAyMTFfdmlmICp2aWYsDQo+ID4gKyAgICAgc3RydWN0IGll
ZWU4MDIxMV9ic3NfY29uZiAqaW5mbywNCj4gPiArICAgICB1MzIgY2hhbmdlZCkNCj4gPiArew0K
PiA+ICsgaWYgKGNoYW5nZWQgJiBCU1NfQ0hBTkdFRF9FUlBfUFJFQU1CTEUpDQo+ID4gK213bF9m
d2NtZF9zZXRfcmFkaW9fcHJlYW1ibGUoaHcsDQo+ID4gKyAgICAgdmlmLT5ic3NfY29uZi51c2Vf
c2hvcnRfcHJlYW1ibGUpOw0KPiA+ICsNCj4gPiArIGlmIChjaGFuZ2VkICYgQlNTX0NIQU5HRURf
QkFTSUNfUkFURVMpIHsgaW50IGlkeDsgaW50IHJhdGU7DQo+ID4gKw0KPiA+ICsgLyogVXNlIGxv
d2VzdCBzdXBwb3J0ZWQgYmFzaWMgcmF0ZSBmb3IgbXVsdGljYXN0cw0KPiA+ICsgKiBhbmQgbWFu
YWdlbWVudCBmcmFtZXMgKHN1Y2ggYXMgcHJvYmUgcmVzcG9uc2VzIC0tDQo+ID4gKyAqIGJlYWNv
bnMgd2lsbCBhbHdheXMgZ28gb3V0IGF0IDEgTWIvcykuDQo+ID4gKyAqLw0KPiA+ICsgaWR4ID0g
ZmZzKHZpZi0+YnNzX2NvbmYuYmFzaWNfcmF0ZXMpOyBpZiAoaWR4KSBpZHgtLTsNCj4gPiArDQo+
ID4gKyBpZiAoaHctPmNvbmYuY2hhbmRlZi5jaGFuLT5iYW5kID09IE5MODAyMTFfQkFORF8yR0ha
KQ0KPiANCj4gQ2F1c2VzIGNvbXBpbGUgd2FybmluZy4gU2hvdWxkIGJlIElFRUU4MDIxMV9CQU5E
XzJHSFouDQo+IA0KDQpObywgeW91IG5lZWQgdG8gdXNlIHVwZGF0ZWQgbWFjODAyMTEuDQoNCj4g
DQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvbmV0L3dpcmVsZXNzL21hcnZlbGwvbXdsd2lmaS9t
YWluLmMNCj4gPiBiL2RyaXZlcnMvbmV0L3dpcmVsZXNzL21hcnZlbGwvbXdsd2lmaS9tYWluLmMN
Cj4gPiBuZXcgZmlsZSBtb2RlIDEwMDY0NA0KPiAuLi4NCj4gPiArI2luY2x1ZGUgPGxpbnV4L21v
ZHVsZS5oPg0KPiA+ICsjaWZkZWYgQ09ORklHX09GDQo+ID4gKyNpbmNsdWRlIDxsaW51eC9vZi5o
Pg0KPiA+ICsjZW5kaWYNCj4gDQo+IElzbid0IG9mLmggaW50ZXJuYWxseSBndWFyZGVkPw0KPiAN
Cg0KWWVzLiBJdCBpcyBtZW50aW9uZWQgYnkgSm9oYW5uZXMsIEkgd2lsbCByZW1vdmUgaXQuDQoN
Cj4gPiArDQo+ID4gKyNpbmNsdWRlICJzeXNhZHB0LmgiDQo+ID4gKyNpbmNsdWRlICJkZXYuaCIN
Cj4gPiArI2luY2x1ZGUgImZ3ZGwuaCINCj4gPiArI2luY2x1ZGUgImZ3Y21kLmgiDQo+ID4gKyNp
bmNsdWRlICJ0eC5oIg0KPiA+ICsjaW5jbHVkZSAicnguaCINCj4gPiArI2luY2x1ZGUgImlzci5o
Ig0KPiA+ICsjaW5jbHVkZSAidGhlcm1hbC5oIg0KPiA+ICsjaWZkZWYgQ09ORklHX0RFQlVHX0ZT
DQo+ID4gKyNpbmNsdWRlICJkZWJ1Z2ZzLmgiDQo+ID4gKyNlbmRpZg0KPiANCj4gWW91ciBkZWJ1
Z2ZzLmggc2hvdWxkIGJlIGludGVybmFsbHkgZ3VhcmRlZC4gTW9yZSBsYXRlci4uLg0KPiANCg0K
U2FtZSBhcyBhYm92ZS4NCg0KPiA+ICsNCj4gPiArI2RlZmluZSBNV0xfREVTQyAgICAgICAgICJN
YXJ2ZWxsIDgwMi4xMWFjIFdpcmVsZXNzIE5ldHdvcmsgRHJpdmVyIg0KPiA+ICsjZGVmaW5lIE1X
TF9ERVZfTkFNRSAgICAgIk1hcnZlbGwgODAyLjExYWMgQWRhcHRlciINCj4gPiArDQo+ID4gKyNk
ZWZpbmUgRklMRV9QQVRIX0xFTiAgICA2NA0KPiA+ICsjZGVmaW5lIENNRF9CVUZfU0laRSAgICAg
MHg0MDAwDQo+ID4gKw0KPiA+ICtzdGF0aWMgc3RydWN0IHBjaV9kZXZpY2VfaWQgbXdsX3BjaV9p
ZF90YmxbXSA9IHsgIHsNCj4gPiArUENJX1ZERVZJQ0UoTUFSVkVMTCwgMHgyYTU1KSwgLmRyaXZl
cl9kYXRhID0gTVdMODg2NCwgfSwgIHsNCj4gPiArUENJX1ZERVZJQ0UoTUFSVkVMTCwgMHgyYjM4
KSwgLmRyaXZlcl9kYXRhID0gTVdMODg5NywgfSwgIHsgfSwgfTsNCj4gPiArDQo+ID4gK3N0YXRp
YyBzdHJ1Y3QgbXdsX2NoaXBfaW5mbyBtd2xfY2hpcF90YmxbXSA9IHsgIFtNV0w4ODY0XSA9IHsN
Cj4gPiArLnBhcnRfbmFtZSA9ICI4OFc4ODY0IiwgIC5md19pbWFnZSA9ICJtd2x3aWZpLzg4Vzg4
NjQuYmluIiwNCj4gPiArLmFudGVubmFfdHggPSBBTlRFTk5BX1RYXzRfQVVUTywgIC5hbnRlbm5h
X3J4ID0NCj4gQU5URU5OQV9SWF80X0FVVE8sDQo+ID4gK30sICBbTVdMODg5N10gPSB7ICAucGFy
dF9uYW1lID0gIjg4Vzg4OTciLCAgLmZ3X2ltYWdlID0NCj4gPiArIm13bHdpZmkvODhXODg5Ny5i
aW4iLCAgLmFudGVubmFfdHggPSBBTlRFTk5BX1RYXzIsICAuYW50ZW5uYV9yeCA9DQo+ID4gK0FO
VEVOTkFfUlhfMiwgIH0sIH07DQo+ID4gKw0KPiA+ICtzdGF0aWMgY29uc3Qgc3RydWN0IGllZWU4
MDIxMV9jaGFubmVsIG13bF9jaGFubmVsc18yNFtdID0geyAgeyAuYmFuZA0KPiA+ICs9IE5MODAy
MTFfQkFORF8yR0haLCAuY2VudGVyX2ZyZXEgPSAyNDEyLCAuaHdfdmFsdWUgPSAxLCB9LCAgeyAu
YmFuZA0KPiA+ICs9IE5MODAyMTFfQkFORF8yR0haLCAuY2VudGVyX2ZyZXEgPSAyNDE3LCAuaHdf
dmFsdWUgPSAyLCB9LCAgeyAuYmFuZA0KPiA+ICs9IE5MODAyMTFfQkFORF8yR0haLCAuY2VudGVy
X2ZyZXEgPSAyNDIyLCAuaHdfdmFsdWUgPSAzLCB9LCAgeyAuYmFuZA0KPiA+ICs9IE5MODAyMTFf
QkFORF8yR0haLCAuY2VudGVyX2ZyZXEgPSAyNDI3LCAuaHdfdmFsdWUgPSA0LCB9LCAgeyAuYmFu
ZA0KPiA+ICs9IE5MODAyMTFfQkFORF8yR0haLCAuY2VudGVyX2ZyZXEgPSAyNDMyLCAuaHdfdmFs
dWUgPSA1LCB9LCAgeyAuYmFuZA0KPiA+ICs9IE5MODAyMTFfQkFORF8yR0haLCAuY2VudGVyX2Zy
ZXEgPSAyNDM3LCAuaHdfdmFsdWUgPSA2LCB9LCAgeyAuYmFuZA0KPiA+ICs9IE5MODAyMTFfQkFO
RF8yR0haLCAuY2VudGVyX2ZyZXEgPSAyNDQyLCAuaHdfdmFsdWUgPSA3LCB9LCAgeyAuYmFuZA0K
PiA+ICs9IE5MODAyMTFfQkFORF8yR0haLCAuY2VudGVyX2ZyZXEgPSAyNDQ3LCAuaHdfdmFsdWUg
PSA4LCB9LCAgeyAuYmFuZA0KPiA+ICs9IE5MODAyMTFfQkFORF8yR0haLCAuY2VudGVyX2ZyZXEg
PSAyNDUyLCAuaHdfdmFsdWUgPSA5LCB9LCAgeyAuYmFuZA0KPiA+ICs9IE5MODAyMTFfQkFORF8y
R0haLCAuY2VudGVyX2ZyZXEgPSAyNDU3LCAuaHdfdmFsdWUgPSAxMCwgfSwgIHsgLmJhbmQNCj4g
PiArPSBOTDgwMjExX0JBTkRfMkdIWiwgLmNlbnRlcl9mcmVxID0gMjQ2MiwgLmh3X3ZhbHVlID0g
MTEsIH0sICB7IC5iYW5kDQo+ID4gKz0gTkw4MDIxMV9CQU5EXzJHSFosIC5jZW50ZXJfZnJlcSA9
IDI0NjcsIC5od192YWx1ZSA9IDEyLCB9LCAgeyAuYmFuZA0KPiA+ICs9IE5MODAyMTFfQkFORF8y
R0haLCAuY2VudGVyX2ZyZXEgPSAyNDcyLCAuaHdfdmFsdWUgPSAxMywgfSwgIHsgLmJhbmQNCj4g
PiArPSBOTDgwMjExX0JBTkRfMkdIWiwgLmNlbnRlcl9mcmVxID0gMjQ4NCwgLmh3X3ZhbHVlID0g
MTQsIH0sIH07DQo+ID4gKw0KPiANCj4gU28sIGludGVyZXN0aW5nIHRoaW5nLi4uIHRoZXJlJ3Mg
NjIgdXNlcyBvZiBOTDgwMjExX0JBTkRfeCBpbiB0aGlzIGRyaXZlciwgYnV0DQo+IG9ubHkgdGhl
IGZldyBzcG90cyBJIG1lbnRpb25lZCBjYXVzZSB3YXJuaW5ncy4gSSBub3RpY2UgaW4gdGhlIG1v
c3QgcmVjZW50DQo+IGludGVybmFsIGRyb3AgeW91J3ZlIGNoYW5nZWQgdGhlIGFib3ZlIHRvIElF
RUU4MDIxMV9CQU5EXzJHSFouIEkgd29uZGVyDQo+IGlmIHRoYXQgaXMgd2hhdCBzaG91bGQgYmUg
ZG9uZSBldmVyeXdoZXJlPw0KPiANCg0KUGxlYXNlIHVzZSB1cGRhdGVkIG1hYzgwMjExLg0KDQo+
ID4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgaWVlZTgwMjExX3JhdGUgbXdsX3JhdGVzXzI0W10gPSB7
ICB7IC5iaXRyYXRlID0NCj4gPiArMTAsIC5od192YWx1ZSA9IDIsIH0sICB7IC5iaXRyYXRlID0g
MjAsIC5od192YWx1ZSA9IDQsIH0sICB7IC5iaXRyYXRlDQo+ID4gKz0gNTUsIC5od192YWx1ZSA9
IDExLCB9LCAgeyAuYml0cmF0ZSA9IDExMCwgLmh3X3ZhbHVlID0gMjIsIH0sICB7DQo+ID4gKy5i
aXRyYXRlID0gMjIwLCAuaHdfdmFsdWUgPSA0NCwgfSwgIHsgLmJpdHJhdGUgPSA2MCwgLmh3X3Zh
bHVlID0gMTIsDQo+ID4gK30sICB7IC5iaXRyYXRlID0gOTAsIC5od192YWx1ZSA9IDE4LCB9LCAg
eyAuYml0cmF0ZSA9IDEyMCwgLmh3X3ZhbHVlDQo+ID4gKz0gMjQsIH0sICB7IC5iaXRyYXRlID0g
MTgwLCAuaHdfdmFsdWUgPSAzNiwgfSwgIHsgLmJpdHJhdGUgPSAyNDAsDQo+ID4gKy5od192YWx1
ZSA9IDQ4LCB9LCAgeyAuYml0cmF0ZSA9IDM2MCwgLmh3X3ZhbHVlID0gNzIsIH0sICB7IC5iaXRy
YXRlDQo+ID4gKz0gNDgwLCAuaHdfdmFsdWUgPSA5NiwgfSwgIHsgLmJpdHJhdGUgPSA1NDAsIC5o
d192YWx1ZSA9IDEwOCwgfSwgfTsNCj4gPiArDQo+ID4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgaWVl
ZTgwMjExX2NoYW5uZWwgbXdsX2NoYW5uZWxzXzUwW10gPSB7ICB7IC5iYW5kDQo+ID4gKz0gTkw4
MDIxMV9CQU5EXzVHSFosIC5jZW50ZXJfZnJlcSA9IDUxODAsIC5od192YWx1ZSA9IDM2LCB9LCAg
eyAuYmFuZA0KPiA+ICs9IE5MODAyMTFfQkFORF81R0haLCAuY2VudGVyX2ZyZXEgPSA1MjAwLCAu
aHdfdmFsdWUgPSA0MCwgfSwgIHsgLmJhbmQNCj4gPiArPSBOTDgwMjExX0JBTkRfNUdIWiwgLmNl
bnRlcl9mcmVxID0gNTIyMCwgLmh3X3ZhbHVlID0gNDQsIH0sICB7IC5iYW5kDQo+ID4gKz0gTkw4
MDIxMV9CQU5EXzVHSFosIC5jZW50ZXJfZnJlcSA9IDUyNDAsIC5od192YWx1ZSA9IDQ4LCB9LCAg
eyAuYmFuZA0KPiA+ICs9IE5MODAyMTFfQkFORF81R0haLCAuY2VudGVyX2ZyZXEgPSA1MjYwLCAu
aHdfdmFsdWUgPSA1MiwgfSwgIHsgLmJhbmQNCj4gPiArPSBOTDgwMjExX0JBTkRfNUdIWiwgLmNl
bnRlcl9mcmVxID0gNTI4MCwgLmh3X3ZhbHVlID0gNTYsIH0sICB7IC5iYW5kDQo+ID4gKz0gTkw4
MDIxMV9CQU5EXzVHSFosIC5jZW50ZXJfZnJlcSA9IDUzMDAsIC5od192YWx1ZSA9IDYwLCB9LCAg
eyAuYmFuZA0KPiA+ICs9IE5MODAyMTFfQkFORF81R0haLCAuY2VudGVyX2ZyZXEgPSA1MzIwLCAu
aHdfdmFsdWUgPSA2NCwgfSwgIHsgLmJhbmQNCj4gPiArPSBOTDgwMjExX0JBTkRfNUdIWiwgLmNl
bnRlcl9mcmVxID0gNTUwMCwgLmh3X3ZhbHVlID0gMTAwLCB9LCAgew0KPiA+ICsuYmFuZCA9IE5M
ODAyMTFfQkFORF81R0haLCAuY2VudGVyX2ZyZXEgPSA1NTIwLCAuaHdfdmFsdWUgPSAxMDQsIH0s
DQo+ID4gK3sgLmJhbmQgPSBOTDgwMjExX0JBTkRfNUdIWiwgLmNlbnRlcl9mcmVxID0gNTU0MCwg
Lmh3X3ZhbHVlID0gMTA4LCB9LA0KPiA+ICt7IC5iYW5kID0gTkw4MDIxMV9CQU5EXzVHSFosIC5j
ZW50ZXJfZnJlcSA9IDU1NjAsIC5od192YWx1ZSA9IDExMiwgfSwNCj4gPiAreyAuYmFuZCA9IE5M
ODAyMTFfQkFORF81R0haLCAuY2VudGVyX2ZyZXEgPSA1NTgwLCAuaHdfdmFsdWUgPSAxMTYsIH0s
DQo+ID4gK3sgLmJhbmQgPSBOTDgwMjExX0JBTkRfNUdIWiwgLmNlbnRlcl9mcmVxID0gNTYwMCwg
Lmh3X3ZhbHVlID0gMTIwLCB9LA0KPiA+ICt7IC5iYW5kID0gTkw4MDIxMV9CQU5EXzVHSFosIC5j
ZW50ZXJfZnJlcSA9IDU2MjAsIC5od192YWx1ZSA9IDEyNCwgfSwNCj4gPiAreyAuYmFuZCA9IE5M
ODAyMTFfQkFORF81R0haLCAuY2VudGVyX2ZyZXEgPSA1NjQwLCAuaHdfdmFsdWUgPSAxMjgsIH0s
DQo+ID4gK3sgLmJhbmQgPSBOTDgwMjExX0JBTkRfNUdIWiwgLmNlbnRlcl9mcmVxID0gNTY2MCwg
Lmh3X3ZhbHVlID0gMTMyLCB9LA0KPiA+ICt7IC5iYW5kID0gTkw4MDIxMV9CQU5EXzVHSFosIC5j
ZW50ZXJfZnJlcSA9IDU2ODAsIC5od192YWx1ZSA9IDEzNiwgfSwNCj4gPiAreyAuYmFuZCA9IE5M
ODAyMTFfQkFORF81R0haLCAuY2VudGVyX2ZyZXEgPSA1NzAwLCAuaHdfdmFsdWUgPSAxNDAsIH0s
DQo+ID4gK3sgLmJhbmQgPSBOTDgwMjExX0JBTkRfNUdIWiwgLmNlbnRlcl9mcmVxID0gNTcyMCwg
Lmh3X3ZhbHVlID0gMTQ0LCB9LA0KPiA+ICt7IC5iYW5kID0gTkw4MDIxMV9CQU5EXzVHSFosIC5j
ZW50ZXJfZnJlcSA9IDU3NDUsIC5od192YWx1ZSA9IDE0OSwgfSwNCj4gPiAreyAuYmFuZCA9IE5M
ODAyMTFfQkFORF81R0haLCAuY2VudGVyX2ZyZXEgPSA1NzY1LCAuaHdfdmFsdWUgPSAxNTMsIH0s
DQo+ID4gK3sgLmJhbmQgPSBOTDgwMjExX0JBTkRfNUdIWiwgLmNlbnRlcl9mcmVxID0gNTc4NSwg
Lmh3X3ZhbHVlID0gMTU3LCB9LA0KPiA+ICt7IC5iYW5kID0gTkw4MDIxMV9CQU5EXzVHSFosIC5j
ZW50ZXJfZnJlcSA9IDU4MDUsIC5od192YWx1ZSA9IDE2MSwgfSwNCj4gPiArfTsNCj4gDQo+IERp
dHRvLg0KPiANCj4gLi4uDQo+ID4gKw0KPiA+ICtzdGF0aWMgdm9pZCBtd2xfcHJvY2Vzc19vZl9k
dHMoc3RydWN0IG13bF9wcml2ICpwcml2KSB7ICNpZmRlZg0KPiA+ICtDT05GSUdfT0YNCj4gDQo+
IFNvIGEgbW9yZSBjb21tb24gaWRpb20gaW4gdGhlIGRyaXZlcnMgaXM6DQo+ICAgI2lmZGVmIENP
TkZJR19PRg0KPiAgIGRlZmluZSBwcm9jZXNzIG9mX2R0cyBmdW5jdGlvbiB7fQ0KPiAgICNlbHNl
DQo+ICAgZGVmaW5lIEVNUFRZIG9mX2R0cyBmdW5jdGlvbiB7fQ0KPiAgICNlbmRpZg0KPiANCj4g
SSBndWVzcyBpdCdzIHRoZSBzYW1lIGVmZmVjdCBlaXRoZXIgd2F5LiBJIGRvbid0IG11Y2ggY2Fy
ZSBhcyBsb25nIGFzDQo+IG13bF9wcm9jZXNzX29mX2R0cygpIGNhbiBiZSBjYWxsZWQgd2l0aG91
dCBndWFyZHMsIHdoaWNoIGl0IGNhbi4gQnV0IEkgdGhvdWdodA0KPiBJIHNob3VsZCBtZW50aW9u
IGl0IGluY2FzZSBhbnlvbmUgZWxzZSBjYXJlcy4gQW5kIHRoaXMgaXMgbm90IGNvbnNpc3RlbnQg
d2l0aA0KPiBob3cgeW91IGhhdmUgZG9uZSB0aGUgc2FtZSB0aGluZyBmb3IgQ09ORklHX0RFQlVH
X0ZTDQo+IA0KDQpJIHdpbGwgbW9kaWZ5IGl0Lg0KDQo+ID4gKyBzdHJ1Y3QgcHJvcGVydHkgKnBy
b3A7DQo+ID4gKyB1MzIgcHJvcF92YWx1ZTsNCj4gPiArDQo+ID4gKyBwcml2LT5kdF9ub2RlID0N
Cj4gPiArIG9mX2ZpbmRfbm9kZV9ieV9uYW1lKHBjaV9idXNfdG9fT0Zfbm9kZShwcml2LT5wZGV2
LT5idXMpLA0KPiA+ICsgICAgICJtd2x3aWZpIik7DQo+ID4gKyBpZiAoIXByaXYtPmR0X25vZGUp
DQo+ID4gKyByZXR1cm47DQo+ID4gKw0KPiA+ICsgLyogbG9vayBmb3IgYWxsIG1hdGNoaW5nIHBy
b3BlcnR5IG5hbWVzICovDQo+ID4gKyBmb3JfZWFjaF9wcm9wZXJ0eV9vZl9ub2RlKHByaXYtPmR0
X25vZGUsIHByb3ApIHsgaWYNCj4gPiArIChzdHJjbXAocHJvcC0+bmFtZSwgIm1hcnZlbGwsMmdo
eiIpID09IDApDQo+ID4gKyBwcml2LT5kaXNhYmxlXzJnID0gdHJ1ZTsNCj4gPiArIGlmIChzdHJj
bXAocHJvcC0+bmFtZSwgIm1hcnZlbGwsNWdoeiIpID09IDApDQo+ID4gKyBwcml2LT5kaXNhYmxl
XzVnID0gdHJ1ZTsNCj4gPiArIGlmIChzdHJjbXAocHJvcC0+bmFtZSwgIm1hcnZlbGwsY2hhaW5t
YXNrIikgPT0gMCkgeyBwcm9wX3ZhbHVlID0NCj4gPiArIGJlMzJfdG9fY3B1KCooKF9fYmUzMiAq
KXByb3AtPnZhbHVlKSk7IGlmIChwcm9wX3ZhbHVlID09IDIpDQo+ID4gKyBwcml2LT5hbnRlbm5h
X3R4ID0gQU5URU5OQV9UWF8yOw0KPiA+ICsNCj4gPiArIHByb3BfdmFsdWUgPSBiZTMyX3RvX2Nw
dSgqKChfX2JlMzIgKikgKHByb3AtPnZhbHVlICsgNCkpKTsgaWYNCj4gPiArIChwcm9wX3ZhbHVl
ID09IDIpDQo+ID4gKyBwcml2LT5hbnRlbm5hX3J4ID0gQU5URU5OQV9SWF8yOw0KPiA+ICsgfQ0K
PiA+ICsgfQ0KPiA+ICsNCj4gPiArIHByaXYtPnB3cl9ub2RlID0gb2ZfZmluZF9ub2RlX2J5X25h
bWUocHJpdi0+ZHRfbm9kZSwNCj4gPiArICAgICAgIm1hcnZlbGwscG93ZXJ0YWJsZSIpOw0KPiA+
ICsjZW5kaWYNCj4gPiArfQ0KPiANCj4gQUZBSUNULCB0aGVyZSdzIG5vIGRvY3VtZW50YXRpb24g
Zm9yIHRoZXNlIERUIGJpbmRpbmdzLiBUaGUgbXdsd2lmaSBub2RlIGFuZA0KPiB0aGUgbWFydmVs
bCxwb3dlcnRhYmxlIGJvdGggbmVlZCBmdWxsIGRvY3VtZW50YXRpb24gaW4NCj4gRG9jdW1lbnRh
dGlvbi9kZXZpY2V0cmVlL2JpbmRpbmdzLy4uLiAuDQo+IA0KPiBGcmFua2x5IEkgaGF2ZSBhIGZl
ZWxpbmcgSSdtIGdvaW5nIHRvIG5lZWQgdGhlc2UgRFQgbm9kZXMgYW5kIEknZCBsaWtlIHRvIG5v
dA0KPiBoYXZlIHRvIGd1ZXNzLWFuZC1jaGVjayBiYXNlZCBvbiB0aGUgY29kZS4NCj4gDQoNClBv
d2VyIHRhYmxlIHdvbid0IGJlIHVzZWQgZm9yIGNoaXAgd2l0aCBkZXZpY2UgcG93ZXIgdGFibGUs
IGl0IGlzIG9ubHkgdXNlZCBmb3Igb2xkIHByb2R1Y3RzIG9mIE9wZW5XcnQuIEZvciB1cHN0cmVh
bSwgdGhlIHJlbGF0ZWQgY29kZSBpcyByZW1vdmVkLg0KPG1hcnZlbGwsMmdoej4gaXMgdXNlZCB0
byBkaXNhYmxlIDJnIGJhbmQuDQo8bWFydmVsbCw1Z2h6PiBpcyB1c2VkIHRvIGRpc2FibGUgNWcg
YmFuZC4NCjxtYXJ2ZWxsLCBjaGFpbm1hc2s+IGlzc3VlZCB0byBzcGVjaWZ5IGFudGVubmEgbnVt
YmVyIChpZiBkZWZhdWx0IG51bWJlciBpcyBzdWl0YWJsZSBmb3IgeW91LCB0aGVyZSBpcyBubyBu
ZWVkIHRvIHVzZSB0aGlzIHBhcmFtZXRlcikuIA0KDQo+IC4uLg0KPiANCj4gPiArc3RhdGljIGlu
dCBtd2xfd2xfaW5pdChzdHJ1Y3QgbXdsX3ByaXYgKnByaXYpIHsgIHN0cnVjdCBpZWVlODAyMTFf
aHcNCj4gPiArKmh3OyAgaW50IHJjOyAgaW50IGk7DQo+ID4gKw0KPiA+ICsgaHcgPSBwcml2LT5o
dzsNCj4gPiArDQo+ID4gKyBody0+ZXh0cmFfdHhfaGVhZHJvb20gPSBTWVNBRFBUX01JTl9CWVRF
U19IRUFEUk9PTTsgcXVldWVzID0NCj4gPiArIGh3LT5TWVNBRFBUX1RYX1dNTV9RVUVVRVM7DQo+
ID4gKw0KPiA+ICsgLyogU2V0IHJzc2kgdmFsdWVzIHRvIGRCbSAqLw0KPiA+ICsgaWVlZTgwMjEx
X2h3X3NldChodywgU0lHTkFMX0RCTSk7DQo+ID4gKyBpZWVlODAyMTFfaHdfc2V0KGh3LCBIQVNf
UkFURV9DT05UUk9MKTsNCj4gPiArDQo+ID4gKyAvKiBBc2sgbWFjODAyMTEgbm90IHRvIHRyaWdn
ZXIgUFMgbW9kZQ0KPiA+ICsgKiBiYXNlZCBvbiBQTSBiaXQgb2YgaW5jb21pbmcgZnJhbWVzLg0K
PiA+ICsgKi8NCj4gPiArIGllZWU4MDIxMV9od19zZXQoaHcsIEFQX0xJTktfUFMpOw0KPiA+ICsN
Cj4gPiArIGllZWU4MDIxMV9od19zZXQoaHcsIFNVUFBPUlRTX1BFUl9TVEFfR1RLKTsgaWVlZTgw
MjExX2h3X3NldChodywNCj4gPiArIE1GUF9DQVBBQkxFKTsNCj4gPiArDQo+ID4gKyBody0+d2lw
aHktPmZsYWdzIHw9IFdJUEhZX0ZMQUdfSUJTU19SU047IGZsYWdzIHw9DQo+ID4gKyBody0+d2lw
aHktPldJUEhZX0ZMQUdfSEFTX0NIQU5ORUxfU1dJVENIOw0KPiA+ICsNCj4gPiArIGh3LT53aXBo
eS0+ZmxhZ3MgfD0gV0lQSFlfRkxBR19TVVBQT1JUU19URExTOw0KPiA+ICsNCj4gPiArIGh3LT52
aWZfZGF0YV9zaXplID0gc2l6ZW9mKHN0cnVjdCBtd2xfdmlmKTsgc3RhX2RhdGFfc2l6ZSA9DQo+
ID4gKyBody0+c2l6ZW9mKHN0cnVjdCBtd2xfc3RhKTsNCj4gPiArDQo+ID4gKyBwcml2LT5hcF9t
YWNpZHNfc3VwcG9ydGVkID0gMHgwMDAwZmZmZjsgc3RhX21hY2lkc19zdXBwb3J0ZWQgPQ0KPiA+
ICsgcHJpdi0+MHgwMDAxMDAwMDsNCj4gDQo+IEhvdyBhYm91dCB3ZSBkb2N1bWVudCB3aGF0IHRo
ZXNlIG1hZ2ljIG51bWJlcnMgYXJlPyBBIG5pY2UgbmFtZWQNCj4gY29uc3RhbnQgYXQgbGVhc3Qg
d291bGQgYmUgbmljZS4NCg0KVGhleSBhcmUgdXNlZCB0byBrZWVwIHN0YXR1cyBvZiB1c2VkIEFQ
IGFuZCBTVEEuDQoNCj4gDQo+ID4gK3N0YXRpYyBpbnQgbXdsX3Byb2JlKHN0cnVjdCBwY2lfZGV2
ICpwZGV2LCBjb25zdCBzdHJ1Y3QgcGNpX2RldmljZV9pZA0KPiA+ICsqaWQpIHsNCj4gLi4uDQo+
ID4gKyB3aXBoeV9pbmZvKHByaXYtPmh3LT53aXBoeSwgIiVzIFRYIGFudGVubmFzLCAlcyBSWCBh
bnRlbm5hc1xuIiwNCj4gPiArICAgKHByaXYtPmFudGVubmFfdHggPT0gQU5URU5OQV9UWF80X0FV
VE8pID8gIjQiIDogIjIiLA0KPiA+ICsgICAocHJpdi0+YW50ZW5uYV9yeCA9PSBBTlRFTk5BX1JY
XzRfQVVUTykgPyAiNCIgOiAiMiIpOw0KPiA+ICsNCj4gPiArI2lmZGVmIENPTkZJR19ERUJVR19G
Uw0KPiA+ICsgbXdsX2RlYnVnZnNfaW5pdChodyk7DQo+IA0KPiBUaGUgZ3VhcmRzIHNob3VsZCBi
ZSBpbnRlcm5hbCB0byBtd2xfZGVidWdmc19pbml0KCkgc28gd2UgZG9uJ3QgaGF2ZSB0byBndWFy
ZA0KPiBpdCB3aGVuIHdlIGNhbGwgaXQuIE11Y2ggbGlrZSBtd2xfcHJvY2Vzc19vZl9kdHMoKSBp
cyBhYmxlIHRvIGJlIGNhbGxlZCBhbmQNCj4gY29tcGlsZXMgb3V0IGlmIENPTkZJR19PRiBpc24n
dCBkZWZpbmVkLCBtd2xfZGVidWdmc19pbml0KCkgc2hvdWxkIGhhdmUgdGhlDQo+IGd1YXJkcyBp
bnRlcm5hbCB0byBkZWJ1Z2ZzLmgvZGVidWdmcy5jIGFuZCB3ZSBzaG91bGRuJ3QgbmVlZCB0byB3
b3JyeSBhYm91dA0KPiBpdCB3aGVuIHdlIGNhbGwgaXQuDQo+IA0KDQpJIHdpbGwgbW9kaWZ5IGl0
Lg0KDQo+ID4gK3N0YXRpYyB2b2lkIG13bF9yZW1vdmUoc3RydWN0IHBjaV9kZXYgKnBkZXYpIHsg
IHN0cnVjdCBpZWVlODAyMTFfaHcNCj4gPiArKmh3ID0gcGNpX2dldF9kcnZkYXRhKHBkZXYpOyAg
c3RydWN0IG13bF9wcml2ICpwcml2Ow0KPiA+ICsNCj4gPiArIGlmICghaHcpDQo+ID4gKyByZXR1
cm47DQo+ID4gKw0KPiA+ICsgcHJpdiA9IGh3LT5wcml2Ow0KPiA+ICsNCj4gPiArIG13bF93bF9k
ZWluaXQocHJpdik7DQo+ID4gKyBwY2lfc2V0X2RydmRhdGEocGRldiwgTlVMTCk7DQo+ID4gKyBp
ZWVlODAyMTFfZnJlZV9odyhodyk7DQo+ID4gKyBwY2lfZGlzYWJsZV9kZXZpY2UocGRldik7DQo+
ID4gKw0KPiA+ICsjaWZkZWYgQ09ORklHX0RFQlVHX0ZTDQo+ID4gKyBtd2xfZGVidWdmc19yZW1v
dmUoaHcpOw0KPiA+ICsjZW5kaWYNCj4gDQo+IEFzIHByZXZpb3VzbHkgY29tbWVudGVkIG9uLg0K
PiANCj4gPiArKysgYi9kcml2ZXJzL25ldC93aXJlbGVzcy9tYXJ2ZWxsL213bHdpZmkvdGhlcm1h
bC5jDQo+IC4uLg0KPiA+ICtzdGF0aWMgU0VOU09SX0RFVklDRV9BVFRSKHRlbXAxX2lucHV0LCAw
NDQ0LA0KPiBtd2xfdGhlcm1hbF9zaG93X3RlbXAsDQo+ID4gKyAgTlVMTCwgMCk7DQo+ID4gKw0K
PiANCj4gU2hvdWxkIHVzZSBTX0lSVUdPIGluc3RlYWQgb2YgbnVtZXJpYyAwNDQ0Lg0KPiANCj4g
T0ssIHRoYXQncyBpdCBmb3Igc3BlY2lmaWNzLiBJIGtub3cgYSBudW1iZXIgb2YgdGhlbSBhcmUg
anVzdCBuaXRzLg0KPiANCg0KWW91IG5lZWQgdG8gbW9kaWZ5IHRoaXMgd2F5IHRvIHBhc3MgY2hl
Y2twYXRjaC5wbC4NCg0KPiBBIGZldyBnZW5lcmFsIGNvbW1lbnRzOg0KPiANCj4gKiBJIHNhdyBp
dCBpdCBxdWl0ZSBhIGJpdCwgYnV0IGRpZG4ndCBjb21tZW50IG9uIGl0IGV2ZXJ5IHRpbWU6IHRo
ZXJlJ3MgbWFueQ0KPiBwbGFjZXMgd2hlcmUgYSB2YXJpYWJsZSBkZWNsYXJhdGlvbiBjYW4gYmUg
Y29tYmluZWQgd2l0aCBpdHMgaW5pdGlhbA0KPiBhc3NpZ25tZW50Lg0KPiANCj4gKiBJIGhhcHBl
biB0byBjb25jdXIgd2l0aCBKb2hhbm5lcycgY29tbWVudHMgcmVnYXJkaW5nIHRoZSBJRXMgYW5k
IHlvdXINCj4gYmVhY29uIHByb2Nlc3NpbmcuIFRoaXMgaXMgYSBzaWduaWZpY2FudCBpc3N1ZSwg
d2l0aCBwb3RlbnRpYWwgZm9yIGJpZyBidWdzIGRvd24NCj4gdGhlIHJvYWQuIEF0IHRoZSB2ZXJ5
IGxlYXN0LCBpdCdzIGEgbWFpbnRlbmFuY2UgaGVhZGFjaGUuDQo+IA0KPiBGcm9tIG15IHBlcnNw
ZWN0aXZlLCBJJ2QgY29uc2lkZXIgaXQgYSBmaXJtd2FyZSBidWcgaWYgdGhlcmUncyBubyB3YXkg
YXJvdW5kIGl0Lg0KPiBJcyB0aGUgZmlybXdhcmUgZ29pbmcgdG8gc3RyaXAgdGhlIElFcyB0aGF0
IGhvc3RhcGQgaGFwcGVucyB0byBhZGQgdG8gdGhlDQo+IGJlYWNvbnM/IElzIHRoZXJlIHNvbWUg
InBhc3N0aHJvdWdoIiBvciBzb21lIG90aGVyIHdheSB0aGF0IGl0IGNhbiBiZQ0KPiByZWNvbmNp
bGVkPw0KPiANCj4gSSBzdHJvbmdseSBzdXNwZWN0IHRoZXJlJ3MgYmV0dGVyIHdheXMgdG8gaGFu
ZGxlIGl0LCBldmVuIHdpdGhvdXQgY2hhbmdpbmcgdGhlDQo+IGZpcm13YXJlLCBidXQgSSBoYXZl
bid0IHlldCB0YWtlbiBhIGxvb2sgdG8gc2VlIGlmIHRoZXJlIGlzLg0KPiANCj4gSW4gYW55IGNh
c2UsIHdoaWxlIHRoZXJlJ3Mgc3R1ZmYgSSB3b3VsZG4ndCBtaW5kIHNlZWluZyBjaGFuZ2VkLCBJ
IHJhdGhlciBzZWUgaXQNCj4gZ28gaW4gc29vbmVyIHJhdGhlciB0aGFuIGxhdGVyIHNvIEkgYW5k
IG90aGVycyBjYW4gY29udHJpYnV0ZSBvbiB0b3Agb2YgaXQsDQo+IGluc3RlYWQgb2Ygd2FpdGlu
ZyB0byBzZWUgaXQgInBlcmZlY3QiIGZpcnN0Lg0KDQotIFRoZSBvYmplY3RpdmUgaXMgdG8gdXNl
IHRoZSBzYW1lIHByb2R1Y3Rpb24gZmlybXdhcmUgYmluYXJ5IGZvciBib3RoIHRoZSBvcGVuIHNv
dXJjZSBhbmQgcHJvcHJpZXRhcnkgZHJpdmVyLiBTYW1lIGludGVyZmFjZSBpcyBjdXJyZW50bHkg
dXNlZCBieSBwcm9wcmlldGFyeSBkcml2ZXIgZm9yIGhpc3RvcmljYWxseSByZWFzb24sIHdoaWxl
IHRoZSBvcGVuIHNvdXJjZSBIQUwgaXMgYWRhcHRpbmcgdG8gaXQgZm9yIHRoZSBleGlzdGluZyBz
aGlwcGluZyBwcm9kdWN0Lg0KLSBXZSB3aWxsIG1ha2UgY2hhbmdlcyBhbmQgY2xlYW4gdGhpbmdz
IHVwIGluIGZ1dHVyZS4gSSB3aWxsIHNwZW5kIGVmZm9ydCB0byBjb250aW51ZSBpdHMgbWFpbnRl
bmFuY2UgYW5kIGNsZWFuLXVwLg0KDQo+IA0KPiBQbGVhc2UgYWRkIG15IHJldmlld2VkLWJ5LiAg
SWYgd2UncmUgd2FpdGluZyBvbiBhIHYxMCwgZG8geW91IGhhdmUgYW4gRVRBPw0KDQpJdCBsb29r
cyBsaWtlIHRoZXJlIGlzIG5vIG1vcmUgY29tbWVudHMgZm9yIHBhdGNoIHY5LCBJIHdpbGwgc2Vu
ZCBvdXQgcGF0Y2ggdjEwIGVuZCBvZiB0aGlzIG1vbnRoLg0KDQo+IA0KPiBUaGFua3MsDQo+IC0g
U3RldmUNCg==
PiBGcm9tOiBzdGV2ZS5kZXJvc2llckBnbWFpbC5jb20gV3JvdGU6DQo+IEhpIERhdmlkLA0KPiAN
Cj4gRmlyc3Qgb2ZmLCBJIHdhbnRlZCB0byBzYXkgdGhhbmsteW91IGZvciB5b3VyIHdvcmsgYW5k
IGVmZm9ydCBpbiANCj4gdHJ5aW5nIHRvIGdldCBtd2x3aWZpIHVwc3RyZWFtLiBNeSBjb21tZW50
cyBhcmUgaW4tbGluZSB3aXRoIG15IA0KPiBnZW5lcmFsIG5vdGVzIGFmdGVyd2FyZHMuDQo+IA0K
PiBPbiBUdWUsIERlYyAyMCwgMjAxNiBhdCA4OjExIFBNLCBEYXZpZCBMaW4gPGRsaW5AbWFydmVs
bC5jb20+IHdyb3RlOg0KPiANCj4gPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9uZXQvd2lyZWxlc3Mv
bWFydmVsbC9td2x3aWZpL2RlYnVnZnMuaA0KPiA+IGIvZHJpdmVycy9uZXQvd2lyZWxlc3MvbWFy
dmVsbC9td2x3aWZpL2RlYnVnZnMuaA0KPiA+IG5ldyBmaWxlIG1vZGUgMTAwNjQ0DQo+ID4gKyNp
Zm5kZWYgX01XTF9ERUJVR0ZTX0hfDQo+ID4gKyNkZWZpbmUgX01XTF9ERUJVR0ZTX0hfDQo+ID4g
Kw0KPiA+ICt2b2lkIG13bF9kZWJ1Z2ZzX2luaXQoc3RydWN0IGllZWU4MDIxMV9odyAqaHcpOyB2
b2lkIA0KPiA+ICttd2xfZGVidWdmc19yZW1vdmUoc3RydWN0IGllZWU4MDIxMV9odyAqaHcpOw0K
PiA+ICsNCj4gDQo+IFlvdSBzaG91bGQgZ3VhcmQgdGhlc2Ugc28gdGhleSBkZWZpbmUgdG8gZW1w
dHkgZnVuY3Rpb25zIGlmIA0KPiBDT05GSUdfREVCVUdfRlMgaXNuJ3QgZW5hYmxlZCBzbyB0aGV5
IGNhbiBiZSB1c2VkIGJ5IGEgY2FsbGVyIHdpdGhvdXQgDQo+IGV4cGxpY2l0IGd1YXJkcy4NCj4g
DQoNCkkgd2lsbCBtb2RpZnkgaXQuDQoNCj4NCj4gPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9uZXQv
d2lyZWxlc3MvbWFydmVsbC9td2x3aWZpL2Rldi5oDQo+ID4gYi9kcml2ZXJzL25ldC93aXJlbGVz
cy9tYXJ2ZWxsL213bHdpZmkvZGV2LmgNCj4gPiBuZXcgZmlsZSBtb2RlIDEwMDY0NA0KPiA+IGlu
ZGV4IDAwMDAwMDAuLmM3YjEwYWMNCj4gPiAtLS0gL2Rldi9udWxsDQo+ID4gKysrIGIvZHJpdmVy
cy9uZXQvd2lyZWxlc3MvbWFydmVsbC9td2x3aWZpL2Rldi5oDQo+ID4gKyNpZmRlZiBDT05GSUdf
REVCVUdfRlMNCj4gPiArI2RlZmluZSBNQUNfUkVHX0FERFJfUENJKG9mZnNldCkgICAgICAoKHBy
aXYtPmlvYmFzZTEgKyAweEEwMDApICsNCj4gKG9mZnNldCkpDQo+ID4gKw0KPiA+ICsjZGVmaW5l
IE1XTF9BQ0NFU1NfTUFDICAgICAgICAgICAgICAgIDENCj4gPiArI2RlZmluZSBNV0xfQUNDRVNT
X1JGICAgICAgICAgICAgICAgICAyDQo+ID4gKyNkZWZpbmUgTVdMX0FDQ0VTU19CQlAgICAgICAg
ICAgICAgICAgMw0KPiA+ICsjZGVmaW5lIE1XTF9BQ0NFU1NfQ0FVICAgICAgICAgICAgICAgIDQN
Cj4gPiArI2RlZmluZSBNV0xfQUNDRVNTX0FERFIwICAgICAgICAgICAgICA1DQo+ID4gKyNkZWZp
bmUgTVdMX0FDQ0VTU19BRERSMSAgICAgICAgICAgICAgNg0KPiA+ICsjZGVmaW5lIE1XTF9BQ0NF
U1NfQUREUiAgICAgICAgICAgICAgIDcNCj4gPiArI2VuZGlmDQo+IA0KPiBPSywgSSBnZXQgdGhh
dCB5b3UncmUgb25seSB1c2luZyB0aGUgYWJvdmUgaW4gdGhlIGRlYnVnZnMgZnVuY3Rpb25zLCAN
Cj4gYnV0IGFzIGZvciBnZW5lcmljIHJlZ2lzdGVyIGFjY2VzcyBmdW5jdGlvbnMsIHRoZXNlIHdv
dWxkIGJlIHZhbGlkIG5vIA0KPiBtYXR0ZXIgaWYgQ09ORklHX0RFQlVHX0ZTIGlzIGRlZmluZWQu
IEhhdmluZyB0aGUgZ3VhcmQgaGVyZSBqdXN0IHNlZW1zIA0KPiB0byBjb21wbGljYXRlIHRoaW5n
cy4NCj4gDQoNCkkgd2lsbCByZW1vdmUgaXQuDQoNCj4gDQo+ID4gKw0KPiA+ICtzdHJ1Y3QgbXds
X3ByaXYgew0KPiA+ICsgc3RydWN0IGllZWU4MDIxMV9odyAqaHc7DQo+ID4gKyBzdHJ1Y3QgZmly
bXdhcmUgKmZ3X3Vjb2RlOw0KPiA+ICsgYm9vbCBmd19kZXZpY2VfcHdydGJsOw0KPiA+ICsgYm9v
bCBmb3JiaWRkZW5fc2V0dGluZzsNCj4gPiArDQo+ID4gKyBzdHJ1Y3QgdGhlcm1hbF9jb29saW5n
X2RldmljZSAqY2RldjsNCj4gPiArIHUzMiB0aHJvdHRsZV9zdGF0ZTsNCj4gPiArIHUzMiBxdWll
dF9wZXJpb2Q7DQo+ID4gKyBpbnQgdGVtcGVyYXR1cmU7DQo+ID4gKw0KPiA+ICsjaWZkZWYgQ09O
RklHX0RFQlVHX0ZTDQo+ID4gKyBzdHJ1Y3QgZGVudHJ5ICpkZWJ1Z2ZzX3BoeTsNCj4gPiArIHUz
MiByZWdfdHlwZTsNCj4gPiArIHUzMiByZWdfb2Zmc2V0Ow0KPiA+ICsgdTMyIHJlZ192YWx1ZTsN
Cj4gPiArIGludCB0eF9kZXNjX251bTsNCj4gPiArI2VuZGlmDQo+IA0KPiBXZSdyZSBzYXZpbmcg
YSBmZXcgYnl0ZXMgaGVyZSBhdCB0aGUgY29zdCBvZiBzb21lIGNvbXBsZXhpdHkuIEkgY291bGQg
DQo+IGdvIGVpdGhlciB3YXksIGJ1dCBJIGhhdGUgd2hlbiBwcml2IHN0cnVjdHVyZXMgbXV0YXRl
Lg0KPiANCg0KSSB3aWxsIHJlbW92ZSB0aGUgY29tcGlsZSBjb250cm9sLg0KDQo+IA0KPiA+ICsv
KiBEZWZpbmVkIGluIG1hYzgwMjExLmMuICovDQo+ID4gK2V4dGVybiBjb25zdCBzdHJ1Y3QgaWVl
ZTgwMjExX29wcyBtd2xfbWFjODAyMTFfb3BzOw0KPiANCj4gRG9lcyB0aGlzIG5lZWQgdG8gYmUg
aW4gZGV2Lmg/DQo+IA0KPiANCg0KSXQgY2FuIGJlIG1vdmVkIHRvIG1haW4uYy4NCg0KPiANCj4g
PiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9uZXQvd2lyZWxlc3MvbWFydmVsbC9td2x3aWZpL2Z3Y21k
LmMNCj4gPiBiL2RyaXZlcnMvbmV0L3dpcmVsZXNzL21hcnZlbGwvbXdsd2lmaS9md2NtZC5jDQo+
ID4gbmV3IGZpbGUgbW9kZSAxMDA2NDQNCj4gPiBpbmRleCAwMDAwMDAwLi45YzNjY2Y5DQo+ID4g
LS0tIC9kZXYvbnVsbA0KPiA+ICsrKyBiL2RyaXZlcnMvbmV0L3dpcmVsZXNzL21hcnZlbGwvbXds
d2lmaS9md2NtZC5jDQo+ID4gQEAgLTAsMCArMSwyODM3IEBADQo+ID4gKy8qDQo+ID4gKyAqIENv
cHlyaWdodCAoQykgMjAwNi0yMDE2LCBNYXJ2ZWxsIEludGVybmF0aW9uYWwgTHRkLg0KPiA+ICsg
Kg0KPiA+ICsgKiBUaGlzIHNvZnR3YXJlIGZpbGUgKHRoZSAiRmlsZSIpIGlzIGRpc3RyaWJ1dGVk
IGJ5IE1hcnZlbGwgDQo+ID4gK0ludGVybmF0aW9uYWwNCj4gPiArICogTHRkLiB1bmRlciB0aGUg
dGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIFZlcnNpb24gDQo+ID4gKzIs
IEp1bmUgMTk5MQ0KPiA+ICsgKiAodGhlICJMaWNlbnNlIikuICBZb3UgbWF5IHVzZSwgcmVkaXN0
cmlidXRlIGFuZC9vciBtb2RpZnkgdGhpcw0KPiA+ICsNCj4gPiArI2RlZmluZSBNQVhfV0FJVF9G
V19DT01QTEVURV9JVEVSQVRJT05TICAgICAgICAgMjAwMA0KPiA+ICsjZGVmaW5lIE1BWF9XQUlU
X0dFVF9IV19TUEVDU19JVEVSQVRPTlMgICAgICAgICAzDQo+ID4gKw0KPiA+ICtzdHJ1Y3QgY21k
X2hlYWRlciB7DQo+ID4gKyBfX2xlMTYgY29tbWFuZDsNCj4gPiArIF9fbGUxNiBsZW47DQo+ID4g
K30gX19wYWNrZWQ7DQo+ID4gKw0KPiA+ICtzdGF0aWMgYm9vbCBtd2xfZndjbWRfY2hrX2FkYXB0
ZXIoc3RydWN0IG13bF9wcml2ICpwcml2KSB7DQo+ID4gKyB1MzIgcmVndmFsOw0KPiA+ICsNCj4g
PiArIHJlZ3ZhbCA9IHJlYWRsKHByaXYtPmlvYmFzZTEgKyBNQUNSRUdfUkVHX0lOVF9DT0RFKTsN
Cj4gDQo+IENvdWxkbid0IHRoZSBhYm92ZSBiZSBvbmUgbGluZT8NCj4NCg0KWWVzLiBJIHdpbGwg
bW9kaWZ5IGl0Lg0KIA0KPiANCj4gPiArDQo+ID4gK3N0YXRpYyBpbnQgbXdsX2Z3Y21kX2dldF90
eF9wb3dlcnMoc3RydWN0IG13bF9wcml2ICpwcml2LCB1MTYgDQo+ID4gKypwb3dsaXN0LA0KPiB1
MTYgY2gsDQo+ID4gKyAgIHUxNiBiYW5kLCB1MTYgd2lkdGgsIHUxNiBzdWJfY2gpIHsgIHN0cnVj
dCANCj4gPiAraG9zdGNtZF9jbWRfODAyXzExX3R4X3Bvd2VyICpwY21kOyAgaW50IGk7DQo+ID4g
Kw0KPiA+ICsgcGNtZCA9IChzdHJ1Y3QgaG9zdGNtZF9jbWRfODAyXzExX3R4X3Bvd2VyICopJnBy
aXYtPnBjbWRfYnVmWzBdOw0KPiANCj4gT2Z0ZW4gaGFwcGVucywgc28gSSBwcm9iYWJseSB3b24n
dCBjYXRjaCB0aGVtIGFsbCwgYnV0IHRoaXMgY291bGQgDQo+IGxpa2V3aXNlIGJlIGRvbmUgaW4g
b25lIGxpbmUuIFRoZW4gYWdhaW4uLi4gbGluZSBsZW5ndGggaXNzdWVzLCBzbyA1MC81MCBvbiB0
aGF0Lg0KPiANCj4NCg0KSSB3aWxsIG1vZGlmeSBpdC4NCg0KPiA+ICsNCj4gPiArc3RhdGljIHU4
IG13bF9md2NtZF9nZXRfODBtX3ByaV9jaG5sKHU4IGNoYW5uZWwpIHsNCj4gPiArIHU4IGFjdF9w
cmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8wOw0KPiA+ICsNCj4gPiArIHN3aXRjaCAoY2hhbm5l
bCkgew0KPiA+ICsgY2FzZSAzNjoNCj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hB
Tl8wOw0KPiA+ICsgYnJlYWs7DQo+ID4gKyBjYXNlIDQwOg0KPiA+ICsgYWN0X3ByaW1hcnkgPSBB
Q1RfUFJJTUFSWV9DSEFOXzE7DQo+ID4gKyBicmVhazsNCj4gPiArIGNhc2UgNDQ6DQo+ID4gKyBh
Y3RfcHJpbWFyeSA9IEFDVF9QUklNQVJZX0NIQU5fMjsNCj4gPiArIGJyZWFrOw0KPiA+ICsgY2Fz
ZSA0ODoNCj4gPiArIGFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8zOw0KPiA+ICsgYnJl
YWs7DQo+ID4gKyBjYXNlIDUyOg0KPiA+ICsgYWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFO
XzA7DQo+ID4gKyBicmVhazsNCj4gPiArIGNhc2UgNTY6DQo+ID4gKyBhY3RfcHJpbWFyeSA9IEFD
VF9QUklNQVJZX0NIQU5fMTsNCj4gPiArIGJyZWFrOw0KPiA+ICsgY2FzZSA2MDoNCj4gPiArIGFj
dF9wcmltYXJ5ID0gQUNUX1BSSU1BUllfQ0hBTl8yOw0KPiA+ICsgYnJlYWs7DQo+ID4gKyBjYXNl
IDY0Og0KPiA+ICsgYWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFOXzM7DQo+ID4gKyBicmVh
azsNCj4gPiArIGNhc2UgMTAwOg0KPiA+ICsgYWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFO
XzA7DQo+ID4gKyBicmVhazsNCj4gPiArIGNhc2UgMTA0Og0KPiA+ICsgYWN0X3ByaW1hcnkgPSBB
Q1RfUFJJTUFSWV9DSEFOXzE7DQo+ID4gKyBicmVhazsNCj4gPiArIGNhc2UgMTA4Og0KPiA+ICsg
YWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFOXzI7DQo+ID4gKyBicmVhazsNCj4gPiArIGNh
c2UgMTEyOg0KPiA+ICsgYWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFOXzM7DQo+ID4gKyBi
cmVhazsNCj4gPiArIGNhc2UgMTE2Og0KPiA+ICsgYWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9D
SEFOXzA7DQo+ID4gKyBicmVhazsNCj4gPiArIGNhc2UgMTIwOg0KPiA+ICsgYWN0X3ByaW1hcnkg
PSBBQ1RfUFJJTUFSWV9DSEFOXzE7DQo+ID4gKyBicmVhazsNCj4gPiArIGNhc2UgMTI0Og0KPiA+
ICsgYWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFOXzI7DQo+ID4gKyBicmVhazsNCj4gPiAr
IGNhc2UgMTI4Og0KPiA+ICsgYWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFOXzM7DQo+ID4g
KyBicmVhazsNCj4gPiArIGNhc2UgMTMyOg0KPiA+ICsgYWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFS
WV9DSEFOXzA7DQo+ID4gKyBicmVhazsNCj4gPiArIGNhc2UgMTM2Og0KPiA+ICsgYWN0X3ByaW1h
cnkgPSBBQ1RfUFJJTUFSWV9DSEFOXzE7DQo+ID4gKyBicmVhazsNCj4gPiArIGNhc2UgMTQwOg0K
PiA+ICsgYWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFOXzI7DQo+ID4gKyBicmVhazsNCj4g
PiArIGNhc2UgMTQ0Og0KPiA+ICsgYWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFOXzM7DQo+
ID4gKyBicmVhazsNCj4gPiArIGNhc2UgMTQ5Og0KPiA+ICsgYWN0X3ByaW1hcnkgPSBBQ1RfUFJJ
TUFSWV9DSEFOXzA7DQo+ID4gKyBicmVhazsNCj4gPiArIGNhc2UgMTUzOg0KPiA+ICsgYWN0X3By
aW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFOXzE7DQo+ID4gKyBicmVhazsNCj4gPiArIGNhc2UgMTU3
Og0KPiA+ICsgYWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFOXzI7DQo+ID4gKyBicmVhazsN
Cj4gPiArIGNhc2UgMTYxOg0KPiA+ICsgYWN0X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFOXzM7
DQo+ID4gKyBicmVhazsNCj4gPiArIH0NCj4gPiArDQo+ID4gKyByZXR1cm4gYWN0X3ByaW1hcnk7
DQo+ID4gK30NCj4gPiArDQo+IA0KPiBJZ25vcmFuY2Ugc3BlYWtpbmcgaGVyZSBwZXJoYXBzLCBi
dXQgdGhlIGFib3ZlIGxvb2tzIGxpa2Ugc29tZXRoaW5nIA0KPiB0aGF0IG5lYXJseSBldmVyeSBk
cml2ZXIgd291bGQgbmVlZCB0byBkZWFsIHdpdGguIElzbid0IHRoZXJlIGEgaGVscGVyIA0KPiBm
dW5jdGlvbiBpbg0KPiBtYWM4MDIxMSBvciBzb21lIGJldHRlciB3YXkgdG8gZG8gdGhpcyB0aGFu
IGV4cGxpY2l0bHkgZG9pbmcgYSBzd2l0Y2ggDQo+IGxvZ2ljIGxvb2t1cD8NCj4gDQoNClRoaXMg
aXMgdGhlIGZ1bmN0aW9uIG5lZWRlZCBmb3Igb3VyIGhvc3QgY29tbWFuZC4gVGhlcmUgaXMgbm8g
aGVscGVyIGZ1bmN0aW9uIGluIG1hYzgwMjExLiBJIHdpbGwgbW9kaWZ5IHRoZSBjb2RlLg0KDQo+
ID4gK2ludCBtd2xfZndjbWRfc2V0X25ld19zdG5fYWRkKHN0cnVjdCBpZWVlODAyMTFfaHcgKmh3
LA0KPiA+ICsgICAgICBzdHJ1Y3QgaWVlZTgwMjExX3ZpZiAqdmlmLA0KPiA+ICsgICAgICBzdHJ1
Y3QgaWVlZTgwMjExX3N0YSAqc3RhKQ0KPiA+ICt7DQo+ID4gKyBzdHJ1Y3QgbXdsX3ByaXYgKnBy
aXYgPSBody0+cHJpdjsNCj4gPiArIHN0cnVjdCBtd2xfdmlmICptd2xfdmlmOw0KPiA+ICsgc3Ry
dWN0IGhvc3RjbWRfY21kX3NldF9uZXdfc3RuICpwY21kOw0KPiA+ICsgdTMyIHJhdGVzOw0KPiA+
ICsNCj4gPiArIG13bF92aWYgPSBtd2xfZGV2X2dldF92aWYodmlmKTsNCj4gPiArDQo+ID4gKyBw
Y21kID0gKHN0cnVjdCBob3N0Y21kX2NtZF9zZXRfbmV3X3N0biAqKSZwcml2LT5wY21kX2J1Zlsw
XTsNCj4gPiArDQo+ID4gKyBtdXRleF9sb2NrKCZwcml2LT5md2NtZF9tdXRleCk7DQo+ID4gKw0K
PiA+ICsgbWVtc2V0KHBjbWQsIDB4MDAsIHNpemVvZigqcGNtZCkpOw0KPiA+ICsgcGNtZC0+Y21k
X2hkci5jbWQgPSBjcHVfdG9fbGUxNihIT1NUQ01EX0NNRF9TRVRfTkVXX1NUTik7DQo+ID4gKyBw
Y21kLT5jbWRfaGRyLmxlbiA9IGNwdV90b19sZTE2KHNpemVvZigqcGNtZCkpOyBjbWRfaGRyLm1h
Y2lkID0gDQo+ID4gKyBwY21kLT5td2xfdmlmLT5tYWNpZDsNCj4gPiArDQo+ID4gKyBwY21kLT5h
Y3Rpb24gPSBjcHVfdG9fbGUxNihIT1NUQ01EX0FDVF9TVEFfQUNUSU9OX0FERCk7DQo+ID4gKyBp
ZiAodmlmLT50eXBlID09IE5MODAyMTFfSUZUWVBFX1NUQVRJT04pIHsNCj4gPiArIHBjbWQtPmFp
ZCA9IDA7DQo+ID4gKyBwY21kLT5zdG5faWQgPSAwOw0KPiA+ICsgfSBlbHNlIHsNCj4gPiArIHBj
bWQtPmFpZCA9IGNwdV90b19sZTE2KHN0YS0+YWlkKTsgc3RuX2lkID0gY3B1X3RvX2xlMTYoc3Rh
LT5haWQpOw0KPiA+ICsgfQ0KPiA+ICsgZXRoZXJfYWRkcl9jb3B5KHBjbWQtPm1hY19hZGRyLCBz
dGEtPmFkZHIpOw0KPiA+ICsNCj4gPiArIGlmIChody0+Y29uZi5jaGFuZGVmLmNoYW4tPmJhbmQg
PT0gTkw4MDIxMV9CQU5EXzJHSFopIHJhdGVzID0NCj4gPiArIHN0YS0+c3VwcF9yYXRlc1tOTDgw
MjExX0JBTkRfMkdIWl07DQo+ID4gKyBlbHNlDQo+ID4gKyByYXRlcyA9IHN0YS0+c3VwcF9yYXRl
c1tOTDgwMjExX0JBTkRfNUdIWl0gPDwgNTsNCj4gPiArIHBjbWQtPnBlZXJfaW5mby5sZWdhY3lf
cmF0ZV9iaXRtYXAgPSBjcHVfdG9fbGUzMihyYXRlcyk7DQo+ID4gKw0KPiA+ICsgaWYgKHN0YS0+
aHRfY2FwLmh0X3N1cHBvcnRlZCkgew0KPiA+ICsgcGNtZC0+cGVlcl9pbmZvLmh0X3JhdGVzWzBd
ID0gc3RhLT5odF9jYXAubWNzLnJ4X21hc2tbMF07IA0KPiA+ICsgcGNtZC0+cGVlcl9pbmZvLmh0
X3JhdGVzWzFdID0gc3RhLT5odF9jYXAubWNzLnJ4X21hc2tbMV07IA0KPiA+ICsgcGNtZC0+cGVl
cl9pbmZvLmh0X3JhdGVzWzJdID0gc3RhLT5odF9jYXAubWNzLnJ4X21hc2tbMl07IA0KPiA+ICsg
cGNtZC0+cGVlcl9pbmZvLmh0X3JhdGVzWzNdID0gc3RhLT5odF9jYXAubWNzLnJ4X21hc2tbM107
IA0KPiA+ICsgcGNtZC0+cGVlcl9pbmZvLmh0X2NhcF9pbmZvID0gY3B1X3RvX2xlMTYoc3RhLT5o
dF9jYXAuY2FwKTsgDQo+ID4gKyBwY21kLT5wZWVyX2luZm8ubWFjX2h0X3BhcmFtX2luZm8gPQ0K
PiA+ICsgKHN0YS0+aHRfY2FwLmFtcGR1X2ZhY3RvciAmIDMpIHwNCj4gPiArICgoc3RhLT5odF9j
YXAuYW1wZHVfZGVuc2l0eSAmIDcpIDw8IDIpOyB9DQo+ID4gKw0KPiA+ICsgaWYgKHN0YS0+dmh0
X2NhcC52aHRfc3VwcG9ydGVkKSB7DQo+ID4gKyBwY21kLT5wZWVyX2luZm8udmh0X21heF9yeF9t
Y3MgPQ0KPiA+ICsgY3B1X3RvX2xlMzIoKigodTMyICopDQo+ID4gKyAmc3RhLT52aHRfY2FwLnZo
dF9tY3MucnhfbWNzX21hcCkpOw0KPiA+ICsgcGNtZC0+cGVlcl9pbmZvLnZodF9jYXAgPSBjcHVf
dG9fbGUzMihzdGEtPnZodF9jYXAuY2FwKTsgDQo+ID4gKyBwY21kLT5wZWVyX2luZm8udmh0X3J4
X2NoYW5uZWxfd2lkdGggPSBzdGEtPmJhbmR3aWR0aDsNCj4gPiArIH0NCj4gPiArDQo+ID4gKyBw
Y21kLT5pc19xb3Nfc3RhID0gc3RhLT53bWU7DQo+ID4gKyBwY21kLT5xb3NfaW5mbyA9ICgoc3Rh
LT51YXBzZF9xdWV1ZXMgPDwgNCkgfCAoc3RhLT5tYXhfc3AgPDwgMSkpOw0KPiA+ICsNCj4gPiAr
IGlmIChtd2xfZndjbWRfZXhlY19jbWQocHJpdiwgSE9TVENNRF9DTURfU0VUX05FV19TVE4pKSB7
IA0KPiA+ICsgbXV0ZXhfdW5sb2NrKCZwcml2LT5md2NtZF9tdXRleCk7IHdpcGh5X2Vycihody0+
d2lwaHksICJmYWlsZWQgDQo+ID4gKyBleGVjdXRpb25cbiIpOyByZXR1cm4gLUVJTzsgfQ0KPiA+
ICsNCj4gPiArIGlmICh2aWYtPnR5cGUgPT0gTkw4MDIxMV9JRlRZUEVfU1RBVElPTikgeyANCj4g
PiArIGV0aGVyX2FkZHJfY29weShwY21kLT5tYWNfYWRkciwgbXdsX3ZpZi0+c3RhX21hYyk7DQo+
ID4gKw0KPiA+ICsgaWYgKG13bF9md2NtZF9leGVjX2NtZChwcml2LCBIT1NUQ01EX0NNRF9TRVRf
TkVXX1NUTikpIHsgDQo+ID4gKyBtdXRleF91bmxvY2soJnByaXYtPmZ3Y21kX211dGV4KTsgd2lw
aHlfZXJyKGh3LT53aXBoeSwgImZhaWxlZCANCj4gPiArIGV4ZWN1dGlvblxuIik7IHJldHVybiAt
RUlPOyB9IH0NCj4gDQo+IE9LLCB0aW1lIHRvIGFzay4gV2h5IGFyZSB3ZSB0ZWxsaW5nIHRoZSBm
aXJtd2FyZSB0aGF0IHdlJ3JlIGNvbm5lY3RlZCANCj4gdG8gb3Vyc2VsdmVzPyBJIHdvdWxkIHBy
ZXN1bWUgdGhlIGZpcm13YXJlIGFscmVhZHkga25vd3Mgb3VyIE1BQyBhZGRyZXNzLg0KPiANCj4g
SSBub3RpY2VkIHRoaXMgb3JpZ2luYWxseSBiZWNhdXNlIHRoZXJlJ3MgYSBuYXN0eSBidWcgd2l0
aCByZWN5Y2xpbmcgDQo+IHRoZSBjb21tYW5kIGJ1ZmZlciAoZm9yIHNkaW8sIGl0J3Mgbm90IHJl
bGV2YW50IGZvciB0aGlzIGRyaXZlcikgYW5kIA0KPiBpbiBkb2luZyBleHBlcmltZW50cyBJIG5v
dGljZWQgdGhhdCB0aHJvdWdocHV0IHNpZ25pZmljYW50bHkgaW5jcmVhc2VzIA0KPiBpbiBTVEEg
bW9kZSBpZiB3ZSBqdXN0IGxlYXZlIG91dCB0aGUgZW50aXJlIGNsYXVzZS4NCj4gDQo+IEluIGJy
aWVmbHkgZXhhbWluaW5nIHRoZSBmaXJtd2FyZSBzb3VyY2UgSSBzZWUgbm8gcmVhc29uIHRvIGRv
IHRoaXMsIA0KPiBidXQgdGhlcmUncyBhIGhpZGRlbiBjaHVuayBhbmQgSSBkb24ndCBrbm93IHdo
YXQgdGhlIGhhcmR3YXJlIGl0c2VsZiANCj4gZG9lcyB3aXRoIHRoZSBNQUMgdGFibGUuDQo+IA0K
PiBTbywgd2h5IGlzIGl0IG5lY2Vzc2FyeT8NCj4gDQoNCkkgd2lsbCBjaGVjay4NCg0KPiANCj4g
PiAraW50IG13bF9md2NtZF9zZXRfbmV3X3N0bl9kZWwoc3RydWN0IGllZWU4MDIxMV9odyAqaHcs
DQo+ID4gKyAgICAgIHN0cnVjdCBpZWVlODAyMTFfdmlmICp2aWYsIHU4ICphZGRyKSB7ICBzdHJ1
Y3QgbXdsX3ByaXYgKnByaXYgDQo+ID4gKz0NCj4gPiAraHctPnByaXY7ICBzdHJ1Y3QgbXdsX3Zp
ZiAqbXdsX3ZpZjsgIHN0cnVjdCBob3N0Y21kX2NtZF9zZXRfbmV3X3N0bg0KPiA+ICsqcGNtZDsN
Cj4gPiArDQo+ID4gKyBtd2xfdmlmID0gbXdsX2Rldl9nZXRfdmlmKHZpZik7DQo+ID4gKw0KPiA+
ICsgcGNtZCA9IChzdHJ1Y3QgaG9zdGNtZF9jbWRfc2V0X25ld19zdG4gKikmcHJpdi0+cGNtZF9i
dWZbMF07DQo+ID4gKw0KPiA+ICsgbXV0ZXhfbG9jaygmcHJpdi0+ZndjbWRfbXV0ZXgpOw0KPiA+
ICsNCj4gPiArIG1lbXNldChwY21kLCAweDAwLCBzaXplb2YoKnBjbWQpKTsNCj4gPiArIHBjbWQt
PmNtZF9oZHIuY21kID0gY3B1X3RvX2xlMTYoSE9TVENNRF9DTURfU0VUX05FV19TVE4pOw0KPiA+
ICsgcGNtZC0+Y21kX2hkci5sZW4gPSBjcHVfdG9fbGUxNihzaXplb2YoKnBjbWQpKTsgY21kX2hk
ci5tYWNpZCA9IA0KPiA+ICsgcGNtZC0+bXdsX3ZpZi0+bWFjaWQ7DQo+ID4gKw0KPiA+ICsgcGNt
ZC0+YWN0aW9uID0gY3B1X3RvX2xlMTYoSE9TVENNRF9BQ1RfU1RBX0FDVElPTl9SRU1PVkUpOw0K
PiA+ICsgZXRoZXJfYWRkcl9jb3B5KHBjbWQtPm1hY19hZGRyLCBhZGRyKTsNCj4gPiArDQo+ID4g
KyBpZiAobXdsX2Z3Y21kX2V4ZWNfY21kKHByaXYsIEhPU1RDTURfQ01EX1NFVF9ORVdfU1ROKSkg
eyANCj4gPiArIG11dGV4X3VubG9jaygmcHJpdi0+ZndjbWRfbXV0ZXgpOyB3aXBoeV9lcnIoaHct
PndpcGh5LCAiZmFpbGVkIA0KPiA+ICsgZXhlY3V0aW9uXG4iKTsgcmV0dXJuIC1FSU87IH0NCj4g
PiArDQo+ID4gKyBpZiAodmlmLT50eXBlID09IE5MODAyMTFfSUZUWVBFX1NUQVRJT04pIHsgDQo+
ID4gKyBldGhlcl9hZGRyX2NvcHkocGNtZC0+bWFjX2FkZHIsIG13bF92aWYtPnN0YV9tYWMpOw0K
PiA+ICsNCj4gPiArIGlmIChtd2xfZndjbWRfZXhlY19jbWQocHJpdiwgSE9TVENNRF9DTURfU0VU
X05FV19TVE4pKSB7IA0KPiA+ICsgbXV0ZXhfdW5sb2NrKCZwcml2LT5md2NtZF9tdXRleCk7IHdp
cGh5X2Vycihody0+d2lwaHksICJmYWlsZWQgDQo+ID4gKyBleGVjdXRpb25cbiIpOyByZXR1cm4g
LUVJTzsgfSB9DQo+ID4gKw0KPiANCj4gRGl0dG8uDQo+IA0KPiA+IGRpZmYgLS1naXQgYS9kcml2
ZXJzL25ldC93aXJlbGVzcy9tYXJ2ZWxsL213bHdpZmkvZndkbC5jDQo+ID4gYi9kcml2ZXJzL25l
dC93aXJlbGVzcy9tYXJ2ZWxsL213bHdpZmkvZndkbC5jDQo+ID4gbmV3IGZpbGUgbW9kZSAxMDA2
NDQNCj4gPiBpbmRleCAwMDAwMDAwLi5mNGQ1ZmExDQo+ID4gLS0tIC9kZXYvbnVsbA0KPiA+ICsr
KyBiL2RyaXZlcnMvbmV0L3dpcmVsZXNzL21hcnZlbGwvbXdsd2lmaS9md2RsLmMNCj4gLi4uDQo+
ID4gKw0KPiA+ICtzdGF0aWMgdm9pZCBtd2xfZndkbF90cmlnX3BjaWNtZChzdHJ1Y3QgbXdsX3By
aXYgKnByaXYpIHsgDQo+ID4gK3dyaXRlbChwcml2LT5wcGh5c19jbWRfYnVmLCBwcml2LT5pb2Jh
c2UxICsgTUFDUkVHX1JFR19HRU5fUFRSKTsNCj4gPiArDQo+ID4gKyB3cml0ZWwoMHgwMCwgcHJp
di0+aW9iYXNlMSArIE1BQ1JFR19SRUdfSU5UX0NPREUpOw0KPiA+ICsNCj4gPiArIHdyaXRlbChN
QUNSRUdfSDJBUklDX0JJVF9ET09SX0JFTEwsDQo+ID4gKyAgICAgICBwcml2LT5pb2Jhc2UxICsg
TUFDUkVHX1JFR19IMkFfSU5URVJSVVBUX0VWRU5UUyk7IH0NCj4gPiArDQo+ID4gK3N0YXRpYyB2
b2lkIG13bF9md2RsX3RyaWdfcGNpY21kX2Jvb3Rjb2RlKHN0cnVjdCBtd2xfcHJpdiAqcHJpdikg
eyANCj4gPiArd3JpdGVsKHByaXYtPnBwaHlzX2NtZF9idWYsIHByaXYtPmlvYmFzZTEgKyBNQUNS
RUdfUkVHX0dFTl9QVFIpOw0KPiA+ICsNCj4gPiArIHdyaXRlbCgweDAwLCBwcml2LT5pb2Jhc2Ux
ICsgTUFDUkVHX1JFR19JTlRfQ09ERSk7DQo+ID4gKw0KPiA+ICsgd3JpdGVsKE1BQ1JFR19IMkFS
SUNfQklUX0RPT1JfQkVMTCwNCj4gPiArICAgICAgIHByaXYtPmlvYmFzZTEgKyBNQUNSRUdfUkVH
X0gyQV9JTlRFUlJVUFRfRVZFTlRTKTsgfQ0KPiA+ICsNCj4gDQo+IFVubGVzcyBJJ20gbWlzdGFr
ZW4gdGhlIGFib3ZlIHR3byBmdW5jdGlvbnMgYXJlIDEwMCUgaWRlbnRpY2FsLiBJbiBteSANCj4g
dmVyc2lvbiBJIGNvbGxhcHNlZCB0aGVtIHRvIGEgc2luZ2xlIG9uZSBhbmQgaXQgd29ya3MgZmlu
ZS4NCj4gDQoNCkkgd2lsbCBjaGVjayBhbmQgbW9kaWZ5IGl0Lg0KDQo+ID4gK2ludCBtd2xfZndk
bF9kb3dubG9hZF9maXJtd2FyZShzdHJ1Y3QgaWVlZTgwMjExX2h3ICpodykgew0KPiAuLi4NCj4g
PiArDQo+ID4gKyB3aGlsZSAoc2l6ZV9md19kb3dubG9hZGVkIDwgZnctPnNpemUpIHsgbGVuID0g
cmVhZGwocHJpdi0+aW9iYXNlMSANCj4gPiArICsgMHhjNDApOw0KPiA+ICsNCj4gPiArIGlmICgh
bGVuKQ0KPiA+ICsgYnJlYWs7DQo+ID4gKw0KPiA+ICsgLyogdGhpcyBjb3BpZXMgdGhlIG5leHQg
Y2h1bmsgb2YgZncgYmluYXJ5IHRvIGJlIGRlbGl2ZXJlZCAqLyANCj4gPiArIG1lbWNweSgoY2hh
ciAqKSZwcml2LT5wY21kX2J1ZlswXSwNCj4gPiArICAgICAgIChmdy0+ZGF0YSArIHNpemVfZndf
ZG93bmxvYWRlZCksIGxlbik7DQo+ID4gKw0KPiA+ICsgLyogdGhpcyBmdW5jdGlvbiB3cml0ZXMg
cGRhdGEgdG8gYzEwLCB0aGVuIHdyaXRlIDIgdG8gYzE4ICovIA0KPiA+ICsgbXdsX2Z3ZGxfdHJp
Z19wY2ljbWRfYm9vdGNvZGUocHJpdik7DQo+ID4gKw0KPiA+ICsgLyogdGhpcyBpcyBhcmJpdHJh
cnkgcGVyIHlvdXIgcGxhdGZvcm07IHdlIHVzZSAweGZmZmYgKi8gDQo+ID4gKyBjdXJyX2l0ZXJh
dGlvbiA9IEZXX01BWF9OVU1fQ0hFQ0tTOw0KPiA+ICsNCj4gPiArIC8qIE5PVEU6IHRoZSBmb2xs
b3dpbmcgYmFjayB0byBiYWNrIGNoZWNrcyBvbiBDMUMgaXMgdGltZQ0KPiA+ICsgKiBzZW5zaXRp
dmUsIGhlbmNlIG1heSBuZWVkIHRvIGJlIHR3ZWFrZWQgZGVwZW5kZW50IG9uIGhvc3QNCj4gPiAr
ICogcHJvY2Vzc29yLiBUaW1lIGZvciBTQzIgdG8gZ28gZnJvbSB0aGUgd3JpdGUgb2YgZXZlbnQg
MiB0bw0KPiA+ICsgKiBDMUMgPT0gMiBpcyB+MTMwMCBuU2VjLiBIZW5jZSB0aGUgY2hlY2tpbmdz
IG9uIGhvc3QgaGFzIHRvDQo+ID4gKyAqIGNvbnNpZGVyIGhvdyBlZmZpY2llbnQgeW91ciBjb2Rl
IGNhbiBiZSB0byBtZWV0IHRoaXMgdGltaW5nLA0KPiA+ICsgKiBvciB5b3UgY2FuIGFsdGVybmF0
aXZlbHkgdHdlYWsgdGhpcyByb3V0aW5lcyB0byBmaXQgeW91cg0KPiA+ICsgKiBwbGF0Zm9ybQ0K
PiA+ICsgKi8NCj4gPiArIGRvIHsNCj4gPiArIGludF9jb2RlID0gcmVhZGwocHJpdi0+aW9iYXNl
MSArIDB4YzFjKTsgaWYgKGludF9jb2RlICE9IDApIGJyZWFrOyANCj4gPiArIGNvbmRfcmVzY2hl
ZCgpOyBjdXJyX2l0ZXJhdGlvbi0tOyB9IHdoaWxlIChjdXJyX2l0ZXJhdGlvbik7DQo+ID4gKw0K
PiANCj4gVGhlcmUncyBzb21ldGhpbmcgZmlzaHkgd2l0aCB0aGUgYWJvdmUuIEhhdmluZyB0byAi
dHdlYWsiIGRyaXZlciANCj4gdGltaW5nIGJhc2VkIG9uIHBsYXRmb3JtIGlzIGEgaHVnZSByZWQg
ZmxhZy4gVGhlcmUncyBnb3QgdG8gYmUgDQo+IHNvbWV0aGluZyBiZXR0ZXIgdGhhbiB0aGUgYWJv
dmUuDQo+IA0KDQpJdCB3aWxsIHJlbGVhc2UgQ1BVLg0KDQo+IA0KPiA+IGRpZmYgLS1naXQgYS9k
cml2ZXJzL25ldC93aXJlbGVzcy9tYXJ2ZWxsL213bHdpZmkvbWFjODAyMTEuYw0KPiA+IGIvZHJp
dmVycy9uZXQvd2lyZWxlc3MvbWFydmVsbC9td2x3aWZpL21hYzgwMjExLmMNCj4gLi4uDQo+ID4g
K3N0YXRpYyBpbnQgbXdsX21hYzgwMjExX2NvbmZpZyhzdHJ1Y3QgaWVlZTgwMjExX2h3ICpodywN
Cj4gPiArICAgICAgIHUzMiBjaGFuZ2VkKQ0KPiA+ICt7DQo+ID4gKyBzdHJ1Y3QgaWVlZTgwMjEx
X2NvbmYgKmNvbmYgPSAmaHctPmNvbmY7ICBpbnQgcmM7DQo+ID4gKw0KPiA+ICsgd2lwaHlfZGVi
dWcoaHctPndpcGh5LCAiY2hhbmdlOiAweCV4XG4iLCBjaGFuZ2VkKTsNCj4gPiArDQo+ID4gKyBp
ZiAoY29uZi0+ZmxhZ3MgJiBJRUVFODAyMTFfQ09ORl9JRExFKSByYyA9IA0KPiA+ICsgbXdsX2Z3
Y21kX3JhZGlvX2Rpc2FibGUoaHcpOyBlbHNlIHJjID0gbXdsX2Z3Y21kX3JhZGlvX2VuYWJsZSho
dyk7DQo+ID4gKw0KPiA+ICsgaWYgKHJjKQ0KPiA+ICsgZ290byBvdXQ7DQo+ID4gKw0KPiA+ICsg
aWYgKGNoYW5nZWQgJiBJRUVFODAyMTFfQ09ORl9DSEFOR0VfQ0hBTk5FTCkgeyBpbnQgcmF0ZSA9
IDA7DQo+ID4gKw0KPiA+ICsgaWYgKGNvbmYtPmNoYW5kZWYuY2hhbi0+YmFuZCA9PSBOTDgwMjEx
X0JBTkRfMkdIWikgew0KPiANCj4gQ2F1c2VzIGNvbXBpbGUgd2FybmluZy4gU2hvdWxkIGJlIElF
RUU4MDIxMV9CQU5EXzJHSFouDQo+IA0KDQpObywgeW91IG5lZWQgdG8gdXNlIHVwZGF0ZWQgbWFj
ODAyMTEuDQoNCj4gDQo+ID4gKyBtd2xfZndjbWRfc2V0X2FwbW9kZShodywgQVBfTU9ERV8yXzRH
SFpfMTFBQ19NSVhFRCk7IA0KPiA+ICsgbXdsX2Z3Y21kX3NldF9saW5rYWRhcHRfY3NfbW9kZSho
dywNCj4gPiArIExJTktfQ1NfU1RBVEVfQ09OU0VSVik7DQo+ID4gKyByYXRlID0gbXdsX3JhdGVz
XzI0WzBdLmh3X3ZhbHVlOw0KPiA+ICsgfSBlbHNlIGlmIChjb25mLT5jaGFuZGVmLmNoYW4tPmJh
bmQgPT0gTkw4MDIxMV9CQU5EXzVHSFopIHsNCj4gDQo+IENhdXNlcyBjb21waWxlIHdhcm5pbmcu
IFNob3VsZCBiZSBJRUVFODAyMTFfQkFORF81R0haLg0KPiANCj4gPiArc3RhdGljIHZvaWQgbXds
X21hYzgwMjExX2Jzc19pbmZvX2NoYW5nZWRfYXAoc3RydWN0IGllZWU4MDIxMV9odw0KPiAqaHcs
DQo+ID4gKyAgICAgc3RydWN0IGllZWU4MDIxMV92aWYgKnZpZiwNCj4gPiArICAgICBzdHJ1Y3Qg
aWVlZTgwMjExX2Jzc19jb25mICppbmZvLA0KPiA+ICsgICAgIHUzMiBjaGFuZ2VkKQ0KPiA+ICt7
DQo+ID4gKyBpZiAoY2hhbmdlZCAmIEJTU19DSEFOR0VEX0VSUF9QUkVBTUJMRSkgDQo+ID4gK213
bF9md2NtZF9zZXRfcmFkaW9fcHJlYW1ibGUoaHcsDQo+ID4gKyAgICAgdmlmLT5ic3NfY29uZi51
c2Vfc2hvcnRfcHJlYW1ibGUpOw0KPiA+ICsNCj4gPiArIGlmIChjaGFuZ2VkICYgQlNTX0NIQU5H
RURfQkFTSUNfUkFURVMpIHsgaW50IGlkeDsgaW50IHJhdGU7DQo+ID4gKw0KPiA+ICsgLyogVXNl
IGxvd2VzdCBzdXBwb3J0ZWQgYmFzaWMgcmF0ZSBmb3IgbXVsdGljYXN0cw0KPiA+ICsgKiBhbmQg
bWFuYWdlbWVudCBmcmFtZXMgKHN1Y2ggYXMgcHJvYmUgcmVzcG9uc2VzIC0tDQo+ID4gKyAqIGJl
YWNvbnMgd2lsbCBhbHdheXMgZ28gb3V0IGF0IDEgTWIvcykuDQo+ID4gKyAqLw0KPiA+ICsgaWR4
ID0gZmZzKHZpZi0+YnNzX2NvbmYuYmFzaWNfcmF0ZXMpOyBpZiAoaWR4KSBpZHgtLTsNCj4gPiAr
DQo+ID4gKyBpZiAoaHctPmNvbmYuY2hhbmRlZi5jaGFuLT5iYW5kID09IE5MODAyMTFfQkFORF8y
R0haKQ0KPiANCj4gQ2F1c2VzIGNvbXBpbGUgd2FybmluZy4gU2hvdWxkIGJlIElFRUU4MDIxMV9C
QU5EXzJHSFouDQo+IA0KDQpObywgeW91IG5lZWQgdG8gdXNlIHVwZGF0ZWQgbWFjODAyMTEuDQoN
Cj4gDQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvbmV0L3dpcmVsZXNzL21hcnZlbGwvbXdsd2lm
aS9tYWluLmMNCj4gPiBiL2RyaXZlcnMvbmV0L3dpcmVsZXNzL21hcnZlbGwvbXdsd2lmaS9tYWlu
LmMNCj4gPiBuZXcgZmlsZSBtb2RlIDEwMDY0NA0KPiAuLi4NCj4gPiArI2luY2x1ZGUgPGxpbnV4
L21vZHVsZS5oPg0KPiA+ICsjaWZkZWYgQ09ORklHX09GDQo+ID4gKyNpbmNsdWRlIDxsaW51eC9v
Zi5oPg0KPiA+ICsjZW5kaWYNCj4gDQo+IElzbid0IG9mLmggaW50ZXJuYWxseSBndWFyZGVkPw0K
PiANCg0KWWVzLiBJdCBpcyBtZW50aW9uZWQgYnkgSm9oYW5uZXMsIEkgd2lsbCByZW1vdmUgaXQu
DQoNCj4gPiArDQo+ID4gKyNpbmNsdWRlICJzeXNhZHB0LmgiDQo+ID4gKyNpbmNsdWRlICJkZXYu
aCINCj4gPiArI2luY2x1ZGUgImZ3ZGwuaCINCj4gPiArI2luY2x1ZGUgImZ3Y21kLmgiDQo+ID4g
KyNpbmNsdWRlICJ0eC5oIg0KPiA+ICsjaW5jbHVkZSAicnguaCINCj4gPiArI2luY2x1ZGUgImlz
ci5oIg0KPiA+ICsjaW5jbHVkZSAidGhlcm1hbC5oIg0KPiA+ICsjaWZkZWYgQ09ORklHX0RFQlVH
X0ZTDQo+ID4gKyNpbmNsdWRlICJkZWJ1Z2ZzLmgiDQo+ID4gKyNlbmRpZg0KPiANCj4gWW91ciBk
ZWJ1Z2ZzLmggc2hvdWxkIGJlIGludGVybmFsbHkgZ3VhcmRlZC4gTW9yZSBsYXRlci4uLg0KPiAN
Cg0KU2FtZSBhcyBhYm92ZS4NCg0KPiA+ICsNCj4gPiArI2RlZmluZSBNV0xfREVTQyAgICAgICAg
ICJNYXJ2ZWxsIDgwMi4xMWFjIFdpcmVsZXNzIE5ldHdvcmsgRHJpdmVyIg0KPiA+ICsjZGVmaW5l
IE1XTF9ERVZfTkFNRSAgICAgIk1hcnZlbGwgODAyLjExYWMgQWRhcHRlciINCj4gPiArDQo+ID4g
KyNkZWZpbmUgRklMRV9QQVRIX0xFTiAgICA2NA0KPiA+ICsjZGVmaW5lIENNRF9CVUZfU0laRSAg
ICAgMHg0MDAwDQo+ID4gKw0KPiA+ICtzdGF0aWMgc3RydWN0IHBjaV9kZXZpY2VfaWQgbXdsX3Bj
aV9pZF90YmxbXSA9IHsgIHsgDQo+ID4gK1BDSV9WREVWSUNFKE1BUlZFTEwsIDB4MmE1NSksIC5k
cml2ZXJfZGF0YSA9IE1XTDg4NjQsIH0sICB7IA0KPiA+ICtQQ0lfVkRFVklDRShNQVJWRUxMLCAw
eDJiMzgpLCAuZHJpdmVyX2RhdGEgPSBNV0w4ODk3LCB9LCAgeyB9LCB9Ow0KPiA+ICsNCj4gPiAr
c3RhdGljIHN0cnVjdCBtd2xfY2hpcF9pbmZvIG13bF9jaGlwX3RibFtdID0geyAgW01XTDg4NjRd
ID0geyANCj4gPiArLnBhcnRfbmFtZSA9ICI4OFc4ODY0IiwgIC5md19pbWFnZSA9ICJtd2x3aWZp
Lzg4Vzg4NjQuYmluIiwgDQo+ID4gKy5hbnRlbm5hX3R4ID0gQU5URU5OQV9UWF80X0FVVE8sICAu
YW50ZW5uYV9yeCA9DQo+IEFOVEVOTkFfUlhfNF9BVVRPLA0KPiA+ICt9LCAgW01XTDg4OTddID0g
eyAgLnBhcnRfbmFtZSA9ICI4OFc4ODk3IiwgIC5md19pbWFnZSA9IA0KPiA+ICsibXdsd2lmaS84
OFc4ODk3LmJpbiIsICAuYW50ZW5uYV90eCA9IEFOVEVOTkFfVFhfMiwgIC5hbnRlbm5hX3J4ID0g
DQo+ID4gK0FOVEVOTkFfUlhfMiwgIH0sIH07DQo+ID4gKw0KPiA+ICtzdGF0aWMgY29uc3Qgc3Ry
dWN0IGllZWU4MDIxMV9jaGFubmVsIG13bF9jaGFubmVsc18yNFtdID0geyAgeyANCj4gPiArLmJh
bmQgPSBOTDgwMjExX0JBTkRfMkdIWiwgLmNlbnRlcl9mcmVxID0gMjQxMiwgLmh3X3ZhbHVlID0g
MSwgfSwgIA0KPiA+ICt7IC5iYW5kID0gTkw4MDIxMV9CQU5EXzJHSFosIC5jZW50ZXJfZnJlcSA9
IDI0MTcsIC5od192YWx1ZSA9IDIsIH0sICANCj4gPiAreyAuYmFuZCA9IE5MODAyMTFfQkFORF8y
R0haLCAuY2VudGVyX2ZyZXEgPSAyNDIyLCAuaHdfdmFsdWUgPSAzLCB9LCAgDQo+ID4gK3sgLmJh
bmQgPSBOTDgwMjExX0JBTkRfMkdIWiwgLmNlbnRlcl9mcmVxID0gMjQyNywgLmh3X3ZhbHVlID0g
NCwgfSwgIA0KPiA+ICt7IC5iYW5kID0gTkw4MDIxMV9CQU5EXzJHSFosIC5jZW50ZXJfZnJlcSA9
IDI0MzIsIC5od192YWx1ZSA9IDUsIH0sICANCj4gPiAreyAuYmFuZCA9IE5MODAyMTFfQkFORF8y
R0haLCAuY2VudGVyX2ZyZXEgPSAyNDM3LCAuaHdfdmFsdWUgPSA2LCB9LCAgDQo+ID4gK3sgLmJh
bmQgPSBOTDgwMjExX0JBTkRfMkdIWiwgLmNlbnRlcl9mcmVxID0gMjQ0MiwgLmh3X3ZhbHVlID0g
NywgfSwgIA0KPiA+ICt7IC5iYW5kID0gTkw4MDIxMV9CQU5EXzJHSFosIC5jZW50ZXJfZnJlcSA9
IDI0NDcsIC5od192YWx1ZSA9IDgsIH0sICANCj4gPiAreyAuYmFuZCA9IE5MODAyMTFfQkFORF8y
R0haLCAuY2VudGVyX2ZyZXEgPSAyNDUyLCAuaHdfdmFsdWUgPSA5LCB9LCAgDQo+ID4gK3sgLmJh
bmQgPSBOTDgwMjExX0JBTkRfMkdIWiwgLmNlbnRlcl9mcmVxID0gMjQ1NywgLmh3X3ZhbHVlID0g
MTAsIA0KPiA+ICt9LCAgeyAuYmFuZCA9IE5MODAyMTFfQkFORF8yR0haLCAuY2VudGVyX2ZyZXEg
PSAyNDYyLCAuaHdfdmFsdWUgPSANCj4gPiArMTEsIH0sICB7IC5iYW5kID0gTkw4MDIxMV9CQU5E
XzJHSFosIC5jZW50ZXJfZnJlcSA9IDI0NjcsIC5od192YWx1ZSANCj4gPiArPSAxMiwgfSwgIHsg
LmJhbmQgPSBOTDgwMjExX0JBTkRfMkdIWiwgLmNlbnRlcl9mcmVxID0gMjQ3MiwgDQo+ID4gKy5o
d192YWx1ZSA9IDEzLCB9LCAgeyAuYmFuZCA9IE5MODAyMTFfQkFORF8yR0haLCAuY2VudGVyX2Zy
ZXEgPSANCj4gPiArMjQ4NCwgLmh3X3ZhbHVlID0gMTQsIH0sIH07DQo+ID4gKw0KPiANCj4gU28s
IGludGVyZXN0aW5nIHRoaW5nLi4uIHRoZXJlJ3MgNjIgdXNlcyBvZiBOTDgwMjExX0JBTkRfeCBp
biB0aGlzIA0KPiBkcml2ZXIsIGJ1dCBvbmx5IHRoZSBmZXcgc3BvdHMgSSBtZW50aW9uZWQgY2F1
c2Ugd2FybmluZ3MuIEkgbm90aWNlIGluIA0KPiB0aGUgbW9zdCByZWNlbnQgaW50ZXJuYWwgZHJv
cCB5b3UndmUgY2hhbmdlZCB0aGUgYWJvdmUgdG8gDQo+IElFRUU4MDIxMV9CQU5EXzJHSFouIEkg
d29uZGVyIGlmIHRoYXQgaXMgd2hhdCBzaG91bGQgYmUgZG9uZSBldmVyeXdoZXJlPw0KPiANCg0K
UGxlYXNlIHVzZSB1cGRhdGVkIG1hYzgwMjExLg0KDQo+ID4gK3N0YXRpYyBjb25zdCBzdHJ1Y3Qg
aWVlZTgwMjExX3JhdGUgbXdsX3JhdGVzXzI0W10gPSB7ICB7IC5iaXRyYXRlID0gDQo+ID4gKzEw
LCAuaHdfdmFsdWUgPSAyLCB9LCAgeyAuYml0cmF0ZSA9IDIwLCAuaHdfdmFsdWUgPSA0LCB9LCAg
eyANCj4gPiArLmJpdHJhdGUgPSA1NSwgLmh3X3ZhbHVlID0gMTEsIH0sICB7IC5iaXRyYXRlID0g
MTEwLCAuaHdfdmFsdWUgPSANCj4gPiArMjIsIH0sICB7IC5iaXRyYXRlID0gMjIwLCAuaHdfdmFs
dWUgPSA0NCwgfSwgIHsgLmJpdHJhdGUgPSA2MCwgDQo+ID4gKy5od192YWx1ZSA9IDEyLCB9LCAg
eyAuYml0cmF0ZSA9IDkwLCAuaHdfdmFsdWUgPSAxOCwgfSwgIHsgLmJpdHJhdGUgDQo+ID4gKz0g
MTIwLCAuaHdfdmFsdWUgPSAyNCwgfSwgIHsgLmJpdHJhdGUgPSAxODAsIC5od192YWx1ZSA9IDM2
LCB9LCAgeyANCj4gPiArLmJpdHJhdGUgPSAyNDAsIC5od192YWx1ZSA9IDQ4LCB9LCAgeyAuYml0
cmF0ZSA9IDM2MCwgLmh3X3ZhbHVlID0gDQo+ID4gKzcyLCB9LCAgeyAuYml0cmF0ZSA9IDQ4MCwg
Lmh3X3ZhbHVlID0gOTYsIH0sICB7IC5iaXRyYXRlID0gNTQwLCANCj4gPiArLmh3X3ZhbHVlID0g
MTA4LCB9LCB9Ow0KPiA+ICsNCj4gPiArc3RhdGljIGNvbnN0IHN0cnVjdCBpZWVlODAyMTFfY2hh
bm5lbCBtd2xfY2hhbm5lbHNfNTBbXSA9IHsgIHsgDQo+ID4gKy5iYW5kID0gTkw4MDIxMV9CQU5E
XzVHSFosIC5jZW50ZXJfZnJlcSA9IDUxODAsIC5od192YWx1ZSA9IDM2LCB9LCAgDQo+ID4gK3sg
LmJhbmQgPSBOTDgwMjExX0JBTkRfNUdIWiwgLmNlbnRlcl9mcmVxID0gNTIwMCwgLmh3X3ZhbHVl
ID0gNDAsIA0KPiA+ICt9LCAgeyAuYmFuZCA9IE5MODAyMTFfQkFORF81R0haLCAuY2VudGVyX2Zy
ZXEgPSA1MjIwLCAuaHdfdmFsdWUgPSANCj4gPiArNDQsIH0sICB7IC5iYW5kID0gTkw4MDIxMV9C
QU5EXzVHSFosIC5jZW50ZXJfZnJlcSA9IDUyNDAsIC5od192YWx1ZSANCj4gPiArPSA0OCwgfSwg
IHsgLmJhbmQgPSBOTDgwMjExX0JBTkRfNUdIWiwgLmNlbnRlcl9mcmVxID0gNTI2MCwgDQo+ID4g
Ky5od192YWx1ZSA9IDUyLCB9LCAgeyAuYmFuZCA9IE5MODAyMTFfQkFORF81R0haLCAuY2VudGVy
X2ZyZXEgPSANCj4gPiArNTI4MCwgLmh3X3ZhbHVlID0gNTYsIH0sICB7IC5iYW5kID0gTkw4MDIx
MV9CQU5EXzVHSFosIC5jZW50ZXJfZnJlcSANCj4gPiArPSA1MzAwLCAuaHdfdmFsdWUgPSA2MCwg
fSwgIHsgLmJhbmQgPSBOTDgwMjExX0JBTkRfNUdIWiwgDQo+ID4gKy5jZW50ZXJfZnJlcSA9IDUz
MjAsIC5od192YWx1ZSA9IDY0LCB9LCAgeyAuYmFuZCA9IA0KPiA+ICtOTDgwMjExX0JBTkRfNUdI
WiwgLmNlbnRlcl9mcmVxID0gNTUwMCwgLmh3X3ZhbHVlID0gMTAwLCB9LCAgeyANCj4gPiArLmJh
bmQgPSBOTDgwMjExX0JBTkRfNUdIWiwgLmNlbnRlcl9mcmVxID0gNTUyMCwgLmh3X3ZhbHVlID0g
MTA0LCB9LCANCj4gPiAreyAuYmFuZCA9IE5MODAyMTFfQkFORF81R0haLCAuY2VudGVyX2ZyZXEg
PSA1NTQwLCAuaHdfdmFsdWUgPSAxMDgsIA0KPiA+ICt9LCB7IC5iYW5kID0gTkw4MDIxMV9CQU5E
XzVHSFosIC5jZW50ZXJfZnJlcSA9IDU1NjAsIC5od192YWx1ZSA9IA0KPiA+ICsxMTIsIH0sIHsg
LmJhbmQgPSBOTDgwMjExX0JBTkRfNUdIWiwgLmNlbnRlcl9mcmVxID0gNTU4MCwgLmh3X3ZhbHVl
IA0KPiA+ICs9IDExNiwgfSwgeyAuYmFuZCA9IE5MODAyMTFfQkFORF81R0haLCAuY2VudGVyX2Zy
ZXEgPSA1NjAwLCANCj4gPiArLmh3X3ZhbHVlID0gMTIwLCB9LCB7IC5iYW5kID0gTkw4MDIxMV9C
QU5EXzVHSFosIC5jZW50ZXJfZnJlcSA9IA0KPiA+ICs1NjIwLCAuaHdfdmFsdWUgPSAxMjQsIH0s
IHsgLmJhbmQgPSBOTDgwMjExX0JBTkRfNUdIWiwgLmNlbnRlcl9mcmVxIA0KPiA+ICs9IDU2NDAs
IC5od192YWx1ZSA9IDEyOCwgfSwgeyAuYmFuZCA9IE5MODAyMTFfQkFORF81R0haLCANCj4gPiAr
LmNlbnRlcl9mcmVxID0gNTY2MCwgLmh3X3ZhbHVlID0gMTMyLCB9LCB7IC5iYW5kID0gDQo+ID4g
K05MODAyMTFfQkFORF81R0haLCAuY2VudGVyX2ZyZXEgPSA1NjgwLCAuaHdfdmFsdWUgPSAxMzYs
IH0sIHsgLmJhbmQgDQo+ID4gKz0gTkw4MDIxMV9CQU5EXzVHSFosIC5jZW50ZXJfZnJlcSA9IDU3
MDAsIC5od192YWx1ZSA9IDE0MCwgfSwgeyANCj4gPiArLmJhbmQgPSBOTDgwMjExX0JBTkRfNUdI
WiwgLmNlbnRlcl9mcmVxID0gNTcyMCwgLmh3X3ZhbHVlID0gMTQ0LCB9LCANCj4gPiAreyAuYmFu
ZCA9IE5MODAyMTFfQkFORF81R0haLCAuY2VudGVyX2ZyZXEgPSA1NzQ1LCAuaHdfdmFsdWUgPSAx
NDksIA0KPiA+ICt9LCB7IC5iYW5kID0gTkw4MDIxMV9CQU5EXzVHSFosIC5jZW50ZXJfZnJlcSA9
IDU3NjUsIC5od192YWx1ZSA9IA0KPiA+ICsxNTMsIH0sIHsgLmJhbmQgPSBOTDgwMjExX0JBTkRf
NUdIWiwgLmNlbnRlcl9mcmVxID0gNTc4NSwgLmh3X3ZhbHVlIA0KPiA+ICs9IDE1NywgfSwgeyAu
YmFuZCA9IE5MODAyMTFfQkFORF81R0haLCAuY2VudGVyX2ZyZXEgPSA1ODA1LCANCj4gPiArLmh3
X3ZhbHVlID0gMTYxLCB9LCB9Ow0KPiANCj4gRGl0dG8uDQo+IA0KPiAuLi4NCj4gPiArDQo+ID4g
K3N0YXRpYyB2b2lkIG13bF9wcm9jZXNzX29mX2R0cyhzdHJ1Y3QgbXdsX3ByaXYgKnByaXYpIHsg
I2lmZGVmIA0KPiA+ICtDT05GSUdfT0YNCj4gDQo+IFNvIGEgbW9yZSBjb21tb24gaWRpb20gaW4g
dGhlIGRyaXZlcnMgaXM6DQo+ICAgI2lmZGVmIENPTkZJR19PRg0KPiAgIGRlZmluZSBwcm9jZXNz
IG9mX2R0cyBmdW5jdGlvbiB7fQ0KPiAgICNlbHNlDQo+ICAgZGVmaW5lIEVNUFRZIG9mX2R0cyBm
dW5jdGlvbiB7fQ0KPiAgICNlbmRpZg0KPiANCj4gSSBndWVzcyBpdCdzIHRoZSBzYW1lIGVmZmVj
dCBlaXRoZXIgd2F5LiBJIGRvbid0IG11Y2ggY2FyZSBhcyBsb25nIGFzDQo+IG13bF9wcm9jZXNz
X29mX2R0cygpIGNhbiBiZSBjYWxsZWQgd2l0aG91dCBndWFyZHMsIHdoaWNoIGl0IGNhbi4gQnV0
IEkgDQo+IHRob3VnaHQgSSBzaG91bGQgbWVudGlvbiBpdCBpbmNhc2UgYW55b25lIGVsc2UgY2Fy
ZXMuIEFuZCB0aGlzIGlzIG5vdCANCj4gY29uc2lzdGVudCB3aXRoIGhvdyB5b3UgaGF2ZSBkb25l
IHRoZSBzYW1lIHRoaW5nIGZvciBDT05GSUdfREVCVUdfRlMNCj4gDQoNCkkgd2lsbCBtb2RpZnkg
aXQuDQoNCj4gPiArIHN0cnVjdCBwcm9wZXJ0eSAqcHJvcDsNCj4gPiArIHUzMiBwcm9wX3ZhbHVl
Ow0KPiA+ICsNCj4gPiArIHByaXYtPmR0X25vZGUgPQ0KPiA+ICsgb2ZfZmluZF9ub2RlX2J5X25h
bWUocGNpX2J1c190b19PRl9ub2RlKHByaXYtPnBkZXYtPmJ1cyksDQo+ID4gKyAgICAgIm13bHdp
ZmkiKTsNCj4gPiArIGlmICghcHJpdi0+ZHRfbm9kZSkNCj4gPiArIHJldHVybjsNCj4gPiArDQo+
ID4gKyAvKiBsb29rIGZvciBhbGwgbWF0Y2hpbmcgcHJvcGVydHkgbmFtZXMgKi8gDQo+ID4gKyBm
b3JfZWFjaF9wcm9wZXJ0eV9vZl9ub2RlKHByaXYtPmR0X25vZGUsIHByb3ApIHsgaWYgDQo+ID4g
KyAoc3RyY21wKHByb3AtPm5hbWUsICJtYXJ2ZWxsLDJnaHoiKSA9PSAwKQ0KPiA+ICsgcHJpdi0+
ZGlzYWJsZV8yZyA9IHRydWU7DQo+ID4gKyBpZiAoc3RyY21wKHByb3AtPm5hbWUsICJtYXJ2ZWxs
LDVnaHoiKSA9PSAwKQ0KPiA+ICsgcHJpdi0+ZGlzYWJsZV81ZyA9IHRydWU7DQo+ID4gKyBpZiAo
c3RyY21wKHByb3AtPm5hbWUsICJtYXJ2ZWxsLGNoYWlubWFzayIpID09IDApIHsgcHJvcF92YWx1
ZSA9DQo+ID4gKyBiZTMyX3RvX2NwdSgqKChfX2JlMzIgKilwcm9wLT52YWx1ZSkpOyBpZiAocHJv
cF92YWx1ZSA9PSAyKQ0KPiA+ICsgcHJpdi0+YW50ZW5uYV90eCA9IEFOVEVOTkFfVFhfMjsNCj4g
PiArDQo+ID4gKyBwcm9wX3ZhbHVlID0gYmUzMl90b19jcHUoKigoX19iZTMyICopIChwcm9wLT52
YWx1ZSArIDQpKSk7IGlmIA0KPiA+ICsgKHByb3BfdmFsdWUgPT0gMikNCj4gPiArIHByaXYtPmFu
dGVubmFfcnggPSBBTlRFTk5BX1JYXzI7DQo+ID4gKyB9DQo+ID4gKyB9DQo+ID4gKw0KPiA+ICsg
cHJpdi0+cHdyX25vZGUgPSBvZl9maW5kX25vZGVfYnlfbmFtZShwcml2LT5kdF9ub2RlLA0KPiA+
ICsgICAgICAibWFydmVsbCxwb3dlcnRhYmxlIik7DQo+ID4gKyNlbmRpZg0KPiA+ICt9DQo+IA0K
PiBBRkFJQ1QsIHRoZXJlJ3Mgbm8gZG9jdW1lbnRhdGlvbiBmb3IgdGhlc2UgRFQgYmluZGluZ3Mu
IFRoZSBtd2x3aWZpIA0KPiBub2RlIGFuZCB0aGUgbWFydmVsbCxwb3dlcnRhYmxlIGJvdGggbmVl
ZCBmdWxsIGRvY3VtZW50YXRpb24gaW4gDQo+IERvY3VtZW50YXRpb24vZGV2aWNldHJlZS9iaW5k
aW5ncy8uLi4gLg0KPiANCj4gRnJhbmtseSBJIGhhdmUgYSBmZWVsaW5nIEknbSBnb2luZyB0byBu
ZWVkIHRoZXNlIERUIG5vZGVzIGFuZCBJJ2QgbGlrZSANCj4gdG8gbm90IGhhdmUgdG8gZ3Vlc3Mt
YW5kLWNoZWNrIGJhc2VkIG9uIHRoZSBjb2RlLg0KPiANCg0KUG93ZXIgdGFibGUgd29uJ3QgYmUg
dXNlZCBmb3IgY2hpcCB3aXRoIGRldmljZSBwb3dlciB0YWJsZSwgaXQgaXMgb25seSB1c2VkIGZv
ciBvbGQgcHJvZHVjdHMgb2YgT3BlbldydC4gRm9yIHVwc3RyZWFtLCB0aGUgcmVsYXRlZCBjb2Rl
IGlzIHJlbW92ZWQuDQo8bWFydmVsbCwyZ2h6PiBpcyB1c2VkIHRvIGRpc2FibGUgMmcgYmFuZC4N
CjxtYXJ2ZWxsLDVnaHo+IGlzIHVzZWQgdG8gZGlzYWJsZSA1ZyBiYW5kLg0KPG1hcnZlbGwsIGNo
YWlubWFzaz4gaXMgdXNlZCB0byBzcGVjaWZ5IGFudGVubmEgbnVtYmVyIChpZiBkZWZhdWx0IG51
bWJlciBpcyBzdWl0YWJsZSBmb3IgeW91LCB0aGVyZSBpcyBubyBuZWVkIHRvIHVzZSB0aGlzIHBh
cmFtZXRlcikuIA0KDQo+IC4uLg0KPiANCj4gPiArc3RhdGljIGludCBtd2xfd2xfaW5pdChzdHJ1
Y3QgbXdsX3ByaXYgKnByaXYpIHsgIHN0cnVjdCANCj4gPiAraWVlZTgwMjExX2h3ICpodzsgIGlu
dCByYzsgIGludCBpOw0KPiA+ICsNCj4gPiArIGh3ID0gcHJpdi0+aHc7DQo+ID4gKw0KPiA+ICsg
aHctPmV4dHJhX3R4X2hlYWRyb29tID0gU1lTQURQVF9NSU5fQllURVNfSEVBRFJPT007IHF1ZXVl
cyA9IA0KPiA+ICsgaHctPlNZU0FEUFRfVFhfV01NX1FVRVVFUzsNCj4gPiArDQo+ID4gKyAvKiBT
ZXQgcnNzaSB2YWx1ZXMgdG8gZEJtICovDQo+ID4gKyBpZWVlODAyMTFfaHdfc2V0KGh3LCBTSUdO
QUxfREJNKTsNCj4gPiArIGllZWU4MDIxMV9od19zZXQoaHcsIEhBU19SQVRFX0NPTlRST0wpOw0K
PiA+ICsNCj4gPiArIC8qIEFzayBtYWM4MDIxMSBub3QgdG8gdHJpZ2dlciBQUyBtb2RlDQo+ID4g
KyAqIGJhc2VkIG9uIFBNIGJpdCBvZiBpbmNvbWluZyBmcmFtZXMuDQo+ID4gKyAqLw0KPiA+ICsg
aWVlZTgwMjExX2h3X3NldChodywgQVBfTElOS19QUyk7DQo+ID4gKw0KPiA+ICsgaWVlZTgwMjEx
X2h3X3NldChodywgU1VQUE9SVFNfUEVSX1NUQV9HVEspOyBpZWVlODAyMTFfaHdfc2V0KGh3LCAN
Cj4gPiArIE1GUF9DQVBBQkxFKTsNCj4gPiArDQo+ID4gKyBody0+d2lwaHktPmZsYWdzIHw9IFdJ
UEhZX0ZMQUdfSUJTU19SU047IGZsYWdzIHw9IA0KPiA+ICsgaHctPndpcGh5LT5XSVBIWV9GTEFH
X0hBU19DSEFOTkVMX1NXSVRDSDsNCj4gPiArDQo+ID4gKyBody0+d2lwaHktPmZsYWdzIHw9IFdJ
UEhZX0ZMQUdfU1VQUE9SVFNfVERMUzsNCj4gPiArDQo+ID4gKyBody0+dmlmX2RhdGFfc2l6ZSA9
IHNpemVvZihzdHJ1Y3QgbXdsX3ZpZik7IHN0YV9kYXRhX3NpemUgPSANCj4gPiArIGh3LT5zaXpl
b2Yoc3RydWN0IG13bF9zdGEpOw0KPiA+ICsNCj4gPiArIHByaXYtPmFwX21hY2lkc19zdXBwb3J0
ZWQgPSAweDAwMDBmZmZmOyBzdGFfbWFjaWRzX3N1cHBvcnRlZCA9IA0KPiA+ICsgcHJpdi0+MHgw
MDAxMDAwMDsNCj4gDQo+IEhvdyBhYm91dCB3ZSBkb2N1bWVudCB3aGF0IHRoZXNlIG1hZ2ljIG51
bWJlcnMgYXJlPyBBIG5pY2UgbmFtZWQgDQo+IGNvbnN0YW50IGF0IGxlYXN0IHdvdWxkIGJlIG5p
Y2UuDQoNClRoZXkgYXJlIHVzZWQgdG8ga2VlcCBzdGF0dXMgb2YgdXNlZCBBUCBhbmQgU1RBLg0K
DQo+IA0KPiA+ICtzdGF0aWMgaW50IG13bF9wcm9iZShzdHJ1Y3QgcGNpX2RldiAqcGRldiwgY29u
c3Qgc3RydWN0IA0KPiA+ICtwY2lfZGV2aWNlX2lkDQo+ID4gKyppZCkgew0KPiAuLi4NCj4gPiAr
IHdpcGh5X2luZm8ocHJpdi0+aHctPndpcGh5LCAiJXMgVFggYW50ZW5uYXMsICVzIFJYIGFudGVu
bmFzXG4iLA0KPiA+ICsgICAocHJpdi0+YW50ZW5uYV90eCA9PSBBTlRFTk5BX1RYXzRfQVVUTykg
PyAiNCIgOiAiMiIsDQo+ID4gKyAgIChwcml2LT5hbnRlbm5hX3J4ID09IEFOVEVOTkFfUlhfNF9B
VVRPKSA/ICI0IiA6ICIyIik7DQo+ID4gKw0KPiA+ICsjaWZkZWYgQ09ORklHX0RFQlVHX0ZTDQo+
ID4gKyBtd2xfZGVidWdmc19pbml0KGh3KTsNCj4gDQo+IFRoZSBndWFyZHMgc2hvdWxkIGJlIGlu
dGVybmFsIHRvIG13bF9kZWJ1Z2ZzX2luaXQoKSBzbyB3ZSBkb24ndCBoYXZlIA0KPiB0byBndWFy
ZCBpdCB3aGVuIHdlIGNhbGwgaXQuIE11Y2ggbGlrZSBtd2xfcHJvY2Vzc19vZl9kdHMoKSBpcyBh
YmxlIHRvIA0KPiBiZSBjYWxsZWQgYW5kIGNvbXBpbGVzIG91dCBpZiBDT05GSUdfT0YgaXNuJ3Qg
ZGVmaW5lZCwgDQo+IG13bF9kZWJ1Z2ZzX2luaXQoKSBzaG91bGQgaGF2ZSB0aGUgZ3VhcmRzIGlu
dGVybmFsIHRvIA0KPiBkZWJ1Z2ZzLmgvZGVidWdmcy5jIGFuZCB3ZSBzaG91bGRuJ3QgbmVlZCB0
byB3b3JyeSBhYm91dCBpdCB3aGVuIHdlIGNhbGwgaXQuDQo+IA0KDQpJIHdpbGwgbW9kaWZ5IGl0
Lg0KDQo+ID4gK3N0YXRpYyB2b2lkIG13bF9yZW1vdmUoc3RydWN0IHBjaV9kZXYgKnBkZXYpIHsg
IHN0cnVjdCBpZWVlODAyMTFfaHcgDQo+ID4gKypodyA9IHBjaV9nZXRfZHJ2ZGF0YShwZGV2KTsg
IHN0cnVjdCBtd2xfcHJpdiAqcHJpdjsNCj4gPiArDQo+ID4gKyBpZiAoIWh3KQ0KPiA+ICsgcmV0
dXJuOw0KPiA+ICsNCj4gPiArIHByaXYgPSBody0+cHJpdjsNCj4gPiArDQo+ID4gKyBtd2xfd2xf
ZGVpbml0KHByaXYpOw0KPiA+ICsgcGNpX3NldF9kcnZkYXRhKHBkZXYsIE5VTEwpOw0KPiA+ICsg
aWVlZTgwMjExX2ZyZWVfaHcoaHcpOw0KPiA+ICsgcGNpX2Rpc2FibGVfZGV2aWNlKHBkZXYpOw0K
PiA+ICsNCj4gPiArI2lmZGVmIENPTkZJR19ERUJVR19GUw0KPiA+ICsgbXdsX2RlYnVnZnNfcmVt
b3ZlKGh3KTsNCj4gPiArI2VuZGlmDQo+IA0KPiBBcyBwcmV2aW91c2x5IGNvbW1lbnRlZCBvbi4N
Cj4gDQo+ID4gKysrIGIvZHJpdmVycy9uZXQvd2lyZWxlc3MvbWFydmVsbC9td2x3aWZpL3RoZXJt
YWwuYw0KPiAuLi4NCj4gPiArc3RhdGljIFNFTlNPUl9ERVZJQ0VfQVRUUih0ZW1wMV9pbnB1dCwg
MDQ0NCwNCj4gbXdsX3RoZXJtYWxfc2hvd190ZW1wLA0KPiA+ICsgIE5VTEwsIDApOw0KPiA+ICsN
Cj4gDQo+IFNob3VsZCB1c2UgU19JUlVHTyBpbnN0ZWFkIG9mIG51bWVyaWMgMDQ0NC4NCj4gDQo+
IE9LLCB0aGF0J3MgaXQgZm9yIHNwZWNpZmljcy4gSSBrbm93IGEgbnVtYmVyIG9mIHRoZW0gYXJl
IGp1c3Qgbml0cy4NCj4gDQoNCllvdSBuZWVkIHRvIG1vZGlmeSB0aGlzIHdheSB0byBwYXNzIGNo
ZWNrcGF0Y2gucGwuDQoNCj4gQSBmZXcgZ2VuZXJhbCBjb21tZW50czoNCj4gDQo+ICogSSBzYXcg
aXQgaXQgcXVpdGUgYSBiaXQsIGJ1dCBkaWRuJ3QgY29tbWVudCBvbiBpdCBldmVyeSB0aW1lOiAN
Cj4gdGhlcmUncyBtYW55IHBsYWNlcyB3aGVyZSBhIHZhcmlhYmxlIGRlY2xhcmF0aW9uIGNhbiBi
ZSBjb21iaW5lZCB3aXRoIA0KPiBpdHMgaW5pdGlhbCBhc3NpZ25tZW50Lg0KPiANCj4gKiBJIGhh
cHBlbiB0byBjb25jdXIgd2l0aCBKb2hhbm5lcycgY29tbWVudHMgcmVnYXJkaW5nIHRoZSBJRXMg
YW5kIA0KPiB5b3VyIGJlYWNvbiBwcm9jZXNzaW5nLiBUaGlzIGlzIGEgc2lnbmlmaWNhbnQgaXNz
dWUsIHdpdGggcG90ZW50aWFsIA0KPiBmb3IgYmlnIGJ1Z3MgZG93biB0aGUgcm9hZC4gQXQgdGhl
IHZlcnkgbGVhc3QsIGl0J3MgYSBtYWludGVuYW5jZSBoZWFkYWNoZS4NCj4gDQo+IEZyb20gbXkg
cGVyc3BlY3RpdmUsIEknZCBjb25zaWRlciBpdCBhIGZpcm13YXJlIGJ1ZyBpZiB0aGVyZSdzIG5v
IHdheSBhcm91bmQgaXQuDQo+IElzIHRoZSBmaXJtd2FyZSBnb2luZyB0byBzdHJpcCB0aGUgSUVz
IHRoYXQgaG9zdGFwZCBoYXBwZW5zIHRvIGFkZCB0byANCj4gdGhlIGJlYWNvbnM/IElzIHRoZXJl
IHNvbWUgInBhc3N0aHJvdWdoIiBvciBzb21lIG90aGVyIHdheSB0aGF0IGl0IGNhbiANCj4gYmUg
cmVjb25jaWxlZD8NCj4gDQo+IEkgc3Ryb25nbHkgc3VzcGVjdCB0aGVyZSdzIGJldHRlciB3YXlz
IHRvIGhhbmRsZSBpdCwgZXZlbiB3aXRob3V0IA0KPiBjaGFuZ2luZyB0aGUgZmlybXdhcmUsIGJ1
dCBJIGhhdmVuJ3QgeWV0IHRha2VuIGEgbG9vayB0byBzZWUgaWYgdGhlcmUgaXMuDQo+IA0KPiBJ
biBhbnkgY2FzZSwgd2hpbGUgdGhlcmUncyBzdHVmZiBJIHdvdWxkbid0IG1pbmQgc2VlaW5nIGNo
YW5nZWQsIEkgDQo+IHJhdGhlciBzZWUgaXQgZ28gaW4gc29vbmVyIHJhdGhlciB0aGFuIGxhdGVy
IHNvIEkgYW5kIG90aGVycyBjYW4gDQo+IGNvbnRyaWJ1dGUgb24gdG9wIG9mIGl0LCBpbnN0ZWFk
IG9mIHdhaXRpbmcgdG8gc2VlIGl0ICJwZXJmZWN0IiBmaXJzdC4NCg0KLSBUaGUgb2JqZWN0aXZl
IGlzIHRvIHVzZSB0aGUgc2FtZSBwcm9kdWN0aW9uIGZpcm13YXJlIGJpbmFyeSBmb3IgYm90aCB0
aGUgb3BlbiBzb3VyY2UgYW5kIHByb3ByaWV0YXJ5IGRyaXZlci4gU2FtZSBpbnRlcmZhY2UgaXMg
Y3VycmVudGx5IHVzZWQgYnkgcHJvcHJpZXRhcnkgZHJpdmVyIGZvciBoaXN0b3JpY2FsbHkgcmVh
c29uLCB3aGlsZSB0aGUgb3BlbiBzb3VyY2UgSEFMIGlzIGFkYXB0aW5nIHRvIGl0IGZvciB0aGUg
ZXhpc3Rpbmcgc2hpcHBpbmcgcHJvZHVjdC4NCi0gV2Ugd2lsbCBtYWtlIGNoYW5nZXMgYW5kIGNs
ZWFuIHRoaW5ncyB1cCBpbiBmdXR1cmUuIEkgd2lsbCBzcGVuZCBlZmZvcnQgdG8gY29udGludWUg
aXRzIG1haW50ZW5hbmNlIGFuZCBjbGVhbi11cC4NCg0KPiANCj4gUGxlYXNlIGFkZCBteSByZXZp
ZXdlZC1ieS4gIElmIHdlJ3JlIHdhaXRpbmcgb24gYSB2MTAsIGRvIHlvdSBoYXZlIGFuIEVUQT8N
Cg0KSXQgbG9va3MgbGlrZSB0aGVyZSBpcyBubyBtb3JlIGNvbW1lbnRzIGZvciBwYXRjaCB2OSwg
SSB3aWxsIHNlbmQgb3V0IHBhdGNoIHYxMCBlbmQgb2YgdGhpcyBtb250aC4NCg0KVGhhbmtzLA0K
RGF2aWQNCg0KPiANCj4gVGhhbmtzLA0KPiAtIFN0ZXZlDQo=
On Tue, Feb 7, 2017 at 10:15 PM, David Lin <[email protected]> wrote:
>> Rafa=C5=82 Mi=C5=82ecki [mailto:[email protected]] wrote:
>> On 7 February 2017 at 20:12, Steve deRosier <[email protected]> wrote:
>> >> + /* look for all matching property names */
>> >> + for_each_property_of_node(priv->dt_node, prop) { if
>> >> + (strcmp(prop->name, "marvell,2ghz") =3D=3D 0)
>> >> + priv->disable_2g =3D true;
>> >> + if (strcmp(prop->name, "marvell,5ghz") =3D=3D 0)
>> >> + priv->disable_5g =3D true;
>> >> + if (strcmp(prop->name, "marvell,chainmask") =3D=3D 0) { prop_value =
=3D
>> >> + be32_to_cpu(*((__be32 *)prop->value)); if (prop_value =3D=3D 2)
>> >> + priv->antenna_tx =3D ANTENNA_TX_2;
>> >> +
>> >> + prop_value =3D be32_to_cpu(*((__be32 *) (prop->value + 4))); if
>> >> + (prop_value =3D=3D 2)
>> >> + priv->antenna_rx =3D ANTENNA_RX_2;
>> >> + }
>> >> + }
>> >> +
>> >> + priv->pwr_node =3D of_find_node_by_name(priv->dt_node,
>> >> + "marvell,powertable");
>> >> +#endif
>> >> +}
>> >
>> > AFAICT, there's no documentation for these DT bindings. The mwlwifi
>> > node and the marvell,powertable both need full documentation in
>> > Documentation/devicetree/bindings/... .
>> >
>> > Frankly I have a feeling I'm going to need these DT nodes and I'd like
>> > to not have to guess-and-check based on the code.
>>
>> Please use ieee80211-freq-limit:
>> https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?=
id=3Db3
>> 30b25eaabda00d74e47566d9200907da381896
>>
>> Most likely with wiphy_read_of_freq_limits helper:
>> https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?=
id=3De6
>> 91ac2f75b69bee743f0370d79454ba4429b175
>
> I already replied meaning of these parameters:
> <marvell,2ghz> is used to disable 2g band.
> <marvell,5ghz> is used to disable 5g band.
> <marvell, chainmask> is used to specify antenna number (if default number=
is suitable for you, there is no need to use this parameter).
> <marvell,powertable> should not be used for chip with device power table.=
In fact, this parameter should not be used any more.
>
David, I think you're not understanding the comment, or at least
that's what it looks like to me. Yes, you did reply as to the meaning.
And, your reply, while informative, didn't tell us you understood and
were willing to fix the problem. I doubt you meant it this way, but it
feels defensive and like a brush-off.
First off, you will still have to document any DT bindings you're
adding. Just because you answer the question in the review doesn't
mean you're not responsible for doing so.
And second off, I think that Rafal (and sorry about my spelling, looks
like there's some sort of accent on the l that I don't know how to
make my keyboard do) is saying: there's already some generic bindings
that can be used to disable the 2g or 5g bands. Granted they're even
newer than your patch, but I do think if said bindings exist and are
appropriate, you should use them.
- Steve
On 8 February 2017 at 07:30, David Lin <[email protected]> wrote:
> [email protected] [mailto:[email protected]] wrote:
>> On Tue, Feb 7, 2017 at 10:15 PM, David Lin <[email protected]> wrote:
>> >> Rafa=C5=82 Mi=C5=82ecki [mailto:[email protected]] wrote:
>> >> Please use ieee80211-freq-limit:
>> >> https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commi
>> >> t/?id=3Db3
>> >> 30b25eaabda00d74e47566d9200907da381896
>> >>
>> >> Most likely with wiphy_read_of_freq_limits helper:
>> >> https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commi
>> >> t/?id=3De6
>> >> 91ac2f75b69bee743f0370d79454ba4429b175
>> >
>> > I already replied meaning of these parameters:
>> > <marvell,2ghz> is used to disable 2g band.
>> > <marvell,5ghz> is used to disable 5g band.
>> > <marvell, chainmask> is used to specify antenna number (if default num=
ber
>> is suitable for you, there is no need to use this parameter).
>> > <marvell,powertable> should not be used for chip with device power tab=
le.
>> In fact, this parameter should not be used any more.
>> >
>>
>> David, I think you're not understanding the comment, or at least that's =
what it
>> looks like to me. Yes, you did reply as to the meaning.
>> And, your reply, while informative, didn't tell us you understood and we=
re
>> willing to fix the problem. I doubt you meant it this way, but it feels =
defensive
>> and like a brush-off.
>>
>> First off, you will still have to document any DT bindings you're adding=
. Just
>> because you answer the question in the review doesn't mean you're not
>> responsible for doing so.
>>
>> And second off, I think that Rafal (and sorry about my spelling, looks l=
ike
>> there's some sort of accent on the l that I don't know how to make my
>> keyboard do) is saying: there's already some generic bindings that can b=
e used
>> to disable the 2g or 5g bands. Granted they're even newer than your patc=
h,
>> but I do think if said bindings exist and are appropriate, you should us=
e them.
>>
>> - Steve
>
> These parameters are marvell proprietary parameters, I don't think it sho=
uld be added to DT bindings.
Steve is correct.
You have to document new properties, just because they are Marvell
specific doesn't change anything. You just document them in a proper
place.
> BTW, <marvell,2ghz> and <marvell,5ghz> are only used for mwlwifi to repor=
t supported bands, it is not related to limitation of frequency.
How reporting a single band doesn't limit reported frequencies? You
can achieve exactly the same using generic property, so there is no
place for Marvell specific ones.
In fact there were drivers of 3 vendors requiring band/freq-related
description in DT: Broadcom, Marvell & Mediatek. This property was
discussed & designed to support all limitation cases we found possible
to make it usable with (hopefully) all drivers.
--=20
Rafa=C5=82
On Wed, 8 Feb 2017 at 08:55, David Lin <[email protected]> wrote:
> > From: Rafał Miłecki [mailto:[email protected]] worte:
> > On 8 February 2017 at 08:28, David Lin <[email protected]> wrote:
> > >> From: Rafał Miłecki [mailto:[email protected]] worte:
> > >> Sent: Wednesday, February 08, 2017 3:24 PM On 8 February 2017 at
> > >> 07:30, David Lin <[email protected]> wrote:
> > >> > [email protected] [mailto:[email protected]] wrote:
> > >> >> On Tue, Feb 7, 2017 at 10:15 PM, David Lin <[email protected]> wrote:
> > >> >> >> Rafał Miłecki [mailto:[email protected]] wrote:
> > >> >> >> Please use ieee80211-freq-limit:
> > >> >> >> https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git
> > >> >> >> /co
> > >> >> >> mmi
> > >> >> >> t/?id=b3
> > >> >> >> 30b25eaabda00d74e47566d9200907da381896
> > >> >> >>
> > >> >> >> Most likely with wiphy_read_of_freq_limits helper:
> > >> >> >> https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git
> > >> >> >> /co
> > >> >> >> mmi
> > >> >> >> t/?id=e6
> > >> >> >> 91ac2f75b69bee743f0370d79454ba4429b175
> > >> >> >
> > >> >> > I already replied meaning of these parameters:
> > >> >> > <marvell,2ghz> is used to disable 2g band.
> > >> >> > <marvell,5ghz> is used to disable 5g band.
> > >> >> > <marvell, chainmask> is used to specify antenna number (if
> > >> >> > default number
> > >> >> is suitable for you, there is no need to use this parameter).
> > >> >> > <marvell,powertable> should not be used for chip with device
> > >> >> > power
> > >> table.
> > >> >> In fact, this parameter should not be used any more.
> > >> >> >
> > >> >>
> > >> >> David, I think you're not understanding the comment, or at least
> > >> >> that's what it looks like to me. Yes, you did reply as to the meaning.
> > >> >> And, your reply, while informative, didn't tell us you understood
> > >> >> and were willing to fix the problem. I doubt you meant it this
> > >> >> way, but it feels defensive and like a brush-off.
> > >> >>
> > >> >> First off, you will still have to document any DT bindings you're
> > >> >> adding. Just because you answer the question in the review doesn't
> > >> >> mean you're not responsible for doing so.
> > >> >>
> > >> >> And second off, I think that Rafal (and sorry about my spelling,
> > >> >> looks like there's some sort of accent on the l that I don't know
> > >> >> how to make my keyboard do) is saying: there's already some
> > >> >> generic bindings that can be used to disable the 2g or 5g bands.
> > >> >> Granted they're even newer than your patch, but I do think if said
> > >> >> bindings exist and
> > >> are appropriate, you should use them.
> > >> >>
> > >> >> - Steve
> > >> >
> > >> > These parameters are marvell proprietary parameters, I don't think
> > >> > it should
> > >> be added to DT bindings.
> > >>
> > >> Steve is correct.
> > >>
> > >> You have to document new properties, just because they are Marvell
> > >> specific doesn't change anything. You just document them in a proper
> > place.
> > >>
> > >
> > > All right. I will do that.
> > >
> > >>
> > >> > BTW, <marvell,2ghz> and <marvell,5ghz> are only used for mwlwifi to
> > >> > report
> > >> supported bands, it is not related to limitation of frequency.
> > >>
> > >> How reporting a single band doesn't limit reported frequencies? You
> > >> can achieve exactly the same using generic property, so there is no
> > >> place for Marvell specific ones.
> > >>
> > >> In fact there were drivers of 3 vendors requiring band/freq-related
> > >> description in DT: Broadcom, Marvell & Mediatek. This property was
> > >> discussed & designed to support all limitation cases we found
> > >> possible to make it usable with
> > >> (hopefully) all drivers.
> > >>
> > >
> > > I only need simple way to disable 2g or 5g band. I will follow your suggestion
> > to document these marvell proprietary parameters.
> >
> > Seriously? Refusing to use generic binding because you think marvell,5ghz; is
> > simpler than ieee80211-freq-limit = <2402000 2482000>; (not to mention your
> > property seems reversed!)?
> >
> > I don't know how else to explain this to you. We don't want duplicated
> > properties where one can work. Just use existing one. Don't add new one even
> > if documented.
> >
>
> All right. I will check and let patch v10 to use it. For previous parameters, they will only be used by previous OpenWrt projects.
Hi David, how are things going? Could we get v10?
--
Rafał
On Wednesday 21 November 2018 09:24:55 Rafał Miłecki wrote:
> On Wed, 8 Feb 2017 at 08:55, David Lin <[email protected]> wrote:
> > > From: Rafał Miłecki [mailto:[email protected]] worte:
> > > On 8 February 2017 at 08:28, David Lin <[email protected]> wrote:
> > > >> From: Rafał Miłecki [mailto:[email protected]] worte:
> > > >> Sent: Wednesday, February 08, 2017 3:24 PM On 8 February 2017 at
> > > >> 07:30, David Lin <[email protected]> wrote:
> > > >> > [email protected] [mailto:[email protected]] wrote:
> > > >> >> On Tue, Feb 7, 2017 at 10:15 PM, David Lin <[email protected]> wrote:
> > > >> >> >> Rafał Miłecki [mailto:[email protected]] wrote:
> > > >> >> >> Please use ieee80211-freq-limit:
> > > >> >> >> https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git
> > > >> >> >> /co
> > > >> >> >> mmi
> > > >> >> >> t/?id=b3
> > > >> >> >> 30b25eaabda00d74e47566d9200907da381896
> > > >> >> >>
> > > >> >> >> Most likely with wiphy_read_of_freq_limits helper:
> > > >> >> >> https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git
> > > >> >> >> /co
> > > >> >> >> mmi
> > > >> >> >> t/?id=e6
> > > >> >> >> 91ac2f75b69bee743f0370d79454ba4429b175
> > > >> >> >
> > > >> >> > I already replied meaning of these parameters:
> > > >> >> > <marvell,2ghz> is used to disable 2g band.
> > > >> >> > <marvell,5ghz> is used to disable 5g band.
> > > >> >> > <marvell, chainmask> is used to specify antenna number (if
> > > >> >> > default number
> > > >> >> is suitable for you, there is no need to use this parameter).
> > > >> >> > <marvell,powertable> should not be used for chip with device
> > > >> >> > power
> > > >> table.
> > > >> >> In fact, this parameter should not be used any more.
> > > >> >> >
> > > >> >>
> > > >> >> David, I think you're not understanding the comment, or at least
> > > >> >> that's what it looks like to me. Yes, you did reply as to the meaning.
> > > >> >> And, your reply, while informative, didn't tell us you understood
> > > >> >> and were willing to fix the problem. I doubt you meant it this
> > > >> >> way, but it feels defensive and like a brush-off.
> > > >> >>
> > > >> >> First off, you will still have to document any DT bindings you're
> > > >> >> adding. Just because you answer the question in the review doesn't
> > > >> >> mean you're not responsible for doing so.
> > > >> >>
> > > >> >> And second off, I think that Rafal (and sorry about my spelling,
> > > >> >> looks like there's some sort of accent on the l that I don't know
> > > >> >> how to make my keyboard do) is saying: there's already some
> > > >> >> generic bindings that can be used to disable the 2g or 5g bands.
> > > >> >> Granted they're even newer than your patch, but I do think if said
> > > >> >> bindings exist and
> > > >> are appropriate, you should use them.
> > > >> >>
> > > >> >> - Steve
> > > >> >
> > > >> > These parameters are marvell proprietary parameters, I don't think
> > > >> > it should
> > > >> be added to DT bindings.
> > > >>
> > > >> Steve is correct.
> > > >>
> > > >> You have to document new properties, just because they are Marvell
> > > >> specific doesn't change anything. You just document them in a proper
> > > place.
> > > >>
> > > >
> > > > All right. I will do that.
> > > >
> > > >>
> > > >> > BTW, <marvell,2ghz> and <marvell,5ghz> are only used for mwlwifi to
> > > >> > report
> > > >> supported bands, it is not related to limitation of frequency.
> > > >>
> > > >> How reporting a single band doesn't limit reported frequencies? You
> > > >> can achieve exactly the same using generic property, so there is no
> > > >> place for Marvell specific ones.
> > > >>
> > > >> In fact there were drivers of 3 vendors requiring band/freq-related
> > > >> description in DT: Broadcom, Marvell & Mediatek. This property was
> > > >> discussed & designed to support all limitation cases we found
> > > >> possible to make it usable with
> > > >> (hopefully) all drivers.
> > > >>
> > > >
> > > > I only need simple way to disable 2g or 5g band. I will follow your suggestion
> > > to document these marvell proprietary parameters.
> > >
> > > Seriously? Refusing to use generic binding because you think marvell,5ghz; is
> > > simpler than ieee80211-freq-limit = <2402000 2482000>; (not to mention your
> > > property seems reversed!)?
> > >
> > > I don't know how else to explain this to you. We don't want duplicated
> > > properties where one can work. Just use existing one. Don't add new one even
> > > if documented.
> > >
> >
> > All right. I will check and let patch v10 to use it. For previous parameters, they will only be used by previous OpenWrt projects.
>
> Hi David, how are things going? Could we get v10?
Hello! I would like to ask, is there any plan for future updates for this mwlwifi softmac driver?
(resending email, now with correct David's NXP email address)
On Tuesday 19 May 2020 17:12:08 Pali Rohár wrote:
> On Wednesday 21 November 2018 09:24:55 Rafał Miłecki wrote:
> > On Wed, 8 Feb 2017 at 08:55, David Lin <[email protected]> wrote:
> > > > From: Rafał Miłecki [mailto:[email protected]] worte:
> > > > On 8 February 2017 at 08:28, David Lin <[email protected]> wrote:
> > > > >> From: Rafał Miłecki [mailto:[email protected]] worte:
> > > > >> Sent: Wednesday, February 08, 2017 3:24 PM On 8 February 2017 at
> > > > >> 07:30, David Lin <[email protected]> wrote:
> > > > >> > [email protected] [mailto:[email protected]] wrote:
> > > > >> >> On Tue, Feb 7, 2017 at 10:15 PM, David Lin <[email protected]> wrote:
> > > > >> >> >> Rafał Miłecki [mailto:[email protected]] wrote:
> > > > >> >> >> Please use ieee80211-freq-limit:
> > > > >> >> >> https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git
> > > > >> >> >> /co
> > > > >> >> >> mmi
> > > > >> >> >> t/?id=b3
> > > > >> >> >> 30b25eaabda00d74e47566d9200907da381896
> > > > >> >> >>
> > > > >> >> >> Most likely with wiphy_read_of_freq_limits helper:
> > > > >> >> >> https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git
> > > > >> >> >> /co
> > > > >> >> >> mmi
> > > > >> >> >> t/?id=e6
> > > > >> >> >> 91ac2f75b69bee743f0370d79454ba4429b175
> > > > >> >> >
> > > > >> >> > I already replied meaning of these parameters:
> > > > >> >> > <marvell,2ghz> is used to disable 2g band.
> > > > >> >> > <marvell,5ghz> is used to disable 5g band.
> > > > >> >> > <marvell, chainmask> is used to specify antenna number (if
> > > > >> >> > default number
> > > > >> >> is suitable for you, there is no need to use this parameter).
> > > > >> >> > <marvell,powertable> should not be used for chip with device
> > > > >> >> > power
> > > > >> table.
> > > > >> >> In fact, this parameter should not be used any more.
> > > > >> >> >
> > > > >> >>
> > > > >> >> David, I think you're not understanding the comment, or at least
> > > > >> >> that's what it looks like to me. Yes, you did reply as to the meaning.
> > > > >> >> And, your reply, while informative, didn't tell us you understood
> > > > >> >> and were willing to fix the problem. I doubt you meant it this
> > > > >> >> way, but it feels defensive and like a brush-off.
> > > > >> >>
> > > > >> >> First off, you will still have to document any DT bindings you're
> > > > >> >> adding. Just because you answer the question in the review doesn't
> > > > >> >> mean you're not responsible for doing so.
> > > > >> >>
> > > > >> >> And second off, I think that Rafal (and sorry about my spelling,
> > > > >> >> looks like there's some sort of accent on the l that I don't know
> > > > >> >> how to make my keyboard do) is saying: there's already some
> > > > >> >> generic bindings that can be used to disable the 2g or 5g bands.
> > > > >> >> Granted they're even newer than your patch, but I do think if said
> > > > >> >> bindings exist and
> > > > >> are appropriate, you should use them.
> > > > >> >>
> > > > >> >> - Steve
> > > > >> >
> > > > >> > These parameters are marvell proprietary parameters, I don't think
> > > > >> > it should
> > > > >> be added to DT bindings.
> > > > >>
> > > > >> Steve is correct.
> > > > >>
> > > > >> You have to document new properties, just because they are Marvell
> > > > >> specific doesn't change anything. You just document them in a proper
> > > > place.
> > > > >>
> > > > >
> > > > > All right. I will do that.
> > > > >
> > > > >>
> > > > >> > BTW, <marvell,2ghz> and <marvell,5ghz> are only used for mwlwifi to
> > > > >> > report
> > > > >> supported bands, it is not related to limitation of frequency.
> > > > >>
> > > > >> How reporting a single band doesn't limit reported frequencies? You
> > > > >> can achieve exactly the same using generic property, so there is no
> > > > >> place for Marvell specific ones.
> > > > >>
> > > > >> In fact there were drivers of 3 vendors requiring band/freq-related
> > > > >> description in DT: Broadcom, Marvell & Mediatek. This property was
> > > > >> discussed & designed to support all limitation cases we found
> > > > >> possible to make it usable with
> > > > >> (hopefully) all drivers.
> > > > >>
> > > > >
> > > > > I only need simple way to disable 2g or 5g band. I will follow your suggestion
> > > > to document these marvell proprietary parameters.
> > > >
> > > > Seriously? Refusing to use generic binding because you think marvell,5ghz; is
> > > > simpler than ieee80211-freq-limit = <2402000 2482000>; (not to mention your
> > > > property seems reversed!)?
> > > >
> > > > I don't know how else to explain this to you. We don't want duplicated
> > > > properties where one can work. Just use existing one. Don't add new one even
> > > > if documented.
> > > >
> > >
> > > All right. I will check and let patch v10 to use it. For previous parameters, they will only be used by previous OpenWrt projects.
> >
> > Hi David, how are things going? Could we get v10?
Hello! I would like to ask, is there any plan for future updates for this mwlwifi softmac driver?