xref: /linux/drivers/net/wireless/nxp/nxpwifi/util.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
173b01e57SJeff Chen // SPDX-License-Identifier: GPL-2.0-only
273b01e57SJeff Chen /*
373b01e57SJeff Chen  * NXP Wireless LAN device driver: utility functions
473b01e57SJeff Chen  *
573b01e57SJeff Chen  * Copyright 2011-2024 NXP
673b01e57SJeff Chen  */
773b01e57SJeff Chen 
873b01e57SJeff Chen #include "cfg.h"
973b01e57SJeff Chen #include "util.h"
1073b01e57SJeff Chen #include "fw.h"
1173b01e57SJeff Chen #include "main.h"
1273b01e57SJeff Chen #include "cmdevt.h"
1373b01e57SJeff Chen #include "wmm.h"
1473b01e57SJeff Chen #include "11n.h"
1573b01e57SJeff Chen #include <net/netlink.h>
1673b01e57SJeff Chen #include <linux/mmc/sdio_ids.h>
1773b01e57SJeff Chen #include <linux/mmc/sdio_func.h>
1873b01e57SJeff Chen #include <linux/bitfield.h>
1973b01e57SJeff Chen #include <net/ieee80211_radiotap.h>
2073b01e57SJeff Chen 
2173b01e57SJeff Chen #define RX_RATE_FORMAT_MASK   GENMASK(1, 0)
2273b01e57SJeff Chen #define RX_RATE_BW_MASK       GENMASK(3, 2)
2373b01e57SJeff Chen #define RX_RATE_GI_MASK       BIT(4)
2473b01e57SJeff Chen #define RX_RATE_STBC_MASK     BIT(5)
2573b01e57SJeff Chen #define RX_RATE_LDPC_MASK     BIT(6)
2673b01e57SJeff Chen 
2773b01e57SJeff Chen static struct nxpwifi_debug_data items[] = {
2873b01e57SJeff Chen 	{"debug_mask", item_size(debug_mask),
2973b01e57SJeff Chen 	 item_addr(debug_mask), 1},
3073b01e57SJeff Chen 	{"int_counter", item_size(int_counter),
3173b01e57SJeff Chen 	 item_addr(int_counter), 1},
3273b01e57SJeff Chen 	{"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]),
3373b01e57SJeff Chen 	 item_addr(packets_out[WMM_AC_VO]), 1},
3473b01e57SJeff Chen 	{"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]),
3573b01e57SJeff Chen 	 item_addr(packets_out[WMM_AC_VI]), 1},
3673b01e57SJeff Chen 	{"wmm_ac_be", item_size(packets_out[WMM_AC_BE]),
3773b01e57SJeff Chen 	 item_addr(packets_out[WMM_AC_BE]), 1},
3873b01e57SJeff Chen 	{"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]),
3973b01e57SJeff Chen 	 item_addr(packets_out[WMM_AC_BK]), 1},
4073b01e57SJeff Chen 	{"tx_buf_size", item_size(tx_buf_size),
4173b01e57SJeff Chen 	 item_addr(tx_buf_size), 1},
4273b01e57SJeff Chen 	{"curr_tx_buf_size", item_size(curr_tx_buf_size),
4373b01e57SJeff Chen 	 item_addr(curr_tx_buf_size), 1},
4473b01e57SJeff Chen 	{"ps_mode", item_size(ps_mode),
4573b01e57SJeff Chen 	 item_addr(ps_mode), 1},
4673b01e57SJeff Chen 	{"ps_state", item_size(ps_state),
4773b01e57SJeff Chen 	 item_addr(ps_state), 1},
4873b01e57SJeff Chen 	{"is_deep_sleep", item_size(is_deep_sleep),
4973b01e57SJeff Chen 	 item_addr(is_deep_sleep), 1},
5073b01e57SJeff Chen 	{"wakeup_dev_req", item_size(pm_wakeup_card_req),
5173b01e57SJeff Chen 	 item_addr(pm_wakeup_card_req), 1},
5273b01e57SJeff Chen 	{"wakeup_tries", item_size(pm_wakeup_fw_try),
5373b01e57SJeff Chen 	 item_addr(pm_wakeup_fw_try), 1},
5473b01e57SJeff Chen 	{"hs_configured", item_size(is_hs_configured),
5573b01e57SJeff Chen 	 item_addr(is_hs_configured), 1},
5673b01e57SJeff Chen 	{"hs_activated", item_size(hs_activated),
5773b01e57SJeff Chen 	 item_addr(hs_activated), 1},
5873b01e57SJeff Chen 	{"num_tx_timeout", item_size(num_tx_timeout),
5973b01e57SJeff Chen 	 item_addr(num_tx_timeout), 1},
6073b01e57SJeff Chen 	{"is_cmd_timedout", item_size(is_cmd_timedout),
6173b01e57SJeff Chen 	 item_addr(is_cmd_timedout), 1},
6273b01e57SJeff Chen 	{"timeout_cmd_id", item_size(timeout_cmd_id),
6373b01e57SJeff Chen 	 item_addr(timeout_cmd_id), 1},
6473b01e57SJeff Chen 	{"timeout_cmd_act", item_size(timeout_cmd_act),
6573b01e57SJeff Chen 	 item_addr(timeout_cmd_act), 1},
6673b01e57SJeff Chen 	{"last_cmd_id", item_size(last_cmd_id),
6773b01e57SJeff Chen 	 item_addr(last_cmd_id), DBG_CMD_NUM},
6873b01e57SJeff Chen 	{"last_cmd_act", item_size(last_cmd_act),
6973b01e57SJeff Chen 	 item_addr(last_cmd_act), DBG_CMD_NUM},
7073b01e57SJeff Chen 	{"last_cmd_index", item_size(last_cmd_index),
7173b01e57SJeff Chen 	 item_addr(last_cmd_index), 1},
7273b01e57SJeff Chen 	{"last_cmd_resp_id", item_size(last_cmd_resp_id),
7373b01e57SJeff Chen 	 item_addr(last_cmd_resp_id), DBG_CMD_NUM},
7473b01e57SJeff Chen 	{"last_cmd_resp_index", item_size(last_cmd_resp_index),
7573b01e57SJeff Chen 	 item_addr(last_cmd_resp_index), 1},
7673b01e57SJeff Chen 	{"last_event", item_size(last_event),
7773b01e57SJeff Chen 	 item_addr(last_event), DBG_CMD_NUM},
7873b01e57SJeff Chen 	{"last_event_index", item_size(last_event_index),
7973b01e57SJeff Chen 	 item_addr(last_event_index), 1},
8073b01e57SJeff Chen 	{"last_mp_wr_bitmap", item_size(last_mp_wr_bitmap),
8173b01e57SJeff Chen 	 item_addr(last_mp_wr_bitmap), NXPWIFI_DBG_SDIO_MP_NUM},
8273b01e57SJeff Chen 	{"last_mp_wr_ports", item_size(last_mp_wr_ports),
8373b01e57SJeff Chen 	 item_addr(last_mp_wr_ports), NXPWIFI_DBG_SDIO_MP_NUM},
8473b01e57SJeff Chen 	{"last_mp_wr_len", item_size(last_mp_wr_len),
8573b01e57SJeff Chen 	 item_addr(last_mp_wr_len), NXPWIFI_DBG_SDIO_MP_NUM},
8673b01e57SJeff Chen 	{"last_mp_curr_wr_port", item_size(last_mp_curr_wr_port),
8773b01e57SJeff Chen 	 item_addr(last_mp_curr_wr_port), NXPWIFI_DBG_SDIO_MP_NUM},
8873b01e57SJeff Chen 	{"last_sdio_mp_index", item_size(last_sdio_mp_index),
8973b01e57SJeff Chen 	 item_addr(last_sdio_mp_index), 1},
9073b01e57SJeff Chen 	{"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure),
9173b01e57SJeff Chen 	 item_addr(num_cmd_host_to_card_failure), 1},
9273b01e57SJeff Chen 	{"num_cmd_sleep_cfm_fail",
9373b01e57SJeff Chen 	 item_size(num_cmd_sleep_cfm_host_to_card_failure),
9473b01e57SJeff Chen 	 item_addr(num_cmd_sleep_cfm_host_to_card_failure), 1},
9573b01e57SJeff Chen 	{"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure),
9673b01e57SJeff Chen 	 item_addr(num_tx_host_to_card_failure), 1},
9773b01e57SJeff Chen 	{"num_evt_deauth", item_size(num_event_deauth),
9873b01e57SJeff Chen 	 item_addr(num_event_deauth), 1},
9973b01e57SJeff Chen 	{"num_evt_disassoc", item_size(num_event_disassoc),
10073b01e57SJeff Chen 	 item_addr(num_event_disassoc), 1},
10173b01e57SJeff Chen 	{"num_evt_link_lost", item_size(num_event_link_lost),
10273b01e57SJeff Chen 	 item_addr(num_event_link_lost), 1},
10373b01e57SJeff Chen 	{"num_cmd_deauth", item_size(num_cmd_deauth),
10473b01e57SJeff Chen 	 item_addr(num_cmd_deauth), 1},
10573b01e57SJeff Chen 	{"num_cmd_assoc_ok", item_size(num_cmd_assoc_success),
10673b01e57SJeff Chen 	 item_addr(num_cmd_assoc_success), 1},
10773b01e57SJeff Chen 	{"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure),
10873b01e57SJeff Chen 	 item_addr(num_cmd_assoc_failure), 1},
10973b01e57SJeff Chen 	{"cmd_sent", item_size(cmd_sent),
11073b01e57SJeff Chen 	 item_addr(cmd_sent), 1},
11173b01e57SJeff Chen 	{"data_sent", item_size(data_sent),
11273b01e57SJeff Chen 	 item_addr(data_sent), 1},
11373b01e57SJeff Chen 	{"cmd_resp_received", item_size(cmd_resp_received),
11473b01e57SJeff Chen 	 item_addr(cmd_resp_received), 1},
11573b01e57SJeff Chen 	{"event_received", item_size(event_received),
11673b01e57SJeff Chen 	 item_addr(event_received), 1},
11773b01e57SJeff Chen 
11873b01e57SJeff Chen 	/* variables defined in struct nxpwifi_adapter */
11973b01e57SJeff Chen 	{"cmd_pending", adapter_item_size(cmd_pending),
12073b01e57SJeff Chen 	 adapter_item_addr(cmd_pending), 1},
12173b01e57SJeff Chen 	{"tx_pending", adapter_item_size(tx_pending),
12273b01e57SJeff Chen 	 adapter_item_addr(tx_pending), 1},
12373b01e57SJeff Chen 	{"rx_pending", adapter_item_size(rx_pending),
12473b01e57SJeff Chen 	 adapter_item_addr(rx_pending), 1},
12573b01e57SJeff Chen };
12673b01e57SJeff Chen 
12773b01e57SJeff Chen static int num_of_items = ARRAY_SIZE(items);
12873b01e57SJeff Chen 
12973b01e57SJeff Chen /* Send init or shutdown command to firmware. */
nxpwifi_init_shutdown_fw(struct nxpwifi_private * priv,u32 func_init_shutdown)13073b01e57SJeff Chen int nxpwifi_init_shutdown_fw(struct nxpwifi_private *priv,
13173b01e57SJeff Chen 			     u32 func_init_shutdown)
13273b01e57SJeff Chen {
13373b01e57SJeff Chen 	u16 cmd;
13473b01e57SJeff Chen 
13573b01e57SJeff Chen 	if (func_init_shutdown == NXPWIFI_FUNC_INIT) {
13673b01e57SJeff Chen 		cmd = HOST_CMD_FUNC_INIT;
13773b01e57SJeff Chen 	} else if (func_init_shutdown == NXPWIFI_FUNC_SHUTDOWN) {
13873b01e57SJeff Chen 		cmd = HOST_CMD_FUNC_SHUTDOWN;
13973b01e57SJeff Chen 	} else {
14073b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, ERROR,
14173b01e57SJeff Chen 			    "unsupported parameter\n");
14273b01e57SJeff Chen 		return -EINVAL;
14373b01e57SJeff Chen 	}
14473b01e57SJeff Chen 
14573b01e57SJeff Chen 	return nxpwifi_send_cmd(priv, cmd, HOST_ACT_GEN_SET, 0, NULL, true);
14673b01e57SJeff Chen }
14773b01e57SJeff Chen EXPORT_SYMBOL_GPL(nxpwifi_init_shutdown_fw);
14873b01e57SJeff Chen 
14973b01e57SJeff Chen /* Handle IOCTL get/set of debug info across driver structures. */
nxpwifi_get_debug_info(struct nxpwifi_private * priv,struct nxpwifi_debug_info * info)15073b01e57SJeff Chen int nxpwifi_get_debug_info(struct nxpwifi_private *priv,
15173b01e57SJeff Chen 			   struct nxpwifi_debug_info *info)
15273b01e57SJeff Chen {
15373b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
15473b01e57SJeff Chen 
15573b01e57SJeff Chen 	if (info) {
15673b01e57SJeff Chen 		info->debug_mask = adapter->debug_mask;
15773b01e57SJeff Chen 		memcpy(info->packets_out,
15873b01e57SJeff Chen 		       priv->wmm.packets_out,
15973b01e57SJeff Chen 		       sizeof(priv->wmm.packets_out));
16073b01e57SJeff Chen 		info->curr_tx_buf_size = (u32)adapter->curr_tx_buf_size;
16173b01e57SJeff Chen 		info->tx_buf_size = (u32)adapter->tx_buf_size;
16273b01e57SJeff Chen 		info->rx_tbl_num = nxpwifi_get_rx_reorder_tbl(priv,
16373b01e57SJeff Chen 							      info->rx_tbl);
16473b01e57SJeff Chen 		info->tx_tbl_num = nxpwifi_get_tx_ba_stream_tbl(priv,
16573b01e57SJeff Chen 								info->tx_tbl);
16673b01e57SJeff Chen 		info->ps_mode = adapter->ps_mode;
16773b01e57SJeff Chen 		info->ps_state = adapter->ps_state;
16873b01e57SJeff Chen 		info->is_deep_sleep = adapter->is_deep_sleep;
16973b01e57SJeff Chen 		info->pm_wakeup_card_req = adapter->pm_wakeup_card_req;
17073b01e57SJeff Chen 		info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try;
17173b01e57SJeff Chen 		info->is_hs_configured = test_bit(NXPWIFI_IS_HS_CONFIGURED,
17273b01e57SJeff Chen 						  &adapter->work_flags);
17373b01e57SJeff Chen 		info->hs_activated = adapter->hs_activated;
17473b01e57SJeff Chen 		info->is_cmd_timedout = test_bit(NXPWIFI_IS_CMD_TIMEDOUT,
17573b01e57SJeff Chen 						 &adapter->work_flags);
17673b01e57SJeff Chen 		info->num_cmd_host_to_card_failure =
17773b01e57SJeff Chen 			adapter->dbg.num_cmd_host_to_card_failure;
17873b01e57SJeff Chen 		info->num_cmd_sleep_cfm_host_to_card_failure =
17973b01e57SJeff Chen 			adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure;
18073b01e57SJeff Chen 		info->num_tx_host_to_card_failure =
18173b01e57SJeff Chen 			adapter->dbg.num_tx_host_to_card_failure;
18273b01e57SJeff Chen 		info->num_event_deauth = adapter->dbg.num_event_deauth;
18373b01e57SJeff Chen 		info->num_event_disassoc = adapter->dbg.num_event_disassoc;
18473b01e57SJeff Chen 		info->num_event_link_lost = adapter->dbg.num_event_link_lost;
18573b01e57SJeff Chen 		info->num_cmd_deauth = adapter->dbg.num_cmd_deauth;
18673b01e57SJeff Chen 		info->num_cmd_assoc_success =
18773b01e57SJeff Chen 			adapter->dbg.num_cmd_assoc_success;
18873b01e57SJeff Chen 		info->num_cmd_assoc_failure =
18973b01e57SJeff Chen 			adapter->dbg.num_cmd_assoc_failure;
19073b01e57SJeff Chen 		info->num_tx_timeout = adapter->dbg.num_tx_timeout;
19173b01e57SJeff Chen 		info->timeout_cmd_id = adapter->dbg.timeout_cmd_id;
19273b01e57SJeff Chen 		info->timeout_cmd_act = adapter->dbg.timeout_cmd_act;
19373b01e57SJeff Chen 		memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id,
19473b01e57SJeff Chen 		       sizeof(adapter->dbg.last_cmd_id));
19573b01e57SJeff Chen 		memcpy(info->last_cmd_act, adapter->dbg.last_cmd_act,
19673b01e57SJeff Chen 		       sizeof(adapter->dbg.last_cmd_act));
19773b01e57SJeff Chen 		info->last_cmd_index = adapter->dbg.last_cmd_index;
19873b01e57SJeff Chen 		memcpy(info->last_cmd_resp_id, adapter->dbg.last_cmd_resp_id,
19973b01e57SJeff Chen 		       sizeof(adapter->dbg.last_cmd_resp_id));
20073b01e57SJeff Chen 		info->last_cmd_resp_index = adapter->dbg.last_cmd_resp_index;
20173b01e57SJeff Chen 		memcpy(info->last_event, adapter->dbg.last_event,
20273b01e57SJeff Chen 		       sizeof(adapter->dbg.last_event));
20373b01e57SJeff Chen 		info->last_event_index = adapter->dbg.last_event_index;
20473b01e57SJeff Chen 		memcpy(info->last_mp_wr_bitmap, adapter->dbg.last_mp_wr_bitmap,
20573b01e57SJeff Chen 		       sizeof(adapter->dbg.last_mp_wr_bitmap));
20673b01e57SJeff Chen 		memcpy(info->last_mp_wr_ports, adapter->dbg.last_mp_wr_ports,
20773b01e57SJeff Chen 		       sizeof(adapter->dbg.last_mp_wr_ports));
20873b01e57SJeff Chen 		memcpy(info->last_mp_curr_wr_port,
20973b01e57SJeff Chen 		       adapter->dbg.last_mp_curr_wr_port,
21073b01e57SJeff Chen 		       sizeof(adapter->dbg.last_mp_curr_wr_port));
21173b01e57SJeff Chen 		memcpy(info->last_mp_wr_len, adapter->dbg.last_mp_wr_len,
21273b01e57SJeff Chen 		       sizeof(adapter->dbg.last_mp_wr_len));
21373b01e57SJeff Chen 		info->last_sdio_mp_index = adapter->dbg.last_sdio_mp_index;
21473b01e57SJeff Chen 		info->data_sent = adapter->data_sent;
21573b01e57SJeff Chen 		info->cmd_sent = adapter->cmd_sent;
21673b01e57SJeff Chen 		info->cmd_resp_received = adapter->cmd_resp_received;
21773b01e57SJeff Chen 	}
21873b01e57SJeff Chen 
21973b01e57SJeff Chen 	return 0;
22073b01e57SJeff Chen }
22173b01e57SJeff Chen 
nxpwifi_debug_info_to_buffer(struct nxpwifi_private * priv,char * buf,struct nxpwifi_debug_info * info)22273b01e57SJeff Chen int nxpwifi_debug_info_to_buffer(struct nxpwifi_private *priv, char *buf,
22373b01e57SJeff Chen 				 struct nxpwifi_debug_info *info)
22473b01e57SJeff Chen {
22573b01e57SJeff Chen 	char *p = buf;
22673b01e57SJeff Chen 	struct nxpwifi_debug_data *d = &items[0];
22773b01e57SJeff Chen 	size_t size, addr;
22873b01e57SJeff Chen 	long val;
22973b01e57SJeff Chen 	int i, j;
23073b01e57SJeff Chen 
23173b01e57SJeff Chen 	if (!info)
23273b01e57SJeff Chen 		return 0;
23373b01e57SJeff Chen 
23473b01e57SJeff Chen 	for (i = 0; i < num_of_items; i++) {
23573b01e57SJeff Chen 		p += sprintf(p, "%s=", d[i].name);
23673b01e57SJeff Chen 
23773b01e57SJeff Chen 		size = d[i].size / d[i].num;
23873b01e57SJeff Chen 
23973b01e57SJeff Chen 		if (i < (num_of_items - 3))
24073b01e57SJeff Chen 			addr = d[i].addr + (size_t)info;
24173b01e57SJeff Chen 		else /* The last 3 items are struct nxpwifi_adapter variables */
24273b01e57SJeff Chen 			addr = d[i].addr + (size_t)priv->adapter;
24373b01e57SJeff Chen 
24473b01e57SJeff Chen 		for (j = 0; j < d[i].num; j++) {
24573b01e57SJeff Chen 			switch (size) {
24673b01e57SJeff Chen 			case 1:
24773b01e57SJeff Chen 				val = *((u8 *)addr);
24873b01e57SJeff Chen 				break;
24973b01e57SJeff Chen 			case 2:
25073b01e57SJeff Chen 				val = get_unaligned((u16 *)addr);
25173b01e57SJeff Chen 				break;
25273b01e57SJeff Chen 			case 4:
25373b01e57SJeff Chen 				val = get_unaligned((u32 *)addr);
25473b01e57SJeff Chen 				break;
25573b01e57SJeff Chen 			case 8:
25673b01e57SJeff Chen 				val = get_unaligned((long long *)addr);
25773b01e57SJeff Chen 				break;
25873b01e57SJeff Chen 			default:
25973b01e57SJeff Chen 				val = -1;
26073b01e57SJeff Chen 				break;
26173b01e57SJeff Chen 			}
26273b01e57SJeff Chen 
26373b01e57SJeff Chen 			p += sprintf(p, "%#lx ", val);
26473b01e57SJeff Chen 			addr += size;
26573b01e57SJeff Chen 		}
26673b01e57SJeff Chen 
26773b01e57SJeff Chen 		p += sprintf(p, "\n");
26873b01e57SJeff Chen 	}
26973b01e57SJeff Chen 
27073b01e57SJeff Chen 	if (info->tx_tbl_num) {
27173b01e57SJeff Chen 		p += sprintf(p, "Tx BA stream table:\n");
27273b01e57SJeff Chen 		for (i = 0; i < info->tx_tbl_num; i++)
27373b01e57SJeff Chen 			p += sprintf(p, "tid = %d, ra = %pM\n",
27473b01e57SJeff Chen 				     info->tx_tbl[i].tid, info->tx_tbl[i].ra);
27573b01e57SJeff Chen 	}
27673b01e57SJeff Chen 
27773b01e57SJeff Chen 	if (info->rx_tbl_num) {
27873b01e57SJeff Chen 		p += sprintf(p, "Rx reorder table:\n");
27973b01e57SJeff Chen 		for (i = 0; i < info->rx_tbl_num; i++) {
28073b01e57SJeff Chen 			p += sprintf(p, "tid = %d, ta = %pM, ",
28173b01e57SJeff Chen 				     info->rx_tbl[i].tid,
28273b01e57SJeff Chen 				     info->rx_tbl[i].ta);
28373b01e57SJeff Chen 			p += sprintf(p, "start_win = %d, ",
28473b01e57SJeff Chen 				     info->rx_tbl[i].start_win);
28573b01e57SJeff Chen 			p += sprintf(p, "win_size = %d, buffer: ",
28673b01e57SJeff Chen 				     info->rx_tbl[i].win_size);
28773b01e57SJeff Chen 
28873b01e57SJeff Chen 			for (j = 0; j < info->rx_tbl[i].win_size; j++)
28973b01e57SJeff Chen 				p += sprintf(p, "%c ",
29073b01e57SJeff Chen 					     info->rx_tbl[i].buffer[j] ?
29173b01e57SJeff Chen 					     '1' : '0');
29273b01e57SJeff Chen 
29373b01e57SJeff Chen 			p += sprintf(p, "\n");
29473b01e57SJeff Chen 		}
29573b01e57SJeff Chen 	}
29673b01e57SJeff Chen 
29773b01e57SJeff Chen 	return p - buf;
29873b01e57SJeff Chen }
29973b01e57SJeff Chen 
nxpwifi_is_channel_setting_allowable(struct nxpwifi_private * priv,struct ieee80211_channel * check_chan)30073b01e57SJeff Chen bool nxpwifi_is_channel_setting_allowable(struct nxpwifi_private *priv,
30173b01e57SJeff Chen 					  struct ieee80211_channel *check_chan)
30273b01e57SJeff Chen {
30373b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
30473b01e57SJeff Chen 	int i;
30573b01e57SJeff Chen 	struct nxpwifi_private *tmp_priv;
30673b01e57SJeff Chen 	u8 bss_role = GET_BSS_ROLE(priv);
30773b01e57SJeff Chen 	struct ieee80211_channel *set_chan;
30873b01e57SJeff Chen 
30973b01e57SJeff Chen 	for (i = 0; i < adapter->priv_num; i++) {
31073b01e57SJeff Chen 		tmp_priv = adapter->priv[i];
31173b01e57SJeff Chen 		if (tmp_priv == priv)
31273b01e57SJeff Chen 			continue;
31373b01e57SJeff Chen 
31473b01e57SJeff Chen 		set_chan = NULL;
31573b01e57SJeff Chen 		if (bss_role == NXPWIFI_BSS_ROLE_STA) {
31673b01e57SJeff Chen 			if (GET_BSS_ROLE(tmp_priv) == NXPWIFI_BSS_ROLE_UAP &&
31773b01e57SJeff Chen 			    netif_carrier_ok(tmp_priv->netdev) &&
31873b01e57SJeff Chen 			    cfg80211_chandef_valid(&tmp_priv->bss_chandef))
31973b01e57SJeff Chen 				set_chan = tmp_priv->bss_chandef.chan;
32073b01e57SJeff Chen 		} else if (bss_role == NXPWIFI_BSS_ROLE_UAP) {
32173b01e57SJeff Chen 			struct nxpwifi_current_bss_params *bss_params =
32273b01e57SJeff Chen 				&tmp_priv->curr_bss_params;
32373b01e57SJeff Chen 			int channel = bss_params->bss_descriptor.channel;
32473b01e57SJeff Chen 			enum nl80211_band band =
32573b01e57SJeff Chen 				nxpwifi_band_to_radio_type(bss_params->band);
32673b01e57SJeff Chen 			int freq =
32773b01e57SJeff Chen 				ieee80211_channel_to_frequency(channel, band);
32873b01e57SJeff Chen 
32973b01e57SJeff Chen 			if (GET_BSS_ROLE(tmp_priv) == NXPWIFI_BSS_ROLE_STA &&
33073b01e57SJeff Chen 			    tmp_priv->media_connected)
33173b01e57SJeff Chen 				set_chan = ieee80211_get_channel(adapter->wiphy, freq);
33273b01e57SJeff Chen 		}
33373b01e57SJeff Chen 
33473b01e57SJeff Chen 		if (set_chan && !ieee80211_channel_equal(check_chan, set_chan)) {
33573b01e57SJeff Chen 			nxpwifi_dbg(adapter, ERROR,
33673b01e57SJeff Chen 				    "AP/STA must run on the same channel\n");
33773b01e57SJeff Chen 			return false;
33873b01e57SJeff Chen 		}
33973b01e57SJeff Chen 	}
34073b01e57SJeff Chen 
34173b01e57SJeff Chen 	return true;
34273b01e57SJeff Chen }
34373b01e57SJeff Chen 
nxpwifi_convert_chan_to_band_cfg(struct nxpwifi_private * priv,u8 * band_cfg,struct cfg80211_chan_def * chan_def)34473b01e57SJeff Chen void nxpwifi_convert_chan_to_band_cfg(struct nxpwifi_private *priv,
34573b01e57SJeff Chen 				      u8 *band_cfg,
34673b01e57SJeff Chen 				      struct cfg80211_chan_def *chan_def)
34773b01e57SJeff Chen {
34873b01e57SJeff Chen 	u8 chan_band = 0, chan_width = 0, chan2_offset = 0;
34973b01e57SJeff Chen 
35073b01e57SJeff Chen 	switch (chan_def->chan->band) {
35173b01e57SJeff Chen 	case NL80211_BAND_2GHZ:
35273b01e57SJeff Chen 		chan_band = BAND_2GHZ;
35373b01e57SJeff Chen 		break;
35473b01e57SJeff Chen 	case NL80211_BAND_5GHZ:
35573b01e57SJeff Chen 		chan_band = BAND_5GHZ;
35673b01e57SJeff Chen 		break;
35773b01e57SJeff Chen 	default:
35873b01e57SJeff Chen 		break;
35973b01e57SJeff Chen 	}
36073b01e57SJeff Chen 
36173b01e57SJeff Chen 	switch (chan_def->width) {
36273b01e57SJeff Chen 	case NL80211_CHAN_WIDTH_20_NOHT:
36373b01e57SJeff Chen 	case NL80211_CHAN_WIDTH_20:
36473b01e57SJeff Chen 		chan_width = CHAN_BW_20MHZ;
36573b01e57SJeff Chen 		break;
36673b01e57SJeff Chen 	case NL80211_CHAN_WIDTH_40:
36773b01e57SJeff Chen 		chan_width = CHAN_BW_40MHZ;
36873b01e57SJeff Chen 		if (chan_def->center_freq1 > chan_def->chan->center_freq)
36973b01e57SJeff Chen 			chan2_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
37073b01e57SJeff Chen 		else
37173b01e57SJeff Chen 			chan2_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
37273b01e57SJeff Chen 		break;
37373b01e57SJeff Chen 	case NL80211_CHAN_WIDTH_80:
37473b01e57SJeff Chen 		chan2_offset =
37573b01e57SJeff Chen 			nxpwifi_get_sec_chan_offset(chan_def->chan->hw_value);
37673b01e57SJeff Chen 		chan_width = CHAN_BW_80MHZ;
37773b01e57SJeff Chen 		break;
37873b01e57SJeff Chen 	case NL80211_CHAN_WIDTH_80P80:
37973b01e57SJeff Chen 	case NL80211_CHAN_WIDTH_160:
38073b01e57SJeff Chen 	default:
38173b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter,
38273b01e57SJeff Chen 			    WARN, "Unknown channel width: %d\n",
38373b01e57SJeff Chen 			    chan_def->width);
38473b01e57SJeff Chen 		break;
38573b01e57SJeff Chen 	}
38673b01e57SJeff Chen 
38773b01e57SJeff Chen 	*band_cfg = ((chan2_offset << BAND_CFG_CHAN2_SHIFT_BIT) &
38873b01e57SJeff Chen 		     BAND_CFG_CHAN2_OFFSET_MASK) |
38973b01e57SJeff Chen 		    ((chan_width << BAND_CFG_CHAN_WIDTH_SHIFT_BIT) &
39073b01e57SJeff Chen 		     BAND_CFG_CHAN_WIDTH_MASK) |
39173b01e57SJeff Chen 		    ((chan_band << BAND_CFG_CHAN_BAND_SHIFT_BIT) &
39273b01e57SJeff Chen 		     BAND_CFG_CHAN_BAND_MASK);
39373b01e57SJeff Chen }
39473b01e57SJeff Chen 
39573b01e57SJeff Chen static int
nxpwifi_parse_mgmt_packet(struct nxpwifi_private * priv,u8 * payload,u16 len,struct rxpd * rx_pd)39673b01e57SJeff Chen nxpwifi_parse_mgmt_packet(struct nxpwifi_private *priv, u8 *payload, u16 len,
39773b01e57SJeff Chen 			  struct rxpd *rx_pd)
39873b01e57SJeff Chen {
39973b01e57SJeff Chen 	u16 stype;
40073b01e57SJeff Chen 	u8 category;
40173b01e57SJeff Chen 	struct ieee80211_hdr *ieee_hdr = (void *)payload;
40273b01e57SJeff Chen 
40373b01e57SJeff Chen 	stype = (le16_to_cpu(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE);
40473b01e57SJeff Chen 
40573b01e57SJeff Chen 	switch (stype) {
40673b01e57SJeff Chen 	case IEEE80211_STYPE_ACTION:
40773b01e57SJeff Chen 		category = *(payload + sizeof(struct ieee80211_hdr));
40873b01e57SJeff Chen 		switch (category) {
40973b01e57SJeff Chen 		case WLAN_CATEGORY_BACK:
41073b01e57SJeff Chen 			/*we dont indicate BACK action frames to cfg80211*/
41173b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, INFO,
41273b01e57SJeff Chen 				    "drop BACK action frames");
41373b01e57SJeff Chen 			return -EINVAL;
41473b01e57SJeff Chen 		default:
41573b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, INFO,
41673b01e57SJeff Chen 				    "unknown public action frame category %d\n",
41773b01e57SJeff Chen 				    category);
41873b01e57SJeff Chen 		}
41973b01e57SJeff Chen 		break;
42073b01e57SJeff Chen 	default:
42173b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO,
42273b01e57SJeff Chen 			    "unknown mgmt frame subtype %#x\n", stype);
42373b01e57SJeff Chen 		return 0;
42473b01e57SJeff Chen 	}
42573b01e57SJeff Chen 
42673b01e57SJeff Chen 	return 0;
42773b01e57SJeff Chen }
42873b01e57SJeff Chen 
42973b01e57SJeff Chen /* Send deauth frame to cfg80211. */
nxpwifi_host_mlme_disconnect(struct nxpwifi_private * priv,u16 reason_code,u8 * sa)43073b01e57SJeff Chen void nxpwifi_host_mlme_disconnect(struct nxpwifi_private *priv,
43173b01e57SJeff Chen 				  u16 reason_code, u8 *sa)
43273b01e57SJeff Chen {
43373b01e57SJeff Chen 	u8 frame_buf[100];
43473b01e57SJeff Chen 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)frame_buf;
43573b01e57SJeff Chen 
43673b01e57SJeff Chen 	memset(frame_buf, 0, sizeof(frame_buf));
43773b01e57SJeff Chen 	mgmt->frame_control = cpu_to_le16(IEEE80211_STYPE_DEAUTH);
43873b01e57SJeff Chen 	mgmt->duration = 0;
43973b01e57SJeff Chen 	mgmt->seq_ctrl = 0;
44073b01e57SJeff Chen 	mgmt->u.deauth.reason_code = cpu_to_le16(reason_code);
44173b01e57SJeff Chen 
44273b01e57SJeff Chen 	if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) {
44373b01e57SJeff Chen 		eth_broadcast_addr(mgmt->da);
44473b01e57SJeff Chen 		memcpy(mgmt->sa,
44573b01e57SJeff Chen 		       priv->curr_bss_params.bss_descriptor.mac_address,
44673b01e57SJeff Chen 		       ETH_ALEN);
44773b01e57SJeff Chen 		memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN);
44873b01e57SJeff Chen 		priv->auth_flag = 0;
44973b01e57SJeff Chen 		priv->auth_alg = WLAN_AUTH_NONE;
45073b01e57SJeff Chen 	} else {
45173b01e57SJeff Chen 		memcpy(mgmt->da, priv->curr_addr, ETH_ALEN);
45273b01e57SJeff Chen 		memcpy(mgmt->sa, sa, ETH_ALEN);
45373b01e57SJeff Chen 		memcpy(mgmt->bssid, priv->curr_addr, ETH_ALEN);
45473b01e57SJeff Chen 	}
45573b01e57SJeff Chen 
45673b01e57SJeff Chen 	if (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_UAP) {
45773b01e57SJeff Chen 		cfg80211_rx_mlme_mgmt(priv->netdev, frame_buf, 26);
45873b01e57SJeff Chen 	} else {
45973b01e57SJeff Chen 		cfg80211_rx_mgmt(&priv->wdev,
46073b01e57SJeff Chen 				 priv->bss_chandef.chan->center_freq,
46173b01e57SJeff Chen 				 0, frame_buf, 26, 0);
46273b01e57SJeff Chen 	}
46373b01e57SJeff Chen }
46473b01e57SJeff Chen 
46573b01e57SJeff Chen /* Parse and forward received management packet to cfg80211. */
46673b01e57SJeff Chen int
nxpwifi_process_mgmt_packet(struct nxpwifi_private * priv,struct sk_buff * skb)46773b01e57SJeff Chen nxpwifi_process_mgmt_packet(struct nxpwifi_private *priv,
46873b01e57SJeff Chen 			    struct sk_buff *skb)
46973b01e57SJeff Chen {
47073b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
47173b01e57SJeff Chen 	struct rxpd *rx_pd;
47273b01e57SJeff Chen 	u16 pkt_len;
47373b01e57SJeff Chen 	struct ieee80211_hdr *ieee_hdr;
47473b01e57SJeff Chen 	int ret;
47573b01e57SJeff Chen 
47673b01e57SJeff Chen 	if (!skb)
47773b01e57SJeff Chen 		return -ENOMEM;
47873b01e57SJeff Chen 
47973b01e57SJeff Chen 	if (!priv->mgmt_frame_mask ||
48073b01e57SJeff Chen 	    priv->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) {
48173b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR,
48273b01e57SJeff Chen 			    "do not receive mgmt frames on uninitialized intf");
48373b01e57SJeff Chen 		return -EINVAL;
48473b01e57SJeff Chen 	}
48573b01e57SJeff Chen 
48673b01e57SJeff Chen 	rx_pd = (struct rxpd *)skb->data;
48773b01e57SJeff Chen 	pkt_len = le16_to_cpu(rx_pd->rx_pkt_length);
48873b01e57SJeff Chen 	if (pkt_len < sizeof(struct ieee80211_hdr) + sizeof(pkt_len)) {
48973b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR, "invalid rx_pkt_length");
49073b01e57SJeff Chen 		return -EINVAL;
49173b01e57SJeff Chen 	}
49273b01e57SJeff Chen 
49373b01e57SJeff Chen 	skb_pull(skb, le16_to_cpu(rx_pd->rx_pkt_offset));
49473b01e57SJeff Chen 	skb_pull(skb, sizeof(pkt_len));
49573b01e57SJeff Chen 	pkt_len -= sizeof(pkt_len);
49673b01e57SJeff Chen 
49773b01e57SJeff Chen 	ieee_hdr = (void *)skb->data;
49873b01e57SJeff Chen 	if (ieee80211_is_mgmt(ieee_hdr->frame_control)) {
49973b01e57SJeff Chen 		ret = nxpwifi_parse_mgmt_packet(priv, (u8 *)ieee_hdr,
50073b01e57SJeff Chen 						pkt_len, rx_pd);
50173b01e57SJeff Chen 		if (ret)
50273b01e57SJeff Chen 			return ret;
50373b01e57SJeff Chen 	}
50473b01e57SJeff Chen 	/* Remove address4 */
50573b01e57SJeff Chen 	memmove(skb->data + sizeof(struct ieee80211_hdr_3addr),
50673b01e57SJeff Chen 		skb->data + sizeof(struct ieee80211_hdr),
50773b01e57SJeff Chen 		pkt_len - sizeof(struct ieee80211_hdr));
50873b01e57SJeff Chen 
50973b01e57SJeff Chen 	pkt_len -= ETH_ALEN;
51073b01e57SJeff Chen 	rx_pd->rx_pkt_length = cpu_to_le16(pkt_len);
51173b01e57SJeff Chen 
51273b01e57SJeff Chen 	if (priv->host_mlme_reg &&
51373b01e57SJeff Chen 	    (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_UAP) &&
51473b01e57SJeff Chen 	    (ieee80211_is_auth(ieee_hdr->frame_control) ||
51573b01e57SJeff Chen 	     ieee80211_is_deauth(ieee_hdr->frame_control) ||
51673b01e57SJeff Chen 	     ieee80211_is_disassoc(ieee_hdr->frame_control))) {
51773b01e57SJeff Chen 		struct nxpwifi_rxinfo *rx_info;
51873b01e57SJeff Chen 
51973b01e57SJeff Chen 		if (ieee80211_is_auth(ieee_hdr->frame_control)) {
52073b01e57SJeff Chen 			if (priv->auth_flag & HOST_MLME_AUTH_PENDING) {
52173b01e57SJeff Chen 				if (priv->auth_alg != WLAN_AUTH_SAE) {
52273b01e57SJeff Chen 					priv->auth_flag &=
52373b01e57SJeff Chen 						~HOST_MLME_AUTH_PENDING;
52473b01e57SJeff Chen 					priv->auth_flag |=
52573b01e57SJeff Chen 						HOST_MLME_AUTH_DONE;
52673b01e57SJeff Chen 				}
52773b01e57SJeff Chen 			} else {
52873b01e57SJeff Chen 				return 0;
52973b01e57SJeff Chen 			}
53073b01e57SJeff Chen 
53173b01e57SJeff Chen 			nxpwifi_dbg(adapter, MSG,
53273b01e57SJeff Chen 				    "auth: receive authentication from %pM\n",
53373b01e57SJeff Chen 				    ieee_hdr->addr3);
53473b01e57SJeff Chen 		} else {
53573b01e57SJeff Chen 			if (!priv->wdev.connected)
53673b01e57SJeff Chen 				return 0;
53773b01e57SJeff Chen 
53873b01e57SJeff Chen 			if (ieee80211_is_deauth(ieee_hdr->frame_control)) {
53973b01e57SJeff Chen 				nxpwifi_dbg(adapter, MSG,
54073b01e57SJeff Chen 					    "auth: receive deauth from %pM\n",
54173b01e57SJeff Chen 					    ieee_hdr->addr3);
54273b01e57SJeff Chen 				priv->auth_flag = 0;
54373b01e57SJeff Chen 				priv->auth_alg = WLAN_AUTH_NONE;
54473b01e57SJeff Chen 			} else {
54573b01e57SJeff Chen 				nxpwifi_dbg(adapter, MSG,
54673b01e57SJeff Chen 					    "assoc: receive disassoc from %pM\n",
54773b01e57SJeff Chen 					    ieee_hdr->addr3);
54873b01e57SJeff Chen 			}
54973b01e57SJeff Chen 		}
55073b01e57SJeff Chen 
55173b01e57SJeff Chen 		rx_info = NXPWIFI_SKB_RXCB(skb);
55273b01e57SJeff Chen 		rx_info->pkt_len = pkt_len;
55373b01e57SJeff Chen 		skb_queue_tail(&adapter->rx_mlme_q, skb);
55473b01e57SJeff Chen 		nxpwifi_queue_wiphy_work(adapter, &adapter->host_mlme_work);
55573b01e57SJeff Chen 		return -EINPROGRESS;
55673b01e57SJeff Chen 	}
55773b01e57SJeff Chen 
55873b01e57SJeff Chen 	if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
55973b01e57SJeff Chen 		if (ieee80211_is_auth(ieee_hdr->frame_control))
56073b01e57SJeff Chen 			nxpwifi_dbg(adapter, MSG,
56173b01e57SJeff Chen 				    "auth: receive auth from %pM\n",
56273b01e57SJeff Chen 				    ieee_hdr->addr2);
56373b01e57SJeff Chen 		if (ieee80211_is_deauth(ieee_hdr->frame_control))
56473b01e57SJeff Chen 			nxpwifi_dbg(adapter, MSG,
56573b01e57SJeff Chen 				    "auth: receive deauth from %pM\n",
56673b01e57SJeff Chen 				    ieee_hdr->addr2);
56773b01e57SJeff Chen 		if (ieee80211_is_disassoc(ieee_hdr->frame_control))
56873b01e57SJeff Chen 			nxpwifi_dbg(adapter, MSG,
56973b01e57SJeff Chen 				    "assoc: receive disassoc from %pM\n",
57073b01e57SJeff Chen 				    ieee_hdr->addr2);
57173b01e57SJeff Chen 		if (ieee80211_is_assoc_req(ieee_hdr->frame_control))
57273b01e57SJeff Chen 			nxpwifi_dbg(adapter, MSG,
57373b01e57SJeff Chen 				    "assoc: receive assoc req from %pM\n",
57473b01e57SJeff Chen 				    ieee_hdr->addr2);
57573b01e57SJeff Chen 		if (ieee80211_is_reassoc_req(ieee_hdr->frame_control))
57673b01e57SJeff Chen 			nxpwifi_dbg(adapter, MSG,
57773b01e57SJeff Chen 				    "assoc: receive reassoc req from %pM\n",
57873b01e57SJeff Chen 				    ieee_hdr->addr2);
57973b01e57SJeff Chen 	}
58073b01e57SJeff Chen 
58173b01e57SJeff Chen 	cfg80211_rx_mgmt(&priv->wdev, priv->roc_cfg.chan.center_freq,
58273b01e57SJeff Chen 			 CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len,
58373b01e57SJeff Chen 			 0);
58473b01e57SJeff Chen 
58573b01e57SJeff Chen 	return 0;
58673b01e57SJeff Chen }
58773b01e57SJeff Chen 
58873b01e57SJeff Chen #define RTAP_MAX_LEN 128
58973b01e57SJeff Chen 
nxpwifi_recv_packet_to_monif(struct nxpwifi_private * priv,struct sk_buff * skb)59073b01e57SJeff Chen int nxpwifi_recv_packet_to_monif(struct nxpwifi_private *priv,
59173b01e57SJeff Chen 				 struct sk_buff *skb)
59273b01e57SJeff Chen {
59373b01e57SJeff Chen 	struct rxpd *rxpd;
59473b01e57SJeff Chen 	struct rxpd_extra_info ext;
59573b01e57SJeff Chen 	struct ieee80211_hdr *dot11;
59673b01e57SJeff Chen 	struct ieee80211_radiotap_header *hdr;
59773b01e57SJeff Chen 	int freq, band;
59873b01e57SJeff Chen 	bool has_ext = false, has_ts = false, use_tsft = false;
59973b01e57SJeff Chen 	u16 rx_flags = 0, off, chan_flags, rx_pkt_offset;
60073b01e57SJeff Chen 	__le16 freq_le, flags_le;
60173b01e57SJeff Chen 	u8 rthdr[RTAP_MAX_LEN] = {0};
60273b01e57SJeff Chen 	s8 signal, noise;
60373b01e57SJeff Chen 	u8 flags = 0, chan, ant, format, bw, gi, ldpc, mcs, nss, stbc;
60473b01e57SJeff Chen 
60573b01e57SJeff Chen 	if (!skb)
60673b01e57SJeff Chen 		return -EINVAL;
60773b01e57SJeff Chen 
60873b01e57SJeff Chen 	rxpd = (struct rxpd *)skb->data;
60973b01e57SJeff Chen 	rx_pkt_offset = le16_to_cpu(rxpd->rx_pkt_offset);
61073b01e57SJeff Chen 
61173b01e57SJeff Chen 	if (rx_pkt_offset > skb->len || rx_pkt_offset < sizeof(struct rxpd))
61273b01e57SJeff Chen 		return -EINVAL;
61373b01e57SJeff Chen 
61473b01e57SJeff Chen 	if (skb->len - rx_pkt_offset < sizeof(struct ieee80211_hdr))
61573b01e57SJeff Chen 		return -EINVAL;
61673b01e57SJeff Chen 
61773b01e57SJeff Chen 	has_ext = rxpd->flags & RXPD_FLAG_EXTRA_HEADER;
61873b01e57SJeff Chen 
61973b01e57SJeff Chen 	if (has_ext)
62073b01e57SJeff Chen 		memcpy((void *)&ext, (void *)(rxpd + 1), sizeof(ext));
62173b01e57SJeff Chen 
62273b01e57SJeff Chen 	dot11 = (void *)skb->data + rx_pkt_offset;
62373b01e57SJeff Chen 	memset(rthdr, 0, sizeof(rthdr));
62473b01e57SJeff Chen 	hdr = (void *)rthdr;
62573b01e57SJeff Chen 	hdr->it_version = 0;
62673b01e57SJeff Chen 	hdr->it_pad = 0;
62773b01e57SJeff Chen 	hdr->it_present = 0;
62873b01e57SJeff Chen 	off = sizeof(*hdr);
62973b01e57SJeff Chen 	hdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_FLAGS));
63073b01e57SJeff Chen 
63173b01e57SJeff Chen 	if (has_ext) {
63273b01e57SJeff Chen 		has_ts = true;
63373b01e57SJeff Chen 		use_tsft = (ext.timestamp.position == 0);
63473b01e57SJeff Chen 
63573b01e57SJeff Chen 		if (has_ts) {
63673b01e57SJeff Chen 			if (use_tsft)
63773b01e57SJeff Chen 				hdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_TSFT));
63873b01e57SJeff Chen 			else
63973b01e57SJeff Chen 				hdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_TIMESTAMP));
64073b01e57SJeff Chen 		}
64173b01e57SJeff Chen 
64273b01e57SJeff Chen 		flags = ext.flags & ~IEEE80211_RADIOTAP_F_BADFCS;
64373b01e57SJeff Chen 		/* reverse fail fcs, 1 means pass FCS in FW,
64473b01e57SJeff Chen 		 * but means fail FCS in radiotap
64573b01e57SJeff Chen 		 */
64673b01e57SJeff Chen 		flags &= ~((~ext.flags) & IEEE80211_RADIOTAP_F_BADFCS);
64773b01e57SJeff Chen 
64873b01e57SJeff Chen 		if (ext.plcp_crc_failed)
64973b01e57SJeff Chen 			rx_flags |= IEEE80211_RADIOTAP_F_RX_BADPLCP;
65073b01e57SJeff Chen 	}
65173b01e57SJeff Chen 
65273b01e57SJeff Chen 	if (has_ts && use_tsft) {
65373b01e57SJeff Chen 		off = ALIGN(off, 8);
65473b01e57SJeff Chen 		memcpy(rthdr + off, &ext.timestamp.device_timestamp, 8);
65573b01e57SJeff Chen 		off += 8;
65673b01e57SJeff Chen 	}
65773b01e57SJeff Chen 
65873b01e57SJeff Chen 	if (ieee80211_has_morefrags(dot11->frame_control))
65973b01e57SJeff Chen 		flags |= IEEE80211_RADIOTAP_F_FRAG;
66073b01e57SJeff Chen 
66173b01e57SJeff Chen 	if (ieee80211_has_protected(dot11->frame_control))
66273b01e57SJeff Chen 		flags |= IEEE80211_RADIOTAP_F_WEP;
66373b01e57SJeff Chen 
66473b01e57SJeff Chen 	rthdr[off++] = flags;
66573b01e57SJeff Chen 	off = ALIGN(off, 2);
66673b01e57SJeff Chen 	hdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_CHANNEL));
66773b01e57SJeff Chen 	chan = (le32_to_cpu(rxpd->rx_info) >> 5) & 0xff;
66873b01e57SJeff Chen 	band = (chan <= 14) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
66973b01e57SJeff Chen 	freq = ieee80211_channel_to_frequency(chan, band);
67073b01e57SJeff Chen 
67173b01e57SJeff Chen 	if (has_ext)
67273b01e57SJeff Chen 		chan_flags = ext.channel_flags;
67373b01e57SJeff Chen 	else if (band == NL80211_BAND_2GHZ)
67473b01e57SJeff Chen 		chan_flags = IEEE80211_CHAN_2GHZ;
67573b01e57SJeff Chen 	else
67673b01e57SJeff Chen 		chan_flags = IEEE80211_CHAN_5GHZ;
67773b01e57SJeff Chen 
67873b01e57SJeff Chen 	freq_le  = cpu_to_le16(freq);
67973b01e57SJeff Chen 	flags_le = cpu_to_le16(chan_flags);
68073b01e57SJeff Chen 	memcpy(rthdr + off, &freq_le, 2);
68173b01e57SJeff Chen 	off += 2;
68273b01e57SJeff Chen 	memcpy(rthdr + off, &flags_le, 2);
68373b01e57SJeff Chen 	off += 2;
68473b01e57SJeff Chen 	hdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL));
68573b01e57SJeff Chen 	signal = -(rxpd->nf - rxpd->snr);
68673b01e57SJeff Chen 	rthdr[off++] = signal;
68773b01e57SJeff Chen 	hdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_DBM_ANTNOISE));
68873b01e57SJeff Chen 	noise = -rxpd->nf;
68973b01e57SJeff Chen 	rthdr[off++] = noise;
69073b01e57SJeff Chen 	hdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_ANTENNA));
69173b01e57SJeff Chen 	ant = rxpd->antenna >> 1;
69273b01e57SJeff Chen 	rthdr[off++] = ant;
69373b01e57SJeff Chen 
69473b01e57SJeff Chen 	if (rx_flags) {
69573b01e57SJeff Chen 		hdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_RX_FLAGS));
69673b01e57SJeff Chen 		off = ALIGN(off, 2);
69773b01e57SJeff Chen 		memcpy(rthdr + off, &rx_flags, 2);
69873b01e57SJeff Chen 		off += 2;
69973b01e57SJeff Chen 	}
70073b01e57SJeff Chen 
70173b01e57SJeff Chen 	format = FIELD_GET(RX_RATE_FORMAT_MASK, rxpd->rate_info);
70273b01e57SJeff Chen 	bw = FIELD_GET(RX_RATE_BW_MASK, rxpd->rate_info);
70373b01e57SJeff Chen 	ldpc = FIELD_GET(RX_RATE_LDPC_MASK, rxpd->rate_info) ? 1 : 0;
70473b01e57SJeff Chen 	stbc = FIELD_GET(RX_RATE_STBC_MASK, rxpd->rate_info) ? 1 : 0;
70573b01e57SJeff Chen 
70673b01e57SJeff Chen 	if (format == NXPWIFI_RATE_FORMAT_HE) {
70773b01e57SJeff Chen 		gi = ((rxpd->rate_info >> 7) & 0x1) << 1 |
70873b01e57SJeff Chen 			((rxpd->rate_info >> 4) & 0x1);
70973b01e57SJeff Chen 	} else {
71073b01e57SJeff Chen 		gi = FIELD_GET(RX_RATE_GI_MASK, rxpd->rate_info);
71173b01e57SJeff Chen 	}
71273b01e57SJeff Chen 
71373b01e57SJeff Chen 	mcs = rxpd->rx_rate & 0xf;
71473b01e57SJeff Chen 	nss = ((rxpd->rx_rate >> 4) & 0xf) + 1;
71573b01e57SJeff Chen 
71673b01e57SJeff Chen 	if (format == NXPWIFI_RATE_FORMAT_HT) {
71773b01e57SJeff Chen 		u8 mcs_flags = 0;
71873b01e57SJeff Chen 
71973b01e57SJeff Chen 		hdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_MCS));
72073b01e57SJeff Chen 		rthdr[off++] = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
72173b01e57SJeff Chen 					IEEE80211_RADIOTAP_MCS_HAVE_BW |
72273b01e57SJeff Chen 					IEEE80211_RADIOTAP_MCS_HAVE_GI;
72373b01e57SJeff Chen 
72473b01e57SJeff Chen 		if (bw == 1)
72573b01e57SJeff Chen 			mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_40;
72673b01e57SJeff Chen 
72773b01e57SJeff Chen 		if (gi)
72873b01e57SJeff Chen 			mcs_flags |= IEEE80211_RADIOTAP_MCS_SGI;
72973b01e57SJeff Chen 
73073b01e57SJeff Chen 		if (ldpc)
73173b01e57SJeff Chen 			mcs_flags |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;
73273b01e57SJeff Chen 
73373b01e57SJeff Chen 		if (stbc)
73473b01e57SJeff Chen 			mcs_flags |= (1 << IEEE80211_RADIOTAP_MCS_STBC_SHIFT);
73573b01e57SJeff Chen 
73673b01e57SJeff Chen 		rthdr[off++] = mcs_flags;
73773b01e57SJeff Chen 		rthdr[off++] = mcs;
73873b01e57SJeff Chen 	}
73973b01e57SJeff Chen 
74073b01e57SJeff Chen 	if (format == NXPWIFI_RATE_FORMAT_VHT && has_ext) {
74173b01e57SJeff Chen 		struct ieee80211_radiotap_vht vht = {0};
74273b01e57SJeff Chen 		u32 vht_sig1 = 0, vht_sig2 = 0;
74373b01e57SJeff Chen 		__le16 partial_aid = 0;
74473b01e57SJeff Chen 		u8 bw_field = 0;
74573b01e57SJeff Chen 
74673b01e57SJeff Chen 		vht_sig1 = ext.vht_he_sig1;
74773b01e57SJeff Chen 		vht_sig2 = ext.vht_he_sig2;
74873b01e57SJeff Chen 		hdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_VHT));
74973b01e57SJeff Chen 		vht.known = cpu_to_le16(IEEE80211_RADIOTAP_VHT_KNOWN_GI |
75073b01e57SJeff Chen 					IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH |
75173b01e57SJeff Chen 					IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
75273b01e57SJeff Chen 					IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED |
75373b01e57SJeff Chen 					IEEE80211_RADIOTAP_VHT_KNOWN_GROUP_ID);
75473b01e57SJeff Chen 
75573b01e57SJeff Chen 		if (stbc)
75673b01e57SJeff Chen 			vht.flags |= IEEE80211_RADIOTAP_VHT_FLAG_STBC;
75773b01e57SJeff Chen 
75873b01e57SJeff Chen 		if (gi)
75973b01e57SJeff Chen 			vht.flags |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
76073b01e57SJeff Chen 
76173b01e57SJeff Chen 		if (vht_sig2 & BIT(1))
76273b01e57SJeff Chen 			vht.flags |= IEEE80211_RADIOTAP_VHT_FLAG_SGI_NSYM_M10_9;
76373b01e57SJeff Chen 
76473b01e57SJeff Chen 		if (vht_sig2 & BIT(8))
76573b01e57SJeff Chen 			vht.flags |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED;
76673b01e57SJeff Chen 
76773b01e57SJeff Chen 		switch (bw) {
76873b01e57SJeff Chen 		case 1:
76973b01e57SJeff Chen 			bw_field = 1; /* 40 MHz */
77073b01e57SJeff Chen 			break;
77173b01e57SJeff Chen 		case 2:
77273b01e57SJeff Chen 			bw_field = 4; /* 80 MHz */
77373b01e57SJeff Chen 			break;
77473b01e57SJeff Chen 		case 3:
77573b01e57SJeff Chen 			bw_field = 11; /* 160 MHz */
77673b01e57SJeff Chen 			break;
77773b01e57SJeff Chen 		default:
77873b01e57SJeff Chen 			bw_field = 0; /* 20 MHz */
77973b01e57SJeff Chen 		}
78073b01e57SJeff Chen 
78173b01e57SJeff Chen 		vht.bandwidth = bw_field;
78273b01e57SJeff Chen 		vht.mcs_nss[0] = (nss & 0xf) | (mcs << 4);
78373b01e57SJeff Chen 
78473b01e57SJeff Chen 		if (vht_sig2 & BIT(2))
78573b01e57SJeff Chen 			vht.coding |= IEEE80211_RADIOTAP_CODING_LDPC_USER0;
78673b01e57SJeff Chen 
78773b01e57SJeff Chen 		vht.group_id = (vht_sig1 >> 4) & 0x3f;
78873b01e57SJeff Chen 		memcpy(&vht.partial_aid, &partial_aid, 2);
78973b01e57SJeff Chen 		off = ALIGN(off, 2);
79073b01e57SJeff Chen 		memcpy(rthdr + off, &vht, sizeof(vht));
79173b01e57SJeff Chen 		off += sizeof(vht);
79273b01e57SJeff Chen 	}
79373b01e57SJeff Chen 
79473b01e57SJeff Chen 	/* TIMESTAMP */
79573b01e57SJeff Chen 	if (has_ts && !use_tsft) {
79673b01e57SJeff Chen 		u64 ts;
79773b01e57SJeff Chen 		__le64 ts_le;
79873b01e57SJeff Chen 		u16 accuracy = 0;
79973b01e57SJeff Chen 		__le16 acc_le;
80073b01e57SJeff Chen 		u8 flags = 0;
80173b01e57SJeff Chen 
802*00c786a7SJeff Chen 		hdr->it_present |=
803*00c786a7SJeff Chen 			cpu_to_le32(BIT(IEEE80211_RADIOTAP_TIMESTAMP));
80473b01e57SJeff Chen 		off = ALIGN(off, 8);
80573b01e57SJeff Chen 
80673b01e57SJeff Chen 		if (ext.timestamp.flags & 0x01) {
80773b01e57SJeff Chen 			flags |= IEEE80211_RADIOTAP_TIMESTAMP_FLAG_32BIT;
80873b01e57SJeff Chen 			ts = (u32)ext.timestamp.device_timestamp;
80973b01e57SJeff Chen 		} else {
81073b01e57SJeff Chen 			flags |= IEEE80211_RADIOTAP_TIMESTAMP_FLAG_64BIT;
81173b01e57SJeff Chen 			ts = ext.timestamp.device_timestamp;
81273b01e57SJeff Chen 		}
81373b01e57SJeff Chen 
81473b01e57SJeff Chen 		ts_le = cpu_to_le64(ts);
81573b01e57SJeff Chen 		memcpy(rthdr + off, &ts_le, sizeof(ts_le));
81673b01e57SJeff Chen 		off += sizeof(ts_le);
81773b01e57SJeff Chen 
81873b01e57SJeff Chen 		if (ext.timestamp.flags & 0x02) {
81973b01e57SJeff Chen 			accuracy = ext.timestamp.accuracy;
82073b01e57SJeff Chen 			flags |= IEEE80211_RADIOTAP_TIMESTAMP_FLAG_ACCURACY;
82173b01e57SJeff Chen 		}
82273b01e57SJeff Chen 
82373b01e57SJeff Chen 		acc_le = cpu_to_le16(accuracy);
82473b01e57SJeff Chen 		memcpy(rthdr + off, &acc_le, sizeof(acc_le));
82573b01e57SJeff Chen 		off += sizeof(acc_le);
82673b01e57SJeff Chen 		rthdr[off++] = (ext.timestamp.unit & 0x0f) |
82773b01e57SJeff Chen 				((ext.timestamp.position & 0x0f) << 4);
82873b01e57SJeff Chen 		rthdr[off++] = flags;
82973b01e57SJeff Chen 	}
83073b01e57SJeff Chen 
83173b01e57SJeff Chen 	if (format == NXPWIFI_RATE_FORMAT_HE && has_ext) {
83273b01e57SJeff Chen 		struct ieee80211_radiotap_he he = {0};
83373b01e57SJeff Chen 		u16 data1 = 0, data2 = 0, data3 = 0, data5 = 0, data6 = 0;
83473b01e57SJeff Chen 		u8 bw_val = 0;
83573b01e57SJeff Chen 
83673b01e57SJeff Chen 		memcpy((void *)&ext, (void *)(rxpd + 1), sizeof(ext));
83773b01e57SJeff Chen 
83873b01e57SJeff Chen 		off = ALIGN(off, 2);
83973b01e57SJeff Chen 		hdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_HE));
84073b01e57SJeff Chen 		data1 |= IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN;
84173b01e57SJeff Chen 		data1 |= IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN;
84273b01e57SJeff Chen 		data1 |= IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN;
84373b01e57SJeff Chen 		data1 |= IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN;
84473b01e57SJeff Chen 		data2 |= IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN;
84573b01e57SJeff Chen 		data3 = mcs << 8;
84673b01e57SJeff Chen 		data3 |= FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS, mcs);
84773b01e57SJeff Chen 
84873b01e57SJeff Chen 		if (stbc)
84973b01e57SJeff Chen 			data3 |= IEEE80211_RADIOTAP_HE_DATA3_STBC;
85073b01e57SJeff Chen 
85173b01e57SJeff Chen 		switch (bw) {
85273b01e57SJeff Chen 		case 0:
85373b01e57SJeff Chen 			bw_val |= IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ;
85473b01e57SJeff Chen 			break;
85573b01e57SJeff Chen 		case 1:
85673b01e57SJeff Chen 			bw_val |= IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ;
85773b01e57SJeff Chen 			break;
85873b01e57SJeff Chen 		case 2:
85973b01e57SJeff Chen 			bw_val |= IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ;
86073b01e57SJeff Chen 			break;
86173b01e57SJeff Chen 		case 3:
86273b01e57SJeff Chen 			bw_val |= IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ;
86373b01e57SJeff Chen 			break;
86473b01e57SJeff Chen 		}
86573b01e57SJeff Chen 
86673b01e57SJeff Chen 		data5 |= FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC,
86773b01e57SJeff Chen 						    bw_val);
86873b01e57SJeff Chen 
86973b01e57SJeff Chen 		switch (gi) {
87073b01e57SJeff Chen 		case 0:
87173b01e57SJeff Chen 			data5 |= IEEE80211_RADIOTAP_HE_DATA5_GI_0_8;
87273b01e57SJeff Chen 			break;
87373b01e57SJeff Chen 		case 1:
87473b01e57SJeff Chen 			data5 |= IEEE80211_RADIOTAP_HE_DATA5_GI_1_6;
87573b01e57SJeff Chen 			break;
87673b01e57SJeff Chen 		case 2:
87773b01e57SJeff Chen 			data5 |= IEEE80211_RADIOTAP_HE_DATA5_GI_3_2;
87873b01e57SJeff Chen 			break;
87973b01e57SJeff Chen 		}
88073b01e57SJeff Chen 
88173b01e57SJeff Chen 		data6 |= FIELD_PREP(IEEE80211_RADIOTAP_HE_DATA6_NSTS, nss);
88273b01e57SJeff Chen 		he.data1 = cpu_to_le16(data1);
88373b01e57SJeff Chen 		he.data2 = cpu_to_le16(data2);
88473b01e57SJeff Chen 		he.data3 = cpu_to_le16(data3);
88573b01e57SJeff Chen 		he.data5 = cpu_to_le16(data5);
88673b01e57SJeff Chen 		he.data6 = cpu_to_le16(data6);
88773b01e57SJeff Chen 		memcpy(rthdr + off, &he, sizeof(he));
88873b01e57SJeff Chen 		off += sizeof(he);
88973b01e57SJeff Chen 	}
89073b01e57SJeff Chen 
89173b01e57SJeff Chen 	hdr->it_len = cpu_to_le16(off);
89273b01e57SJeff Chen 
89373b01e57SJeff Chen 	if (off > sizeof(rthdr))
89473b01e57SJeff Chen 		return -EINVAL;
89573b01e57SJeff Chen 
89673b01e57SJeff Chen 	/* Remove RXPD */
89773b01e57SJeff Chen 	skb_pull(skb, rx_pkt_offset);
89873b01e57SJeff Chen 
89973b01e57SJeff Chen 	/* Ensure enough headroom */
90073b01e57SJeff Chen 	if (skb_cow_head(skb, off))
90173b01e57SJeff Chen 		return -ENOMEM;
90273b01e57SJeff Chen 
90373b01e57SJeff Chen 	/* Push radiotap header */
90473b01e57SJeff Chen 	skb_push(skb, off);
90573b01e57SJeff Chen 	memcpy(skb->data, rthdr, off);
90673b01e57SJeff Chen 	skb_reset_mac_header(skb);
90773b01e57SJeff Chen 	skb->ip_summed = CHECKSUM_NONE;
90873b01e57SJeff Chen 	skb->pkt_type  = PACKET_OTHERHOST;
90973b01e57SJeff Chen 	skb->dev       = priv->netdev;
91073b01e57SJeff Chen 	skb->protocol  = htons(ETH_P_802_2);
91173b01e57SJeff Chen 	netif_rx(skb);
91273b01e57SJeff Chen 	return 0;
91373b01e57SJeff Chen }
91473b01e57SJeff Chen 
91573b01e57SJeff Chen /* Process received packet and pass to net stack; reuse or build skb as needed. */
nxpwifi_recv_packet(struct nxpwifi_private * priv,struct sk_buff * skb)91673b01e57SJeff Chen int nxpwifi_recv_packet(struct nxpwifi_private *priv, struct sk_buff *skb)
91773b01e57SJeff Chen {
91873b01e57SJeff Chen 	struct nxpwifi_sta_node *src_node;
91973b01e57SJeff Chen 	struct ethhdr *p_ethhdr;
92073b01e57SJeff Chen 
92173b01e57SJeff Chen 	if (!skb)
92273b01e57SJeff Chen 		return -ENOMEM;
92373b01e57SJeff Chen 
92473b01e57SJeff Chen 	priv->stats.rx_bytes += skb->len;
92573b01e57SJeff Chen 	priv->stats.rx_packets++;
92673b01e57SJeff Chen 
92773b01e57SJeff Chen 	if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
92873b01e57SJeff Chen 		p_ethhdr = (void *)skb->data;
92973b01e57SJeff Chen 		rcu_read_lock();
93073b01e57SJeff Chen 		src_node = nxpwifi_get_sta_entry(priv, p_ethhdr->h_source);
93173b01e57SJeff Chen 		if (src_node) {
93273b01e57SJeff Chen 			src_node->stats.last_rx = jiffies;
93373b01e57SJeff Chen 			src_node->stats.rx_bytes += skb->len;
93473b01e57SJeff Chen 			src_node->stats.rx_packets++;
93573b01e57SJeff Chen 		}
93673b01e57SJeff Chen 		rcu_read_unlock();
93773b01e57SJeff Chen 	}
93873b01e57SJeff Chen 
93973b01e57SJeff Chen 	skb->dev = priv->netdev;
94073b01e57SJeff Chen 	skb->protocol = eth_type_trans(skb, priv->netdev);
94173b01e57SJeff Chen 	skb->ip_summed = CHECKSUM_NONE;
94273b01e57SJeff Chen 
94373b01e57SJeff Chen 	netif_rx(skb);
94473b01e57SJeff Chen 	return 0;
94573b01e57SJeff Chen }
94673b01e57SJeff Chen 
94773b01e57SJeff Chen /* IOCTL completion callback: wake waiters or process response as needed. */
nxpwifi_complete_cmd(struct nxpwifi_adapter * adapter,struct cmd_ctrl_node * cmd_node)94873b01e57SJeff Chen int nxpwifi_complete_cmd(struct nxpwifi_adapter *adapter,
94973b01e57SJeff Chen 			 struct cmd_ctrl_node *cmd_node)
95073b01e57SJeff Chen {
95173b01e57SJeff Chen 	WARN_ON(!cmd_node->wait_q_enabled);
95273b01e57SJeff Chen 	nxpwifi_dbg(adapter, CMD, "cmd completed: status=%d\n",
95373b01e57SJeff Chen 		    adapter->cmd_wait_q.status);
95473b01e57SJeff Chen 
95573b01e57SJeff Chen 	*cmd_node->condition = true;
95673b01e57SJeff Chen 	wake_up_interruptible(&adapter->cmd_wait_q.wait);
95773b01e57SJeff Chen 
95873b01e57SJeff Chen 	return 0;
95973b01e57SJeff Chen }
96073b01e57SJeff Chen 
96173b01e57SJeff Chen /* Find STA entry by MAC under rcu_read_lock(); return NULL if not found. */
96273b01e57SJeff Chen struct nxpwifi_sta_node *
nxpwifi_get_sta_entry(struct nxpwifi_private * priv,const u8 * mac)96373b01e57SJeff Chen nxpwifi_get_sta_entry(struct nxpwifi_private *priv, const u8 *mac)
96473b01e57SJeff Chen {
96573b01e57SJeff Chen 	struct nxpwifi_sta_node *node;
96673b01e57SJeff Chen 	struct nxpwifi_sta_node *found = NULL;
96773b01e57SJeff Chen 
96873b01e57SJeff Chen 	if (!mac)
96973b01e57SJeff Chen 		return NULL;
97073b01e57SJeff Chen 	list_for_each_entry_rcu(node, &priv->sta_list, list) {
97173b01e57SJeff Chen 		if (!memcmp(node->mac_addr, mac, ETH_ALEN)) {
97273b01e57SJeff Chen 			found = node;
97373b01e57SJeff Chen 			break;
97473b01e57SJeff Chen 		}
97573b01e57SJeff Chen 	}
97673b01e57SJeff Chen 
97773b01e57SJeff Chen 	return found;
97873b01e57SJeff Chen }
97973b01e57SJeff Chen 
98073b01e57SJeff Chen struct nxpwifi_sta_node *
nxpwifi_get_sta_entry_rcu(struct nxpwifi_private * priv,const u8 * mac)98173b01e57SJeff Chen nxpwifi_get_sta_entry_rcu(struct nxpwifi_private *priv, const u8 *mac)
98273b01e57SJeff Chen {
98373b01e57SJeff Chen 	struct nxpwifi_sta_node *node;
98473b01e57SJeff Chen 
98573b01e57SJeff Chen 	rcu_read_lock();
98673b01e57SJeff Chen 	node = nxpwifi_get_sta_entry(priv, mac);
98773b01e57SJeff Chen 	rcu_read_unlock();
98873b01e57SJeff Chen 
98973b01e57SJeff Chen 	return node;
99073b01e57SJeff Chen }
99173b01e57SJeff Chen 
99273b01e57SJeff Chen /* Add STA entry by MAC; return existing entry or NULL on invalid MAC. */
99373b01e57SJeff Chen struct nxpwifi_sta_node *
nxpwifi_add_sta_entry(struct nxpwifi_private * priv,const u8 * mac)99473b01e57SJeff Chen nxpwifi_add_sta_entry(struct nxpwifi_private *priv, const u8 *mac)
99573b01e57SJeff Chen {
99673b01e57SJeff Chen 	struct nxpwifi_sta_node *node;
99773b01e57SJeff Chen 
99873b01e57SJeff Chen 	if (!mac)
99973b01e57SJeff Chen 		return NULL;
100073b01e57SJeff Chen 
100173b01e57SJeff Chen 	spin_lock_bh(&priv->sta_list_spinlock);
100273b01e57SJeff Chen 	node = nxpwifi_get_sta_entry_rcu(priv, mac);
100373b01e57SJeff Chen 
100473b01e57SJeff Chen 	if (node)
100573b01e57SJeff Chen 		goto done;
100673b01e57SJeff Chen 
100773b01e57SJeff Chen 	node = kzalloc_obj(*node, GFP_ATOMIC);
100873b01e57SJeff Chen 	if (!node)
100973b01e57SJeff Chen 		goto done;
101073b01e57SJeff Chen 
101173b01e57SJeff Chen 	memcpy(node->mac_addr, mac, ETH_ALEN);
101273b01e57SJeff Chen 	list_add_tail_rcu(&node->list, &priv->sta_list);
101373b01e57SJeff Chen 
101473b01e57SJeff Chen done:
101573b01e57SJeff Chen 	spin_unlock_bh(&priv->sta_list_spinlock);
101673b01e57SJeff Chen 	return node;
101773b01e57SJeff Chen }
101873b01e57SJeff Chen 
101973b01e57SJeff Chen /* Parse HT cap IE from association IEs and set STA HT parameters. */
102073b01e57SJeff Chen void
nxpwifi_set_sta_ht_cap(struct nxpwifi_private * priv,const u8 * ies,int ies_len,struct nxpwifi_sta_node * node)102173b01e57SJeff Chen nxpwifi_set_sta_ht_cap(struct nxpwifi_private *priv, const u8 *ies,
102273b01e57SJeff Chen 		       int ies_len, struct nxpwifi_sta_node *node)
102373b01e57SJeff Chen {
102473b01e57SJeff Chen 	struct element *ht_cap_ie;
102573b01e57SJeff Chen 	const struct ieee80211_ht_cap *ht_cap;
102673b01e57SJeff Chen 
102773b01e57SJeff Chen 	if (!ies)
102873b01e57SJeff Chen 		return;
102973b01e57SJeff Chen 
103073b01e57SJeff Chen 	ht_cap_ie = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies,
103173b01e57SJeff Chen 					     ies_len);
103273b01e57SJeff Chen 	if (ht_cap_ie) {
103373b01e57SJeff Chen 		ht_cap = (void *)(ht_cap_ie + 1);
103473b01e57SJeff Chen 		node->is_11n_enabled = 1;
103573b01e57SJeff Chen 		node->max_amsdu = le16_to_cpu(ht_cap->cap_info) &
103673b01e57SJeff Chen 				  IEEE80211_HT_CAP_MAX_AMSDU ?
103773b01e57SJeff Chen 				  NXPWIFI_TX_DATA_BUF_SIZE_8K :
103873b01e57SJeff Chen 				  NXPWIFI_TX_DATA_BUF_SIZE_4K;
103973b01e57SJeff Chen 	} else {
104073b01e57SJeff Chen 		node->is_11n_enabled = 0;
104173b01e57SJeff Chen 	}
104273b01e57SJeff Chen }
104373b01e57SJeff Chen 
104473b01e57SJeff Chen /* Delete a station from list; called under cfg80211 mutex. */
104573b01e57SJeff Chen 
nxpwifi_del_sta_entry(struct nxpwifi_private * priv,const u8 * mac)104673b01e57SJeff Chen void nxpwifi_del_sta_entry(struct nxpwifi_private *priv, const u8 *mac)
104773b01e57SJeff Chen {
104873b01e57SJeff Chen 	struct nxpwifi_sta_node *node;
104973b01e57SJeff Chen 
105073b01e57SJeff Chen 	list_for_each_entry_rcu(node, &priv->sta_list, list) {
105173b01e57SJeff Chen 		if (!memcmp(node->mac_addr, mac, ETH_ALEN)) {
105273b01e57SJeff Chen 			list_del_rcu(&node->list);
105373b01e57SJeff Chen 			kfree_rcu(node, rcu);
105473b01e57SJeff Chen 			break;
105573b01e57SJeff Chen 		}
105673b01e57SJeff Chen 	}
105773b01e57SJeff Chen }
105873b01e57SJeff Chen 
105973b01e57SJeff Chen /* Delete all stations from list. */
nxpwifi_del_all_sta_list(struct nxpwifi_private * priv)106073b01e57SJeff Chen void nxpwifi_del_all_sta_list(struct nxpwifi_private *priv)
106173b01e57SJeff Chen {
106273b01e57SJeff Chen 	struct nxpwifi_sta_node *node, *tmp;
106373b01e57SJeff Chen 
106473b01e57SJeff Chen 	spin_lock_bh(&priv->sta_list_spinlock);
106573b01e57SJeff Chen 
106673b01e57SJeff Chen 	list_for_each_entry_safe(node, tmp, &priv->sta_list, list) {
106773b01e57SJeff Chen 		list_del_rcu(&node->list);
106873b01e57SJeff Chen 		kfree_rcu(node, rcu);
106973b01e57SJeff Chen 	}
107073b01e57SJeff Chen 
107173b01e57SJeff Chen 	INIT_LIST_HEAD(&priv->sta_list);
107273b01e57SJeff Chen 	spin_unlock_bh(&priv->sta_list_spinlock);
107373b01e57SJeff Chen }
107473b01e57SJeff Chen 
107573b01e57SJeff Chen /* Add one histogram sample. */
nxpwifi_hist_data_add(struct nxpwifi_private * priv,u8 rx_rate,s8 snr,s8 nflr)107673b01e57SJeff Chen void nxpwifi_hist_data_add(struct nxpwifi_private *priv,
107773b01e57SJeff Chen 			   u8 rx_rate, s8 snr, s8 nflr)
107873b01e57SJeff Chen {
107973b01e57SJeff Chen 	struct nxpwifi_histogram_data *phist_data = priv->hist_data;
108073b01e57SJeff Chen 
108173b01e57SJeff Chen 	if (atomic_read(&phist_data->num_samples) > NXPWIFI_HIST_MAX_SAMPLES)
108273b01e57SJeff Chen 		nxpwifi_hist_data_reset(priv);
108373b01e57SJeff Chen 	nxpwifi_hist_data_set(priv, rx_rate, snr, nflr);
108473b01e57SJeff Chen }
108573b01e57SJeff Chen 
108673b01e57SJeff Chen /* function to add histogram record */
nxpwifi_hist_data_set(struct nxpwifi_private * priv,u8 rx_rate,s8 snr,s8 nflr)108773b01e57SJeff Chen void nxpwifi_hist_data_set(struct nxpwifi_private *priv, u8 rx_rate, s8 snr,
108873b01e57SJeff Chen 			   s8 nflr)
108973b01e57SJeff Chen {
109073b01e57SJeff Chen 	struct nxpwifi_histogram_data *phist_data = priv->hist_data;
109173b01e57SJeff Chen 	s8 nf   = -nflr;
109273b01e57SJeff Chen 	s8 rssi = snr - nflr;
109373b01e57SJeff Chen 
109473b01e57SJeff Chen 	atomic_inc(&phist_data->num_samples);
109573b01e57SJeff Chen 	atomic_inc(&phist_data->rx_rate[rx_rate]);
109673b01e57SJeff Chen 	atomic_inc(&phist_data->snr[snr + 128]);
109773b01e57SJeff Chen 	atomic_inc(&phist_data->noise_flr[nf + 128]);
109873b01e57SJeff Chen 	atomic_inc(&phist_data->sig_str[rssi + 128]);
109973b01e57SJeff Chen }
110073b01e57SJeff Chen 
110173b01e57SJeff Chen /* function to reset histogram data during init/reset */
nxpwifi_hist_data_reset(struct nxpwifi_private * priv)110273b01e57SJeff Chen void nxpwifi_hist_data_reset(struct nxpwifi_private *priv)
110373b01e57SJeff Chen {
110473b01e57SJeff Chen 	int ix;
110573b01e57SJeff Chen 	struct nxpwifi_histogram_data *phist_data = priv->hist_data;
110673b01e57SJeff Chen 
110773b01e57SJeff Chen 	atomic_set(&phist_data->num_samples, 0);
110873b01e57SJeff Chen 	for (ix = 0; ix < NXPWIFI_MAX_AC_RX_RATES; ix++)
110973b01e57SJeff Chen 		atomic_set(&phist_data->rx_rate[ix], 0);
111073b01e57SJeff Chen 	for (ix = 0; ix < NXPWIFI_MAX_SNR; ix++)
111173b01e57SJeff Chen 		atomic_set(&phist_data->snr[ix], 0);
111273b01e57SJeff Chen 	for (ix = 0; ix < NXPWIFI_MAX_NOISE_FLR; ix++)
111373b01e57SJeff Chen 		atomic_set(&phist_data->noise_flr[ix], 0);
111473b01e57SJeff Chen 	for (ix = 0; ix < NXPWIFI_MAX_SIG_STRENGTH; ix++)
111573b01e57SJeff Chen 		atomic_set(&phist_data->sig_str[ix], 0);
111673b01e57SJeff Chen }
111773b01e57SJeff Chen 
nxpwifi_alloc_dma_align_buf(int rx_len,gfp_t flags)111873b01e57SJeff Chen void *nxpwifi_alloc_dma_align_buf(int rx_len, gfp_t flags)
111973b01e57SJeff Chen {
112073b01e57SJeff Chen 	struct sk_buff *skb;
112173b01e57SJeff Chen 	int buf_len, pad;
112273b01e57SJeff Chen 
112373b01e57SJeff Chen 	buf_len = rx_len + NXPWIFI_RX_HEADROOM + NXPWIFI_DMA_ALIGN_SZ;
112473b01e57SJeff Chen 
112573b01e57SJeff Chen 	skb = __dev_alloc_skb(buf_len, flags);
112673b01e57SJeff Chen 
112773b01e57SJeff Chen 	if (!skb)
112873b01e57SJeff Chen 		return NULL;
112973b01e57SJeff Chen 
113073b01e57SJeff Chen 	skb_reserve(skb, NXPWIFI_RX_HEADROOM);
113173b01e57SJeff Chen 
113273b01e57SJeff Chen 	pad = NXPWIFI_ALIGN_ADDR(skb->data, NXPWIFI_DMA_ALIGN_SZ) -
113373b01e57SJeff Chen 	      (long)skb->data;
113473b01e57SJeff Chen 
113573b01e57SJeff Chen 	skb_reserve(skb, pad);
113673b01e57SJeff Chen 
113773b01e57SJeff Chen 	return skb;
113873b01e57SJeff Chen }
113973b01e57SJeff Chen EXPORT_SYMBOL_GPL(nxpwifi_alloc_dma_align_buf);
114073b01e57SJeff Chen 
nxpwifi_fw_dump_event(struct nxpwifi_private * priv)114173b01e57SJeff Chen void nxpwifi_fw_dump_event(struct nxpwifi_private *priv)
114273b01e57SJeff Chen {
114373b01e57SJeff Chen 	nxpwifi_send_cmd(priv, HOST_CMD_FW_DUMP_EVENT, HOST_ACT_GEN_SET,
114473b01e57SJeff Chen 			 0, NULL, true);
114573b01e57SJeff Chen }
114673b01e57SJeff Chen EXPORT_SYMBOL_GPL(nxpwifi_fw_dump_event);
114773b01e57SJeff Chen 
nxpwifi_append_data_tlv(u16 id,u8 * data,int len,u8 * pos,u8 * cmd_end)114873b01e57SJeff Chen int nxpwifi_append_data_tlv(u16 id, u8 *data, int len, u8 *pos, u8 *cmd_end)
114973b01e57SJeff Chen {
115073b01e57SJeff Chen 	struct nxpwifi_ie_types_data *tlv;
115173b01e57SJeff Chen 	u16 header_len = sizeof(struct nxpwifi_ie_types_header);
115273b01e57SJeff Chen 
115373b01e57SJeff Chen 	tlv = (struct nxpwifi_ie_types_data *)pos;
115473b01e57SJeff Chen 	tlv->header.len = cpu_to_le16(len);
115573b01e57SJeff Chen 
115673b01e57SJeff Chen 	if (id == WLAN_EID_EXT_HE_CAPABILITY) {
115773b01e57SJeff Chen 		if ((pos + header_len + len + 1) > cmd_end)
115873b01e57SJeff Chen 			return 0;
115973b01e57SJeff Chen 
116073b01e57SJeff Chen 		tlv->header.type = cpu_to_le16(WLAN_EID_EXTENSION);
116173b01e57SJeff Chen 		tlv->data[0] = WLAN_EID_EXT_HE_CAPABILITY;
116273b01e57SJeff Chen 		memcpy(tlv->data + 1, data, len);
116373b01e57SJeff Chen 	} else {
116473b01e57SJeff Chen 		if ((pos + header_len + len) > cmd_end)
116573b01e57SJeff Chen 			return 0;
116673b01e57SJeff Chen 
116773b01e57SJeff Chen 		tlv->header.type = cpu_to_le16(id);
116873b01e57SJeff Chen 		memcpy(tlv->data, data, len);
116973b01e57SJeff Chen 	}
117073b01e57SJeff Chen 
117173b01e57SJeff Chen 	return (header_len + len);
117273b01e57SJeff Chen }
117373b01e57SJeff Chen 
nxpwifi_get_vdll_image(struct nxpwifi_adapter * adapter,u32 vdll_len)117473b01e57SJeff Chen static int nxpwifi_get_vdll_image(struct nxpwifi_adapter *adapter, u32 vdll_len)
117573b01e57SJeff Chen {
117673b01e57SJeff Chen 	struct vdll_dnld_ctrl *ctrl = &adapter->vdll_ctrl;
117773b01e57SJeff Chen 	bool req_fw = false;
117873b01e57SJeff Chen 	u32 offset;
117973b01e57SJeff Chen 
118073b01e57SJeff Chen 	if (ctrl->vdll_mem) {
118173b01e57SJeff Chen 		nxpwifi_dbg(adapter, EVENT,
118273b01e57SJeff Chen 			    "VDLL mem is not empty: %p old_len=%d new_len=%d\n",
118373b01e57SJeff Chen 			    ctrl->vdll_mem, ctrl->vdll_len, vdll_len);
118473b01e57SJeff Chen 		vfree(ctrl->vdll_mem);
118573b01e57SJeff Chen 		ctrl->vdll_mem = NULL;
118673b01e57SJeff Chen 		ctrl->vdll_len = 0;
118773b01e57SJeff Chen 	}
118873b01e57SJeff Chen 
118973b01e57SJeff Chen 	ctrl->vdll_mem = vmalloc(vdll_len);
119073b01e57SJeff Chen 	if (!ctrl->vdll_mem)
119173b01e57SJeff Chen 		return -ENOMEM;
119273b01e57SJeff Chen 
119373b01e57SJeff Chen 	if (!adapter->firmware) {
119473b01e57SJeff Chen 		req_fw = true;
119573b01e57SJeff Chen 		if (request_firmware(&adapter->firmware, adapter->fw_name,
119673b01e57SJeff Chen 				     adapter->dev))
119773b01e57SJeff Chen 			return -ENOENT;
119873b01e57SJeff Chen 	}
119973b01e57SJeff Chen 
120073b01e57SJeff Chen 	if (adapter->firmware) {
120173b01e57SJeff Chen 		if (vdll_len < adapter->firmware->size) {
120273b01e57SJeff Chen 			offset = adapter->firmware->size - vdll_len;
120373b01e57SJeff Chen 			memcpy(ctrl->vdll_mem, adapter->firmware->data + offset,
120473b01e57SJeff Chen 			       vdll_len);
120573b01e57SJeff Chen 		} else {
120673b01e57SJeff Chen 			nxpwifi_dbg(adapter, ERROR,
120773b01e57SJeff Chen 				    "Invalid VDLL length = %d, fw_len=%d\n",
120873b01e57SJeff Chen 				    vdll_len, (int)adapter->firmware->size);
120973b01e57SJeff Chen 			return -EINVAL;
121073b01e57SJeff Chen 		}
121173b01e57SJeff Chen 		if (req_fw) {
121273b01e57SJeff Chen 			release_firmware(adapter->firmware);
121373b01e57SJeff Chen 			adapter->firmware = NULL;
121473b01e57SJeff Chen 		}
121573b01e57SJeff Chen 	}
121673b01e57SJeff Chen 
121773b01e57SJeff Chen 	ctrl->vdll_len = vdll_len;
121873b01e57SJeff Chen 	nxpwifi_dbg(adapter, MSG, "VDLL image: len=%d\n", ctrl->vdll_len);
121973b01e57SJeff Chen 
122073b01e57SJeff Chen 	return 0;
122173b01e57SJeff Chen }
122273b01e57SJeff Chen 
nxpwifi_download_vdll_block(struct nxpwifi_adapter * adapter,u8 * block,u16 block_len)122373b01e57SJeff Chen int nxpwifi_download_vdll_block(struct nxpwifi_adapter *adapter,
122473b01e57SJeff Chen 				u8 *block, u16 block_len)
122573b01e57SJeff Chen {
122673b01e57SJeff Chen 	struct vdll_dnld_ctrl *ctrl = &adapter->vdll_ctrl;
122773b01e57SJeff Chen 	struct host_cmd_ds_command *host_cmd;
122873b01e57SJeff Chen 	u16 msg_len = block_len + S_DS_GEN;
122973b01e57SJeff Chen 	int ret = 0;
123073b01e57SJeff Chen 
123173b01e57SJeff Chen 	skb_trim(ctrl->skb, 0);
123273b01e57SJeff Chen 	skb_put_zero(ctrl->skb, msg_len);
123373b01e57SJeff Chen 
123473b01e57SJeff Chen 	host_cmd = (struct host_cmd_ds_command *)(ctrl->skb->data);
123573b01e57SJeff Chen 
123673b01e57SJeff Chen 	host_cmd->command = cpu_to_le16(HOST_CMD_VDLL);
123773b01e57SJeff Chen 	host_cmd->seq_num = cpu_to_le16(0xFF00);
123873b01e57SJeff Chen 	host_cmd->size = cpu_to_le16(msg_len);
123973b01e57SJeff Chen 	memcpy(ctrl->skb->data + S_DS_GEN, block, block_len);
124073b01e57SJeff Chen 
124173b01e57SJeff Chen 	skb_push(ctrl->skb, adapter->intf_hdr_len);
124273b01e57SJeff Chen 	ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_VDLL,
124373b01e57SJeff Chen 					   ctrl->skb, NULL);
124473b01e57SJeff Chen 	skb_pull(ctrl->skb, adapter->intf_hdr_len);
124573b01e57SJeff Chen 
124673b01e57SJeff Chen 	if (ret)
124773b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR,
124873b01e57SJeff Chen 			    "Fail to download VDLL: block: %p, len: %d\n",
124973b01e57SJeff Chen 			    block, block_len);
125073b01e57SJeff Chen 
125173b01e57SJeff Chen 	return ret;
125273b01e57SJeff Chen }
125373b01e57SJeff Chen 
nxpwifi_process_vdll_event(struct nxpwifi_private * priv,struct sk_buff * skb)125473b01e57SJeff Chen int nxpwifi_process_vdll_event(struct nxpwifi_private *priv,
125573b01e57SJeff Chen 			       struct sk_buff *skb)
125673b01e57SJeff Chen {
125773b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
125873b01e57SJeff Chen 	struct vdll_ind_event *vdll_evt =
125973b01e57SJeff Chen 		(struct vdll_ind_event *)(skb->data + sizeof(u32));
126073b01e57SJeff Chen 	u16 type = le16_to_cpu(vdll_evt->type);
126173b01e57SJeff Chen 	u16 vdll_id = le16_to_cpu(vdll_evt->vdll_id);
126273b01e57SJeff Chen 	u32 offset = le32_to_cpu(vdll_evt->offset);
126373b01e57SJeff Chen 	u16 block_len = le16_to_cpu(vdll_evt->block_len);
126473b01e57SJeff Chen 	struct vdll_dnld_ctrl *ctrl = &adapter->vdll_ctrl;
126573b01e57SJeff Chen 	int ret = 0;
126673b01e57SJeff Chen 
126773b01e57SJeff Chen 	switch (type) {
126873b01e57SJeff Chen 	case VDLL_IND_TYPE_REQ:
126973b01e57SJeff Chen 		nxpwifi_dbg(adapter, EVENT,
127073b01e57SJeff Chen 			    "VDLL IND (REG): ID: %d, offset: %#x, len: %d\n",
127173b01e57SJeff Chen 			    vdll_id, offset, block_len);
127273b01e57SJeff Chen 		if (offset <= ctrl->vdll_len) {
127373b01e57SJeff Chen 			block_len =
127473b01e57SJeff Chen 				min((u32)block_len, ctrl->vdll_len - offset);
127573b01e57SJeff Chen 			if (!adapter->cmd_sent) {
127673b01e57SJeff Chen 				ret = nxpwifi_download_vdll_block(adapter,
127773b01e57SJeff Chen 								  ctrl->vdll_mem
127873b01e57SJeff Chen 								  + offset,
127973b01e57SJeff Chen 								  block_len);
128073b01e57SJeff Chen 				if (ret)
128173b01e57SJeff Chen 					nxpwifi_dbg(adapter, ERROR,
128273b01e57SJeff Chen 						    "Download VDLL failed\n");
128373b01e57SJeff Chen 			} else {
128473b01e57SJeff Chen 				nxpwifi_dbg(adapter, EVENT,
128573b01e57SJeff Chen 					    "Delay download VDLL block\n");
128673b01e57SJeff Chen 				ctrl->pending_block_len = block_len;
128773b01e57SJeff Chen 				ctrl->pending_block = ctrl->vdll_mem + offset;
128873b01e57SJeff Chen 			}
128973b01e57SJeff Chen 		} else {
129073b01e57SJeff Chen 			nxpwifi_dbg(adapter, ERROR,
129173b01e57SJeff Chen 				    "Err Req: offset=%#x, len=%d, vdll_len=%d\n",
129273b01e57SJeff Chen 				    offset, block_len, ctrl->vdll_len);
129373b01e57SJeff Chen 			ret = -EINVAL;
129473b01e57SJeff Chen 		}
129573b01e57SJeff Chen 		break;
129673b01e57SJeff Chen 	case VDLL_IND_TYPE_OFFSET:
129773b01e57SJeff Chen 		nxpwifi_dbg(adapter, EVENT,
129873b01e57SJeff Chen 			    "VDLL IND (OFFSET): offset: %#x\n", offset);
129973b01e57SJeff Chen 		ret = nxpwifi_get_vdll_image(adapter, offset);
130073b01e57SJeff Chen 		break;
130173b01e57SJeff Chen 	case VDLL_IND_TYPE_ERR_SIG:
130273b01e57SJeff Chen 	case VDLL_IND_TYPE_ERR_ID:
130373b01e57SJeff Chen 	case VDLL_IND_TYPE_SEC_ERR_ID:
130473b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR, "VDLL IND: error: %d\n", type);
130573b01e57SJeff Chen 		break;
130673b01e57SJeff Chen 	case VDLL_IND_TYPE_INTF_RESET:
130773b01e57SJeff Chen 		nxpwifi_dbg(adapter, EVENT, "VDLL IND: interface reset\n");
130873b01e57SJeff Chen 		break;
130973b01e57SJeff Chen 	default:
131073b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR, "VDLL IND: unknown type: %d", type);
131173b01e57SJeff Chen 		ret = -EINVAL;
131273b01e57SJeff Chen 		break;
131373b01e57SJeff Chen 	}
131473b01e57SJeff Chen 
131573b01e57SJeff Chen 	return ret;
131673b01e57SJeff Chen }
131773b01e57SJeff Chen 
nxpwifi_roc_cookie(struct nxpwifi_adapter * adapter)131873b01e57SJeff Chen u64 nxpwifi_roc_cookie(struct nxpwifi_adapter *adapter)
131973b01e57SJeff Chen {
132073b01e57SJeff Chen 	adapter->roc_cookie_counter++;
132173b01e57SJeff Chen 
132273b01e57SJeff Chen 	/* wow, you wrapped 64 bits ... more likely a bug */
132373b01e57SJeff Chen 	if (WARN_ON(adapter->roc_cookie_counter == 0))
132473b01e57SJeff Chen 		adapter->roc_cookie_counter++;
132573b01e57SJeff Chen 
132673b01e57SJeff Chen 	return adapter->roc_cookie_counter;
132773b01e57SJeff Chen }
132873b01e57SJeff Chen 
nxpwifi_can_queue_work(struct nxpwifi_adapter * adapter)132973b01e57SJeff Chen static bool nxpwifi_can_queue_work(struct nxpwifi_adapter *adapter)
133073b01e57SJeff Chen {
133173b01e57SJeff Chen 	if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags) ||
133273b01e57SJeff Chen 	    test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags) ||
133373b01e57SJeff Chen 	    test_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags)) {
133473b01e57SJeff Chen 		nxpwifi_dbg(adapter, WARN,
133573b01e57SJeff Chen 			    "queueing nxpwifi work while going to suspend\n");
133673b01e57SJeff Chen 		return false;
133773b01e57SJeff Chen 	}
133873b01e57SJeff Chen 
133973b01e57SJeff Chen 	return true;
134073b01e57SJeff Chen }
134173b01e57SJeff Chen 
nxpwifi_queue_work(struct nxpwifi_adapter * adapter,struct work_struct * work)134273b01e57SJeff Chen void nxpwifi_queue_work(struct nxpwifi_adapter *adapter,
134373b01e57SJeff Chen 			struct work_struct *work)
134473b01e57SJeff Chen {
134573b01e57SJeff Chen 	if (!nxpwifi_can_queue_work(adapter))
134673b01e57SJeff Chen 		return;
134773b01e57SJeff Chen 
134873b01e57SJeff Chen 	queue_work(adapter->workqueue, work);
134973b01e57SJeff Chen }
135073b01e57SJeff Chen EXPORT_SYMBOL(nxpwifi_queue_work);
135173b01e57SJeff Chen 
nxpwifi_queue_delayed_work(struct nxpwifi_adapter * adapter,struct delayed_work * dwork,unsigned long delay)135273b01e57SJeff Chen void nxpwifi_queue_delayed_work(struct nxpwifi_adapter *adapter,
135373b01e57SJeff Chen 				struct delayed_work *dwork,
135473b01e57SJeff Chen 				unsigned long delay)
135573b01e57SJeff Chen {
135673b01e57SJeff Chen 	if (!nxpwifi_can_queue_work(adapter))
135773b01e57SJeff Chen 		return;
135873b01e57SJeff Chen 
135973b01e57SJeff Chen 	queue_delayed_work(adapter->workqueue, dwork, delay);
136073b01e57SJeff Chen }
136173b01e57SJeff Chen EXPORT_SYMBOL(nxpwifi_queue_delayed_work);
136273b01e57SJeff Chen 
nxpwifi_queue_wiphy_work(struct nxpwifi_adapter * adapter,struct wiphy_work * work)136373b01e57SJeff Chen void nxpwifi_queue_wiphy_work(struct nxpwifi_adapter *adapter,
136473b01e57SJeff Chen 			      struct wiphy_work *work)
136573b01e57SJeff Chen {
136673b01e57SJeff Chen 	if (!nxpwifi_can_queue_work(adapter))
136773b01e57SJeff Chen 		return;
136873b01e57SJeff Chen 
136973b01e57SJeff Chen 	wiphy_work_queue(adapter->wiphy, work);
137073b01e57SJeff Chen }
137173b01e57SJeff Chen 
nxpwifi_queue_delayed_wiphy_work(struct nxpwifi_adapter * adapter,struct wiphy_delayed_work * dwork,unsigned long delay)137273b01e57SJeff Chen void nxpwifi_queue_delayed_wiphy_work(struct nxpwifi_adapter *adapter,
137373b01e57SJeff Chen 				      struct wiphy_delayed_work *dwork,
137473b01e57SJeff Chen 				      unsigned long delay)
137573b01e57SJeff Chen {
137673b01e57SJeff Chen 	if (!nxpwifi_can_queue_work(adapter))
137773b01e57SJeff Chen 		return;
137873b01e57SJeff Chen 
137973b01e57SJeff Chen 	wiphy_delayed_work_queue(adapter->wiphy, dwork, delay);
138073b01e57SJeff Chen }
1381