18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/******************************************************************************
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Contact Information:
78c2ecf20Sopenharmony_ci *  Intel Linux Wireless <ilw@linux.intel.com>
88c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
98c2ecf20Sopenharmony_ci *****************************************************************************/
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
148c2ecf20Sopenharmony_ci#include <linux/sched.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/types.h>
178c2ecf20Sopenharmony_ci#include <linux/lockdep.h>
188c2ecf20Sopenharmony_ci#include <linux/pci.h>
198c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
208c2ecf20Sopenharmony_ci#include <linux/delay.h>
218c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
228c2ecf20Sopenharmony_ci#include <net/mac80211.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "common.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ciint
278c2ecf20Sopenharmony_ci_il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	const int interval = 10; /* microseconds */
308c2ecf20Sopenharmony_ci	int t = 0;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	do {
338c2ecf20Sopenharmony_ci		if ((_il_rd(il, addr) & mask) == (bits & mask))
348c2ecf20Sopenharmony_ci			return t;
358c2ecf20Sopenharmony_ci		udelay(interval);
368c2ecf20Sopenharmony_ci		t += interval;
378c2ecf20Sopenharmony_ci	} while (t < timeout);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(_il_poll_bit);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_civoid
448c2ecf20Sopenharmony_ciil_set_bit(struct il_priv *p, u32 r, u32 m)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	unsigned long reg_flags;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p->reg_lock, reg_flags);
498c2ecf20Sopenharmony_ci	_il_set_bit(p, r, m);
508c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p->reg_lock, reg_flags);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_bit);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_civoid
558c2ecf20Sopenharmony_ciil_clear_bit(struct il_priv *p, u32 r, u32 m)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	unsigned long reg_flags;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p->reg_lock, reg_flags);
608c2ecf20Sopenharmony_ci	_il_clear_bit(p, r, m);
618c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p->reg_lock, reg_flags);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_clear_bit);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cibool
668c2ecf20Sopenharmony_ci_il_grab_nic_access(struct il_priv *il)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	int ret;
698c2ecf20Sopenharmony_ci	u32 val;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/* this bit wakes up the NIC */
728c2ecf20Sopenharmony_ci	_il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/*
758c2ecf20Sopenharmony_ci	 * These bits say the device is running, and should keep running for
768c2ecf20Sopenharmony_ci	 * at least a short while (at least as long as MAC_ACCESS_REQ stays 1),
778c2ecf20Sopenharmony_ci	 * but they do not indicate that embedded SRAM is restored yet;
788c2ecf20Sopenharmony_ci	 * 3945 and 4965 have volatile SRAM, and must save/restore contents
798c2ecf20Sopenharmony_ci	 * to/from host DRAM when sleeping/waking for power-saving.
808c2ecf20Sopenharmony_ci	 * Each direction takes approximately 1/4 millisecond; with this
818c2ecf20Sopenharmony_ci	 * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a
828c2ecf20Sopenharmony_ci	 * series of register accesses are expected (e.g. reading Event Log),
838c2ecf20Sopenharmony_ci	 * to keep device from sleeping.
848c2ecf20Sopenharmony_ci	 *
858c2ecf20Sopenharmony_ci	 * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that
868c2ecf20Sopenharmony_ci	 * SRAM is okay/restored.  We don't check that here because this call
878c2ecf20Sopenharmony_ci	 * is just for hardware register access; but GP1 MAC_SLEEP check is a
888c2ecf20Sopenharmony_ci	 * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log).
898c2ecf20Sopenharmony_ci	 *
908c2ecf20Sopenharmony_ci	 */
918c2ecf20Sopenharmony_ci	ret =
928c2ecf20Sopenharmony_ci	    _il_poll_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
938c2ecf20Sopenharmony_ci			 (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
948c2ecf20Sopenharmony_ci			  CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000);
958c2ecf20Sopenharmony_ci	if (unlikely(ret < 0)) {
968c2ecf20Sopenharmony_ci		val = _il_rd(il, CSR_GP_CNTRL);
978c2ecf20Sopenharmony_ci		WARN_ONCE(1, "Timeout waiting for ucode processor access "
988c2ecf20Sopenharmony_ci			     "(CSR_GP_CNTRL 0x%08x)\n", val);
998c2ecf20Sopenharmony_ci		_il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
1008c2ecf20Sopenharmony_ci		return false;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return true;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(_il_grab_nic_access);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ciint
1088c2ecf20Sopenharmony_ciil_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	const int interval = 10; /* microseconds */
1118c2ecf20Sopenharmony_ci	int t = 0;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	do {
1148c2ecf20Sopenharmony_ci		if ((il_rd(il, addr) & mask) == mask)
1158c2ecf20Sopenharmony_ci			return t;
1168c2ecf20Sopenharmony_ci		udelay(interval);
1178c2ecf20Sopenharmony_ci		t += interval;
1188c2ecf20Sopenharmony_ci	} while (t < timeout);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_poll_bit);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ciu32
1258c2ecf20Sopenharmony_ciil_rd_prph(struct il_priv *il, u32 reg)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	unsigned long reg_flags;
1288c2ecf20Sopenharmony_ci	u32 val;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->reg_lock, reg_flags);
1318c2ecf20Sopenharmony_ci	_il_grab_nic_access(il);
1328c2ecf20Sopenharmony_ci	val = _il_rd_prph(il, reg);
1338c2ecf20Sopenharmony_ci	_il_release_nic_access(il);
1348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->reg_lock, reg_flags);
1358c2ecf20Sopenharmony_ci	return val;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_rd_prph);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_civoid
1408c2ecf20Sopenharmony_ciil_wr_prph(struct il_priv *il, u32 addr, u32 val)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	unsigned long reg_flags;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->reg_lock, reg_flags);
1458c2ecf20Sopenharmony_ci	if (likely(_il_grab_nic_access(il))) {
1468c2ecf20Sopenharmony_ci		_il_wr_prph(il, addr, val);
1478c2ecf20Sopenharmony_ci		_il_release_nic_access(il);
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->reg_lock, reg_flags);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_wr_prph);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ciu32
1548c2ecf20Sopenharmony_ciil_read_targ_mem(struct il_priv *il, u32 addr)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	unsigned long reg_flags;
1578c2ecf20Sopenharmony_ci	u32 value;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->reg_lock, reg_flags);
1608c2ecf20Sopenharmony_ci	_il_grab_nic_access(il);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	_il_wr(il, HBUS_TARG_MEM_RADDR, addr);
1638c2ecf20Sopenharmony_ci	value = _il_rd(il, HBUS_TARG_MEM_RDAT);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	_il_release_nic_access(il);
1668c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->reg_lock, reg_flags);
1678c2ecf20Sopenharmony_ci	return value;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_read_targ_mem);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_civoid
1728c2ecf20Sopenharmony_ciil_write_targ_mem(struct il_priv *il, u32 addr, u32 val)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	unsigned long reg_flags;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->reg_lock, reg_flags);
1778c2ecf20Sopenharmony_ci	if (likely(_il_grab_nic_access(il))) {
1788c2ecf20Sopenharmony_ci		_il_wr(il, HBUS_TARG_MEM_WADDR, addr);
1798c2ecf20Sopenharmony_ci		_il_wr(il, HBUS_TARG_MEM_WDAT, val);
1808c2ecf20Sopenharmony_ci		_il_release_nic_access(il);
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->reg_lock, reg_flags);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_write_targ_mem);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ciconst char *
1878c2ecf20Sopenharmony_ciil_get_cmd_string(u8 cmd)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	switch (cmd) {
1908c2ecf20Sopenharmony_ci		IL_CMD(N_ALIVE);
1918c2ecf20Sopenharmony_ci		IL_CMD(N_ERROR);
1928c2ecf20Sopenharmony_ci		IL_CMD(C_RXON);
1938c2ecf20Sopenharmony_ci		IL_CMD(C_RXON_ASSOC);
1948c2ecf20Sopenharmony_ci		IL_CMD(C_QOS_PARAM);
1958c2ecf20Sopenharmony_ci		IL_CMD(C_RXON_TIMING);
1968c2ecf20Sopenharmony_ci		IL_CMD(C_ADD_STA);
1978c2ecf20Sopenharmony_ci		IL_CMD(C_REM_STA);
1988c2ecf20Sopenharmony_ci		IL_CMD(C_WEPKEY);
1998c2ecf20Sopenharmony_ci		IL_CMD(N_3945_RX);
2008c2ecf20Sopenharmony_ci		IL_CMD(C_TX);
2018c2ecf20Sopenharmony_ci		IL_CMD(C_RATE_SCALE);
2028c2ecf20Sopenharmony_ci		IL_CMD(C_LEDS);
2038c2ecf20Sopenharmony_ci		IL_CMD(C_TX_LINK_QUALITY_CMD);
2048c2ecf20Sopenharmony_ci		IL_CMD(C_CHANNEL_SWITCH);
2058c2ecf20Sopenharmony_ci		IL_CMD(N_CHANNEL_SWITCH);
2068c2ecf20Sopenharmony_ci		IL_CMD(C_SPECTRUM_MEASUREMENT);
2078c2ecf20Sopenharmony_ci		IL_CMD(N_SPECTRUM_MEASUREMENT);
2088c2ecf20Sopenharmony_ci		IL_CMD(C_POWER_TBL);
2098c2ecf20Sopenharmony_ci		IL_CMD(N_PM_SLEEP);
2108c2ecf20Sopenharmony_ci		IL_CMD(N_PM_DEBUG_STATS);
2118c2ecf20Sopenharmony_ci		IL_CMD(C_SCAN);
2128c2ecf20Sopenharmony_ci		IL_CMD(C_SCAN_ABORT);
2138c2ecf20Sopenharmony_ci		IL_CMD(N_SCAN_START);
2148c2ecf20Sopenharmony_ci		IL_CMD(N_SCAN_RESULTS);
2158c2ecf20Sopenharmony_ci		IL_CMD(N_SCAN_COMPLETE);
2168c2ecf20Sopenharmony_ci		IL_CMD(N_BEACON);
2178c2ecf20Sopenharmony_ci		IL_CMD(C_TX_BEACON);
2188c2ecf20Sopenharmony_ci		IL_CMD(C_TX_PWR_TBL);
2198c2ecf20Sopenharmony_ci		IL_CMD(C_BT_CONFIG);
2208c2ecf20Sopenharmony_ci		IL_CMD(C_STATS);
2218c2ecf20Sopenharmony_ci		IL_CMD(N_STATS);
2228c2ecf20Sopenharmony_ci		IL_CMD(N_CARD_STATE);
2238c2ecf20Sopenharmony_ci		IL_CMD(N_MISSED_BEACONS);
2248c2ecf20Sopenharmony_ci		IL_CMD(C_CT_KILL_CONFIG);
2258c2ecf20Sopenharmony_ci		IL_CMD(C_SENSITIVITY);
2268c2ecf20Sopenharmony_ci		IL_CMD(C_PHY_CALIBRATION);
2278c2ecf20Sopenharmony_ci		IL_CMD(N_RX_PHY);
2288c2ecf20Sopenharmony_ci		IL_CMD(N_RX_MPDU);
2298c2ecf20Sopenharmony_ci		IL_CMD(N_RX);
2308c2ecf20Sopenharmony_ci		IL_CMD(N_COMPRESSED_BA);
2318c2ecf20Sopenharmony_ci	default:
2328c2ecf20Sopenharmony_ci		return "UNKNOWN";
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_cmd_string);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci#define HOST_COMPLETE_TIMEOUT (HZ / 2)
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic void
2418c2ecf20Sopenharmony_ciil_generic_cmd_callback(struct il_priv *il, struct il_device_cmd *cmd,
2428c2ecf20Sopenharmony_ci			struct il_rx_pkt *pkt)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
2458c2ecf20Sopenharmony_ci		IL_ERR("Bad return from %s (0x%08X)\n",
2468c2ecf20Sopenharmony_ci		       il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
2478c2ecf20Sopenharmony_ci		return;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
2508c2ecf20Sopenharmony_ci	switch (cmd->hdr.cmd) {
2518c2ecf20Sopenharmony_ci	case C_TX_LINK_QUALITY_CMD:
2528c2ecf20Sopenharmony_ci	case C_SENSITIVITY:
2538c2ecf20Sopenharmony_ci		D_HC_DUMP("back from %s (0x%08X)\n",
2548c2ecf20Sopenharmony_ci			  il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
2558c2ecf20Sopenharmony_ci		break;
2568c2ecf20Sopenharmony_ci	default:
2578c2ecf20Sopenharmony_ci		D_HC("back from %s (0x%08X)\n", il_get_cmd_string(cmd->hdr.cmd),
2588c2ecf20Sopenharmony_ci		     pkt->hdr.flags);
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci#endif
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic int
2648c2ecf20Sopenharmony_ciil_send_cmd_async(struct il_priv *il, struct il_host_cmd *cmd)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	int ret;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	BUG_ON(!(cmd->flags & CMD_ASYNC));
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* An asynchronous command can not expect an SKB to be set. */
2718c2ecf20Sopenharmony_ci	BUG_ON(cmd->flags & CMD_WANT_SKB);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* Assign a generic callback if one is not provided */
2748c2ecf20Sopenharmony_ci	if (!cmd->callback)
2758c2ecf20Sopenharmony_ci		cmd->callback = il_generic_cmd_callback;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status))
2788c2ecf20Sopenharmony_ci		return -EBUSY;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	ret = il_enqueue_hcmd(il, cmd);
2818c2ecf20Sopenharmony_ci	if (ret < 0) {
2828c2ecf20Sopenharmony_ci		IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n",
2838c2ecf20Sopenharmony_ci		       il_get_cmd_string(cmd->id), ret);
2848c2ecf20Sopenharmony_ci		return ret;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci	return 0;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ciint
2908c2ecf20Sopenharmony_ciil_send_cmd_sync(struct il_priv *il, struct il_host_cmd *cmd)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	int cmd_idx;
2938c2ecf20Sopenharmony_ci	int ret;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	lockdep_assert_held(&il->mutex);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	BUG_ON(cmd->flags & CMD_ASYNC);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/* A synchronous command can not have a callback set. */
3008c2ecf20Sopenharmony_ci	BUG_ON(cmd->callback);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	D_INFO("Attempting to send sync command %s\n",
3038c2ecf20Sopenharmony_ci	       il_get_cmd_string(cmd->id));
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	set_bit(S_HCMD_ACTIVE, &il->status);
3068c2ecf20Sopenharmony_ci	D_INFO("Setting HCMD_ACTIVE for command %s\n",
3078c2ecf20Sopenharmony_ci	       il_get_cmd_string(cmd->id));
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	cmd_idx = il_enqueue_hcmd(il, cmd);
3108c2ecf20Sopenharmony_ci	if (cmd_idx < 0) {
3118c2ecf20Sopenharmony_ci		ret = cmd_idx;
3128c2ecf20Sopenharmony_ci		IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n",
3138c2ecf20Sopenharmony_ci		       il_get_cmd_string(cmd->id), ret);
3148c2ecf20Sopenharmony_ci		goto out;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	ret = wait_event_timeout(il->wait_command_queue,
3188c2ecf20Sopenharmony_ci				 !test_bit(S_HCMD_ACTIVE, &il->status),
3198c2ecf20Sopenharmony_ci				 HOST_COMPLETE_TIMEOUT);
3208c2ecf20Sopenharmony_ci	if (!ret) {
3218c2ecf20Sopenharmony_ci		if (test_bit(S_HCMD_ACTIVE, &il->status)) {
3228c2ecf20Sopenharmony_ci			IL_ERR("Error sending %s: time out after %dms.\n",
3238c2ecf20Sopenharmony_ci			       il_get_cmd_string(cmd->id),
3248c2ecf20Sopenharmony_ci			       jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci			clear_bit(S_HCMD_ACTIVE, &il->status);
3278c2ecf20Sopenharmony_ci			D_INFO("Clearing HCMD_ACTIVE for command %s\n",
3288c2ecf20Sopenharmony_ci			       il_get_cmd_string(cmd->id));
3298c2ecf20Sopenharmony_ci			ret = -ETIMEDOUT;
3308c2ecf20Sopenharmony_ci			goto cancel;
3318c2ecf20Sopenharmony_ci		}
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (test_bit(S_RFKILL, &il->status)) {
3358c2ecf20Sopenharmony_ci		IL_ERR("Command %s aborted: RF KILL Switch\n",
3368c2ecf20Sopenharmony_ci		       il_get_cmd_string(cmd->id));
3378c2ecf20Sopenharmony_ci		ret = -ECANCELED;
3388c2ecf20Sopenharmony_ci		goto fail;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci	if (test_bit(S_FW_ERROR, &il->status)) {
3418c2ecf20Sopenharmony_ci		IL_ERR("Command %s failed: FW Error\n",
3428c2ecf20Sopenharmony_ci		       il_get_cmd_string(cmd->id));
3438c2ecf20Sopenharmony_ci		ret = -EIO;
3448c2ecf20Sopenharmony_ci		goto fail;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci	if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) {
3478c2ecf20Sopenharmony_ci		IL_ERR("Error: Response NULL in '%s'\n",
3488c2ecf20Sopenharmony_ci		       il_get_cmd_string(cmd->id));
3498c2ecf20Sopenharmony_ci		ret = -EIO;
3508c2ecf20Sopenharmony_ci		goto cancel;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	ret = 0;
3548c2ecf20Sopenharmony_ci	goto out;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cicancel:
3578c2ecf20Sopenharmony_ci	if (cmd->flags & CMD_WANT_SKB) {
3588c2ecf20Sopenharmony_ci		/*
3598c2ecf20Sopenharmony_ci		 * Cancel the CMD_WANT_SKB flag for the cmd in the
3608c2ecf20Sopenharmony_ci		 * TX cmd queue. Otherwise in case the cmd comes
3618c2ecf20Sopenharmony_ci		 * in later, it will possibly set an invalid
3628c2ecf20Sopenharmony_ci		 * address (cmd->meta.source).
3638c2ecf20Sopenharmony_ci		 */
3648c2ecf20Sopenharmony_ci		il->txq[il->cmd_queue].meta[cmd_idx].flags &= ~CMD_WANT_SKB;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_cifail:
3678c2ecf20Sopenharmony_ci	if (cmd->reply_page) {
3688c2ecf20Sopenharmony_ci		il_free_pages(il, cmd->reply_page);
3698c2ecf20Sopenharmony_ci		cmd->reply_page = 0;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ciout:
3728c2ecf20Sopenharmony_ci	return ret;
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_cmd_sync);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ciint
3778c2ecf20Sopenharmony_ciil_send_cmd(struct il_priv *il, struct il_host_cmd *cmd)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	if (cmd->flags & CMD_ASYNC)
3808c2ecf20Sopenharmony_ci		return il_send_cmd_async(il, cmd);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	return il_send_cmd_sync(il, cmd);
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_cmd);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ciint
3878c2ecf20Sopenharmony_ciil_send_cmd_pdu(struct il_priv *il, u8 id, u16 len, const void *data)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	struct il_host_cmd cmd = {
3908c2ecf20Sopenharmony_ci		.id = id,
3918c2ecf20Sopenharmony_ci		.len = len,
3928c2ecf20Sopenharmony_ci		.data = data,
3938c2ecf20Sopenharmony_ci	};
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	return il_send_cmd_sync(il, &cmd);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_cmd_pdu);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ciint
4008c2ecf20Sopenharmony_ciil_send_cmd_pdu_async(struct il_priv *il, u8 id, u16 len, const void *data,
4018c2ecf20Sopenharmony_ci		      void (*callback) (struct il_priv *il,
4028c2ecf20Sopenharmony_ci					struct il_device_cmd *cmd,
4038c2ecf20Sopenharmony_ci					struct il_rx_pkt *pkt))
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct il_host_cmd cmd = {
4068c2ecf20Sopenharmony_ci		.id = id,
4078c2ecf20Sopenharmony_ci		.len = len,
4088c2ecf20Sopenharmony_ci		.data = data,
4098c2ecf20Sopenharmony_ci	};
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	cmd.flags |= CMD_ASYNC;
4128c2ecf20Sopenharmony_ci	cmd.callback = callback;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	return il_send_cmd_async(il, &cmd);
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_cmd_pdu_async);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci/* default: IL_LED_BLINK(0) using blinking idx table */
4198c2ecf20Sopenharmony_cistatic int led_mode;
4208c2ecf20Sopenharmony_cimodule_param(led_mode, int, 0444);
4218c2ecf20Sopenharmony_ciMODULE_PARM_DESC(led_mode,
4228c2ecf20Sopenharmony_ci		 "0=system default, " "1=On(RF On)/Off(RF Off), 2=blinking");
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci/* Throughput		OFF time(ms)	ON time (ms)
4258c2ecf20Sopenharmony_ci *	>300			25		25
4268c2ecf20Sopenharmony_ci *	>200 to 300		40		40
4278c2ecf20Sopenharmony_ci *	>100 to 200		55		55
4288c2ecf20Sopenharmony_ci *	>70 to 100		65		65
4298c2ecf20Sopenharmony_ci *	>50 to 70		75		75
4308c2ecf20Sopenharmony_ci *	>20 to 50		85		85
4318c2ecf20Sopenharmony_ci *	>10 to 20		95		95
4328c2ecf20Sopenharmony_ci *	>5 to 10		110		110
4338c2ecf20Sopenharmony_ci *	>1 to 5			130		130
4348c2ecf20Sopenharmony_ci *	>0 to 1			167		167
4358c2ecf20Sopenharmony_ci *	<=0					SOLID ON
4368c2ecf20Sopenharmony_ci */
4378c2ecf20Sopenharmony_cistatic const struct ieee80211_tpt_blink il_blink[] = {
4388c2ecf20Sopenharmony_ci	{.throughput = 0,		.blink_time = 334},
4398c2ecf20Sopenharmony_ci	{.throughput = 1 * 1024 - 1,	.blink_time = 260},
4408c2ecf20Sopenharmony_ci	{.throughput = 5 * 1024 - 1,	.blink_time = 220},
4418c2ecf20Sopenharmony_ci	{.throughput = 10 * 1024 - 1,	.blink_time = 190},
4428c2ecf20Sopenharmony_ci	{.throughput = 20 * 1024 - 1,	.blink_time = 170},
4438c2ecf20Sopenharmony_ci	{.throughput = 50 * 1024 - 1,	.blink_time = 150},
4448c2ecf20Sopenharmony_ci	{.throughput = 70 * 1024 - 1,	.blink_time = 130},
4458c2ecf20Sopenharmony_ci	{.throughput = 100 * 1024 - 1,	.blink_time = 110},
4468c2ecf20Sopenharmony_ci	{.throughput = 200 * 1024 - 1,	.blink_time = 80},
4478c2ecf20Sopenharmony_ci	{.throughput = 300 * 1024 - 1,	.blink_time = 50},
4488c2ecf20Sopenharmony_ci};
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci/*
4518c2ecf20Sopenharmony_ci * Adjust led blink rate to compensate on a MAC Clock difference on every HW
4528c2ecf20Sopenharmony_ci * Led blink rate analysis showed an average deviation of 0% on 3945,
4538c2ecf20Sopenharmony_ci * 5% on 4965 HW.
4548c2ecf20Sopenharmony_ci * Need to compensate on the led on/off time per HW according to the deviation
4558c2ecf20Sopenharmony_ci * to achieve the desired led frequency
4568c2ecf20Sopenharmony_ci * The calculation is: (100-averageDeviation)/100 * blinkTime
4578c2ecf20Sopenharmony_ci * For code efficiency the calculation will be:
4588c2ecf20Sopenharmony_ci *     compensation = (100 - averageDeviation) * 64 / 100
4598c2ecf20Sopenharmony_ci *     NewBlinkTime = (compensation * BlinkTime) / 64
4608c2ecf20Sopenharmony_ci */
4618c2ecf20Sopenharmony_cistatic inline u8
4628c2ecf20Sopenharmony_ciil_blink_compensation(struct il_priv *il, u8 time, u16 compensation)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	if (!compensation) {
4658c2ecf20Sopenharmony_ci		IL_ERR("undefined blink compensation: "
4668c2ecf20Sopenharmony_ci		       "use pre-defined blinking time\n");
4678c2ecf20Sopenharmony_ci		return time;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	return (u8) ((time * compensation) >> 6);
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci/* Set led pattern command */
4748c2ecf20Sopenharmony_cistatic int
4758c2ecf20Sopenharmony_ciil_led_cmd(struct il_priv *il, unsigned long on, unsigned long off)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	struct il_led_cmd led_cmd = {
4788c2ecf20Sopenharmony_ci		.id = IL_LED_LINK,
4798c2ecf20Sopenharmony_ci		.interval = IL_DEF_LED_INTRVL
4808c2ecf20Sopenharmony_ci	};
4818c2ecf20Sopenharmony_ci	int ret;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (!test_bit(S_READY, &il->status))
4848c2ecf20Sopenharmony_ci		return -EBUSY;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (il->blink_on == on && il->blink_off == off)
4878c2ecf20Sopenharmony_ci		return 0;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if (off == 0) {
4908c2ecf20Sopenharmony_ci		/* led is SOLID_ON */
4918c2ecf20Sopenharmony_ci		on = IL_LED_SOLID;
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	D_LED("Led blink time compensation=%u\n",
4958c2ecf20Sopenharmony_ci	      il->cfg->led_compensation);
4968c2ecf20Sopenharmony_ci	led_cmd.on =
4978c2ecf20Sopenharmony_ci	    il_blink_compensation(il, on,
4988c2ecf20Sopenharmony_ci				  il->cfg->led_compensation);
4998c2ecf20Sopenharmony_ci	led_cmd.off =
5008c2ecf20Sopenharmony_ci	    il_blink_compensation(il, off,
5018c2ecf20Sopenharmony_ci				  il->cfg->led_compensation);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	ret = il->ops->send_led_cmd(il, &led_cmd);
5048c2ecf20Sopenharmony_ci	if (!ret) {
5058c2ecf20Sopenharmony_ci		il->blink_on = on;
5068c2ecf20Sopenharmony_ci		il->blink_off = off;
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci	return ret;
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic void
5128c2ecf20Sopenharmony_ciil_led_brightness_set(struct led_classdev *led_cdev,
5138c2ecf20Sopenharmony_ci		      enum led_brightness brightness)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	struct il_priv *il = container_of(led_cdev, struct il_priv, led);
5168c2ecf20Sopenharmony_ci	unsigned long on = 0;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	if (brightness > 0)
5198c2ecf20Sopenharmony_ci		on = IL_LED_SOLID;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	il_led_cmd(il, on, 0);
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_cistatic int
5258c2ecf20Sopenharmony_ciil_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
5268c2ecf20Sopenharmony_ci		 unsigned long *delay_off)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	struct il_priv *il = container_of(led_cdev, struct il_priv, led);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	return il_led_cmd(il, *delay_on, *delay_off);
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_civoid
5348c2ecf20Sopenharmony_ciil_leds_init(struct il_priv *il)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	int mode = led_mode;
5378c2ecf20Sopenharmony_ci	int ret;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	if (mode == IL_LED_DEFAULT)
5408c2ecf20Sopenharmony_ci		mode = il->cfg->led_mode;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	il->led.name =
5438c2ecf20Sopenharmony_ci	    kasprintf(GFP_KERNEL, "%s-led", wiphy_name(il->hw->wiphy));
5448c2ecf20Sopenharmony_ci	il->led.brightness_set = il_led_brightness_set;
5458c2ecf20Sopenharmony_ci	il->led.blink_set = il_led_blink_set;
5468c2ecf20Sopenharmony_ci	il->led.max_brightness = 1;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	switch (mode) {
5498c2ecf20Sopenharmony_ci	case IL_LED_DEFAULT:
5508c2ecf20Sopenharmony_ci		WARN_ON(1);
5518c2ecf20Sopenharmony_ci		break;
5528c2ecf20Sopenharmony_ci	case IL_LED_BLINK:
5538c2ecf20Sopenharmony_ci		il->led.default_trigger =
5548c2ecf20Sopenharmony_ci		    ieee80211_create_tpt_led_trigger(il->hw,
5558c2ecf20Sopenharmony_ci						     IEEE80211_TPT_LEDTRIG_FL_CONNECTED,
5568c2ecf20Sopenharmony_ci						     il_blink,
5578c2ecf20Sopenharmony_ci						     ARRAY_SIZE(il_blink));
5588c2ecf20Sopenharmony_ci		break;
5598c2ecf20Sopenharmony_ci	case IL_LED_RF_STATE:
5608c2ecf20Sopenharmony_ci		il->led.default_trigger = ieee80211_get_radio_led_name(il->hw);
5618c2ecf20Sopenharmony_ci		break;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	ret = led_classdev_register(&il->pci_dev->dev, &il->led);
5658c2ecf20Sopenharmony_ci	if (ret) {
5668c2ecf20Sopenharmony_ci		kfree(il->led.name);
5678c2ecf20Sopenharmony_ci		return;
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	il->led_registered = true;
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_leds_init);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_civoid
5758c2ecf20Sopenharmony_ciil_leds_exit(struct il_priv *il)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	if (!il->led_registered)
5788c2ecf20Sopenharmony_ci		return;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	led_classdev_unregister(&il->led);
5818c2ecf20Sopenharmony_ci	kfree(il->led.name);
5828c2ecf20Sopenharmony_ci}
5838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_leds_exit);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci/************************** EEPROM BANDS ****************************
5868c2ecf20Sopenharmony_ci *
5878c2ecf20Sopenharmony_ci * The il_eeprom_band definitions below provide the mapping from the
5888c2ecf20Sopenharmony_ci * EEPROM contents to the specific channel number supported for each
5898c2ecf20Sopenharmony_ci * band.
5908c2ecf20Sopenharmony_ci *
5918c2ecf20Sopenharmony_ci * For example, il_priv->eeprom.band_3_channels[4] from the band_3
5928c2ecf20Sopenharmony_ci * definition below maps to physical channel 42 in the 5.2GHz spectrum.
5938c2ecf20Sopenharmony_ci * The specific geography and calibration information for that channel
5948c2ecf20Sopenharmony_ci * is contained in the eeprom map itself.
5958c2ecf20Sopenharmony_ci *
5968c2ecf20Sopenharmony_ci * During init, we copy the eeprom information and channel map
5978c2ecf20Sopenharmony_ci * information into il->channel_info_24/52 and il->channel_map_24/52
5988c2ecf20Sopenharmony_ci *
5998c2ecf20Sopenharmony_ci * channel_map_24/52 provides the idx in the channel_info array for a
6008c2ecf20Sopenharmony_ci * given channel.  We have to have two separate maps as there is channel
6018c2ecf20Sopenharmony_ci * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and
6028c2ecf20Sopenharmony_ci * band_2
6038c2ecf20Sopenharmony_ci *
6048c2ecf20Sopenharmony_ci * A value of 0xff stored in the channel_map indicates that the channel
6058c2ecf20Sopenharmony_ci * is not supported by the hardware at all.
6068c2ecf20Sopenharmony_ci *
6078c2ecf20Sopenharmony_ci * A value of 0xfe in the channel_map indicates that the channel is not
6088c2ecf20Sopenharmony_ci * valid for Tx with the current hardware.  This means that
6098c2ecf20Sopenharmony_ci * while the system can tune and receive on a given channel, it may not
6108c2ecf20Sopenharmony_ci * be able to associate or transmit any frames on that
6118c2ecf20Sopenharmony_ci * channel.  There is no corresponding channel information for that
6128c2ecf20Sopenharmony_ci * entry.
6138c2ecf20Sopenharmony_ci *
6148c2ecf20Sopenharmony_ci *********************************************************************/
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci/* 2.4 GHz */
6178c2ecf20Sopenharmony_ciconst u8 il_eeprom_band_1[14] = {
6188c2ecf20Sopenharmony_ci	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
6198c2ecf20Sopenharmony_ci};
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci/* 5.2 GHz bands */
6228c2ecf20Sopenharmony_cistatic const u8 il_eeprom_band_2[] = {	/* 4915-5080MHz */
6238c2ecf20Sopenharmony_ci	183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16
6248c2ecf20Sopenharmony_ci};
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic const u8 il_eeprom_band_3[] = {	/* 5170-5320MHz */
6278c2ecf20Sopenharmony_ci	34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64
6288c2ecf20Sopenharmony_ci};
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic const u8 il_eeprom_band_4[] = {	/* 5500-5700MHz */
6318c2ecf20Sopenharmony_ci	100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140
6328c2ecf20Sopenharmony_ci};
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic const u8 il_eeprom_band_5[] = {	/* 5725-5825MHz */
6358c2ecf20Sopenharmony_ci	145, 149, 153, 157, 161, 165
6368c2ecf20Sopenharmony_ci};
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_cistatic const u8 il_eeprom_band_6[] = {	/* 2.4 ht40 channel */
6398c2ecf20Sopenharmony_ci	1, 2, 3, 4, 5, 6, 7
6408c2ecf20Sopenharmony_ci};
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cistatic const u8 il_eeprom_band_7[] = {	/* 5.2 ht40 channel */
6438c2ecf20Sopenharmony_ci	36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157
6448c2ecf20Sopenharmony_ci};
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci/******************************************************************************
6478c2ecf20Sopenharmony_ci *
6488c2ecf20Sopenharmony_ci * EEPROM related functions
6498c2ecf20Sopenharmony_ci *
6508c2ecf20Sopenharmony_ci******************************************************************************/
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic int
6538c2ecf20Sopenharmony_ciil_eeprom_verify_signature(struct il_priv *il)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	u32 gp = _il_rd(il, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK;
6568c2ecf20Sopenharmony_ci	int ret = 0;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	D_EEPROM("EEPROM signature=0x%08x\n", gp);
6598c2ecf20Sopenharmony_ci	switch (gp) {
6608c2ecf20Sopenharmony_ci	case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K:
6618c2ecf20Sopenharmony_ci	case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K:
6628c2ecf20Sopenharmony_ci		break;
6638c2ecf20Sopenharmony_ci	default:
6648c2ecf20Sopenharmony_ci		IL_ERR("bad EEPROM signature," "EEPROM_GP=0x%08x\n", gp);
6658c2ecf20Sopenharmony_ci		ret = -ENOENT;
6668c2ecf20Sopenharmony_ci		break;
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci	return ret;
6698c2ecf20Sopenharmony_ci}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ciconst u8 *
6728c2ecf20Sopenharmony_ciil_eeprom_query_addr(const struct il_priv *il, size_t offset)
6738c2ecf20Sopenharmony_ci{
6748c2ecf20Sopenharmony_ci	BUG_ON(offset >= il->cfg->eeprom_size);
6758c2ecf20Sopenharmony_ci	return &il->eeprom[offset];
6768c2ecf20Sopenharmony_ci}
6778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_eeprom_query_addr);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ciu16
6808c2ecf20Sopenharmony_ciil_eeprom_query16(const struct il_priv *il, size_t offset)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	if (!il->eeprom)
6838c2ecf20Sopenharmony_ci		return 0;
6848c2ecf20Sopenharmony_ci	return (u16) il->eeprom[offset] | ((u16) il->eeprom[offset + 1] << 8);
6858c2ecf20Sopenharmony_ci}
6868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_eeprom_query16);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci/*
6898c2ecf20Sopenharmony_ci * il_eeprom_init - read EEPROM contents
6908c2ecf20Sopenharmony_ci *
6918c2ecf20Sopenharmony_ci * Load the EEPROM contents from adapter into il->eeprom
6928c2ecf20Sopenharmony_ci *
6938c2ecf20Sopenharmony_ci * NOTE:  This routine uses the non-debug IO access functions.
6948c2ecf20Sopenharmony_ci */
6958c2ecf20Sopenharmony_ciint
6968c2ecf20Sopenharmony_ciil_eeprom_init(struct il_priv *il)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	__le16 *e;
6998c2ecf20Sopenharmony_ci	u32 gp = _il_rd(il, CSR_EEPROM_GP);
7008c2ecf20Sopenharmony_ci	int sz;
7018c2ecf20Sopenharmony_ci	int ret;
7028c2ecf20Sopenharmony_ci	int addr;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	/* allocate eeprom */
7058c2ecf20Sopenharmony_ci	sz = il->cfg->eeprom_size;
7068c2ecf20Sopenharmony_ci	D_EEPROM("NVM size = %d\n", sz);
7078c2ecf20Sopenharmony_ci	il->eeprom = kzalloc(sz, GFP_KERNEL);
7088c2ecf20Sopenharmony_ci	if (!il->eeprom)
7098c2ecf20Sopenharmony_ci		return -ENOMEM;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	e = (__le16 *) il->eeprom;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	il->ops->apm_init(il);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	ret = il_eeprom_verify_signature(il);
7168c2ecf20Sopenharmony_ci	if (ret < 0) {
7178c2ecf20Sopenharmony_ci		IL_ERR("EEPROM not found, EEPROM_GP=0x%08x\n", gp);
7188c2ecf20Sopenharmony_ci		ret = -ENOENT;
7198c2ecf20Sopenharmony_ci		goto err;
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	/* Make sure driver (instead of uCode) is allowed to read EEPROM */
7238c2ecf20Sopenharmony_ci	ret = il->ops->eeprom_acquire_semaphore(il);
7248c2ecf20Sopenharmony_ci	if (ret < 0) {
7258c2ecf20Sopenharmony_ci		IL_ERR("Failed to acquire EEPROM semaphore.\n");
7268c2ecf20Sopenharmony_ci		ret = -ENOENT;
7278c2ecf20Sopenharmony_ci		goto err;
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	/* eeprom is an array of 16bit values */
7318c2ecf20Sopenharmony_ci	for (addr = 0; addr < sz; addr += sizeof(u16)) {
7328c2ecf20Sopenharmony_ci		u32 r;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci		_il_wr(il, CSR_EEPROM_REG,
7358c2ecf20Sopenharmony_ci		       CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci		ret =
7388c2ecf20Sopenharmony_ci		    _il_poll_bit(il, CSR_EEPROM_REG,
7398c2ecf20Sopenharmony_ci				 CSR_EEPROM_REG_READ_VALID_MSK,
7408c2ecf20Sopenharmony_ci				 CSR_EEPROM_REG_READ_VALID_MSK,
7418c2ecf20Sopenharmony_ci				 IL_EEPROM_ACCESS_TIMEOUT);
7428c2ecf20Sopenharmony_ci		if (ret < 0) {
7438c2ecf20Sopenharmony_ci			IL_ERR("Time out reading EEPROM[%d]\n", addr);
7448c2ecf20Sopenharmony_ci			goto done;
7458c2ecf20Sopenharmony_ci		}
7468c2ecf20Sopenharmony_ci		r = _il_rd(il, CSR_EEPROM_REG);
7478c2ecf20Sopenharmony_ci		e[addr / 2] = cpu_to_le16(r >> 16);
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	D_EEPROM("NVM Type: %s, version: 0x%x\n", "EEPROM",
7518c2ecf20Sopenharmony_ci		 il_eeprom_query16(il, EEPROM_VERSION));
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	ret = 0;
7548c2ecf20Sopenharmony_cidone:
7558c2ecf20Sopenharmony_ci	il->ops->eeprom_release_semaphore(il);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cierr:
7588c2ecf20Sopenharmony_ci	if (ret)
7598c2ecf20Sopenharmony_ci		il_eeprom_free(il);
7608c2ecf20Sopenharmony_ci	/* Reset chip to save power until we load uCode during "up". */
7618c2ecf20Sopenharmony_ci	il_apm_stop(il);
7628c2ecf20Sopenharmony_ci	return ret;
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_eeprom_init);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_civoid
7678c2ecf20Sopenharmony_ciil_eeprom_free(struct il_priv *il)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	kfree(il->eeprom);
7708c2ecf20Sopenharmony_ci	il->eeprom = NULL;
7718c2ecf20Sopenharmony_ci}
7728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_eeprom_free);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cistatic void
7758c2ecf20Sopenharmony_ciil_init_band_reference(const struct il_priv *il, int eep_band,
7768c2ecf20Sopenharmony_ci		       int *eeprom_ch_count,
7778c2ecf20Sopenharmony_ci		       const struct il_eeprom_channel **eeprom_ch_info,
7788c2ecf20Sopenharmony_ci		       const u8 **eeprom_ch_idx)
7798c2ecf20Sopenharmony_ci{
7808c2ecf20Sopenharmony_ci	u32 offset = il->cfg->regulatory_bands[eep_band - 1];
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	switch (eep_band) {
7838c2ecf20Sopenharmony_ci	case 1:		/* 2.4GHz band */
7848c2ecf20Sopenharmony_ci		*eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_1);
7858c2ecf20Sopenharmony_ci		*eeprom_ch_info =
7868c2ecf20Sopenharmony_ci		    (struct il_eeprom_channel *)il_eeprom_query_addr(il,
7878c2ecf20Sopenharmony_ci								     offset);
7888c2ecf20Sopenharmony_ci		*eeprom_ch_idx = il_eeprom_band_1;
7898c2ecf20Sopenharmony_ci		break;
7908c2ecf20Sopenharmony_ci	case 2:		/* 4.9GHz band */
7918c2ecf20Sopenharmony_ci		*eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_2);
7928c2ecf20Sopenharmony_ci		*eeprom_ch_info =
7938c2ecf20Sopenharmony_ci		    (struct il_eeprom_channel *)il_eeprom_query_addr(il,
7948c2ecf20Sopenharmony_ci								     offset);
7958c2ecf20Sopenharmony_ci		*eeprom_ch_idx = il_eeprom_band_2;
7968c2ecf20Sopenharmony_ci		break;
7978c2ecf20Sopenharmony_ci	case 3:		/* 5.2GHz band */
7988c2ecf20Sopenharmony_ci		*eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_3);
7998c2ecf20Sopenharmony_ci		*eeprom_ch_info =
8008c2ecf20Sopenharmony_ci		    (struct il_eeprom_channel *)il_eeprom_query_addr(il,
8018c2ecf20Sopenharmony_ci								     offset);
8028c2ecf20Sopenharmony_ci		*eeprom_ch_idx = il_eeprom_band_3;
8038c2ecf20Sopenharmony_ci		break;
8048c2ecf20Sopenharmony_ci	case 4:		/* 5.5GHz band */
8058c2ecf20Sopenharmony_ci		*eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_4);
8068c2ecf20Sopenharmony_ci		*eeprom_ch_info =
8078c2ecf20Sopenharmony_ci		    (struct il_eeprom_channel *)il_eeprom_query_addr(il,
8088c2ecf20Sopenharmony_ci								     offset);
8098c2ecf20Sopenharmony_ci		*eeprom_ch_idx = il_eeprom_band_4;
8108c2ecf20Sopenharmony_ci		break;
8118c2ecf20Sopenharmony_ci	case 5:		/* 5.7GHz band */
8128c2ecf20Sopenharmony_ci		*eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_5);
8138c2ecf20Sopenharmony_ci		*eeprom_ch_info =
8148c2ecf20Sopenharmony_ci		    (struct il_eeprom_channel *)il_eeprom_query_addr(il,
8158c2ecf20Sopenharmony_ci								     offset);
8168c2ecf20Sopenharmony_ci		*eeprom_ch_idx = il_eeprom_band_5;
8178c2ecf20Sopenharmony_ci		break;
8188c2ecf20Sopenharmony_ci	case 6:		/* 2.4GHz ht40 channels */
8198c2ecf20Sopenharmony_ci		*eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_6);
8208c2ecf20Sopenharmony_ci		*eeprom_ch_info =
8218c2ecf20Sopenharmony_ci		    (struct il_eeprom_channel *)il_eeprom_query_addr(il,
8228c2ecf20Sopenharmony_ci								     offset);
8238c2ecf20Sopenharmony_ci		*eeprom_ch_idx = il_eeprom_band_6;
8248c2ecf20Sopenharmony_ci		break;
8258c2ecf20Sopenharmony_ci	case 7:		/* 5 GHz ht40 channels */
8268c2ecf20Sopenharmony_ci		*eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_7);
8278c2ecf20Sopenharmony_ci		*eeprom_ch_info =
8288c2ecf20Sopenharmony_ci		    (struct il_eeprom_channel *)il_eeprom_query_addr(il,
8298c2ecf20Sopenharmony_ci								     offset);
8308c2ecf20Sopenharmony_ci		*eeprom_ch_idx = il_eeprom_band_7;
8318c2ecf20Sopenharmony_ci		break;
8328c2ecf20Sopenharmony_ci	default:
8338c2ecf20Sopenharmony_ci		BUG();
8348c2ecf20Sopenharmony_ci	}
8358c2ecf20Sopenharmony_ci}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \
8388c2ecf20Sopenharmony_ci			    ? # x " " : "")
8398c2ecf20Sopenharmony_ci/*
8408c2ecf20Sopenharmony_ci * il_mod_ht40_chan_info - Copy ht40 channel info into driver's il.
8418c2ecf20Sopenharmony_ci *
8428c2ecf20Sopenharmony_ci * Does not set up a command, or touch hardware.
8438c2ecf20Sopenharmony_ci */
8448c2ecf20Sopenharmony_cistatic int
8458c2ecf20Sopenharmony_ciil_mod_ht40_chan_info(struct il_priv *il, enum nl80211_band band, u16 channel,
8468c2ecf20Sopenharmony_ci		      const struct il_eeprom_channel *eeprom_ch,
8478c2ecf20Sopenharmony_ci		      u8 clear_ht40_extension_channel)
8488c2ecf20Sopenharmony_ci{
8498c2ecf20Sopenharmony_ci	struct il_channel_info *ch_info;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	ch_info =
8528c2ecf20Sopenharmony_ci	    (struct il_channel_info *)il_get_channel_info(il, band, channel);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	if (!il_is_channel_valid(ch_info))
8558c2ecf20Sopenharmony_ci		return -1;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	D_EEPROM("HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):"
8588c2ecf20Sopenharmony_ci		 " Ad-Hoc %ssupported\n", ch_info->channel,
8598c2ecf20Sopenharmony_ci		 il_is_channel_a_band(ch_info) ? "5.2" : "2.4",
8608c2ecf20Sopenharmony_ci		 CHECK_AND_PRINT(IBSS), CHECK_AND_PRINT(ACTIVE),
8618c2ecf20Sopenharmony_ci		 CHECK_AND_PRINT(RADAR), CHECK_AND_PRINT(WIDE),
8628c2ecf20Sopenharmony_ci		 CHECK_AND_PRINT(DFS), eeprom_ch->flags,
8638c2ecf20Sopenharmony_ci		 eeprom_ch->max_power_avg,
8648c2ecf20Sopenharmony_ci		 ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) &&
8658c2ecf20Sopenharmony_ci		  !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" : "not ");
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	ch_info->ht40_eeprom = *eeprom_ch;
8688c2ecf20Sopenharmony_ci	ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg;
8698c2ecf20Sopenharmony_ci	ch_info->ht40_flags = eeprom_ch->flags;
8708c2ecf20Sopenharmony_ci	if (eeprom_ch->flags & EEPROM_CHANNEL_VALID)
8718c2ecf20Sopenharmony_ci		ch_info->ht40_extension_channel &=
8728c2ecf20Sopenharmony_ci		    ~clear_ht40_extension_channel;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	return 0;
8758c2ecf20Sopenharmony_ci}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci#define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \
8788c2ecf20Sopenharmony_ci			    ? # x " " : "")
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci/*
8818c2ecf20Sopenharmony_ci * il_init_channel_map - Set up driver's info for all possible channels
8828c2ecf20Sopenharmony_ci */
8838c2ecf20Sopenharmony_ciint
8848c2ecf20Sopenharmony_ciil_init_channel_map(struct il_priv *il)
8858c2ecf20Sopenharmony_ci{
8868c2ecf20Sopenharmony_ci	int eeprom_ch_count = 0;
8878c2ecf20Sopenharmony_ci	const u8 *eeprom_ch_idx = NULL;
8888c2ecf20Sopenharmony_ci	const struct il_eeprom_channel *eeprom_ch_info = NULL;
8898c2ecf20Sopenharmony_ci	int band, ch;
8908c2ecf20Sopenharmony_ci	struct il_channel_info *ch_info;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	if (il->channel_count) {
8938c2ecf20Sopenharmony_ci		D_EEPROM("Channel map already initialized.\n");
8948c2ecf20Sopenharmony_ci		return 0;
8958c2ecf20Sopenharmony_ci	}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	D_EEPROM("Initializing regulatory info from EEPROM\n");
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	il->channel_count =
9008c2ecf20Sopenharmony_ci	    ARRAY_SIZE(il_eeprom_band_1) + ARRAY_SIZE(il_eeprom_band_2) +
9018c2ecf20Sopenharmony_ci	    ARRAY_SIZE(il_eeprom_band_3) + ARRAY_SIZE(il_eeprom_band_4) +
9028c2ecf20Sopenharmony_ci	    ARRAY_SIZE(il_eeprom_band_5);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	D_EEPROM("Parsing data for %d channels.\n", il->channel_count);
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	il->channel_info =
9078c2ecf20Sopenharmony_ci	    kcalloc(il->channel_count, sizeof(struct il_channel_info),
9088c2ecf20Sopenharmony_ci		    GFP_KERNEL);
9098c2ecf20Sopenharmony_ci	if (!il->channel_info) {
9108c2ecf20Sopenharmony_ci		IL_ERR("Could not allocate channel_info\n");
9118c2ecf20Sopenharmony_ci		il->channel_count = 0;
9128c2ecf20Sopenharmony_ci		return -ENOMEM;
9138c2ecf20Sopenharmony_ci	}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	ch_info = il->channel_info;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	/* Loop through the 5 EEPROM bands adding them in order to the
9188c2ecf20Sopenharmony_ci	 * channel map we maintain (that contains additional information than
9198c2ecf20Sopenharmony_ci	 * what just in the EEPROM) */
9208c2ecf20Sopenharmony_ci	for (band = 1; band <= 5; band++) {
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci		il_init_band_reference(il, band, &eeprom_ch_count,
9238c2ecf20Sopenharmony_ci				       &eeprom_ch_info, &eeprom_ch_idx);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci		/* Loop through each band adding each of the channels */
9268c2ecf20Sopenharmony_ci		for (ch = 0; ch < eeprom_ch_count; ch++) {
9278c2ecf20Sopenharmony_ci			ch_info->channel = eeprom_ch_idx[ch];
9288c2ecf20Sopenharmony_ci			ch_info->band =
9298c2ecf20Sopenharmony_ci			    (band ==
9308c2ecf20Sopenharmony_ci			     1) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci			/* permanently store EEPROM's channel regulatory flags
9338c2ecf20Sopenharmony_ci			 *   and max power in channel info database. */
9348c2ecf20Sopenharmony_ci			ch_info->eeprom = eeprom_ch_info[ch];
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci			/* Copy the run-time flags so they are there even on
9378c2ecf20Sopenharmony_ci			 * invalid channels */
9388c2ecf20Sopenharmony_ci			ch_info->flags = eeprom_ch_info[ch].flags;
9398c2ecf20Sopenharmony_ci			/* First write that ht40 is not enabled, and then enable
9408c2ecf20Sopenharmony_ci			 * one by one */
9418c2ecf20Sopenharmony_ci			ch_info->ht40_extension_channel =
9428c2ecf20Sopenharmony_ci			    IEEE80211_CHAN_NO_HT40;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci			if (!(il_is_channel_valid(ch_info))) {
9458c2ecf20Sopenharmony_ci				D_EEPROM("Ch. %d Flags %x [%sGHz] - "
9468c2ecf20Sopenharmony_ci					 "No traffic\n", ch_info->channel,
9478c2ecf20Sopenharmony_ci					 ch_info->flags,
9488c2ecf20Sopenharmony_ci					 il_is_channel_a_band(ch_info) ? "5.2" :
9498c2ecf20Sopenharmony_ci					 "2.4");
9508c2ecf20Sopenharmony_ci				ch_info++;
9518c2ecf20Sopenharmony_ci				continue;
9528c2ecf20Sopenharmony_ci			}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci			/* Initialize regulatory-based run-time data */
9558c2ecf20Sopenharmony_ci			ch_info->max_power_avg = ch_info->curr_txpow =
9568c2ecf20Sopenharmony_ci			    eeprom_ch_info[ch].max_power_avg;
9578c2ecf20Sopenharmony_ci			ch_info->scan_power = eeprom_ch_info[ch].max_power_avg;
9588c2ecf20Sopenharmony_ci			ch_info->min_power = 0;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci			D_EEPROM("Ch. %d [%sGHz] " "%s%s%s%s%s%s(0x%02x %ddBm):"
9618c2ecf20Sopenharmony_ci				 " Ad-Hoc %ssupported\n", ch_info->channel,
9628c2ecf20Sopenharmony_ci				 il_is_channel_a_band(ch_info) ? "5.2" : "2.4",
9638c2ecf20Sopenharmony_ci				 CHECK_AND_PRINT_I(VALID),
9648c2ecf20Sopenharmony_ci				 CHECK_AND_PRINT_I(IBSS),
9658c2ecf20Sopenharmony_ci				 CHECK_AND_PRINT_I(ACTIVE),
9668c2ecf20Sopenharmony_ci				 CHECK_AND_PRINT_I(RADAR),
9678c2ecf20Sopenharmony_ci				 CHECK_AND_PRINT_I(WIDE),
9688c2ecf20Sopenharmony_ci				 CHECK_AND_PRINT_I(DFS),
9698c2ecf20Sopenharmony_ci				 eeprom_ch_info[ch].flags,
9708c2ecf20Sopenharmony_ci				 eeprom_ch_info[ch].max_power_avg,
9718c2ecf20Sopenharmony_ci				 ((eeprom_ch_info[ch].
9728c2ecf20Sopenharmony_ci				   flags & EEPROM_CHANNEL_IBSS) &&
9738c2ecf20Sopenharmony_ci				  !(eeprom_ch_info[ch].
9748c2ecf20Sopenharmony_ci				    flags & EEPROM_CHANNEL_RADAR)) ? "" :
9758c2ecf20Sopenharmony_ci				 "not ");
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci			ch_info++;
9788c2ecf20Sopenharmony_ci		}
9798c2ecf20Sopenharmony_ci	}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	/* Check if we do have HT40 channels */
9828c2ecf20Sopenharmony_ci	if (il->cfg->regulatory_bands[5] == EEPROM_REGULATORY_BAND_NO_HT40 &&
9838c2ecf20Sopenharmony_ci	    il->cfg->regulatory_bands[6] == EEPROM_REGULATORY_BAND_NO_HT40)
9848c2ecf20Sopenharmony_ci		return 0;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	/* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */
9878c2ecf20Sopenharmony_ci	for (band = 6; band <= 7; band++) {
9888c2ecf20Sopenharmony_ci		enum nl80211_band ieeeband;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci		il_init_band_reference(il, band, &eeprom_ch_count,
9918c2ecf20Sopenharmony_ci				       &eeprom_ch_info, &eeprom_ch_idx);
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci		/* EEPROM band 6 is 2.4, band 7 is 5 GHz */
9948c2ecf20Sopenharmony_ci		ieeeband =
9958c2ecf20Sopenharmony_ci		    (band == 6) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci		/* Loop through each band adding each of the channels */
9988c2ecf20Sopenharmony_ci		for (ch = 0; ch < eeprom_ch_count; ch++) {
9998c2ecf20Sopenharmony_ci			/* Set up driver's info for lower half */
10008c2ecf20Sopenharmony_ci			il_mod_ht40_chan_info(il, ieeeband, eeprom_ch_idx[ch],
10018c2ecf20Sopenharmony_ci					      &eeprom_ch_info[ch],
10028c2ecf20Sopenharmony_ci					      IEEE80211_CHAN_NO_HT40PLUS);
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci			/* Set up driver's info for upper half */
10058c2ecf20Sopenharmony_ci			il_mod_ht40_chan_info(il, ieeeband,
10068c2ecf20Sopenharmony_ci					      eeprom_ch_idx[ch] + 4,
10078c2ecf20Sopenharmony_ci					      &eeprom_ch_info[ch],
10088c2ecf20Sopenharmony_ci					      IEEE80211_CHAN_NO_HT40MINUS);
10098c2ecf20Sopenharmony_ci		}
10108c2ecf20Sopenharmony_ci	}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	return 0;
10138c2ecf20Sopenharmony_ci}
10148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_init_channel_map);
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci/*
10178c2ecf20Sopenharmony_ci * il_free_channel_map - undo allocations in il_init_channel_map
10188c2ecf20Sopenharmony_ci */
10198c2ecf20Sopenharmony_civoid
10208c2ecf20Sopenharmony_ciil_free_channel_map(struct il_priv *il)
10218c2ecf20Sopenharmony_ci{
10228c2ecf20Sopenharmony_ci	kfree(il->channel_info);
10238c2ecf20Sopenharmony_ci	il->channel_count = 0;
10248c2ecf20Sopenharmony_ci}
10258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_free_channel_map);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci/*
10288c2ecf20Sopenharmony_ci * il_get_channel_info - Find driver's ilate channel info
10298c2ecf20Sopenharmony_ci *
10308c2ecf20Sopenharmony_ci * Based on band and channel number.
10318c2ecf20Sopenharmony_ci */
10328c2ecf20Sopenharmony_ciconst struct il_channel_info *
10338c2ecf20Sopenharmony_ciil_get_channel_info(const struct il_priv *il, enum nl80211_band band,
10348c2ecf20Sopenharmony_ci		    u16 channel)
10358c2ecf20Sopenharmony_ci{
10368c2ecf20Sopenharmony_ci	int i;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	switch (band) {
10398c2ecf20Sopenharmony_ci	case NL80211_BAND_5GHZ:
10408c2ecf20Sopenharmony_ci		for (i = 14; i < il->channel_count; i++) {
10418c2ecf20Sopenharmony_ci			if (il->channel_info[i].channel == channel)
10428c2ecf20Sopenharmony_ci				return &il->channel_info[i];
10438c2ecf20Sopenharmony_ci		}
10448c2ecf20Sopenharmony_ci		break;
10458c2ecf20Sopenharmony_ci	case NL80211_BAND_2GHZ:
10468c2ecf20Sopenharmony_ci		if (channel >= 1 && channel <= 14)
10478c2ecf20Sopenharmony_ci			return &il->channel_info[channel - 1];
10488c2ecf20Sopenharmony_ci		break;
10498c2ecf20Sopenharmony_ci	default:
10508c2ecf20Sopenharmony_ci		BUG();
10518c2ecf20Sopenharmony_ci	}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	return NULL;
10548c2ecf20Sopenharmony_ci}
10558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_channel_info);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci/*
10588c2ecf20Sopenharmony_ci * Setting power level allows the card to go to sleep when not busy.
10598c2ecf20Sopenharmony_ci *
10608c2ecf20Sopenharmony_ci * We calculate a sleep command based on the required latency, which
10618c2ecf20Sopenharmony_ci * we get from mac80211.
10628c2ecf20Sopenharmony_ci */
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci#define SLP_VEC(X0, X1, X2, X3, X4) { \
10658c2ecf20Sopenharmony_ci		cpu_to_le32(X0), \
10668c2ecf20Sopenharmony_ci		cpu_to_le32(X1), \
10678c2ecf20Sopenharmony_ci		cpu_to_le32(X2), \
10688c2ecf20Sopenharmony_ci		cpu_to_le32(X3), \
10698c2ecf20Sopenharmony_ci		cpu_to_le32(X4)  \
10708c2ecf20Sopenharmony_ci}
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_cistatic void
10738c2ecf20Sopenharmony_ciil_build_powertable_cmd(struct il_priv *il, struct il_powertable_cmd *cmd)
10748c2ecf20Sopenharmony_ci{
10758c2ecf20Sopenharmony_ci	static const __le32 interval[3][IL_POWER_VEC_SIZE] = {
10768c2ecf20Sopenharmony_ci		SLP_VEC(2, 2, 4, 6, 0xFF),
10778c2ecf20Sopenharmony_ci		SLP_VEC(2, 4, 7, 10, 10),
10788c2ecf20Sopenharmony_ci		SLP_VEC(4, 7, 10, 10, 0xFF)
10798c2ecf20Sopenharmony_ci	};
10808c2ecf20Sopenharmony_ci	int i, dtim_period, no_dtim;
10818c2ecf20Sopenharmony_ci	u32 max_sleep;
10828c2ecf20Sopenharmony_ci	bool skip;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	memset(cmd, 0, sizeof(*cmd));
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	if (il->power_data.pci_pm)
10878c2ecf20Sopenharmony_ci		cmd->flags |= IL_POWER_PCI_PM_MSK;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	/* if no Power Save, we are done */
10908c2ecf20Sopenharmony_ci	if (il->power_data.ps_disabled)
10918c2ecf20Sopenharmony_ci		return;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	cmd->flags = IL_POWER_DRIVER_ALLOW_SLEEP_MSK;
10948c2ecf20Sopenharmony_ci	cmd->keep_alive_seconds = 0;
10958c2ecf20Sopenharmony_ci	cmd->debug_flags = 0;
10968c2ecf20Sopenharmony_ci	cmd->rx_data_timeout = cpu_to_le32(25 * 1024);
10978c2ecf20Sopenharmony_ci	cmd->tx_data_timeout = cpu_to_le32(25 * 1024);
10988c2ecf20Sopenharmony_ci	cmd->keep_alive_beacons = 0;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	dtim_period = il->vif ? il->vif->bss_conf.dtim_period : 0;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	if (dtim_period <= 2) {
11038c2ecf20Sopenharmony_ci		memcpy(cmd->sleep_interval, interval[0], sizeof(interval[0]));
11048c2ecf20Sopenharmony_ci		no_dtim = 2;
11058c2ecf20Sopenharmony_ci	} else if (dtim_period <= 10) {
11068c2ecf20Sopenharmony_ci		memcpy(cmd->sleep_interval, interval[1], sizeof(interval[1]));
11078c2ecf20Sopenharmony_ci		no_dtim = 2;
11088c2ecf20Sopenharmony_ci	} else {
11098c2ecf20Sopenharmony_ci		memcpy(cmd->sleep_interval, interval[2], sizeof(interval[2]));
11108c2ecf20Sopenharmony_ci		no_dtim = 0;
11118c2ecf20Sopenharmony_ci	}
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	if (dtim_period == 0) {
11148c2ecf20Sopenharmony_ci		dtim_period = 1;
11158c2ecf20Sopenharmony_ci		skip = false;
11168c2ecf20Sopenharmony_ci	} else {
11178c2ecf20Sopenharmony_ci		skip = !!no_dtim;
11188c2ecf20Sopenharmony_ci	}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	if (skip) {
11218c2ecf20Sopenharmony_ci		__le32 tmp = cmd->sleep_interval[IL_POWER_VEC_SIZE - 1];
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci		max_sleep = le32_to_cpu(tmp);
11248c2ecf20Sopenharmony_ci		if (max_sleep == 0xFF)
11258c2ecf20Sopenharmony_ci			max_sleep = dtim_period * (skip + 1);
11268c2ecf20Sopenharmony_ci		else if (max_sleep >  dtim_period)
11278c2ecf20Sopenharmony_ci			max_sleep = (max_sleep / dtim_period) * dtim_period;
11288c2ecf20Sopenharmony_ci		cmd->flags |= IL_POWER_SLEEP_OVER_DTIM_MSK;
11298c2ecf20Sopenharmony_ci	} else {
11308c2ecf20Sopenharmony_ci		max_sleep = dtim_period;
11318c2ecf20Sopenharmony_ci		cmd->flags &= ~IL_POWER_SLEEP_OVER_DTIM_MSK;
11328c2ecf20Sopenharmony_ci	}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	for (i = 0; i < IL_POWER_VEC_SIZE; i++)
11358c2ecf20Sopenharmony_ci		if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep)
11368c2ecf20Sopenharmony_ci			cmd->sleep_interval[i] = cpu_to_le32(max_sleep);
11378c2ecf20Sopenharmony_ci}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_cistatic int
11408c2ecf20Sopenharmony_ciil_set_power(struct il_priv *il, struct il_powertable_cmd *cmd)
11418c2ecf20Sopenharmony_ci{
11428c2ecf20Sopenharmony_ci	D_POWER("Sending power/sleep command\n");
11438c2ecf20Sopenharmony_ci	D_POWER("Flags value = 0x%08X\n", cmd->flags);
11448c2ecf20Sopenharmony_ci	D_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout));
11458c2ecf20Sopenharmony_ci	D_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout));
11468c2ecf20Sopenharmony_ci	D_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n",
11478c2ecf20Sopenharmony_ci		le32_to_cpu(cmd->sleep_interval[0]),
11488c2ecf20Sopenharmony_ci		le32_to_cpu(cmd->sleep_interval[1]),
11498c2ecf20Sopenharmony_ci		le32_to_cpu(cmd->sleep_interval[2]),
11508c2ecf20Sopenharmony_ci		le32_to_cpu(cmd->sleep_interval[3]),
11518c2ecf20Sopenharmony_ci		le32_to_cpu(cmd->sleep_interval[4]));
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	return il_send_cmd_pdu(il, C_POWER_TBL,
11548c2ecf20Sopenharmony_ci			       sizeof(struct il_powertable_cmd), cmd);
11558c2ecf20Sopenharmony_ci}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_cistatic int
11588c2ecf20Sopenharmony_ciil_power_set_mode(struct il_priv *il, struct il_powertable_cmd *cmd, bool force)
11598c2ecf20Sopenharmony_ci{
11608c2ecf20Sopenharmony_ci	int ret;
11618c2ecf20Sopenharmony_ci	bool update_chains;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	lockdep_assert_held(&il->mutex);
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	/* Don't update the RX chain when chain noise calibration is running */
11668c2ecf20Sopenharmony_ci	update_chains = il->chain_noise_data.state == IL_CHAIN_NOISE_DONE ||
11678c2ecf20Sopenharmony_ci	    il->chain_noise_data.state == IL_CHAIN_NOISE_ALIVE;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	if (!memcmp(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force)
11708c2ecf20Sopenharmony_ci		return 0;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	if (!il_is_ready_rf(il))
11738c2ecf20Sopenharmony_ci		return -EIO;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	/* scan complete use sleep_power_next, need to be updated */
11768c2ecf20Sopenharmony_ci	memcpy(&il->power_data.sleep_cmd_next, cmd, sizeof(*cmd));
11778c2ecf20Sopenharmony_ci	if (test_bit(S_SCANNING, &il->status) && !force) {
11788c2ecf20Sopenharmony_ci		D_INFO("Defer power set mode while scanning\n");
11798c2ecf20Sopenharmony_ci		return 0;
11808c2ecf20Sopenharmony_ci	}
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	if (cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK)
11838c2ecf20Sopenharmony_ci		set_bit(S_POWER_PMI, &il->status);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	ret = il_set_power(il, cmd);
11868c2ecf20Sopenharmony_ci	if (!ret) {
11878c2ecf20Sopenharmony_ci		if (!(cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK))
11888c2ecf20Sopenharmony_ci			clear_bit(S_POWER_PMI, &il->status);
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci		if (il->ops->update_chain_flags && update_chains)
11918c2ecf20Sopenharmony_ci			il->ops->update_chain_flags(il);
11928c2ecf20Sopenharmony_ci		else if (il->ops->update_chain_flags)
11938c2ecf20Sopenharmony_ci			D_POWER("Cannot update the power, chain noise "
11948c2ecf20Sopenharmony_ci				"calibration running: %d\n",
11958c2ecf20Sopenharmony_ci				il->chain_noise_data.state);
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci		memcpy(&il->power_data.sleep_cmd, cmd, sizeof(*cmd));
11988c2ecf20Sopenharmony_ci	} else
11998c2ecf20Sopenharmony_ci		IL_ERR("set power fail, ret = %d", ret);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	return ret;
12028c2ecf20Sopenharmony_ci}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ciint
12058c2ecf20Sopenharmony_ciil_power_update_mode(struct il_priv *il, bool force)
12068c2ecf20Sopenharmony_ci{
12078c2ecf20Sopenharmony_ci	struct il_powertable_cmd cmd;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	il_build_powertable_cmd(il, &cmd);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	return il_power_set_mode(il, &cmd, force);
12128c2ecf20Sopenharmony_ci}
12138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_power_update_mode);
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci/* initialize to default */
12168c2ecf20Sopenharmony_civoid
12178c2ecf20Sopenharmony_ciil_power_initialize(struct il_priv *il)
12188c2ecf20Sopenharmony_ci{
12198c2ecf20Sopenharmony_ci	u16 lctl;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl);
12228c2ecf20Sopenharmony_ci	il->power_data.pci_pm = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	il->power_data.debug_sleep_level_override = -1;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	memset(&il->power_data.sleep_cmd, 0, sizeof(il->power_data.sleep_cmd));
12278c2ecf20Sopenharmony_ci}
12288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_power_initialize);
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after
12318c2ecf20Sopenharmony_ci * sending probe req.  This should be set long enough to hear probe responses
12328c2ecf20Sopenharmony_ci * from more than one AP.  */
12338c2ecf20Sopenharmony_ci#define IL_ACTIVE_DWELL_TIME_24    (30)	/* all times in msec */
12348c2ecf20Sopenharmony_ci#define IL_ACTIVE_DWELL_TIME_52    (20)
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci#define IL_ACTIVE_DWELL_FACTOR_24GHZ (3)
12378c2ecf20Sopenharmony_ci#define IL_ACTIVE_DWELL_FACTOR_52GHZ (2)
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel.
12408c2ecf20Sopenharmony_ci * Must be set longer than active dwell time.
12418c2ecf20Sopenharmony_ci * For the most reliable scan, set > AP beacon interval (typically 100msec). */
12428c2ecf20Sopenharmony_ci#define IL_PASSIVE_DWELL_TIME_24   (20)	/* all times in msec */
12438c2ecf20Sopenharmony_ci#define IL_PASSIVE_DWELL_TIME_52   (10)
12448c2ecf20Sopenharmony_ci#define IL_PASSIVE_DWELL_BASE      (100)
12458c2ecf20Sopenharmony_ci#define IL_CHANNEL_TUNE_TIME       5
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_cistatic int
12488c2ecf20Sopenharmony_ciil_send_scan_abort(struct il_priv *il)
12498c2ecf20Sopenharmony_ci{
12508c2ecf20Sopenharmony_ci	int ret;
12518c2ecf20Sopenharmony_ci	struct il_rx_pkt *pkt;
12528c2ecf20Sopenharmony_ci	struct il_host_cmd cmd = {
12538c2ecf20Sopenharmony_ci		.id = C_SCAN_ABORT,
12548c2ecf20Sopenharmony_ci		.flags = CMD_WANT_SKB,
12558c2ecf20Sopenharmony_ci	};
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	/* Exit instantly with error when device is not ready
12588c2ecf20Sopenharmony_ci	 * to receive scan abort command or it does not perform
12598c2ecf20Sopenharmony_ci	 * hardware scan currently */
12608c2ecf20Sopenharmony_ci	if (!test_bit(S_READY, &il->status) ||
12618c2ecf20Sopenharmony_ci	    !test_bit(S_GEO_CONFIGURED, &il->status) ||
12628c2ecf20Sopenharmony_ci	    !test_bit(S_SCAN_HW, &il->status) ||
12638c2ecf20Sopenharmony_ci	    test_bit(S_FW_ERROR, &il->status) ||
12648c2ecf20Sopenharmony_ci	    test_bit(S_EXIT_PENDING, &il->status))
12658c2ecf20Sopenharmony_ci		return -EIO;
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	ret = il_send_cmd_sync(il, &cmd);
12688c2ecf20Sopenharmony_ci	if (ret)
12698c2ecf20Sopenharmony_ci		return ret;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	pkt = (struct il_rx_pkt *)cmd.reply_page;
12728c2ecf20Sopenharmony_ci	if (pkt->u.status != CAN_ABORT_STATUS) {
12738c2ecf20Sopenharmony_ci		/* The scan abort will return 1 for success or
12748c2ecf20Sopenharmony_ci		 * 2 for "failure".  A failure condition can be
12758c2ecf20Sopenharmony_ci		 * due to simply not being in an active scan which
12768c2ecf20Sopenharmony_ci		 * can occur if we send the scan abort before we
12778c2ecf20Sopenharmony_ci		 * the microcode has notified us that a scan is
12788c2ecf20Sopenharmony_ci		 * completed. */
12798c2ecf20Sopenharmony_ci		D_SCAN("SCAN_ABORT ret %d.\n", pkt->u.status);
12808c2ecf20Sopenharmony_ci		ret = -EIO;
12818c2ecf20Sopenharmony_ci	}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	il_free_pages(il, cmd.reply_page);
12848c2ecf20Sopenharmony_ci	return ret;
12858c2ecf20Sopenharmony_ci}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_cistatic void
12888c2ecf20Sopenharmony_ciil_complete_scan(struct il_priv *il, bool aborted)
12898c2ecf20Sopenharmony_ci{
12908c2ecf20Sopenharmony_ci	struct cfg80211_scan_info info = {
12918c2ecf20Sopenharmony_ci		.aborted = aborted,
12928c2ecf20Sopenharmony_ci	};
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	/* check if scan was requested from mac80211 */
12958c2ecf20Sopenharmony_ci	if (il->scan_request) {
12968c2ecf20Sopenharmony_ci		D_SCAN("Complete scan in mac80211\n");
12978c2ecf20Sopenharmony_ci		ieee80211_scan_completed(il->hw, &info);
12988c2ecf20Sopenharmony_ci	}
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	il->scan_vif = NULL;
13018c2ecf20Sopenharmony_ci	il->scan_request = NULL;
13028c2ecf20Sopenharmony_ci}
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_civoid
13058c2ecf20Sopenharmony_ciil_force_scan_end(struct il_priv *il)
13068c2ecf20Sopenharmony_ci{
13078c2ecf20Sopenharmony_ci	lockdep_assert_held(&il->mutex);
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	if (!test_bit(S_SCANNING, &il->status)) {
13108c2ecf20Sopenharmony_ci		D_SCAN("Forcing scan end while not scanning\n");
13118c2ecf20Sopenharmony_ci		return;
13128c2ecf20Sopenharmony_ci	}
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	D_SCAN("Forcing scan end\n");
13158c2ecf20Sopenharmony_ci	clear_bit(S_SCANNING, &il->status);
13168c2ecf20Sopenharmony_ci	clear_bit(S_SCAN_HW, &il->status);
13178c2ecf20Sopenharmony_ci	clear_bit(S_SCAN_ABORTING, &il->status);
13188c2ecf20Sopenharmony_ci	il_complete_scan(il, true);
13198c2ecf20Sopenharmony_ci}
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_cistatic void
13228c2ecf20Sopenharmony_ciil_do_scan_abort(struct il_priv *il)
13238c2ecf20Sopenharmony_ci{
13248c2ecf20Sopenharmony_ci	int ret;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	lockdep_assert_held(&il->mutex);
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	if (!test_bit(S_SCANNING, &il->status)) {
13298c2ecf20Sopenharmony_ci		D_SCAN("Not performing scan to abort\n");
13308c2ecf20Sopenharmony_ci		return;
13318c2ecf20Sopenharmony_ci	}
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	if (test_and_set_bit(S_SCAN_ABORTING, &il->status)) {
13348c2ecf20Sopenharmony_ci		D_SCAN("Scan abort in progress\n");
13358c2ecf20Sopenharmony_ci		return;
13368c2ecf20Sopenharmony_ci	}
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	ret = il_send_scan_abort(il);
13398c2ecf20Sopenharmony_ci	if (ret) {
13408c2ecf20Sopenharmony_ci		D_SCAN("Send scan abort failed %d\n", ret);
13418c2ecf20Sopenharmony_ci		il_force_scan_end(il);
13428c2ecf20Sopenharmony_ci	} else
13438c2ecf20Sopenharmony_ci		D_SCAN("Successfully send scan abort\n");
13448c2ecf20Sopenharmony_ci}
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci/*
13478c2ecf20Sopenharmony_ci * il_scan_cancel - Cancel any currently executing HW scan
13488c2ecf20Sopenharmony_ci */
13498c2ecf20Sopenharmony_ciint
13508c2ecf20Sopenharmony_ciil_scan_cancel(struct il_priv *il)
13518c2ecf20Sopenharmony_ci{
13528c2ecf20Sopenharmony_ci	D_SCAN("Queuing abort scan\n");
13538c2ecf20Sopenharmony_ci	queue_work(il->workqueue, &il->abort_scan);
13548c2ecf20Sopenharmony_ci	return 0;
13558c2ecf20Sopenharmony_ci}
13568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_scan_cancel);
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci/*
13598c2ecf20Sopenharmony_ci * il_scan_cancel_timeout - Cancel any currently executing HW scan
13608c2ecf20Sopenharmony_ci * @ms: amount of time to wait (in milliseconds) for scan to abort
13618c2ecf20Sopenharmony_ci *
13628c2ecf20Sopenharmony_ci */
13638c2ecf20Sopenharmony_ciint
13648c2ecf20Sopenharmony_ciil_scan_cancel_timeout(struct il_priv *il, unsigned long ms)
13658c2ecf20Sopenharmony_ci{
13668c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(ms);
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	lockdep_assert_held(&il->mutex);
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	D_SCAN("Scan cancel timeout\n");
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	il_do_scan_abort(il);
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	while (time_before_eq(jiffies, timeout)) {
13758c2ecf20Sopenharmony_ci		if (!test_bit(S_SCAN_HW, &il->status))
13768c2ecf20Sopenharmony_ci			break;
13778c2ecf20Sopenharmony_ci		msleep(20);
13788c2ecf20Sopenharmony_ci	}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	return test_bit(S_SCAN_HW, &il->status);
13818c2ecf20Sopenharmony_ci}
13828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_scan_cancel_timeout);
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci/* Service response to C_SCAN (0x80) */
13858c2ecf20Sopenharmony_cistatic void
13868c2ecf20Sopenharmony_ciil_hdl_scan(struct il_priv *il, struct il_rx_buf *rxb)
13878c2ecf20Sopenharmony_ci{
13888c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
13898c2ecf20Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
13908c2ecf20Sopenharmony_ci	struct il_scanreq_notification *notif =
13918c2ecf20Sopenharmony_ci	    (struct il_scanreq_notification *)pkt->u.raw;
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	D_SCAN("Scan request status = 0x%x\n", notif->status);
13948c2ecf20Sopenharmony_ci#endif
13958c2ecf20Sopenharmony_ci}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci/* Service N_SCAN_START (0x82) */
13988c2ecf20Sopenharmony_cistatic void
13998c2ecf20Sopenharmony_ciil_hdl_scan_start(struct il_priv *il, struct il_rx_buf *rxb)
14008c2ecf20Sopenharmony_ci{
14018c2ecf20Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
14028c2ecf20Sopenharmony_ci	struct il_scanstart_notification *notif =
14038c2ecf20Sopenharmony_ci	    (struct il_scanstart_notification *)pkt->u.raw;
14048c2ecf20Sopenharmony_ci	il->scan_start_tsf = le32_to_cpu(notif->tsf_low);
14058c2ecf20Sopenharmony_ci	D_SCAN("Scan start: " "%d [802.11%s] "
14068c2ecf20Sopenharmony_ci	       "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", notif->channel,
14078c2ecf20Sopenharmony_ci	       notif->band ? "bg" : "a", le32_to_cpu(notif->tsf_high),
14088c2ecf20Sopenharmony_ci	       le32_to_cpu(notif->tsf_low), notif->status, notif->beacon_timer);
14098c2ecf20Sopenharmony_ci}
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci/* Service N_SCAN_RESULTS (0x83) */
14128c2ecf20Sopenharmony_cistatic void
14138c2ecf20Sopenharmony_ciil_hdl_scan_results(struct il_priv *il, struct il_rx_buf *rxb)
14148c2ecf20Sopenharmony_ci{
14158c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
14168c2ecf20Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
14178c2ecf20Sopenharmony_ci	struct il_scanresults_notification *notif =
14188c2ecf20Sopenharmony_ci	    (struct il_scanresults_notification *)pkt->u.raw;
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	D_SCAN("Scan ch.res: " "%d [802.11%s] " "(TSF: 0x%08X:%08X) - %d "
14218c2ecf20Sopenharmony_ci	       "elapsed=%lu usec\n", notif->channel, notif->band ? "bg" : "a",
14228c2ecf20Sopenharmony_ci	       le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low),
14238c2ecf20Sopenharmony_ci	       le32_to_cpu(notif->stats[0]),
14248c2ecf20Sopenharmony_ci	       le32_to_cpu(notif->tsf_low) - il->scan_start_tsf);
14258c2ecf20Sopenharmony_ci#endif
14268c2ecf20Sopenharmony_ci}
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci/* Service N_SCAN_COMPLETE (0x84) */
14298c2ecf20Sopenharmony_cistatic void
14308c2ecf20Sopenharmony_ciil_hdl_scan_complete(struct il_priv *il, struct il_rx_buf *rxb)
14318c2ecf20Sopenharmony_ci{
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
14348c2ecf20Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
14358c2ecf20Sopenharmony_ci	struct il_scancomplete_notification *scan_notif = (void *)pkt->u.raw;
14368c2ecf20Sopenharmony_ci#endif
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	D_SCAN("Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n",
14398c2ecf20Sopenharmony_ci	       scan_notif->scanned_channels, scan_notif->tsf_low,
14408c2ecf20Sopenharmony_ci	       scan_notif->tsf_high, scan_notif->status);
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	/* The HW is no longer scanning */
14438c2ecf20Sopenharmony_ci	clear_bit(S_SCAN_HW, &il->status);
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	D_SCAN("Scan on %sGHz took %dms\n",
14468c2ecf20Sopenharmony_ci	       (il->scan_band == NL80211_BAND_2GHZ) ? "2.4" : "5.2",
14478c2ecf20Sopenharmony_ci	       jiffies_to_msecs(jiffies - il->scan_start));
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	queue_work(il->workqueue, &il->scan_completed);
14508c2ecf20Sopenharmony_ci}
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_civoid
14538c2ecf20Sopenharmony_ciil_setup_rx_scan_handlers(struct il_priv *il)
14548c2ecf20Sopenharmony_ci{
14558c2ecf20Sopenharmony_ci	/* scan handlers */
14568c2ecf20Sopenharmony_ci	il->handlers[C_SCAN] = il_hdl_scan;
14578c2ecf20Sopenharmony_ci	il->handlers[N_SCAN_START] = il_hdl_scan_start;
14588c2ecf20Sopenharmony_ci	il->handlers[N_SCAN_RESULTS] = il_hdl_scan_results;
14598c2ecf20Sopenharmony_ci	il->handlers[N_SCAN_COMPLETE] = il_hdl_scan_complete;
14608c2ecf20Sopenharmony_ci}
14618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_setup_rx_scan_handlers);
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ciu16
14648c2ecf20Sopenharmony_ciil_get_active_dwell_time(struct il_priv *il, enum nl80211_band band,
14658c2ecf20Sopenharmony_ci			 u8 n_probes)
14668c2ecf20Sopenharmony_ci{
14678c2ecf20Sopenharmony_ci	if (band == NL80211_BAND_5GHZ)
14688c2ecf20Sopenharmony_ci		return IL_ACTIVE_DWELL_TIME_52 +
14698c2ecf20Sopenharmony_ci		    IL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1);
14708c2ecf20Sopenharmony_ci	else
14718c2ecf20Sopenharmony_ci		return IL_ACTIVE_DWELL_TIME_24 +
14728c2ecf20Sopenharmony_ci		    IL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1);
14738c2ecf20Sopenharmony_ci}
14748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_active_dwell_time);
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ciu16
14778c2ecf20Sopenharmony_ciil_get_passive_dwell_time(struct il_priv *il, enum nl80211_band band,
14788c2ecf20Sopenharmony_ci			  struct ieee80211_vif *vif)
14798c2ecf20Sopenharmony_ci{
14808c2ecf20Sopenharmony_ci	u16 value;
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci	u16 passive =
14838c2ecf20Sopenharmony_ci	    (band ==
14848c2ecf20Sopenharmony_ci	     NL80211_BAND_2GHZ) ? IL_PASSIVE_DWELL_BASE +
14858c2ecf20Sopenharmony_ci	    IL_PASSIVE_DWELL_TIME_24 : IL_PASSIVE_DWELL_BASE +
14868c2ecf20Sopenharmony_ci	    IL_PASSIVE_DWELL_TIME_52;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	if (il_is_any_associated(il)) {
14898c2ecf20Sopenharmony_ci		/*
14908c2ecf20Sopenharmony_ci		 * If we're associated, we clamp the maximum passive
14918c2ecf20Sopenharmony_ci		 * dwell time to be 98% of the smallest beacon interval
14928c2ecf20Sopenharmony_ci		 * (minus 2 * channel tune time)
14938c2ecf20Sopenharmony_ci		 */
14948c2ecf20Sopenharmony_ci		value = il->vif ? il->vif->bss_conf.beacon_int : 0;
14958c2ecf20Sopenharmony_ci		if (value > IL_PASSIVE_DWELL_BASE || !value)
14968c2ecf20Sopenharmony_ci			value = IL_PASSIVE_DWELL_BASE;
14978c2ecf20Sopenharmony_ci		value = (value * 98) / 100 - IL_CHANNEL_TUNE_TIME * 2;
14988c2ecf20Sopenharmony_ci		passive = min(value, passive);
14998c2ecf20Sopenharmony_ci	}
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	return passive;
15028c2ecf20Sopenharmony_ci}
15038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_passive_dwell_time);
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_civoid
15068c2ecf20Sopenharmony_ciil_init_scan_params(struct il_priv *il)
15078c2ecf20Sopenharmony_ci{
15088c2ecf20Sopenharmony_ci	u8 ant_idx = fls(il->hw_params.valid_tx_ant) - 1;
15098c2ecf20Sopenharmony_ci	if (!il->scan_tx_ant[NL80211_BAND_5GHZ])
15108c2ecf20Sopenharmony_ci		il->scan_tx_ant[NL80211_BAND_5GHZ] = ant_idx;
15118c2ecf20Sopenharmony_ci	if (!il->scan_tx_ant[NL80211_BAND_2GHZ])
15128c2ecf20Sopenharmony_ci		il->scan_tx_ant[NL80211_BAND_2GHZ] = ant_idx;
15138c2ecf20Sopenharmony_ci}
15148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_init_scan_params);
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_cistatic int
15178c2ecf20Sopenharmony_ciil_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif)
15188c2ecf20Sopenharmony_ci{
15198c2ecf20Sopenharmony_ci	int ret;
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci	lockdep_assert_held(&il->mutex);
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	cancel_delayed_work(&il->scan_check);
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	if (!il_is_ready_rf(il)) {
15268c2ecf20Sopenharmony_ci		IL_WARN("Request scan called when driver not ready.\n");
15278c2ecf20Sopenharmony_ci		return -EIO;
15288c2ecf20Sopenharmony_ci	}
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	if (test_bit(S_SCAN_HW, &il->status)) {
15318c2ecf20Sopenharmony_ci		D_SCAN("Multiple concurrent scan requests in parallel.\n");
15328c2ecf20Sopenharmony_ci		return -EBUSY;
15338c2ecf20Sopenharmony_ci	}
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	if (test_bit(S_SCAN_ABORTING, &il->status)) {
15368c2ecf20Sopenharmony_ci		D_SCAN("Scan request while abort pending.\n");
15378c2ecf20Sopenharmony_ci		return -EBUSY;
15388c2ecf20Sopenharmony_ci	}
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	D_SCAN("Starting scan...\n");
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	set_bit(S_SCANNING, &il->status);
15438c2ecf20Sopenharmony_ci	il->scan_start = jiffies;
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	ret = il->ops->request_scan(il, vif);
15468c2ecf20Sopenharmony_ci	if (ret) {
15478c2ecf20Sopenharmony_ci		clear_bit(S_SCANNING, &il->status);
15488c2ecf20Sopenharmony_ci		return ret;
15498c2ecf20Sopenharmony_ci	}
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci	queue_delayed_work(il->workqueue, &il->scan_check,
15528c2ecf20Sopenharmony_ci			   IL_SCAN_CHECK_WATCHDOG);
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	return 0;
15558c2ecf20Sopenharmony_ci}
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ciint
15588c2ecf20Sopenharmony_ciil_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
15598c2ecf20Sopenharmony_ci	       struct ieee80211_scan_request *hw_req)
15608c2ecf20Sopenharmony_ci{
15618c2ecf20Sopenharmony_ci	struct cfg80211_scan_request *req = &hw_req->req;
15628c2ecf20Sopenharmony_ci	struct il_priv *il = hw->priv;
15638c2ecf20Sopenharmony_ci	int ret;
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	if (req->n_channels == 0) {
15668c2ecf20Sopenharmony_ci		IL_ERR("Can not scan on no channels.\n");
15678c2ecf20Sopenharmony_ci		return -EINVAL;
15688c2ecf20Sopenharmony_ci	}
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	mutex_lock(&il->mutex);
15718c2ecf20Sopenharmony_ci	D_MAC80211("enter\n");
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	if (test_bit(S_SCANNING, &il->status)) {
15748c2ecf20Sopenharmony_ci		D_SCAN("Scan already in progress.\n");
15758c2ecf20Sopenharmony_ci		ret = -EAGAIN;
15768c2ecf20Sopenharmony_ci		goto out_unlock;
15778c2ecf20Sopenharmony_ci	}
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	/* mac80211 will only ask for one band at a time */
15808c2ecf20Sopenharmony_ci	il->scan_request = req;
15818c2ecf20Sopenharmony_ci	il->scan_vif = vif;
15828c2ecf20Sopenharmony_ci	il->scan_band = req->channels[0]->band;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	ret = il_scan_initiate(il, vif);
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ciout_unlock:
15878c2ecf20Sopenharmony_ci	D_MAC80211("leave ret %d\n", ret);
15888c2ecf20Sopenharmony_ci	mutex_unlock(&il->mutex);
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	return ret;
15918c2ecf20Sopenharmony_ci}
15928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_hw_scan);
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_cistatic void
15958c2ecf20Sopenharmony_ciil_bg_scan_check(struct work_struct *data)
15968c2ecf20Sopenharmony_ci{
15978c2ecf20Sopenharmony_ci	struct il_priv *il =
15988c2ecf20Sopenharmony_ci	    container_of(data, struct il_priv, scan_check.work);
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	D_SCAN("Scan check work\n");
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci	/* Since we are here firmware does not finish scan and
16038c2ecf20Sopenharmony_ci	 * most likely is in bad shape, so we don't bother to
16048c2ecf20Sopenharmony_ci	 * send abort command, just force scan complete to mac80211 */
16058c2ecf20Sopenharmony_ci	mutex_lock(&il->mutex);
16068c2ecf20Sopenharmony_ci	il_force_scan_end(il);
16078c2ecf20Sopenharmony_ci	mutex_unlock(&il->mutex);
16088c2ecf20Sopenharmony_ci}
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci/*
16118c2ecf20Sopenharmony_ci * il_fill_probe_req - fill in all required fields and IE for probe request
16128c2ecf20Sopenharmony_ci */
16138c2ecf20Sopenharmony_ciu16
16148c2ecf20Sopenharmony_ciil_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame,
16158c2ecf20Sopenharmony_ci		  const u8 *ta, const u8 *ies, int ie_len, int left)
16168c2ecf20Sopenharmony_ci{
16178c2ecf20Sopenharmony_ci	int len = 0;
16188c2ecf20Sopenharmony_ci	u8 *pos = NULL;
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	/* Make sure there is enough space for the probe request,
16218c2ecf20Sopenharmony_ci	 * two mandatory IEs and the data */
16228c2ecf20Sopenharmony_ci	left -= 24;
16238c2ecf20Sopenharmony_ci	if (left < 0)
16248c2ecf20Sopenharmony_ci		return 0;
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci	frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
16278c2ecf20Sopenharmony_ci	eth_broadcast_addr(frame->da);
16288c2ecf20Sopenharmony_ci	memcpy(frame->sa, ta, ETH_ALEN);
16298c2ecf20Sopenharmony_ci	eth_broadcast_addr(frame->bssid);
16308c2ecf20Sopenharmony_ci	frame->seq_ctrl = 0;
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	len += 24;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	/* ...next IE... */
16358c2ecf20Sopenharmony_ci	pos = &frame->u.probe_req.variable[0];
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	/* fill in our indirect SSID IE */
16388c2ecf20Sopenharmony_ci	left -= 2;
16398c2ecf20Sopenharmony_ci	if (left < 0)
16408c2ecf20Sopenharmony_ci		return 0;
16418c2ecf20Sopenharmony_ci	*pos++ = WLAN_EID_SSID;
16428c2ecf20Sopenharmony_ci	*pos++ = 0;
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	len += 2;
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci	if (WARN_ON(left < ie_len))
16478c2ecf20Sopenharmony_ci		return len;
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	if (ies && ie_len) {
16508c2ecf20Sopenharmony_ci		memcpy(pos, ies, ie_len);
16518c2ecf20Sopenharmony_ci		len += ie_len;
16528c2ecf20Sopenharmony_ci	}
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	return (u16) len;
16558c2ecf20Sopenharmony_ci}
16568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_fill_probe_req);
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_cistatic void
16598c2ecf20Sopenharmony_ciil_bg_abort_scan(struct work_struct *work)
16608c2ecf20Sopenharmony_ci{
16618c2ecf20Sopenharmony_ci	struct il_priv *il = container_of(work, struct il_priv, abort_scan);
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	D_SCAN("Abort scan work\n");
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	/* We keep scan_check work queued in case when firmware will not
16668c2ecf20Sopenharmony_ci	 * report back scan completed notification */
16678c2ecf20Sopenharmony_ci	mutex_lock(&il->mutex);
16688c2ecf20Sopenharmony_ci	il_scan_cancel_timeout(il, 200);
16698c2ecf20Sopenharmony_ci	mutex_unlock(&il->mutex);
16708c2ecf20Sopenharmony_ci}
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_cistatic void
16738c2ecf20Sopenharmony_ciil_bg_scan_completed(struct work_struct *work)
16748c2ecf20Sopenharmony_ci{
16758c2ecf20Sopenharmony_ci	struct il_priv *il = container_of(work, struct il_priv, scan_completed);
16768c2ecf20Sopenharmony_ci	bool aborted;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	D_SCAN("Completed scan.\n");
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	cancel_delayed_work(&il->scan_check);
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	mutex_lock(&il->mutex);
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	aborted = test_and_clear_bit(S_SCAN_ABORTING, &il->status);
16858c2ecf20Sopenharmony_ci	if (aborted)
16868c2ecf20Sopenharmony_ci		D_SCAN("Aborted scan completed.\n");
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	if (!test_and_clear_bit(S_SCANNING, &il->status)) {
16898c2ecf20Sopenharmony_ci		D_SCAN("Scan already completed.\n");
16908c2ecf20Sopenharmony_ci		goto out_settings;
16918c2ecf20Sopenharmony_ci	}
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci	il_complete_scan(il, aborted);
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ciout_settings:
16968c2ecf20Sopenharmony_ci	/* Can we still talk to firmware ? */
16978c2ecf20Sopenharmony_ci	if (!il_is_ready_rf(il))
16988c2ecf20Sopenharmony_ci		goto out;
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	/*
17018c2ecf20Sopenharmony_ci	 * We do not commit power settings while scan is pending,
17028c2ecf20Sopenharmony_ci	 * do it now if the settings changed.
17038c2ecf20Sopenharmony_ci	 */
17048c2ecf20Sopenharmony_ci	il_power_set_mode(il, &il->power_data.sleep_cmd_next, false);
17058c2ecf20Sopenharmony_ci	il_set_tx_power(il, il->tx_power_next, false);
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci	il->ops->post_scan(il);
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ciout:
17108c2ecf20Sopenharmony_ci	mutex_unlock(&il->mutex);
17118c2ecf20Sopenharmony_ci}
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_civoid
17148c2ecf20Sopenharmony_ciil_setup_scan_deferred_work(struct il_priv *il)
17158c2ecf20Sopenharmony_ci{
17168c2ecf20Sopenharmony_ci	INIT_WORK(&il->scan_completed, il_bg_scan_completed);
17178c2ecf20Sopenharmony_ci	INIT_WORK(&il->abort_scan, il_bg_abort_scan);
17188c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&il->scan_check, il_bg_scan_check);
17198c2ecf20Sopenharmony_ci}
17208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_setup_scan_deferred_work);
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_civoid
17238c2ecf20Sopenharmony_ciil_cancel_scan_deferred_work(struct il_priv *il)
17248c2ecf20Sopenharmony_ci{
17258c2ecf20Sopenharmony_ci	cancel_work_sync(&il->abort_scan);
17268c2ecf20Sopenharmony_ci	cancel_work_sync(&il->scan_completed);
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	if (cancel_delayed_work_sync(&il->scan_check)) {
17298c2ecf20Sopenharmony_ci		mutex_lock(&il->mutex);
17308c2ecf20Sopenharmony_ci		il_force_scan_end(il);
17318c2ecf20Sopenharmony_ci		mutex_unlock(&il->mutex);
17328c2ecf20Sopenharmony_ci	}
17338c2ecf20Sopenharmony_ci}
17348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_cancel_scan_deferred_work);
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci/* il->sta_lock must be held */
17378c2ecf20Sopenharmony_cistatic void
17388c2ecf20Sopenharmony_ciil_sta_ucode_activate(struct il_priv *il, u8 sta_id)
17398c2ecf20Sopenharmony_ci{
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci	if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE))
17428c2ecf20Sopenharmony_ci		IL_ERR("ACTIVATE a non DRIVER active station id %u addr %pM\n",
17438c2ecf20Sopenharmony_ci		       sta_id, il->stations[sta_id].sta.sta.addr);
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	if (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) {
17468c2ecf20Sopenharmony_ci		D_ASSOC("STA id %u addr %pM already present"
17478c2ecf20Sopenharmony_ci			" in uCode (according to driver)\n", sta_id,
17488c2ecf20Sopenharmony_ci			il->stations[sta_id].sta.sta.addr);
17498c2ecf20Sopenharmony_ci	} else {
17508c2ecf20Sopenharmony_ci		il->stations[sta_id].used |= IL_STA_UCODE_ACTIVE;
17518c2ecf20Sopenharmony_ci		D_ASSOC("Added STA id %u addr %pM to uCode\n", sta_id,
17528c2ecf20Sopenharmony_ci			il->stations[sta_id].sta.sta.addr);
17538c2ecf20Sopenharmony_ci	}
17548c2ecf20Sopenharmony_ci}
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_cistatic int
17578c2ecf20Sopenharmony_ciil_process_add_sta_resp(struct il_priv *il, struct il_addsta_cmd *addsta,
17588c2ecf20Sopenharmony_ci			struct il_rx_pkt *pkt, bool sync)
17598c2ecf20Sopenharmony_ci{
17608c2ecf20Sopenharmony_ci	u8 sta_id = addsta->sta.sta_id;
17618c2ecf20Sopenharmony_ci	unsigned long flags;
17628c2ecf20Sopenharmony_ci	int ret = -EIO;
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci	if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
17658c2ecf20Sopenharmony_ci		IL_ERR("Bad return from C_ADD_STA (0x%08X)\n", pkt->hdr.flags);
17668c2ecf20Sopenharmony_ci		return ret;
17678c2ecf20Sopenharmony_ci	}
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci	D_INFO("Processing response for adding station %u\n", sta_id);
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci	switch (pkt->u.add_sta.status) {
17748c2ecf20Sopenharmony_ci	case ADD_STA_SUCCESS_MSK:
17758c2ecf20Sopenharmony_ci		D_INFO("C_ADD_STA PASSED\n");
17768c2ecf20Sopenharmony_ci		il_sta_ucode_activate(il, sta_id);
17778c2ecf20Sopenharmony_ci		ret = 0;
17788c2ecf20Sopenharmony_ci		break;
17798c2ecf20Sopenharmony_ci	case ADD_STA_NO_ROOM_IN_TBL:
17808c2ecf20Sopenharmony_ci		IL_ERR("Adding station %d failed, no room in table.\n", sta_id);
17818c2ecf20Sopenharmony_ci		break;
17828c2ecf20Sopenharmony_ci	case ADD_STA_NO_BLOCK_ACK_RESOURCE:
17838c2ecf20Sopenharmony_ci		IL_ERR("Adding station %d failed, no block ack resource.\n",
17848c2ecf20Sopenharmony_ci		       sta_id);
17858c2ecf20Sopenharmony_ci		break;
17868c2ecf20Sopenharmony_ci	case ADD_STA_MODIFY_NON_EXIST_STA:
17878c2ecf20Sopenharmony_ci		IL_ERR("Attempting to modify non-existing station %d\n",
17888c2ecf20Sopenharmony_ci		       sta_id);
17898c2ecf20Sopenharmony_ci		break;
17908c2ecf20Sopenharmony_ci	default:
17918c2ecf20Sopenharmony_ci		D_ASSOC("Received C_ADD_STA:(0x%08X)\n", pkt->u.add_sta.status);
17928c2ecf20Sopenharmony_ci		break;
17938c2ecf20Sopenharmony_ci	}
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci	D_INFO("%s station id %u addr %pM\n",
17968c2ecf20Sopenharmony_ci	       il->stations[sta_id].sta.mode ==
17978c2ecf20Sopenharmony_ci	       STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", sta_id,
17988c2ecf20Sopenharmony_ci	       il->stations[sta_id].sta.sta.addr);
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	/*
18018c2ecf20Sopenharmony_ci	 * XXX: The MAC address in the command buffer is often changed from
18028c2ecf20Sopenharmony_ci	 * the original sent to the device. That is, the MAC address
18038c2ecf20Sopenharmony_ci	 * written to the command buffer often is not the same MAC address
18048c2ecf20Sopenharmony_ci	 * read from the command buffer when the command returns. This
18058c2ecf20Sopenharmony_ci	 * issue has not yet been resolved and this debugging is left to
18068c2ecf20Sopenharmony_ci	 * observe the problem.
18078c2ecf20Sopenharmony_ci	 */
18088c2ecf20Sopenharmony_ci	D_INFO("%s station according to cmd buffer %pM\n",
18098c2ecf20Sopenharmony_ci	       il->stations[sta_id].sta.mode ==
18108c2ecf20Sopenharmony_ci	       STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", addsta->sta.addr);
18118c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	return ret;
18148c2ecf20Sopenharmony_ci}
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_cistatic void
18178c2ecf20Sopenharmony_ciil_add_sta_callback(struct il_priv *il, struct il_device_cmd *cmd,
18188c2ecf20Sopenharmony_ci		    struct il_rx_pkt *pkt)
18198c2ecf20Sopenharmony_ci{
18208c2ecf20Sopenharmony_ci	struct il_addsta_cmd *addsta = (struct il_addsta_cmd *)cmd->cmd.payload;
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci	il_process_add_sta_resp(il, addsta, pkt, false);
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci}
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ciint
18278c2ecf20Sopenharmony_ciil_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags)
18288c2ecf20Sopenharmony_ci{
18298c2ecf20Sopenharmony_ci	struct il_rx_pkt *pkt = NULL;
18308c2ecf20Sopenharmony_ci	int ret = 0;
18318c2ecf20Sopenharmony_ci	u8 data[sizeof(*sta)];
18328c2ecf20Sopenharmony_ci	struct il_host_cmd cmd = {
18338c2ecf20Sopenharmony_ci		.id = C_ADD_STA,
18348c2ecf20Sopenharmony_ci		.flags = flags,
18358c2ecf20Sopenharmony_ci		.data = data,
18368c2ecf20Sopenharmony_ci	};
18378c2ecf20Sopenharmony_ci	u8 sta_id __maybe_unused = sta->sta.sta_id;
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci	D_INFO("Adding sta %u (%pM) %ssynchronously\n", sta_id, sta->sta.addr,
18408c2ecf20Sopenharmony_ci	       flags & CMD_ASYNC ? "a" : "");
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	if (flags & CMD_ASYNC)
18438c2ecf20Sopenharmony_ci		cmd.callback = il_add_sta_callback;
18448c2ecf20Sopenharmony_ci	else {
18458c2ecf20Sopenharmony_ci		cmd.flags |= CMD_WANT_SKB;
18468c2ecf20Sopenharmony_ci		might_sleep();
18478c2ecf20Sopenharmony_ci	}
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci	cmd.len = il->ops->build_addsta_hcmd(sta, data);
18508c2ecf20Sopenharmony_ci	ret = il_send_cmd(il, &cmd);
18518c2ecf20Sopenharmony_ci	if (ret)
18528c2ecf20Sopenharmony_ci		return ret;
18538c2ecf20Sopenharmony_ci	if (flags & CMD_ASYNC)
18548c2ecf20Sopenharmony_ci		return 0;
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci	pkt = (struct il_rx_pkt *)cmd.reply_page;
18578c2ecf20Sopenharmony_ci	ret = il_process_add_sta_resp(il, sta, pkt, true);
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_ci	il_free_pages(il, cmd.reply_page);
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_ci	return ret;
18628c2ecf20Sopenharmony_ci}
18638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_add_sta);
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_cistatic void
18668c2ecf20Sopenharmony_ciil_set_ht_add_station(struct il_priv *il, u8 idx, struct ieee80211_sta *sta)
18678c2ecf20Sopenharmony_ci{
18688c2ecf20Sopenharmony_ci	struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap;
18698c2ecf20Sopenharmony_ci	__le32 sta_flags;
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	if (!sta || !sta_ht_inf->ht_supported)
18728c2ecf20Sopenharmony_ci		goto done;
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	D_ASSOC("spatial multiplexing power save mode: %s\n",
18758c2ecf20Sopenharmony_ci		(sta->smps_mode == IEEE80211_SMPS_STATIC) ? "static" :
18768c2ecf20Sopenharmony_ci		(sta->smps_mode == IEEE80211_SMPS_DYNAMIC) ? "dynamic" :
18778c2ecf20Sopenharmony_ci		"disabled");
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	sta_flags = il->stations[idx].sta.station_flags;
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK);
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci	switch (sta->smps_mode) {
18848c2ecf20Sopenharmony_ci	case IEEE80211_SMPS_STATIC:
18858c2ecf20Sopenharmony_ci		sta_flags |= STA_FLG_MIMO_DIS_MSK;
18868c2ecf20Sopenharmony_ci		break;
18878c2ecf20Sopenharmony_ci	case IEEE80211_SMPS_DYNAMIC:
18888c2ecf20Sopenharmony_ci		sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK;
18898c2ecf20Sopenharmony_ci		break;
18908c2ecf20Sopenharmony_ci	case IEEE80211_SMPS_OFF:
18918c2ecf20Sopenharmony_ci		break;
18928c2ecf20Sopenharmony_ci	default:
18938c2ecf20Sopenharmony_ci		IL_WARN("Invalid MIMO PS mode %d\n", sta->smps_mode);
18948c2ecf20Sopenharmony_ci		break;
18958c2ecf20Sopenharmony_ci	}
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_ci	sta_flags |=
18988c2ecf20Sopenharmony_ci	    cpu_to_le32((u32) sta_ht_inf->
18998c2ecf20Sopenharmony_ci			ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS);
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci	sta_flags |=
19028c2ecf20Sopenharmony_ci	    cpu_to_le32((u32) sta_ht_inf->
19038c2ecf20Sopenharmony_ci			ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS);
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ci	if (il_is_ht40_tx_allowed(il, &sta->ht_cap))
19068c2ecf20Sopenharmony_ci		sta_flags |= STA_FLG_HT40_EN_MSK;
19078c2ecf20Sopenharmony_ci	else
19088c2ecf20Sopenharmony_ci		sta_flags &= ~STA_FLG_HT40_EN_MSK;
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci	il->stations[idx].sta.station_flags = sta_flags;
19118c2ecf20Sopenharmony_cidone:
19128c2ecf20Sopenharmony_ci	return;
19138c2ecf20Sopenharmony_ci}
19148c2ecf20Sopenharmony_ci
19158c2ecf20Sopenharmony_ci/*
19168c2ecf20Sopenharmony_ci * il_prep_station - Prepare station information for addition
19178c2ecf20Sopenharmony_ci *
19188c2ecf20Sopenharmony_ci * should be called with sta_lock held
19198c2ecf20Sopenharmony_ci */
19208c2ecf20Sopenharmony_ciu8
19218c2ecf20Sopenharmony_ciil_prep_station(struct il_priv *il, const u8 *addr, bool is_ap,
19228c2ecf20Sopenharmony_ci		struct ieee80211_sta *sta)
19238c2ecf20Sopenharmony_ci{
19248c2ecf20Sopenharmony_ci	struct il_station_entry *station;
19258c2ecf20Sopenharmony_ci	int i;
19268c2ecf20Sopenharmony_ci	u8 sta_id = IL_INVALID_STATION;
19278c2ecf20Sopenharmony_ci	u16 rate;
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ci	if (is_ap)
19308c2ecf20Sopenharmony_ci		sta_id = IL_AP_ID;
19318c2ecf20Sopenharmony_ci	else if (is_broadcast_ether_addr(addr))
19328c2ecf20Sopenharmony_ci		sta_id = il->hw_params.bcast_id;
19338c2ecf20Sopenharmony_ci	else
19348c2ecf20Sopenharmony_ci		for (i = IL_STA_ID; i < il->hw_params.max_stations; i++) {
19358c2ecf20Sopenharmony_ci			if (ether_addr_equal(il->stations[i].sta.sta.addr,
19368c2ecf20Sopenharmony_ci					     addr)) {
19378c2ecf20Sopenharmony_ci				sta_id = i;
19388c2ecf20Sopenharmony_ci				break;
19398c2ecf20Sopenharmony_ci			}
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ci			if (!il->stations[i].used &&
19428c2ecf20Sopenharmony_ci			    sta_id == IL_INVALID_STATION)
19438c2ecf20Sopenharmony_ci				sta_id = i;
19448c2ecf20Sopenharmony_ci		}
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_ci	/*
19478c2ecf20Sopenharmony_ci	 * These two conditions have the same outcome, but keep them
19488c2ecf20Sopenharmony_ci	 * separate
19498c2ecf20Sopenharmony_ci	 */
19508c2ecf20Sopenharmony_ci	if (unlikely(sta_id == IL_INVALID_STATION))
19518c2ecf20Sopenharmony_ci		return sta_id;
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_ci	/*
19548c2ecf20Sopenharmony_ci	 * uCode is not able to deal with multiple requests to add a
19558c2ecf20Sopenharmony_ci	 * station. Keep track if one is in progress so that we do not send
19568c2ecf20Sopenharmony_ci	 * another.
19578c2ecf20Sopenharmony_ci	 */
19588c2ecf20Sopenharmony_ci	if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) {
19598c2ecf20Sopenharmony_ci		D_INFO("STA %d already in process of being added.\n", sta_id);
19608c2ecf20Sopenharmony_ci		return sta_id;
19618c2ecf20Sopenharmony_ci	}
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci	if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) &&
19648c2ecf20Sopenharmony_ci	    (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) &&
19658c2ecf20Sopenharmony_ci	    ether_addr_equal(il->stations[sta_id].sta.sta.addr, addr)) {
19668c2ecf20Sopenharmony_ci		D_ASSOC("STA %d (%pM) already added, not adding again.\n",
19678c2ecf20Sopenharmony_ci			sta_id, addr);
19688c2ecf20Sopenharmony_ci		return sta_id;
19698c2ecf20Sopenharmony_ci	}
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	station = &il->stations[sta_id];
19728c2ecf20Sopenharmony_ci	station->used = IL_STA_DRIVER_ACTIVE;
19738c2ecf20Sopenharmony_ci	D_ASSOC("Add STA to driver ID %d: %pM\n", sta_id, addr);
19748c2ecf20Sopenharmony_ci	il->num_stations++;
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci	/* Set up the C_ADD_STA command to send to device */
19778c2ecf20Sopenharmony_ci	memset(&station->sta, 0, sizeof(struct il_addsta_cmd));
19788c2ecf20Sopenharmony_ci	memcpy(station->sta.sta.addr, addr, ETH_ALEN);
19798c2ecf20Sopenharmony_ci	station->sta.mode = 0;
19808c2ecf20Sopenharmony_ci	station->sta.sta.sta_id = sta_id;
19818c2ecf20Sopenharmony_ci	station->sta.station_flags = 0;
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci	/*
19848c2ecf20Sopenharmony_ci	 * OK to call unconditionally, since local stations (IBSS BSSID
19858c2ecf20Sopenharmony_ci	 * STA and broadcast STA) pass in a NULL sta, and mac80211
19868c2ecf20Sopenharmony_ci	 * doesn't allow HT IBSS.
19878c2ecf20Sopenharmony_ci	 */
19888c2ecf20Sopenharmony_ci	il_set_ht_add_station(il, sta_id, sta);
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci	/* 3945 only */
19918c2ecf20Sopenharmony_ci	rate = (il->band == NL80211_BAND_5GHZ) ? RATE_6M_PLCP : RATE_1M_PLCP;
19928c2ecf20Sopenharmony_ci	/* Turn on both antennas for the station... */
19938c2ecf20Sopenharmony_ci	station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK);
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	return sta_id;
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_ci}
19988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(il_prep_station);
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci#define STA_WAIT_TIMEOUT (HZ/2)
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_ci/*
20038c2ecf20Sopenharmony_ci * il_add_station_common -
20048c2ecf20Sopenharmony_ci */
20058c2ecf20Sopenharmony_ciint
20068c2ecf20Sopenharmony_ciil_add_station_common(struct il_priv *il, const u8 *addr, bool is_ap,
20078c2ecf20Sopenharmony_ci		      struct ieee80211_sta *sta, u8 *sta_id_r)
20088c2ecf20Sopenharmony_ci{
20098c2ecf20Sopenharmony_ci	unsigned long flags_spin;
20108c2ecf20Sopenharmony_ci	int ret = 0;
20118c2ecf20Sopenharmony_ci	u8 sta_id;
20128c2ecf20Sopenharmony_ci	struct il_addsta_cmd sta_cmd;
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci	*sta_id_r = 0;
20158c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags_spin);
20168c2ecf20Sopenharmony_ci	sta_id = il_prep_station(il, addr, is_ap, sta);
20178c2ecf20Sopenharmony_ci	if (sta_id == IL_INVALID_STATION) {
20188c2ecf20Sopenharmony_ci		IL_ERR("Unable to prepare station %pM for addition\n", addr);
20198c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&il->sta_lock, flags_spin);
20208c2ecf20Sopenharmony_ci		return -EINVAL;
20218c2ecf20Sopenharmony_ci	}
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_ci	/*
20248c2ecf20Sopenharmony_ci	 * uCode is not able to deal with multiple requests to add a
20258c2ecf20Sopenharmony_ci	 * station. Keep track if one is in progress so that we do not send
20268c2ecf20Sopenharmony_ci	 * another.
20278c2ecf20Sopenharmony_ci	 */
20288c2ecf20Sopenharmony_ci	if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) {
20298c2ecf20Sopenharmony_ci		D_INFO("STA %d already in process of being added.\n", sta_id);
20308c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&il->sta_lock, flags_spin);
20318c2ecf20Sopenharmony_ci		return -EEXIST;
20328c2ecf20Sopenharmony_ci	}
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci	if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) &&
20358c2ecf20Sopenharmony_ci	    (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) {
20368c2ecf20Sopenharmony_ci		D_ASSOC("STA %d (%pM) already added, not adding again.\n",
20378c2ecf20Sopenharmony_ci			sta_id, addr);
20388c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&il->sta_lock, flags_spin);
20398c2ecf20Sopenharmony_ci		return -EEXIST;
20408c2ecf20Sopenharmony_ci	}
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci	il->stations[sta_id].used |= IL_STA_UCODE_INPROGRESS;
20438c2ecf20Sopenharmony_ci	memcpy(&sta_cmd, &il->stations[sta_id].sta,
20448c2ecf20Sopenharmony_ci	       sizeof(struct il_addsta_cmd));
20458c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags_spin);
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci	/* Add station to device's station table */
20488c2ecf20Sopenharmony_ci	ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC);
20498c2ecf20Sopenharmony_ci	if (ret) {
20508c2ecf20Sopenharmony_ci		spin_lock_irqsave(&il->sta_lock, flags_spin);
20518c2ecf20Sopenharmony_ci		IL_ERR("Adding station %pM failed.\n",
20528c2ecf20Sopenharmony_ci		       il->stations[sta_id].sta.sta.addr);
20538c2ecf20Sopenharmony_ci		il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE;
20548c2ecf20Sopenharmony_ci		il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS;
20558c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&il->sta_lock, flags_spin);
20568c2ecf20Sopenharmony_ci	}
20578c2ecf20Sopenharmony_ci	*sta_id_r = sta_id;
20588c2ecf20Sopenharmony_ci	return ret;
20598c2ecf20Sopenharmony_ci}
20608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_add_station_common);
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci/*
20638c2ecf20Sopenharmony_ci * il_sta_ucode_deactivate - deactivate ucode status for a station
20648c2ecf20Sopenharmony_ci *
20658c2ecf20Sopenharmony_ci * il->sta_lock must be held
20668c2ecf20Sopenharmony_ci */
20678c2ecf20Sopenharmony_cistatic void
20688c2ecf20Sopenharmony_ciil_sta_ucode_deactivate(struct il_priv *il, u8 sta_id)
20698c2ecf20Sopenharmony_ci{
20708c2ecf20Sopenharmony_ci	/* Ucode must be active and driver must be non active */
20718c2ecf20Sopenharmony_ci	if ((il->stations[sta_id].
20728c2ecf20Sopenharmony_ci	     used & (IL_STA_UCODE_ACTIVE | IL_STA_DRIVER_ACTIVE)) !=
20738c2ecf20Sopenharmony_ci	    IL_STA_UCODE_ACTIVE)
20748c2ecf20Sopenharmony_ci		IL_ERR("removed non active STA %u\n", sta_id);
20758c2ecf20Sopenharmony_ci
20768c2ecf20Sopenharmony_ci	il->stations[sta_id].used &= ~IL_STA_UCODE_ACTIVE;
20778c2ecf20Sopenharmony_ci
20788c2ecf20Sopenharmony_ci	memset(&il->stations[sta_id], 0, sizeof(struct il_station_entry));
20798c2ecf20Sopenharmony_ci	D_ASSOC("Removed STA %u\n", sta_id);
20808c2ecf20Sopenharmony_ci}
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_cistatic int
20838c2ecf20Sopenharmony_ciil_send_remove_station(struct il_priv *il, const u8 * addr, int sta_id,
20848c2ecf20Sopenharmony_ci		       bool temporary)
20858c2ecf20Sopenharmony_ci{
20868c2ecf20Sopenharmony_ci	struct il_rx_pkt *pkt;
20878c2ecf20Sopenharmony_ci	int ret;
20888c2ecf20Sopenharmony_ci
20898c2ecf20Sopenharmony_ci	unsigned long flags_spin;
20908c2ecf20Sopenharmony_ci	struct il_rem_sta_cmd rm_sta_cmd;
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_ci	struct il_host_cmd cmd = {
20938c2ecf20Sopenharmony_ci		.id = C_REM_STA,
20948c2ecf20Sopenharmony_ci		.len = sizeof(struct il_rem_sta_cmd),
20958c2ecf20Sopenharmony_ci		.flags = CMD_SYNC,
20968c2ecf20Sopenharmony_ci		.data = &rm_sta_cmd,
20978c2ecf20Sopenharmony_ci	};
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ci	memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
21008c2ecf20Sopenharmony_ci	rm_sta_cmd.num_sta = 1;
21018c2ecf20Sopenharmony_ci	memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN);
21028c2ecf20Sopenharmony_ci
21038c2ecf20Sopenharmony_ci	cmd.flags |= CMD_WANT_SKB;
21048c2ecf20Sopenharmony_ci
21058c2ecf20Sopenharmony_ci	ret = il_send_cmd(il, &cmd);
21068c2ecf20Sopenharmony_ci
21078c2ecf20Sopenharmony_ci	if (ret)
21088c2ecf20Sopenharmony_ci		return ret;
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci	pkt = (struct il_rx_pkt *)cmd.reply_page;
21118c2ecf20Sopenharmony_ci	if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
21128c2ecf20Sopenharmony_ci		IL_ERR("Bad return from C_REM_STA (0x%08X)\n", pkt->hdr.flags);
21138c2ecf20Sopenharmony_ci		ret = -EIO;
21148c2ecf20Sopenharmony_ci	}
21158c2ecf20Sopenharmony_ci
21168c2ecf20Sopenharmony_ci	if (!ret) {
21178c2ecf20Sopenharmony_ci		switch (pkt->u.rem_sta.status) {
21188c2ecf20Sopenharmony_ci		case REM_STA_SUCCESS_MSK:
21198c2ecf20Sopenharmony_ci			if (!temporary) {
21208c2ecf20Sopenharmony_ci				spin_lock_irqsave(&il->sta_lock, flags_spin);
21218c2ecf20Sopenharmony_ci				il_sta_ucode_deactivate(il, sta_id);
21228c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&il->sta_lock,
21238c2ecf20Sopenharmony_ci						       flags_spin);
21248c2ecf20Sopenharmony_ci			}
21258c2ecf20Sopenharmony_ci			D_ASSOC("C_REM_STA PASSED\n");
21268c2ecf20Sopenharmony_ci			break;
21278c2ecf20Sopenharmony_ci		default:
21288c2ecf20Sopenharmony_ci			ret = -EIO;
21298c2ecf20Sopenharmony_ci			IL_ERR("C_REM_STA failed\n");
21308c2ecf20Sopenharmony_ci			break;
21318c2ecf20Sopenharmony_ci		}
21328c2ecf20Sopenharmony_ci	}
21338c2ecf20Sopenharmony_ci	il_free_pages(il, cmd.reply_page);
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	return ret;
21368c2ecf20Sopenharmony_ci}
21378c2ecf20Sopenharmony_ci
21388c2ecf20Sopenharmony_ci/*
21398c2ecf20Sopenharmony_ci * il_remove_station - Remove driver's knowledge of station.
21408c2ecf20Sopenharmony_ci */
21418c2ecf20Sopenharmony_ciint
21428c2ecf20Sopenharmony_ciil_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr)
21438c2ecf20Sopenharmony_ci{
21448c2ecf20Sopenharmony_ci	unsigned long flags;
21458c2ecf20Sopenharmony_ci
21468c2ecf20Sopenharmony_ci	if (!il_is_ready(il)) {
21478c2ecf20Sopenharmony_ci		D_INFO("Unable to remove station %pM, device not ready.\n",
21488c2ecf20Sopenharmony_ci		       addr);
21498c2ecf20Sopenharmony_ci		/*
21508c2ecf20Sopenharmony_ci		 * It is typical for stations to be removed when we are
21518c2ecf20Sopenharmony_ci		 * going down. Return success since device will be down
21528c2ecf20Sopenharmony_ci		 * soon anyway
21538c2ecf20Sopenharmony_ci		 */
21548c2ecf20Sopenharmony_ci		return 0;
21558c2ecf20Sopenharmony_ci	}
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ci	D_ASSOC("Removing STA from driver:%d  %pM\n", sta_id, addr);
21588c2ecf20Sopenharmony_ci
21598c2ecf20Sopenharmony_ci	if (WARN_ON(sta_id == IL_INVALID_STATION))
21608c2ecf20Sopenharmony_ci		return -EINVAL;
21618c2ecf20Sopenharmony_ci
21628c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci	if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) {
21658c2ecf20Sopenharmony_ci		D_INFO("Removing %pM but non DRIVER active\n", addr);
21668c2ecf20Sopenharmony_ci		goto out_err;
21678c2ecf20Sopenharmony_ci	}
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_ci	if (!(il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) {
21708c2ecf20Sopenharmony_ci		D_INFO("Removing %pM but non UCODE active\n", addr);
21718c2ecf20Sopenharmony_ci		goto out_err;
21728c2ecf20Sopenharmony_ci	}
21738c2ecf20Sopenharmony_ci
21748c2ecf20Sopenharmony_ci	if (il->stations[sta_id].used & IL_STA_LOCAL) {
21758c2ecf20Sopenharmony_ci		kfree(il->stations[sta_id].lq);
21768c2ecf20Sopenharmony_ci		il->stations[sta_id].lq = NULL;
21778c2ecf20Sopenharmony_ci	}
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_ci	il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE;
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_ci	il->num_stations--;
21828c2ecf20Sopenharmony_ci
21838c2ecf20Sopenharmony_ci	BUG_ON(il->num_stations < 0);
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_ci	return il_send_remove_station(il, addr, sta_id, false);
21888c2ecf20Sopenharmony_ciout_err:
21898c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
21908c2ecf20Sopenharmony_ci	return -EINVAL;
21918c2ecf20Sopenharmony_ci}
21928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(il_remove_station);
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci/*
21958c2ecf20Sopenharmony_ci * il_clear_ucode_stations - clear ucode station table bits
21968c2ecf20Sopenharmony_ci *
21978c2ecf20Sopenharmony_ci * This function clears all the bits in the driver indicating
21988c2ecf20Sopenharmony_ci * which stations are active in the ucode. Call when something
21998c2ecf20Sopenharmony_ci * other than explicit station management would cause this in
22008c2ecf20Sopenharmony_ci * the ucode, e.g. unassociated RXON.
22018c2ecf20Sopenharmony_ci */
22028c2ecf20Sopenharmony_civoid
22038c2ecf20Sopenharmony_ciil_clear_ucode_stations(struct il_priv *il)
22048c2ecf20Sopenharmony_ci{
22058c2ecf20Sopenharmony_ci	int i;
22068c2ecf20Sopenharmony_ci	unsigned long flags_spin;
22078c2ecf20Sopenharmony_ci	bool cleared = false;
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	D_INFO("Clearing ucode stations in driver\n");
22108c2ecf20Sopenharmony_ci
22118c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags_spin);
22128c2ecf20Sopenharmony_ci	for (i = 0; i < il->hw_params.max_stations; i++) {
22138c2ecf20Sopenharmony_ci		if (il->stations[i].used & IL_STA_UCODE_ACTIVE) {
22148c2ecf20Sopenharmony_ci			D_INFO("Clearing ucode active for station %d\n", i);
22158c2ecf20Sopenharmony_ci			il->stations[i].used &= ~IL_STA_UCODE_ACTIVE;
22168c2ecf20Sopenharmony_ci			cleared = true;
22178c2ecf20Sopenharmony_ci		}
22188c2ecf20Sopenharmony_ci	}
22198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags_spin);
22208c2ecf20Sopenharmony_ci
22218c2ecf20Sopenharmony_ci	if (!cleared)
22228c2ecf20Sopenharmony_ci		D_INFO("No active stations found to be cleared\n");
22238c2ecf20Sopenharmony_ci}
22248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_clear_ucode_stations);
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci/*
22278c2ecf20Sopenharmony_ci * il_restore_stations() - Restore driver known stations to device
22288c2ecf20Sopenharmony_ci *
22298c2ecf20Sopenharmony_ci * All stations considered active by driver, but not present in ucode, is
22308c2ecf20Sopenharmony_ci * restored.
22318c2ecf20Sopenharmony_ci *
22328c2ecf20Sopenharmony_ci * Function sleeps.
22338c2ecf20Sopenharmony_ci */
22348c2ecf20Sopenharmony_civoid
22358c2ecf20Sopenharmony_ciil_restore_stations(struct il_priv *il)
22368c2ecf20Sopenharmony_ci{
22378c2ecf20Sopenharmony_ci	struct il_addsta_cmd sta_cmd;
22388c2ecf20Sopenharmony_ci	struct il_link_quality_cmd lq;
22398c2ecf20Sopenharmony_ci	unsigned long flags_spin;
22408c2ecf20Sopenharmony_ci	int i;
22418c2ecf20Sopenharmony_ci	bool found = false;
22428c2ecf20Sopenharmony_ci	int ret;
22438c2ecf20Sopenharmony_ci	bool send_lq;
22448c2ecf20Sopenharmony_ci
22458c2ecf20Sopenharmony_ci	if (!il_is_ready(il)) {
22468c2ecf20Sopenharmony_ci		D_INFO("Not ready yet, not restoring any stations.\n");
22478c2ecf20Sopenharmony_ci		return;
22488c2ecf20Sopenharmony_ci	}
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci	D_ASSOC("Restoring all known stations ... start.\n");
22518c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags_spin);
22528c2ecf20Sopenharmony_ci	for (i = 0; i < il->hw_params.max_stations; i++) {
22538c2ecf20Sopenharmony_ci		if ((il->stations[i].used & IL_STA_DRIVER_ACTIVE) &&
22548c2ecf20Sopenharmony_ci		    !(il->stations[i].used & IL_STA_UCODE_ACTIVE)) {
22558c2ecf20Sopenharmony_ci			D_ASSOC("Restoring sta %pM\n",
22568c2ecf20Sopenharmony_ci				il->stations[i].sta.sta.addr);
22578c2ecf20Sopenharmony_ci			il->stations[i].sta.mode = 0;
22588c2ecf20Sopenharmony_ci			il->stations[i].used |= IL_STA_UCODE_INPROGRESS;
22598c2ecf20Sopenharmony_ci			found = true;
22608c2ecf20Sopenharmony_ci		}
22618c2ecf20Sopenharmony_ci	}
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci	for (i = 0; i < il->hw_params.max_stations; i++) {
22648c2ecf20Sopenharmony_ci		if ((il->stations[i].used & IL_STA_UCODE_INPROGRESS)) {
22658c2ecf20Sopenharmony_ci			memcpy(&sta_cmd, &il->stations[i].sta,
22668c2ecf20Sopenharmony_ci			       sizeof(struct il_addsta_cmd));
22678c2ecf20Sopenharmony_ci			send_lq = false;
22688c2ecf20Sopenharmony_ci			if (il->stations[i].lq) {
22698c2ecf20Sopenharmony_ci				memcpy(&lq, il->stations[i].lq,
22708c2ecf20Sopenharmony_ci				       sizeof(struct il_link_quality_cmd));
22718c2ecf20Sopenharmony_ci				send_lq = true;
22728c2ecf20Sopenharmony_ci			}
22738c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&il->sta_lock, flags_spin);
22748c2ecf20Sopenharmony_ci			ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC);
22758c2ecf20Sopenharmony_ci			if (ret) {
22768c2ecf20Sopenharmony_ci				spin_lock_irqsave(&il->sta_lock, flags_spin);
22778c2ecf20Sopenharmony_ci				IL_ERR("Adding station %pM failed.\n",
22788c2ecf20Sopenharmony_ci				       il->stations[i].sta.sta.addr);
22798c2ecf20Sopenharmony_ci				il->stations[i].used &= ~IL_STA_DRIVER_ACTIVE;
22808c2ecf20Sopenharmony_ci				il->stations[i].used &=
22818c2ecf20Sopenharmony_ci				    ~IL_STA_UCODE_INPROGRESS;
22828c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&il->sta_lock,
22838c2ecf20Sopenharmony_ci						       flags_spin);
22848c2ecf20Sopenharmony_ci			}
22858c2ecf20Sopenharmony_ci			/*
22868c2ecf20Sopenharmony_ci			 * Rate scaling has already been initialized, send
22878c2ecf20Sopenharmony_ci			 * current LQ command
22888c2ecf20Sopenharmony_ci			 */
22898c2ecf20Sopenharmony_ci			if (send_lq)
22908c2ecf20Sopenharmony_ci				il_send_lq_cmd(il, &lq, CMD_SYNC, true);
22918c2ecf20Sopenharmony_ci			spin_lock_irqsave(&il->sta_lock, flags_spin);
22928c2ecf20Sopenharmony_ci			il->stations[i].used &= ~IL_STA_UCODE_INPROGRESS;
22938c2ecf20Sopenharmony_ci		}
22948c2ecf20Sopenharmony_ci	}
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags_spin);
22978c2ecf20Sopenharmony_ci	if (!found)
22988c2ecf20Sopenharmony_ci		D_INFO("Restoring all known stations"
22998c2ecf20Sopenharmony_ci		       " .... no stations to be restored.\n");
23008c2ecf20Sopenharmony_ci	else
23018c2ecf20Sopenharmony_ci		D_INFO("Restoring all known stations" " .... complete.\n");
23028c2ecf20Sopenharmony_ci}
23038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_restore_stations);
23048c2ecf20Sopenharmony_ci
23058c2ecf20Sopenharmony_ciint
23068c2ecf20Sopenharmony_ciil_get_free_ucode_key_idx(struct il_priv *il)
23078c2ecf20Sopenharmony_ci{
23088c2ecf20Sopenharmony_ci	int i;
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_ci	for (i = 0; i < il->sta_key_max_num; i++)
23118c2ecf20Sopenharmony_ci		if (!test_and_set_bit(i, &il->ucode_key_table))
23128c2ecf20Sopenharmony_ci			return i;
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci	return WEP_INVALID_OFFSET;
23158c2ecf20Sopenharmony_ci}
23168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_free_ucode_key_idx);
23178c2ecf20Sopenharmony_ci
23188c2ecf20Sopenharmony_civoid
23198c2ecf20Sopenharmony_ciil_dealloc_bcast_stations(struct il_priv *il)
23208c2ecf20Sopenharmony_ci{
23218c2ecf20Sopenharmony_ci	unsigned long flags;
23228c2ecf20Sopenharmony_ci	int i;
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags);
23258c2ecf20Sopenharmony_ci	for (i = 0; i < il->hw_params.max_stations; i++) {
23268c2ecf20Sopenharmony_ci		if (!(il->stations[i].used & IL_STA_BCAST))
23278c2ecf20Sopenharmony_ci			continue;
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci		il->stations[i].used &= ~IL_STA_UCODE_ACTIVE;
23308c2ecf20Sopenharmony_ci		il->num_stations--;
23318c2ecf20Sopenharmony_ci		BUG_ON(il->num_stations < 0);
23328c2ecf20Sopenharmony_ci		kfree(il->stations[i].lq);
23338c2ecf20Sopenharmony_ci		il->stations[i].lq = NULL;
23348c2ecf20Sopenharmony_ci	}
23358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags);
23368c2ecf20Sopenharmony_ci}
23378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(il_dealloc_bcast_stations);
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
23408c2ecf20Sopenharmony_cistatic void
23418c2ecf20Sopenharmony_ciil_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq)
23428c2ecf20Sopenharmony_ci{
23438c2ecf20Sopenharmony_ci	int i;
23448c2ecf20Sopenharmony_ci	D_RATE("lq station id 0x%x\n", lq->sta_id);
23458c2ecf20Sopenharmony_ci	D_RATE("lq ant 0x%X 0x%X\n", lq->general_params.single_stream_ant_msk,
23468c2ecf20Sopenharmony_ci	       lq->general_params.dual_stream_ant_msk);
23478c2ecf20Sopenharmony_ci
23488c2ecf20Sopenharmony_ci	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
23498c2ecf20Sopenharmony_ci		D_RATE("lq idx %d 0x%X\n", i, lq->rs_table[i].rate_n_flags);
23508c2ecf20Sopenharmony_ci}
23518c2ecf20Sopenharmony_ci#else
23528c2ecf20Sopenharmony_cistatic inline void
23538c2ecf20Sopenharmony_ciil_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq)
23548c2ecf20Sopenharmony_ci{
23558c2ecf20Sopenharmony_ci}
23568c2ecf20Sopenharmony_ci#endif
23578c2ecf20Sopenharmony_ci
23588c2ecf20Sopenharmony_ci/*
23598c2ecf20Sopenharmony_ci * il_is_lq_table_valid() - Test one aspect of LQ cmd for validity
23608c2ecf20Sopenharmony_ci *
23618c2ecf20Sopenharmony_ci * It sometimes happens when a HT rate has been in use and we
23628c2ecf20Sopenharmony_ci * loose connectivity with AP then mac80211 will first tell us that the
23638c2ecf20Sopenharmony_ci * current channel is not HT anymore before removing the station. In such a
23648c2ecf20Sopenharmony_ci * scenario the RXON flags will be updated to indicate we are not
23658c2ecf20Sopenharmony_ci * communicating HT anymore, but the LQ command may still contain HT rates.
23668c2ecf20Sopenharmony_ci * Test for this to prevent driver from sending LQ command between the time
23678c2ecf20Sopenharmony_ci * RXON flags are updated and when LQ command is updated.
23688c2ecf20Sopenharmony_ci */
23698c2ecf20Sopenharmony_cistatic bool
23708c2ecf20Sopenharmony_ciil_is_lq_table_valid(struct il_priv *il, struct il_link_quality_cmd *lq)
23718c2ecf20Sopenharmony_ci{
23728c2ecf20Sopenharmony_ci	int i;
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci	if (il->ht.enabled)
23758c2ecf20Sopenharmony_ci		return true;
23768c2ecf20Sopenharmony_ci
23778c2ecf20Sopenharmony_ci	D_INFO("Channel %u is not an HT channel\n", il->active.channel);
23788c2ecf20Sopenharmony_ci	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
23798c2ecf20Sopenharmony_ci		if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & RATE_MCS_HT_MSK) {
23808c2ecf20Sopenharmony_ci			D_INFO("idx %d of LQ expects HT channel\n", i);
23818c2ecf20Sopenharmony_ci			return false;
23828c2ecf20Sopenharmony_ci		}
23838c2ecf20Sopenharmony_ci	}
23848c2ecf20Sopenharmony_ci	return true;
23858c2ecf20Sopenharmony_ci}
23868c2ecf20Sopenharmony_ci
23878c2ecf20Sopenharmony_ci/*
23888c2ecf20Sopenharmony_ci * il_send_lq_cmd() - Send link quality command
23898c2ecf20Sopenharmony_ci * @init: This command is sent as part of station initialization right
23908c2ecf20Sopenharmony_ci *        after station has been added.
23918c2ecf20Sopenharmony_ci *
23928c2ecf20Sopenharmony_ci * The link quality command is sent as the last step of station creation.
23938c2ecf20Sopenharmony_ci * This is the special case in which init is set and we call a callback in
23948c2ecf20Sopenharmony_ci * this case to clear the state indicating that station creation is in
23958c2ecf20Sopenharmony_ci * progress.
23968c2ecf20Sopenharmony_ci */
23978c2ecf20Sopenharmony_ciint
23988c2ecf20Sopenharmony_ciil_send_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq,
23998c2ecf20Sopenharmony_ci	       u8 flags, bool init)
24008c2ecf20Sopenharmony_ci{
24018c2ecf20Sopenharmony_ci	int ret = 0;
24028c2ecf20Sopenharmony_ci	unsigned long flags_spin;
24038c2ecf20Sopenharmony_ci
24048c2ecf20Sopenharmony_ci	struct il_host_cmd cmd = {
24058c2ecf20Sopenharmony_ci		.id = C_TX_LINK_QUALITY_CMD,
24068c2ecf20Sopenharmony_ci		.len = sizeof(struct il_link_quality_cmd),
24078c2ecf20Sopenharmony_ci		.flags = flags,
24088c2ecf20Sopenharmony_ci		.data = lq,
24098c2ecf20Sopenharmony_ci	};
24108c2ecf20Sopenharmony_ci
24118c2ecf20Sopenharmony_ci	if (WARN_ON(lq->sta_id == IL_INVALID_STATION))
24128c2ecf20Sopenharmony_ci		return -EINVAL;
24138c2ecf20Sopenharmony_ci
24148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->sta_lock, flags_spin);
24158c2ecf20Sopenharmony_ci	if (!(il->stations[lq->sta_id].used & IL_STA_DRIVER_ACTIVE)) {
24168c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&il->sta_lock, flags_spin);
24178c2ecf20Sopenharmony_ci		return -EINVAL;
24188c2ecf20Sopenharmony_ci	}
24198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->sta_lock, flags_spin);
24208c2ecf20Sopenharmony_ci
24218c2ecf20Sopenharmony_ci	il_dump_lq_cmd(il, lq);
24228c2ecf20Sopenharmony_ci	BUG_ON(init && (cmd.flags & CMD_ASYNC));
24238c2ecf20Sopenharmony_ci
24248c2ecf20Sopenharmony_ci	if (il_is_lq_table_valid(il, lq))
24258c2ecf20Sopenharmony_ci		ret = il_send_cmd(il, &cmd);
24268c2ecf20Sopenharmony_ci	else
24278c2ecf20Sopenharmony_ci		ret = -EINVAL;
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci	if (cmd.flags & CMD_ASYNC)
24308c2ecf20Sopenharmony_ci		return ret;
24318c2ecf20Sopenharmony_ci
24328c2ecf20Sopenharmony_ci	if (init) {
24338c2ecf20Sopenharmony_ci		D_INFO("init LQ command complete,"
24348c2ecf20Sopenharmony_ci		       " clearing sta addition status for sta %d\n",
24358c2ecf20Sopenharmony_ci		       lq->sta_id);
24368c2ecf20Sopenharmony_ci		spin_lock_irqsave(&il->sta_lock, flags_spin);
24378c2ecf20Sopenharmony_ci		il->stations[lq->sta_id].used &= ~IL_STA_UCODE_INPROGRESS;
24388c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&il->sta_lock, flags_spin);
24398c2ecf20Sopenharmony_ci	}
24408c2ecf20Sopenharmony_ci	return ret;
24418c2ecf20Sopenharmony_ci}
24428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_lq_cmd);
24438c2ecf20Sopenharmony_ci
24448c2ecf20Sopenharmony_ciint
24458c2ecf20Sopenharmony_ciil_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
24468c2ecf20Sopenharmony_ci		  struct ieee80211_sta *sta)
24478c2ecf20Sopenharmony_ci{
24488c2ecf20Sopenharmony_ci	struct il_priv *il = hw->priv;
24498c2ecf20Sopenharmony_ci	struct il_station_priv_common *sta_common = (void *)sta->drv_priv;
24508c2ecf20Sopenharmony_ci	int ret;
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_ci	mutex_lock(&il->mutex);
24538c2ecf20Sopenharmony_ci	D_MAC80211("enter station %pM\n", sta->addr);
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_ci	ret = il_remove_station(il, sta_common->sta_id, sta->addr);
24568c2ecf20Sopenharmony_ci	if (ret)
24578c2ecf20Sopenharmony_ci		IL_ERR("Error removing station %pM\n", sta->addr);
24588c2ecf20Sopenharmony_ci
24598c2ecf20Sopenharmony_ci	D_MAC80211("leave ret %d\n", ret);
24608c2ecf20Sopenharmony_ci	mutex_unlock(&il->mutex);
24618c2ecf20Sopenharmony_ci
24628c2ecf20Sopenharmony_ci	return ret;
24638c2ecf20Sopenharmony_ci}
24648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_sta_remove);
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_ci/************************** RX-FUNCTIONS ****************************/
24678c2ecf20Sopenharmony_ci/*
24688c2ecf20Sopenharmony_ci * Rx theory of operation
24698c2ecf20Sopenharmony_ci *
24708c2ecf20Sopenharmony_ci * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs),
24718c2ecf20Sopenharmony_ci * each of which point to Receive Buffers to be filled by the NIC.  These get
24728c2ecf20Sopenharmony_ci * used not only for Rx frames, but for any command response or notification
24738c2ecf20Sopenharmony_ci * from the NIC.  The driver and NIC manage the Rx buffers by means
24748c2ecf20Sopenharmony_ci * of idxes into the circular buffer.
24758c2ecf20Sopenharmony_ci *
24768c2ecf20Sopenharmony_ci * Rx Queue Indexes
24778c2ecf20Sopenharmony_ci * The host/firmware share two idx registers for managing the Rx buffers.
24788c2ecf20Sopenharmony_ci *
24798c2ecf20Sopenharmony_ci * The READ idx maps to the first position that the firmware may be writing
24808c2ecf20Sopenharmony_ci * to -- the driver can read up to (but not including) this position and get
24818c2ecf20Sopenharmony_ci * good data.
24828c2ecf20Sopenharmony_ci * The READ idx is managed by the firmware once the card is enabled.
24838c2ecf20Sopenharmony_ci *
24848c2ecf20Sopenharmony_ci * The WRITE idx maps to the last position the driver has read from -- the
24858c2ecf20Sopenharmony_ci * position preceding WRITE is the last slot the firmware can place a packet.
24868c2ecf20Sopenharmony_ci *
24878c2ecf20Sopenharmony_ci * The queue is empty (no good data) if WRITE = READ - 1, and is full if
24888c2ecf20Sopenharmony_ci * WRITE = READ.
24898c2ecf20Sopenharmony_ci *
24908c2ecf20Sopenharmony_ci * During initialization, the host sets up the READ queue position to the first
24918c2ecf20Sopenharmony_ci * IDX position, and WRITE to the last (READ - 1 wrapped)
24928c2ecf20Sopenharmony_ci *
24938c2ecf20Sopenharmony_ci * When the firmware places a packet in a buffer, it will advance the READ idx
24948c2ecf20Sopenharmony_ci * and fire the RX interrupt.  The driver can then query the READ idx and
24958c2ecf20Sopenharmony_ci * process as many packets as possible, moving the WRITE idx forward as it
24968c2ecf20Sopenharmony_ci * resets the Rx queue buffers with new memory.
24978c2ecf20Sopenharmony_ci *
24988c2ecf20Sopenharmony_ci * The management in the driver is as follows:
24998c2ecf20Sopenharmony_ci * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free.  When
25008c2ecf20Sopenharmony_ci *   iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
25018c2ecf20Sopenharmony_ci *   to replenish the iwl->rxq->rx_free.
25028c2ecf20Sopenharmony_ci * + In il_rx_replenish (scheduled) if 'processed' != 'read' then the
25038c2ecf20Sopenharmony_ci *   iwl->rxq is replenished and the READ IDX is updated (updating the
25048c2ecf20Sopenharmony_ci *   'processed' and 'read' driver idxes as well)
25058c2ecf20Sopenharmony_ci * + A received packet is processed and handed to the kernel network stack,
25068c2ecf20Sopenharmony_ci *   detached from the iwl->rxq.  The driver 'processed' idx is updated.
25078c2ecf20Sopenharmony_ci * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free
25088c2ecf20Sopenharmony_ci *   list. If there are no allocated buffers in iwl->rxq->rx_free, the READ
25098c2ecf20Sopenharmony_ci *   IDX is not incremented and iwl->status(RX_STALLED) is set.  If there
25108c2ecf20Sopenharmony_ci *   were enough free buffers and RX_STALLED is set it is cleared.
25118c2ecf20Sopenharmony_ci *
25128c2ecf20Sopenharmony_ci *
25138c2ecf20Sopenharmony_ci * Driver sequence:
25148c2ecf20Sopenharmony_ci *
25158c2ecf20Sopenharmony_ci * il_rx_queue_alloc()   Allocates rx_free
25168c2ecf20Sopenharmony_ci * il_rx_replenish()     Replenishes rx_free list from rx_used, and calls
25178c2ecf20Sopenharmony_ci *                            il_rx_queue_restock
25188c2ecf20Sopenharmony_ci * il_rx_queue_restock() Moves available buffers from rx_free into Rx
25198c2ecf20Sopenharmony_ci *                            queue, updates firmware pointers, and updates
25208c2ecf20Sopenharmony_ci *                            the WRITE idx.  If insufficient rx_free buffers
25218c2ecf20Sopenharmony_ci *                            are available, schedules il_rx_replenish
25228c2ecf20Sopenharmony_ci *
25238c2ecf20Sopenharmony_ci * -- enable interrupts --
25248c2ecf20Sopenharmony_ci * ISR - il_rx()         Detach il_rx_bufs from pool up to the
25258c2ecf20Sopenharmony_ci *                            READ IDX, detaching the SKB from the pool.
25268c2ecf20Sopenharmony_ci *                            Moves the packet buffer from queue to rx_used.
25278c2ecf20Sopenharmony_ci *                            Calls il_rx_queue_restock to refill any empty
25288c2ecf20Sopenharmony_ci *                            slots.
25298c2ecf20Sopenharmony_ci * ...
25308c2ecf20Sopenharmony_ci *
25318c2ecf20Sopenharmony_ci */
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_ci/*
25348c2ecf20Sopenharmony_ci * il_rx_queue_space - Return number of free slots available in queue.
25358c2ecf20Sopenharmony_ci */
25368c2ecf20Sopenharmony_ciint
25378c2ecf20Sopenharmony_ciil_rx_queue_space(const struct il_rx_queue *q)
25388c2ecf20Sopenharmony_ci{
25398c2ecf20Sopenharmony_ci	int s = q->read - q->write;
25408c2ecf20Sopenharmony_ci	if (s <= 0)
25418c2ecf20Sopenharmony_ci		s += RX_QUEUE_SIZE;
25428c2ecf20Sopenharmony_ci	/* keep some buffer to not confuse full and empty queue */
25438c2ecf20Sopenharmony_ci	s -= 2;
25448c2ecf20Sopenharmony_ci	if (s < 0)
25458c2ecf20Sopenharmony_ci		s = 0;
25468c2ecf20Sopenharmony_ci	return s;
25478c2ecf20Sopenharmony_ci}
25488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_rx_queue_space);
25498c2ecf20Sopenharmony_ci
25508c2ecf20Sopenharmony_ci/*
25518c2ecf20Sopenharmony_ci * il_rx_queue_update_write_ptr - Update the write pointer for the RX queue
25528c2ecf20Sopenharmony_ci */
25538c2ecf20Sopenharmony_civoid
25548c2ecf20Sopenharmony_ciil_rx_queue_update_write_ptr(struct il_priv *il, struct il_rx_queue *q)
25558c2ecf20Sopenharmony_ci{
25568c2ecf20Sopenharmony_ci	unsigned long flags;
25578c2ecf20Sopenharmony_ci	u32 rx_wrt_ptr_reg = il->hw_params.rx_wrt_ptr_reg;
25588c2ecf20Sopenharmony_ci	u32 reg;
25598c2ecf20Sopenharmony_ci
25608c2ecf20Sopenharmony_ci	spin_lock_irqsave(&q->lock, flags);
25618c2ecf20Sopenharmony_ci
25628c2ecf20Sopenharmony_ci	if (q->need_update == 0)
25638c2ecf20Sopenharmony_ci		goto exit_unlock;
25648c2ecf20Sopenharmony_ci
25658c2ecf20Sopenharmony_ci	/* If power-saving is in use, make sure device is awake */
25668c2ecf20Sopenharmony_ci	if (test_bit(S_POWER_PMI, &il->status)) {
25678c2ecf20Sopenharmony_ci		reg = _il_rd(il, CSR_UCODE_DRV_GP1);
25688c2ecf20Sopenharmony_ci
25698c2ecf20Sopenharmony_ci		if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
25708c2ecf20Sopenharmony_ci			D_INFO("Rx queue requesting wakeup," " GP1 = 0x%x\n",
25718c2ecf20Sopenharmony_ci			       reg);
25728c2ecf20Sopenharmony_ci			il_set_bit(il, CSR_GP_CNTRL,
25738c2ecf20Sopenharmony_ci				   CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
25748c2ecf20Sopenharmony_ci			goto exit_unlock;
25758c2ecf20Sopenharmony_ci		}
25768c2ecf20Sopenharmony_ci
25778c2ecf20Sopenharmony_ci		q->write_actual = (q->write & ~0x7);
25788c2ecf20Sopenharmony_ci		il_wr(il, rx_wrt_ptr_reg, q->write_actual);
25798c2ecf20Sopenharmony_ci
25808c2ecf20Sopenharmony_ci		/* Else device is assumed to be awake */
25818c2ecf20Sopenharmony_ci	} else {
25828c2ecf20Sopenharmony_ci		/* Device expects a multiple of 8 */
25838c2ecf20Sopenharmony_ci		q->write_actual = (q->write & ~0x7);
25848c2ecf20Sopenharmony_ci		il_wr(il, rx_wrt_ptr_reg, q->write_actual);
25858c2ecf20Sopenharmony_ci	}
25868c2ecf20Sopenharmony_ci
25878c2ecf20Sopenharmony_ci	q->need_update = 0;
25888c2ecf20Sopenharmony_ci
25898c2ecf20Sopenharmony_ciexit_unlock:
25908c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&q->lock, flags);
25918c2ecf20Sopenharmony_ci}
25928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_rx_queue_update_write_ptr);
25938c2ecf20Sopenharmony_ci
25948c2ecf20Sopenharmony_ciint
25958c2ecf20Sopenharmony_ciil_rx_queue_alloc(struct il_priv *il)
25968c2ecf20Sopenharmony_ci{
25978c2ecf20Sopenharmony_ci	struct il_rx_queue *rxq = &il->rxq;
25988c2ecf20Sopenharmony_ci	struct device *dev = &il->pci_dev->dev;
25998c2ecf20Sopenharmony_ci	int i;
26008c2ecf20Sopenharmony_ci
26018c2ecf20Sopenharmony_ci	spin_lock_init(&rxq->lock);
26028c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&rxq->rx_free);
26038c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&rxq->rx_used);
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_ci	/* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */
26068c2ecf20Sopenharmony_ci	rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->bd_dma,
26078c2ecf20Sopenharmony_ci				     GFP_KERNEL);
26088c2ecf20Sopenharmony_ci	if (!rxq->bd)
26098c2ecf20Sopenharmony_ci		goto err_bd;
26108c2ecf20Sopenharmony_ci
26118c2ecf20Sopenharmony_ci	rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct il_rb_status),
26128c2ecf20Sopenharmony_ci					  &rxq->rb_stts_dma, GFP_KERNEL);
26138c2ecf20Sopenharmony_ci	if (!rxq->rb_stts)
26148c2ecf20Sopenharmony_ci		goto err_rb;
26158c2ecf20Sopenharmony_ci
26168c2ecf20Sopenharmony_ci	/* Fill the rx_used queue with _all_ of the Rx buffers */
26178c2ecf20Sopenharmony_ci	for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
26188c2ecf20Sopenharmony_ci		list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
26198c2ecf20Sopenharmony_ci
26208c2ecf20Sopenharmony_ci	/* Set us so that we have processed and used all buffers, but have
26218c2ecf20Sopenharmony_ci	 * not restocked the Rx queue with fresh buffers */
26228c2ecf20Sopenharmony_ci	rxq->read = rxq->write = 0;
26238c2ecf20Sopenharmony_ci	rxq->write_actual = 0;
26248c2ecf20Sopenharmony_ci	rxq->free_count = 0;
26258c2ecf20Sopenharmony_ci	rxq->need_update = 0;
26268c2ecf20Sopenharmony_ci	return 0;
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_cierr_rb:
26298c2ecf20Sopenharmony_ci	dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd,
26308c2ecf20Sopenharmony_ci			  rxq->bd_dma);
26318c2ecf20Sopenharmony_cierr_bd:
26328c2ecf20Sopenharmony_ci	return -ENOMEM;
26338c2ecf20Sopenharmony_ci}
26348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_rx_queue_alloc);
26358c2ecf20Sopenharmony_ci
26368c2ecf20Sopenharmony_civoid
26378c2ecf20Sopenharmony_ciil_hdl_spectrum_measurement(struct il_priv *il, struct il_rx_buf *rxb)
26388c2ecf20Sopenharmony_ci{
26398c2ecf20Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
26408c2ecf20Sopenharmony_ci	struct il_spectrum_notification *report = &(pkt->u.spectrum_notif);
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_ci	if (!report->state) {
26438c2ecf20Sopenharmony_ci		D_11H("Spectrum Measure Notification: Start\n");
26448c2ecf20Sopenharmony_ci		return;
26458c2ecf20Sopenharmony_ci	}
26468c2ecf20Sopenharmony_ci
26478c2ecf20Sopenharmony_ci	memcpy(&il->measure_report, report, sizeof(*report));
26488c2ecf20Sopenharmony_ci	il->measurement_status |= MEASUREMENT_READY;
26498c2ecf20Sopenharmony_ci}
26508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_hdl_spectrum_measurement);
26518c2ecf20Sopenharmony_ci
26528c2ecf20Sopenharmony_ci/*
26538c2ecf20Sopenharmony_ci * returns non-zero if packet should be dropped
26548c2ecf20Sopenharmony_ci */
26558c2ecf20Sopenharmony_ciint
26568c2ecf20Sopenharmony_ciil_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr,
26578c2ecf20Sopenharmony_ci		      u32 decrypt_res, struct ieee80211_rx_status *stats)
26588c2ecf20Sopenharmony_ci{
26598c2ecf20Sopenharmony_ci	u16 fc = le16_to_cpu(hdr->frame_control);
26608c2ecf20Sopenharmony_ci
26618c2ecf20Sopenharmony_ci	/*
26628c2ecf20Sopenharmony_ci	 * All contexts have the same setting here due to it being
26638c2ecf20Sopenharmony_ci	 * a module parameter, so OK to check any context.
26648c2ecf20Sopenharmony_ci	 */
26658c2ecf20Sopenharmony_ci	if (il->active.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK)
26668c2ecf20Sopenharmony_ci		return 0;
26678c2ecf20Sopenharmony_ci
26688c2ecf20Sopenharmony_ci	if (!(fc & IEEE80211_FCTL_PROTECTED))
26698c2ecf20Sopenharmony_ci		return 0;
26708c2ecf20Sopenharmony_ci
26718c2ecf20Sopenharmony_ci	D_RX("decrypt_res:0x%x\n", decrypt_res);
26728c2ecf20Sopenharmony_ci	switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) {
26738c2ecf20Sopenharmony_ci	case RX_RES_STATUS_SEC_TYPE_TKIP:
26748c2ecf20Sopenharmony_ci		/* The uCode has got a bad phase 1 Key, pushes the packet.
26758c2ecf20Sopenharmony_ci		 * Decryption will be done in SW. */
26768c2ecf20Sopenharmony_ci		if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
26778c2ecf20Sopenharmony_ci		    RX_RES_STATUS_BAD_KEY_TTAK)
26788c2ecf20Sopenharmony_ci			break;
26798c2ecf20Sopenharmony_ci		fallthrough;
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_ci	case RX_RES_STATUS_SEC_TYPE_WEP:
26828c2ecf20Sopenharmony_ci		if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
26838c2ecf20Sopenharmony_ci		    RX_RES_STATUS_BAD_ICV_MIC) {
26848c2ecf20Sopenharmony_ci			/* bad ICV, the packet is destroyed since the
26858c2ecf20Sopenharmony_ci			 * decryption is inplace, drop it */
26868c2ecf20Sopenharmony_ci			D_RX("Packet destroyed\n");
26878c2ecf20Sopenharmony_ci			return -1;
26888c2ecf20Sopenharmony_ci		}
26898c2ecf20Sopenharmony_ci		fallthrough;
26908c2ecf20Sopenharmony_ci	case RX_RES_STATUS_SEC_TYPE_CCMP:
26918c2ecf20Sopenharmony_ci		if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
26928c2ecf20Sopenharmony_ci		    RX_RES_STATUS_DECRYPT_OK) {
26938c2ecf20Sopenharmony_ci			D_RX("hw decrypt successfully!!!\n");
26948c2ecf20Sopenharmony_ci			stats->flag |= RX_FLAG_DECRYPTED;
26958c2ecf20Sopenharmony_ci		}
26968c2ecf20Sopenharmony_ci		break;
26978c2ecf20Sopenharmony_ci
26988c2ecf20Sopenharmony_ci	default:
26998c2ecf20Sopenharmony_ci		break;
27008c2ecf20Sopenharmony_ci	}
27018c2ecf20Sopenharmony_ci	return 0;
27028c2ecf20Sopenharmony_ci}
27038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_decrypted_flag);
27048c2ecf20Sopenharmony_ci
27058c2ecf20Sopenharmony_ci/*
27068c2ecf20Sopenharmony_ci * il_txq_update_write_ptr - Send new write idx to hardware
27078c2ecf20Sopenharmony_ci */
27088c2ecf20Sopenharmony_civoid
27098c2ecf20Sopenharmony_ciil_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq)
27108c2ecf20Sopenharmony_ci{
27118c2ecf20Sopenharmony_ci	u32 reg = 0;
27128c2ecf20Sopenharmony_ci	int txq_id = txq->q.id;
27138c2ecf20Sopenharmony_ci
27148c2ecf20Sopenharmony_ci	if (txq->need_update == 0)
27158c2ecf20Sopenharmony_ci		return;
27168c2ecf20Sopenharmony_ci
27178c2ecf20Sopenharmony_ci	/* if we're trying to save power */
27188c2ecf20Sopenharmony_ci	if (test_bit(S_POWER_PMI, &il->status)) {
27198c2ecf20Sopenharmony_ci		/* wake up nic if it's powered down ...
27208c2ecf20Sopenharmony_ci		 * uCode will wake up, and interrupt us again, so next
27218c2ecf20Sopenharmony_ci		 * time we'll skip this part. */
27228c2ecf20Sopenharmony_ci		reg = _il_rd(il, CSR_UCODE_DRV_GP1);
27238c2ecf20Sopenharmony_ci
27248c2ecf20Sopenharmony_ci		if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
27258c2ecf20Sopenharmony_ci			D_INFO("Tx queue %d requesting wakeup," " GP1 = 0x%x\n",
27268c2ecf20Sopenharmony_ci			       txq_id, reg);
27278c2ecf20Sopenharmony_ci			il_set_bit(il, CSR_GP_CNTRL,
27288c2ecf20Sopenharmony_ci				   CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
27298c2ecf20Sopenharmony_ci			return;
27308c2ecf20Sopenharmony_ci		}
27318c2ecf20Sopenharmony_ci
27328c2ecf20Sopenharmony_ci		il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8));
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_ci		/*
27358c2ecf20Sopenharmony_ci		 * else not in power-save mode,
27368c2ecf20Sopenharmony_ci		 * uCode will never sleep when we're
27378c2ecf20Sopenharmony_ci		 * trying to tx (during RFKILL, we're not trying to tx).
27388c2ecf20Sopenharmony_ci		 */
27398c2ecf20Sopenharmony_ci	} else
27408c2ecf20Sopenharmony_ci		_il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8));
27418c2ecf20Sopenharmony_ci	txq->need_update = 0;
27428c2ecf20Sopenharmony_ci}
27438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_txq_update_write_ptr);
27448c2ecf20Sopenharmony_ci
27458c2ecf20Sopenharmony_ci/*
27468c2ecf20Sopenharmony_ci * il_tx_queue_unmap -  Unmap any remaining DMA mappings and free skb's
27478c2ecf20Sopenharmony_ci */
27488c2ecf20Sopenharmony_civoid
27498c2ecf20Sopenharmony_ciil_tx_queue_unmap(struct il_priv *il, int txq_id)
27508c2ecf20Sopenharmony_ci{
27518c2ecf20Sopenharmony_ci	struct il_tx_queue *txq = &il->txq[txq_id];
27528c2ecf20Sopenharmony_ci	struct il_queue *q = &txq->q;
27538c2ecf20Sopenharmony_ci
27548c2ecf20Sopenharmony_ci	if (q->n_bd == 0)
27558c2ecf20Sopenharmony_ci		return;
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_ci	while (q->write_ptr != q->read_ptr) {
27588c2ecf20Sopenharmony_ci		il->ops->txq_free_tfd(il, txq);
27598c2ecf20Sopenharmony_ci		q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd);
27608c2ecf20Sopenharmony_ci	}
27618c2ecf20Sopenharmony_ci}
27628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_tx_queue_unmap);
27638c2ecf20Sopenharmony_ci
27648c2ecf20Sopenharmony_ci/*
27658c2ecf20Sopenharmony_ci * il_tx_queue_free - Deallocate DMA queue.
27668c2ecf20Sopenharmony_ci * @txq: Transmit queue to deallocate.
27678c2ecf20Sopenharmony_ci *
27688c2ecf20Sopenharmony_ci * Empty queue by removing and destroying all BD's.
27698c2ecf20Sopenharmony_ci * Free all buffers.
27708c2ecf20Sopenharmony_ci * 0-fill, but do not free "txq" descriptor structure.
27718c2ecf20Sopenharmony_ci */
27728c2ecf20Sopenharmony_civoid
27738c2ecf20Sopenharmony_ciil_tx_queue_free(struct il_priv *il, int txq_id)
27748c2ecf20Sopenharmony_ci{
27758c2ecf20Sopenharmony_ci	struct il_tx_queue *txq = &il->txq[txq_id];
27768c2ecf20Sopenharmony_ci	struct device *dev = &il->pci_dev->dev;
27778c2ecf20Sopenharmony_ci	int i;
27788c2ecf20Sopenharmony_ci
27798c2ecf20Sopenharmony_ci	il_tx_queue_unmap(il, txq_id);
27808c2ecf20Sopenharmony_ci
27818c2ecf20Sopenharmony_ci	/* De-alloc array of command/tx buffers */
27828c2ecf20Sopenharmony_ci	if (txq->cmd) {
27838c2ecf20Sopenharmony_ci		for (i = 0; i < TFD_TX_CMD_SLOTS; i++)
27848c2ecf20Sopenharmony_ci			kfree(txq->cmd[i]);
27858c2ecf20Sopenharmony_ci	}
27868c2ecf20Sopenharmony_ci
27878c2ecf20Sopenharmony_ci	/* De-alloc circular buffer of TFDs */
27888c2ecf20Sopenharmony_ci	if (txq->q.n_bd)
27898c2ecf20Sopenharmony_ci		dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd,
27908c2ecf20Sopenharmony_ci				  txq->tfds, txq->q.dma_addr);
27918c2ecf20Sopenharmony_ci
27928c2ecf20Sopenharmony_ci	/* De-alloc array of per-TFD driver data */
27938c2ecf20Sopenharmony_ci	kfree(txq->skbs);
27948c2ecf20Sopenharmony_ci	txq->skbs = NULL;
27958c2ecf20Sopenharmony_ci
27968c2ecf20Sopenharmony_ci	/* deallocate arrays */
27978c2ecf20Sopenharmony_ci	kfree(txq->cmd);
27988c2ecf20Sopenharmony_ci	kfree(txq->meta);
27998c2ecf20Sopenharmony_ci	txq->cmd = NULL;
28008c2ecf20Sopenharmony_ci	txq->meta = NULL;
28018c2ecf20Sopenharmony_ci
28028c2ecf20Sopenharmony_ci	/* 0-fill queue descriptor structure */
28038c2ecf20Sopenharmony_ci	memset(txq, 0, sizeof(*txq));
28048c2ecf20Sopenharmony_ci}
28058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_tx_queue_free);
28068c2ecf20Sopenharmony_ci
28078c2ecf20Sopenharmony_ci/*
28088c2ecf20Sopenharmony_ci * il_cmd_queue_unmap - Unmap any remaining DMA mappings from command queue
28098c2ecf20Sopenharmony_ci */
28108c2ecf20Sopenharmony_civoid
28118c2ecf20Sopenharmony_ciil_cmd_queue_unmap(struct il_priv *il)
28128c2ecf20Sopenharmony_ci{
28138c2ecf20Sopenharmony_ci	struct il_tx_queue *txq = &il->txq[il->cmd_queue];
28148c2ecf20Sopenharmony_ci	struct il_queue *q = &txq->q;
28158c2ecf20Sopenharmony_ci	int i;
28168c2ecf20Sopenharmony_ci
28178c2ecf20Sopenharmony_ci	if (q->n_bd == 0)
28188c2ecf20Sopenharmony_ci		return;
28198c2ecf20Sopenharmony_ci
28208c2ecf20Sopenharmony_ci	while (q->read_ptr != q->write_ptr) {
28218c2ecf20Sopenharmony_ci		i = il_get_cmd_idx(q, q->read_ptr, 0);
28228c2ecf20Sopenharmony_ci
28238c2ecf20Sopenharmony_ci		if (txq->meta[i].flags & CMD_MAPPED) {
28248c2ecf20Sopenharmony_ci			pci_unmap_single(il->pci_dev,
28258c2ecf20Sopenharmony_ci					 dma_unmap_addr(&txq->meta[i], mapping),
28268c2ecf20Sopenharmony_ci					 dma_unmap_len(&txq->meta[i], len),
28278c2ecf20Sopenharmony_ci					 PCI_DMA_BIDIRECTIONAL);
28288c2ecf20Sopenharmony_ci			txq->meta[i].flags = 0;
28298c2ecf20Sopenharmony_ci		}
28308c2ecf20Sopenharmony_ci
28318c2ecf20Sopenharmony_ci		q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd);
28328c2ecf20Sopenharmony_ci	}
28338c2ecf20Sopenharmony_ci
28348c2ecf20Sopenharmony_ci	i = q->n_win;
28358c2ecf20Sopenharmony_ci	if (txq->meta[i].flags & CMD_MAPPED) {
28368c2ecf20Sopenharmony_ci		pci_unmap_single(il->pci_dev,
28378c2ecf20Sopenharmony_ci				 dma_unmap_addr(&txq->meta[i], mapping),
28388c2ecf20Sopenharmony_ci				 dma_unmap_len(&txq->meta[i], len),
28398c2ecf20Sopenharmony_ci				 PCI_DMA_BIDIRECTIONAL);
28408c2ecf20Sopenharmony_ci		txq->meta[i].flags = 0;
28418c2ecf20Sopenharmony_ci	}
28428c2ecf20Sopenharmony_ci}
28438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_cmd_queue_unmap);
28448c2ecf20Sopenharmony_ci
28458c2ecf20Sopenharmony_ci/*
28468c2ecf20Sopenharmony_ci * il_cmd_queue_free - Deallocate DMA queue.
28478c2ecf20Sopenharmony_ci *
28488c2ecf20Sopenharmony_ci * Empty queue by removing and destroying all BD's.
28498c2ecf20Sopenharmony_ci * Free all buffers.
28508c2ecf20Sopenharmony_ci * 0-fill, but do not free "txq" descriptor structure.
28518c2ecf20Sopenharmony_ci */
28528c2ecf20Sopenharmony_civoid
28538c2ecf20Sopenharmony_ciil_cmd_queue_free(struct il_priv *il)
28548c2ecf20Sopenharmony_ci{
28558c2ecf20Sopenharmony_ci	struct il_tx_queue *txq = &il->txq[il->cmd_queue];
28568c2ecf20Sopenharmony_ci	struct device *dev = &il->pci_dev->dev;
28578c2ecf20Sopenharmony_ci	int i;
28588c2ecf20Sopenharmony_ci
28598c2ecf20Sopenharmony_ci	il_cmd_queue_unmap(il);
28608c2ecf20Sopenharmony_ci
28618c2ecf20Sopenharmony_ci	/* De-alloc array of command/tx buffers */
28628c2ecf20Sopenharmony_ci	if (txq->cmd) {
28638c2ecf20Sopenharmony_ci		for (i = 0; i <= TFD_CMD_SLOTS; i++)
28648c2ecf20Sopenharmony_ci			kfree(txq->cmd[i]);
28658c2ecf20Sopenharmony_ci	}
28668c2ecf20Sopenharmony_ci
28678c2ecf20Sopenharmony_ci	/* De-alloc circular buffer of TFDs */
28688c2ecf20Sopenharmony_ci	if (txq->q.n_bd)
28698c2ecf20Sopenharmony_ci		dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd,
28708c2ecf20Sopenharmony_ci				  txq->tfds, txq->q.dma_addr);
28718c2ecf20Sopenharmony_ci
28728c2ecf20Sopenharmony_ci	/* deallocate arrays */
28738c2ecf20Sopenharmony_ci	kfree(txq->cmd);
28748c2ecf20Sopenharmony_ci	kfree(txq->meta);
28758c2ecf20Sopenharmony_ci	txq->cmd = NULL;
28768c2ecf20Sopenharmony_ci	txq->meta = NULL;
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_ci	/* 0-fill queue descriptor structure */
28798c2ecf20Sopenharmony_ci	memset(txq, 0, sizeof(*txq));
28808c2ecf20Sopenharmony_ci}
28818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_cmd_queue_free);
28828c2ecf20Sopenharmony_ci
28838c2ecf20Sopenharmony_ci/*************** DMA-QUEUE-GENERAL-FUNCTIONS  *****
28848c2ecf20Sopenharmony_ci * DMA services
28858c2ecf20Sopenharmony_ci *
28868c2ecf20Sopenharmony_ci * Theory of operation
28878c2ecf20Sopenharmony_ci *
28888c2ecf20Sopenharmony_ci * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer
28898c2ecf20Sopenharmony_ci * of buffer descriptors, each of which points to one or more data buffers for
28908c2ecf20Sopenharmony_ci * the device to read from or fill.  Driver and device exchange status of each
28918c2ecf20Sopenharmony_ci * queue via "read" and "write" pointers.  Driver keeps minimum of 2 empty
28928c2ecf20Sopenharmony_ci * entries in each circular buffer, to protect against confusing empty and full
28938c2ecf20Sopenharmony_ci * queue states.
28948c2ecf20Sopenharmony_ci *
28958c2ecf20Sopenharmony_ci * The device reads or writes the data in the queues via the device's several
28968c2ecf20Sopenharmony_ci * DMA/FIFO channels.  Each queue is mapped to a single DMA channel.
28978c2ecf20Sopenharmony_ci *
28988c2ecf20Sopenharmony_ci * For Tx queue, there are low mark and high mark limits. If, after queuing
28998c2ecf20Sopenharmony_ci * the packet for Tx, free space become < low mark, Tx queue stopped. When
29008c2ecf20Sopenharmony_ci * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
29018c2ecf20Sopenharmony_ci * Tx queue resumed.
29028c2ecf20Sopenharmony_ci *
29038c2ecf20Sopenharmony_ci * See more detailed info in 4965.h.
29048c2ecf20Sopenharmony_ci ***************************************************/
29058c2ecf20Sopenharmony_ci
29068c2ecf20Sopenharmony_ciint
29078c2ecf20Sopenharmony_ciil_queue_space(const struct il_queue *q)
29088c2ecf20Sopenharmony_ci{
29098c2ecf20Sopenharmony_ci	int s = q->read_ptr - q->write_ptr;
29108c2ecf20Sopenharmony_ci
29118c2ecf20Sopenharmony_ci	if (q->read_ptr > q->write_ptr)
29128c2ecf20Sopenharmony_ci		s -= q->n_bd;
29138c2ecf20Sopenharmony_ci
29148c2ecf20Sopenharmony_ci	if (s <= 0)
29158c2ecf20Sopenharmony_ci		s += q->n_win;
29168c2ecf20Sopenharmony_ci	/* keep some reserve to not confuse empty and full situations */
29178c2ecf20Sopenharmony_ci	s -= 2;
29188c2ecf20Sopenharmony_ci	if (s < 0)
29198c2ecf20Sopenharmony_ci		s = 0;
29208c2ecf20Sopenharmony_ci	return s;
29218c2ecf20Sopenharmony_ci}
29228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_queue_space);
29238c2ecf20Sopenharmony_ci
29248c2ecf20Sopenharmony_ci
29258c2ecf20Sopenharmony_ci/*
29268c2ecf20Sopenharmony_ci * il_queue_init - Initialize queue's high/low-water and read/write idxes
29278c2ecf20Sopenharmony_ci */
29288c2ecf20Sopenharmony_cistatic int
29298c2ecf20Sopenharmony_ciil_queue_init(struct il_priv *il, struct il_queue *q, int slots, u32 id)
29308c2ecf20Sopenharmony_ci{
29318c2ecf20Sopenharmony_ci	/*
29328c2ecf20Sopenharmony_ci	 * TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
29338c2ecf20Sopenharmony_ci	 * il_queue_inc_wrap and il_queue_dec_wrap are broken.
29348c2ecf20Sopenharmony_ci	 */
29358c2ecf20Sopenharmony_ci	BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
29368c2ecf20Sopenharmony_ci	/* FIXME: remove q->n_bd */
29378c2ecf20Sopenharmony_ci	q->n_bd = TFD_QUEUE_SIZE_MAX;
29388c2ecf20Sopenharmony_ci
29398c2ecf20Sopenharmony_ci	q->n_win = slots;
29408c2ecf20Sopenharmony_ci	q->id = id;
29418c2ecf20Sopenharmony_ci
29428c2ecf20Sopenharmony_ci	/* slots_must be power-of-two size, otherwise
29438c2ecf20Sopenharmony_ci	 * il_get_cmd_idx is broken. */
29448c2ecf20Sopenharmony_ci	BUG_ON(!is_power_of_2(slots));
29458c2ecf20Sopenharmony_ci
29468c2ecf20Sopenharmony_ci	q->low_mark = q->n_win / 4;
29478c2ecf20Sopenharmony_ci	if (q->low_mark < 4)
29488c2ecf20Sopenharmony_ci		q->low_mark = 4;
29498c2ecf20Sopenharmony_ci
29508c2ecf20Sopenharmony_ci	q->high_mark = q->n_win / 8;
29518c2ecf20Sopenharmony_ci	if (q->high_mark < 2)
29528c2ecf20Sopenharmony_ci		q->high_mark = 2;
29538c2ecf20Sopenharmony_ci
29548c2ecf20Sopenharmony_ci	q->write_ptr = q->read_ptr = 0;
29558c2ecf20Sopenharmony_ci
29568c2ecf20Sopenharmony_ci	return 0;
29578c2ecf20Sopenharmony_ci}
29588c2ecf20Sopenharmony_ci
29598c2ecf20Sopenharmony_ci/*
29608c2ecf20Sopenharmony_ci * il_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue
29618c2ecf20Sopenharmony_ci */
29628c2ecf20Sopenharmony_cistatic int
29638c2ecf20Sopenharmony_ciil_tx_queue_alloc(struct il_priv *il, struct il_tx_queue *txq, u32 id)
29648c2ecf20Sopenharmony_ci{
29658c2ecf20Sopenharmony_ci	struct device *dev = &il->pci_dev->dev;
29668c2ecf20Sopenharmony_ci	size_t tfd_sz = il->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX;
29678c2ecf20Sopenharmony_ci
29688c2ecf20Sopenharmony_ci	/* Driver ilate data, only for Tx (not command) queues,
29698c2ecf20Sopenharmony_ci	 * not shared with device. */
29708c2ecf20Sopenharmony_ci	if (id != il->cmd_queue) {
29718c2ecf20Sopenharmony_ci		txq->skbs = kcalloc(TFD_QUEUE_SIZE_MAX,
29728c2ecf20Sopenharmony_ci				    sizeof(struct sk_buff *),
29738c2ecf20Sopenharmony_ci				    GFP_KERNEL);
29748c2ecf20Sopenharmony_ci		if (!txq->skbs) {
29758c2ecf20Sopenharmony_ci			IL_ERR("Fail to alloc skbs\n");
29768c2ecf20Sopenharmony_ci			goto error;
29778c2ecf20Sopenharmony_ci		}
29788c2ecf20Sopenharmony_ci	} else
29798c2ecf20Sopenharmony_ci		txq->skbs = NULL;
29808c2ecf20Sopenharmony_ci
29818c2ecf20Sopenharmony_ci	/* Circular buffer of transmit frame descriptors (TFDs),
29828c2ecf20Sopenharmony_ci	 * shared with device */
29838c2ecf20Sopenharmony_ci	txq->tfds =
29848c2ecf20Sopenharmony_ci	    dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr, GFP_KERNEL);
29858c2ecf20Sopenharmony_ci	if (!txq->tfds)
29868c2ecf20Sopenharmony_ci		goto error;
29878c2ecf20Sopenharmony_ci
29888c2ecf20Sopenharmony_ci	txq->q.id = id;
29898c2ecf20Sopenharmony_ci
29908c2ecf20Sopenharmony_ci	return 0;
29918c2ecf20Sopenharmony_ci
29928c2ecf20Sopenharmony_cierror:
29938c2ecf20Sopenharmony_ci	kfree(txq->skbs);
29948c2ecf20Sopenharmony_ci	txq->skbs = NULL;
29958c2ecf20Sopenharmony_ci
29968c2ecf20Sopenharmony_ci	return -ENOMEM;
29978c2ecf20Sopenharmony_ci}
29988c2ecf20Sopenharmony_ci
29998c2ecf20Sopenharmony_ci/*
30008c2ecf20Sopenharmony_ci * il_tx_queue_init - Allocate and initialize one tx/cmd queue
30018c2ecf20Sopenharmony_ci */
30028c2ecf20Sopenharmony_ciint
30038c2ecf20Sopenharmony_ciil_tx_queue_init(struct il_priv *il, u32 txq_id)
30048c2ecf20Sopenharmony_ci{
30058c2ecf20Sopenharmony_ci	int i, len, ret;
30068c2ecf20Sopenharmony_ci	int slots, actual_slots;
30078c2ecf20Sopenharmony_ci	struct il_tx_queue *txq = &il->txq[txq_id];
30088c2ecf20Sopenharmony_ci
30098c2ecf20Sopenharmony_ci	/*
30108c2ecf20Sopenharmony_ci	 * Alloc buffer array for commands (Tx or other types of commands).
30118c2ecf20Sopenharmony_ci	 * For the command queue (#4/#9), allocate command space + one big
30128c2ecf20Sopenharmony_ci	 * command for scan, since scan command is very huge; the system will
30138c2ecf20Sopenharmony_ci	 * not have two scans at the same time, so only one is needed.
30148c2ecf20Sopenharmony_ci	 * For normal Tx queues (all other queues), no super-size command
30158c2ecf20Sopenharmony_ci	 * space is needed.
30168c2ecf20Sopenharmony_ci	 */
30178c2ecf20Sopenharmony_ci	if (txq_id == il->cmd_queue) {
30188c2ecf20Sopenharmony_ci		slots = TFD_CMD_SLOTS;
30198c2ecf20Sopenharmony_ci		actual_slots = slots + 1;
30208c2ecf20Sopenharmony_ci	} else {
30218c2ecf20Sopenharmony_ci		slots = TFD_TX_CMD_SLOTS;
30228c2ecf20Sopenharmony_ci		actual_slots = slots;
30238c2ecf20Sopenharmony_ci	}
30248c2ecf20Sopenharmony_ci
30258c2ecf20Sopenharmony_ci	txq->meta =
30268c2ecf20Sopenharmony_ci	    kcalloc(actual_slots, sizeof(struct il_cmd_meta), GFP_KERNEL);
30278c2ecf20Sopenharmony_ci	txq->cmd =
30288c2ecf20Sopenharmony_ci	    kcalloc(actual_slots, sizeof(struct il_device_cmd *), GFP_KERNEL);
30298c2ecf20Sopenharmony_ci
30308c2ecf20Sopenharmony_ci	if (!txq->meta || !txq->cmd)
30318c2ecf20Sopenharmony_ci		goto out_free_arrays;
30328c2ecf20Sopenharmony_ci
30338c2ecf20Sopenharmony_ci	len = sizeof(struct il_device_cmd);
30348c2ecf20Sopenharmony_ci	for (i = 0; i < actual_slots; i++) {
30358c2ecf20Sopenharmony_ci		/* only happens for cmd queue */
30368c2ecf20Sopenharmony_ci		if (i == slots)
30378c2ecf20Sopenharmony_ci			len = IL_MAX_CMD_SIZE;
30388c2ecf20Sopenharmony_ci
30398c2ecf20Sopenharmony_ci		txq->cmd[i] = kmalloc(len, GFP_KERNEL);
30408c2ecf20Sopenharmony_ci		if (!txq->cmd[i])
30418c2ecf20Sopenharmony_ci			goto err;
30428c2ecf20Sopenharmony_ci	}
30438c2ecf20Sopenharmony_ci
30448c2ecf20Sopenharmony_ci	/* Alloc driver data array and TFD circular buffer */
30458c2ecf20Sopenharmony_ci	ret = il_tx_queue_alloc(il, txq, txq_id);
30468c2ecf20Sopenharmony_ci	if (ret)
30478c2ecf20Sopenharmony_ci		goto err;
30488c2ecf20Sopenharmony_ci
30498c2ecf20Sopenharmony_ci	txq->need_update = 0;
30508c2ecf20Sopenharmony_ci
30518c2ecf20Sopenharmony_ci	/*
30528c2ecf20Sopenharmony_ci	 * For the default queues 0-3, set up the swq_id
30538c2ecf20Sopenharmony_ci	 * already -- all others need to get one later
30548c2ecf20Sopenharmony_ci	 * (if they need one at all).
30558c2ecf20Sopenharmony_ci	 */
30568c2ecf20Sopenharmony_ci	if (txq_id < 4)
30578c2ecf20Sopenharmony_ci		il_set_swq_id(txq, txq_id, txq_id);
30588c2ecf20Sopenharmony_ci
30598c2ecf20Sopenharmony_ci	/* Initialize queue's high/low-water marks, and head/tail idxes */
30608c2ecf20Sopenharmony_ci	il_queue_init(il, &txq->q, slots, txq_id);
30618c2ecf20Sopenharmony_ci
30628c2ecf20Sopenharmony_ci	/* Tell device where to find queue */
30638c2ecf20Sopenharmony_ci	il->ops->txq_init(il, txq);
30648c2ecf20Sopenharmony_ci
30658c2ecf20Sopenharmony_ci	return 0;
30668c2ecf20Sopenharmony_cierr:
30678c2ecf20Sopenharmony_ci	for (i = 0; i < actual_slots; i++)
30688c2ecf20Sopenharmony_ci		kfree(txq->cmd[i]);
30698c2ecf20Sopenharmony_ciout_free_arrays:
30708c2ecf20Sopenharmony_ci	kfree(txq->meta);
30718c2ecf20Sopenharmony_ci	txq->meta = NULL;
30728c2ecf20Sopenharmony_ci	kfree(txq->cmd);
30738c2ecf20Sopenharmony_ci	txq->cmd = NULL;
30748c2ecf20Sopenharmony_ci
30758c2ecf20Sopenharmony_ci	return -ENOMEM;
30768c2ecf20Sopenharmony_ci}
30778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_tx_queue_init);
30788c2ecf20Sopenharmony_ci
30798c2ecf20Sopenharmony_civoid
30808c2ecf20Sopenharmony_ciil_tx_queue_reset(struct il_priv *il, u32 txq_id)
30818c2ecf20Sopenharmony_ci{
30828c2ecf20Sopenharmony_ci	int slots, actual_slots;
30838c2ecf20Sopenharmony_ci	struct il_tx_queue *txq = &il->txq[txq_id];
30848c2ecf20Sopenharmony_ci
30858c2ecf20Sopenharmony_ci	if (txq_id == il->cmd_queue) {
30868c2ecf20Sopenharmony_ci		slots = TFD_CMD_SLOTS;
30878c2ecf20Sopenharmony_ci		actual_slots = TFD_CMD_SLOTS + 1;
30888c2ecf20Sopenharmony_ci	} else {
30898c2ecf20Sopenharmony_ci		slots = TFD_TX_CMD_SLOTS;
30908c2ecf20Sopenharmony_ci		actual_slots = TFD_TX_CMD_SLOTS;
30918c2ecf20Sopenharmony_ci	}
30928c2ecf20Sopenharmony_ci
30938c2ecf20Sopenharmony_ci	memset(txq->meta, 0, sizeof(struct il_cmd_meta) * actual_slots);
30948c2ecf20Sopenharmony_ci	txq->need_update = 0;
30958c2ecf20Sopenharmony_ci
30968c2ecf20Sopenharmony_ci	/* Initialize queue's high/low-water marks, and head/tail idxes */
30978c2ecf20Sopenharmony_ci	il_queue_init(il, &txq->q, slots, txq_id);
30988c2ecf20Sopenharmony_ci
30998c2ecf20Sopenharmony_ci	/* Tell device where to find queue */
31008c2ecf20Sopenharmony_ci	il->ops->txq_init(il, txq);
31018c2ecf20Sopenharmony_ci}
31028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_tx_queue_reset);
31038c2ecf20Sopenharmony_ci
31048c2ecf20Sopenharmony_ci/*************** HOST COMMAND QUEUE FUNCTIONS   *****/
31058c2ecf20Sopenharmony_ci
31068c2ecf20Sopenharmony_ci/*
31078c2ecf20Sopenharmony_ci * il_enqueue_hcmd - enqueue a uCode command
31088c2ecf20Sopenharmony_ci * @il: device ilate data point
31098c2ecf20Sopenharmony_ci * @cmd: a point to the ucode command structure
31108c2ecf20Sopenharmony_ci *
31118c2ecf20Sopenharmony_ci * The function returns < 0 values to indicate the operation is
31128c2ecf20Sopenharmony_ci * failed. On success, it turns the idx (> 0) of command in the
31138c2ecf20Sopenharmony_ci * command queue.
31148c2ecf20Sopenharmony_ci */
31158c2ecf20Sopenharmony_ciint
31168c2ecf20Sopenharmony_ciil_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd)
31178c2ecf20Sopenharmony_ci{
31188c2ecf20Sopenharmony_ci	struct il_tx_queue *txq = &il->txq[il->cmd_queue];
31198c2ecf20Sopenharmony_ci	struct il_queue *q = &txq->q;
31208c2ecf20Sopenharmony_ci	struct il_device_cmd *out_cmd;
31218c2ecf20Sopenharmony_ci	struct il_cmd_meta *out_meta;
31228c2ecf20Sopenharmony_ci	dma_addr_t phys_addr;
31238c2ecf20Sopenharmony_ci	unsigned long flags;
31248c2ecf20Sopenharmony_ci	u32 idx;
31258c2ecf20Sopenharmony_ci	u16 fix_size;
31268c2ecf20Sopenharmony_ci
31278c2ecf20Sopenharmony_ci	cmd->len = il->ops->get_hcmd_size(cmd->id, cmd->len);
31288c2ecf20Sopenharmony_ci	fix_size = (u16) (cmd->len + sizeof(out_cmd->hdr));
31298c2ecf20Sopenharmony_ci
31308c2ecf20Sopenharmony_ci	/* If any of the command structures end up being larger than
31318c2ecf20Sopenharmony_ci	 * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then
31328c2ecf20Sopenharmony_ci	 * we will need to increase the size of the TFD entries
31338c2ecf20Sopenharmony_ci	 * Also, check to see if command buffer should not exceed the size
31348c2ecf20Sopenharmony_ci	 * of device_cmd and max_cmd_size. */
31358c2ecf20Sopenharmony_ci	BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) &&
31368c2ecf20Sopenharmony_ci	       !(cmd->flags & CMD_SIZE_HUGE));
31378c2ecf20Sopenharmony_ci	BUG_ON(fix_size > IL_MAX_CMD_SIZE);
31388c2ecf20Sopenharmony_ci
31398c2ecf20Sopenharmony_ci	if (il_is_rfkill(il) || il_is_ctkill(il)) {
31408c2ecf20Sopenharmony_ci		IL_WARN("Not sending command - %s KILL\n",
31418c2ecf20Sopenharmony_ci			il_is_rfkill(il) ? "RF" : "CT");
31428c2ecf20Sopenharmony_ci		return -EIO;
31438c2ecf20Sopenharmony_ci	}
31448c2ecf20Sopenharmony_ci
31458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->hcmd_lock, flags);
31468c2ecf20Sopenharmony_ci
31478c2ecf20Sopenharmony_ci	if (il_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
31488c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&il->hcmd_lock, flags);
31498c2ecf20Sopenharmony_ci
31508c2ecf20Sopenharmony_ci		IL_ERR("Restarting adapter due to command queue full\n");
31518c2ecf20Sopenharmony_ci		queue_work(il->workqueue, &il->restart);
31528c2ecf20Sopenharmony_ci		return -ENOSPC;
31538c2ecf20Sopenharmony_ci	}
31548c2ecf20Sopenharmony_ci
31558c2ecf20Sopenharmony_ci	idx = il_get_cmd_idx(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE);
31568c2ecf20Sopenharmony_ci	out_cmd = txq->cmd[idx];
31578c2ecf20Sopenharmony_ci	out_meta = &txq->meta[idx];
31588c2ecf20Sopenharmony_ci
31598c2ecf20Sopenharmony_ci	if (WARN_ON(out_meta->flags & CMD_MAPPED)) {
31608c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&il->hcmd_lock, flags);
31618c2ecf20Sopenharmony_ci		return -ENOSPC;
31628c2ecf20Sopenharmony_ci	}
31638c2ecf20Sopenharmony_ci
31648c2ecf20Sopenharmony_ci	memset(out_meta, 0, sizeof(*out_meta));	/* re-initialize to NULL */
31658c2ecf20Sopenharmony_ci	out_meta->flags = cmd->flags | CMD_MAPPED;
31668c2ecf20Sopenharmony_ci	if (cmd->flags & CMD_WANT_SKB)
31678c2ecf20Sopenharmony_ci		out_meta->source = cmd;
31688c2ecf20Sopenharmony_ci	if (cmd->flags & CMD_ASYNC)
31698c2ecf20Sopenharmony_ci		out_meta->callback = cmd->callback;
31708c2ecf20Sopenharmony_ci
31718c2ecf20Sopenharmony_ci	out_cmd->hdr.cmd = cmd->id;
31728c2ecf20Sopenharmony_ci	memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len);
31738c2ecf20Sopenharmony_ci
31748c2ecf20Sopenharmony_ci	/* At this point, the out_cmd now has all of the incoming cmd
31758c2ecf20Sopenharmony_ci	 * information */
31768c2ecf20Sopenharmony_ci
31778c2ecf20Sopenharmony_ci	out_cmd->hdr.flags = 0;
31788c2ecf20Sopenharmony_ci	out_cmd->hdr.sequence =
31798c2ecf20Sopenharmony_ci	    cpu_to_le16(QUEUE_TO_SEQ(il->cmd_queue) | IDX_TO_SEQ(q->write_ptr));
31808c2ecf20Sopenharmony_ci	if (cmd->flags & CMD_SIZE_HUGE)
31818c2ecf20Sopenharmony_ci		out_cmd->hdr.sequence |= SEQ_HUGE_FRAME;
31828c2ecf20Sopenharmony_ci
31838c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
31848c2ecf20Sopenharmony_ci	switch (out_cmd->hdr.cmd) {
31858c2ecf20Sopenharmony_ci	case C_TX_LINK_QUALITY_CMD:
31868c2ecf20Sopenharmony_ci	case C_SENSITIVITY:
31878c2ecf20Sopenharmony_ci		D_HC_DUMP("Sending command %s (#%x), seq: 0x%04X, "
31888c2ecf20Sopenharmony_ci			  "%d bytes at %d[%d]:%d\n",
31898c2ecf20Sopenharmony_ci			  il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd,
31908c2ecf20Sopenharmony_ci			  le16_to_cpu(out_cmd->hdr.sequence), fix_size,
31918c2ecf20Sopenharmony_ci			  q->write_ptr, idx, il->cmd_queue);
31928c2ecf20Sopenharmony_ci		break;
31938c2ecf20Sopenharmony_ci	default:
31948c2ecf20Sopenharmony_ci		D_HC("Sending command %s (#%x), seq: 0x%04X, "
31958c2ecf20Sopenharmony_ci		     "%d bytes at %d[%d]:%d\n",
31968c2ecf20Sopenharmony_ci		     il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd,
31978c2ecf20Sopenharmony_ci		     le16_to_cpu(out_cmd->hdr.sequence), fix_size, q->write_ptr,
31988c2ecf20Sopenharmony_ci		     idx, il->cmd_queue);
31998c2ecf20Sopenharmony_ci	}
32008c2ecf20Sopenharmony_ci#endif
32018c2ecf20Sopenharmony_ci
32028c2ecf20Sopenharmony_ci	phys_addr =
32038c2ecf20Sopenharmony_ci	    pci_map_single(il->pci_dev, &out_cmd->hdr, fix_size,
32048c2ecf20Sopenharmony_ci			   PCI_DMA_BIDIRECTIONAL);
32058c2ecf20Sopenharmony_ci	if (unlikely(pci_dma_mapping_error(il->pci_dev, phys_addr))) {
32068c2ecf20Sopenharmony_ci		idx = -ENOMEM;
32078c2ecf20Sopenharmony_ci		goto out;
32088c2ecf20Sopenharmony_ci	}
32098c2ecf20Sopenharmony_ci	dma_unmap_addr_set(out_meta, mapping, phys_addr);
32108c2ecf20Sopenharmony_ci	dma_unmap_len_set(out_meta, len, fix_size);
32118c2ecf20Sopenharmony_ci
32128c2ecf20Sopenharmony_ci	txq->need_update = 1;
32138c2ecf20Sopenharmony_ci
32148c2ecf20Sopenharmony_ci	if (il->ops->txq_update_byte_cnt_tbl)
32158c2ecf20Sopenharmony_ci		/* Set up entry in queue's byte count circular buffer */
32168c2ecf20Sopenharmony_ci		il->ops->txq_update_byte_cnt_tbl(il, txq, 0);
32178c2ecf20Sopenharmony_ci
32188c2ecf20Sopenharmony_ci	il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, fix_size, 1,
32198c2ecf20Sopenharmony_ci					    U32_PAD(cmd->len));
32208c2ecf20Sopenharmony_ci
32218c2ecf20Sopenharmony_ci	/* Increment and update queue's write idx */
32228c2ecf20Sopenharmony_ci	q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd);
32238c2ecf20Sopenharmony_ci	il_txq_update_write_ptr(il, txq);
32248c2ecf20Sopenharmony_ci
32258c2ecf20Sopenharmony_ciout:
32268c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->hcmd_lock, flags);
32278c2ecf20Sopenharmony_ci	return idx;
32288c2ecf20Sopenharmony_ci}
32298c2ecf20Sopenharmony_ci
32308c2ecf20Sopenharmony_ci/*
32318c2ecf20Sopenharmony_ci * il_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd
32328c2ecf20Sopenharmony_ci *
32338c2ecf20Sopenharmony_ci * When FW advances 'R' idx, all entries between old and new 'R' idx
32348c2ecf20Sopenharmony_ci * need to be reclaimed. As result, some free space forms.  If there is
32358c2ecf20Sopenharmony_ci * enough free space (> low mark), wake the stack that feeds us.
32368c2ecf20Sopenharmony_ci */
32378c2ecf20Sopenharmony_cistatic void
32388c2ecf20Sopenharmony_ciil_hcmd_queue_reclaim(struct il_priv *il, int txq_id, int idx, int cmd_idx)
32398c2ecf20Sopenharmony_ci{
32408c2ecf20Sopenharmony_ci	struct il_tx_queue *txq = &il->txq[txq_id];
32418c2ecf20Sopenharmony_ci	struct il_queue *q = &txq->q;
32428c2ecf20Sopenharmony_ci	int nfreed = 0;
32438c2ecf20Sopenharmony_ci
32448c2ecf20Sopenharmony_ci	if (idx >= q->n_bd || il_queue_used(q, idx) == 0) {
32458c2ecf20Sopenharmony_ci		IL_ERR("Read idx for DMA queue txq id (%d), idx %d, "
32468c2ecf20Sopenharmony_ci		       "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd,
32478c2ecf20Sopenharmony_ci		       q->write_ptr, q->read_ptr);
32488c2ecf20Sopenharmony_ci		return;
32498c2ecf20Sopenharmony_ci	}
32508c2ecf20Sopenharmony_ci
32518c2ecf20Sopenharmony_ci	for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx;
32528c2ecf20Sopenharmony_ci	     q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) {
32538c2ecf20Sopenharmony_ci
32548c2ecf20Sopenharmony_ci		if (nfreed++ > 0) {
32558c2ecf20Sopenharmony_ci			IL_ERR("HCMD skipped: idx (%d) %d %d\n", idx,
32568c2ecf20Sopenharmony_ci			       q->write_ptr, q->read_ptr);
32578c2ecf20Sopenharmony_ci			queue_work(il->workqueue, &il->restart);
32588c2ecf20Sopenharmony_ci		}
32598c2ecf20Sopenharmony_ci
32608c2ecf20Sopenharmony_ci	}
32618c2ecf20Sopenharmony_ci}
32628c2ecf20Sopenharmony_ci
32638c2ecf20Sopenharmony_ci/*
32648c2ecf20Sopenharmony_ci * il_tx_cmd_complete - Pull unused buffers off the queue and reclaim them
32658c2ecf20Sopenharmony_ci * @rxb: Rx buffer to reclaim
32668c2ecf20Sopenharmony_ci *
32678c2ecf20Sopenharmony_ci * If an Rx buffer has an async callback associated with it the callback
32688c2ecf20Sopenharmony_ci * will be executed.  The attached skb (if present) will only be freed
32698c2ecf20Sopenharmony_ci * if the callback returns 1
32708c2ecf20Sopenharmony_ci */
32718c2ecf20Sopenharmony_civoid
32728c2ecf20Sopenharmony_ciil_tx_cmd_complete(struct il_priv *il, struct il_rx_buf *rxb)
32738c2ecf20Sopenharmony_ci{
32748c2ecf20Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
32758c2ecf20Sopenharmony_ci	u16 sequence = le16_to_cpu(pkt->hdr.sequence);
32768c2ecf20Sopenharmony_ci	int txq_id = SEQ_TO_QUEUE(sequence);
32778c2ecf20Sopenharmony_ci	int idx = SEQ_TO_IDX(sequence);
32788c2ecf20Sopenharmony_ci	int cmd_idx;
32798c2ecf20Sopenharmony_ci	bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME);
32808c2ecf20Sopenharmony_ci	struct il_device_cmd *cmd;
32818c2ecf20Sopenharmony_ci	struct il_cmd_meta *meta;
32828c2ecf20Sopenharmony_ci	struct il_tx_queue *txq = &il->txq[il->cmd_queue];
32838c2ecf20Sopenharmony_ci	unsigned long flags;
32848c2ecf20Sopenharmony_ci
32858c2ecf20Sopenharmony_ci	/* If a Tx command is being handled and it isn't in the actual
32868c2ecf20Sopenharmony_ci	 * command queue then there a command routing bug has been introduced
32878c2ecf20Sopenharmony_ci	 * in the queue management code. */
32888c2ecf20Sopenharmony_ci	if (WARN
32898c2ecf20Sopenharmony_ci	    (txq_id != il->cmd_queue,
32908c2ecf20Sopenharmony_ci	     "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n",
32918c2ecf20Sopenharmony_ci	     txq_id, il->cmd_queue, sequence, il->txq[il->cmd_queue].q.read_ptr,
32928c2ecf20Sopenharmony_ci	     il->txq[il->cmd_queue].q.write_ptr)) {
32938c2ecf20Sopenharmony_ci		il_print_hex_error(il, pkt, 32);
32948c2ecf20Sopenharmony_ci		return;
32958c2ecf20Sopenharmony_ci	}
32968c2ecf20Sopenharmony_ci
32978c2ecf20Sopenharmony_ci	cmd_idx = il_get_cmd_idx(&txq->q, idx, huge);
32988c2ecf20Sopenharmony_ci	cmd = txq->cmd[cmd_idx];
32998c2ecf20Sopenharmony_ci	meta = &txq->meta[cmd_idx];
33008c2ecf20Sopenharmony_ci
33018c2ecf20Sopenharmony_ci	txq->time_stamp = jiffies;
33028c2ecf20Sopenharmony_ci
33038c2ecf20Sopenharmony_ci	pci_unmap_single(il->pci_dev, dma_unmap_addr(meta, mapping),
33048c2ecf20Sopenharmony_ci			 dma_unmap_len(meta, len), PCI_DMA_BIDIRECTIONAL);
33058c2ecf20Sopenharmony_ci
33068c2ecf20Sopenharmony_ci	/* Input error checking is done when commands are added to queue. */
33078c2ecf20Sopenharmony_ci	if (meta->flags & CMD_WANT_SKB) {
33088c2ecf20Sopenharmony_ci		meta->source->reply_page = (unsigned long)rxb_addr(rxb);
33098c2ecf20Sopenharmony_ci		rxb->page = NULL;
33108c2ecf20Sopenharmony_ci	} else if (meta->callback)
33118c2ecf20Sopenharmony_ci		meta->callback(il, cmd, pkt);
33128c2ecf20Sopenharmony_ci
33138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->hcmd_lock, flags);
33148c2ecf20Sopenharmony_ci
33158c2ecf20Sopenharmony_ci	il_hcmd_queue_reclaim(il, txq_id, idx, cmd_idx);
33168c2ecf20Sopenharmony_ci
33178c2ecf20Sopenharmony_ci	if (!(meta->flags & CMD_ASYNC)) {
33188c2ecf20Sopenharmony_ci		clear_bit(S_HCMD_ACTIVE, &il->status);
33198c2ecf20Sopenharmony_ci		D_INFO("Clearing HCMD_ACTIVE for command %s\n",
33208c2ecf20Sopenharmony_ci		       il_get_cmd_string(cmd->hdr.cmd));
33218c2ecf20Sopenharmony_ci		wake_up(&il->wait_command_queue);
33228c2ecf20Sopenharmony_ci	}
33238c2ecf20Sopenharmony_ci
33248c2ecf20Sopenharmony_ci	/* Mark as unmapped */
33258c2ecf20Sopenharmony_ci	meta->flags = 0;
33268c2ecf20Sopenharmony_ci
33278c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->hcmd_lock, flags);
33288c2ecf20Sopenharmony_ci}
33298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_tx_cmd_complete);
33308c2ecf20Sopenharmony_ci
33318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("iwl-legacy: common functions for 3945 and 4965");
33328c2ecf20Sopenharmony_ciMODULE_VERSION(IWLWIFI_VERSION);
33338c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
33348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
33358c2ecf20Sopenharmony_ci
33368c2ecf20Sopenharmony_ci/*
33378c2ecf20Sopenharmony_ci * set bt_coex_active to true, uCode will do kill/defer
33388c2ecf20Sopenharmony_ci * every time the priority line is asserted (BT is sending signals on the
33398c2ecf20Sopenharmony_ci * priority line in the PCIx).
33408c2ecf20Sopenharmony_ci * set bt_coex_active to false, uCode will ignore the BT activity and
33418c2ecf20Sopenharmony_ci * perform the normal operation
33428c2ecf20Sopenharmony_ci *
33438c2ecf20Sopenharmony_ci * User might experience transmit issue on some platform due to WiFi/BT
33448c2ecf20Sopenharmony_ci * co-exist problem. The possible behaviors are:
33458c2ecf20Sopenharmony_ci *   Able to scan and finding all the available AP
33468c2ecf20Sopenharmony_ci *   Not able to associate with any AP
33478c2ecf20Sopenharmony_ci * On those platforms, WiFi communication can be restored by set
33488c2ecf20Sopenharmony_ci * "bt_coex_active" module parameter to "false"
33498c2ecf20Sopenharmony_ci *
33508c2ecf20Sopenharmony_ci * default: bt_coex_active = true (BT_COEX_ENABLE)
33518c2ecf20Sopenharmony_ci */
33528c2ecf20Sopenharmony_cistatic bool bt_coex_active = true;
33538c2ecf20Sopenharmony_cimodule_param(bt_coex_active, bool, 0444);
33548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist");
33558c2ecf20Sopenharmony_ci
33568c2ecf20Sopenharmony_ciu32 il_debug_level;
33578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_debug_level);
33588c2ecf20Sopenharmony_ci
33598c2ecf20Sopenharmony_ciconst u8 il_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
33608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_bcast_addr);
33618c2ecf20Sopenharmony_ci
33628c2ecf20Sopenharmony_ci#define MAX_BIT_RATE_40_MHZ 150	/* Mbps */
33638c2ecf20Sopenharmony_ci#define MAX_BIT_RATE_20_MHZ 72	/* Mbps */
33648c2ecf20Sopenharmony_cistatic void
33658c2ecf20Sopenharmony_ciil_init_ht_hw_capab(const struct il_priv *il,
33668c2ecf20Sopenharmony_ci		    struct ieee80211_sta_ht_cap *ht_info,
33678c2ecf20Sopenharmony_ci		    enum nl80211_band band)
33688c2ecf20Sopenharmony_ci{
33698c2ecf20Sopenharmony_ci	u16 max_bit_rate = 0;
33708c2ecf20Sopenharmony_ci	u8 rx_chains_num = il->hw_params.rx_chains_num;
33718c2ecf20Sopenharmony_ci	u8 tx_chains_num = il->hw_params.tx_chains_num;
33728c2ecf20Sopenharmony_ci
33738c2ecf20Sopenharmony_ci	ht_info->cap = 0;
33748c2ecf20Sopenharmony_ci	memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
33758c2ecf20Sopenharmony_ci
33768c2ecf20Sopenharmony_ci	ht_info->ht_supported = true;
33778c2ecf20Sopenharmony_ci
33788c2ecf20Sopenharmony_ci	ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
33798c2ecf20Sopenharmony_ci	max_bit_rate = MAX_BIT_RATE_20_MHZ;
33808c2ecf20Sopenharmony_ci	if (il->hw_params.ht40_channel & BIT(band)) {
33818c2ecf20Sopenharmony_ci		ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
33828c2ecf20Sopenharmony_ci		ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
33838c2ecf20Sopenharmony_ci		ht_info->mcs.rx_mask[4] = 0x01;
33848c2ecf20Sopenharmony_ci		max_bit_rate = MAX_BIT_RATE_40_MHZ;
33858c2ecf20Sopenharmony_ci	}
33868c2ecf20Sopenharmony_ci
33878c2ecf20Sopenharmony_ci	if (il->cfg->mod_params->amsdu_size_8K)
33888c2ecf20Sopenharmony_ci		ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
33898c2ecf20Sopenharmony_ci
33908c2ecf20Sopenharmony_ci	ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF;
33918c2ecf20Sopenharmony_ci	ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF;
33928c2ecf20Sopenharmony_ci
33938c2ecf20Sopenharmony_ci	ht_info->mcs.rx_mask[0] = 0xFF;
33948c2ecf20Sopenharmony_ci	if (rx_chains_num >= 2)
33958c2ecf20Sopenharmony_ci		ht_info->mcs.rx_mask[1] = 0xFF;
33968c2ecf20Sopenharmony_ci	if (rx_chains_num >= 3)
33978c2ecf20Sopenharmony_ci		ht_info->mcs.rx_mask[2] = 0xFF;
33988c2ecf20Sopenharmony_ci
33998c2ecf20Sopenharmony_ci	/* Highest supported Rx data rate */
34008c2ecf20Sopenharmony_ci	max_bit_rate *= rx_chains_num;
34018c2ecf20Sopenharmony_ci	WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK);
34028c2ecf20Sopenharmony_ci	ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate);
34038c2ecf20Sopenharmony_ci
34048c2ecf20Sopenharmony_ci	/* Tx MCS capabilities */
34058c2ecf20Sopenharmony_ci	ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
34068c2ecf20Sopenharmony_ci	if (tx_chains_num != rx_chains_num) {
34078c2ecf20Sopenharmony_ci		ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
34088c2ecf20Sopenharmony_ci		ht_info->mcs.tx_params |=
34098c2ecf20Sopenharmony_ci		    ((tx_chains_num -
34108c2ecf20Sopenharmony_ci		      1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
34118c2ecf20Sopenharmony_ci	}
34128c2ecf20Sopenharmony_ci}
34138c2ecf20Sopenharmony_ci
34148c2ecf20Sopenharmony_ci/*
34158c2ecf20Sopenharmony_ci * il_init_geos - Initialize mac80211's geo/channel info based from eeprom
34168c2ecf20Sopenharmony_ci */
34178c2ecf20Sopenharmony_ciint
34188c2ecf20Sopenharmony_ciil_init_geos(struct il_priv *il)
34198c2ecf20Sopenharmony_ci{
34208c2ecf20Sopenharmony_ci	struct il_channel_info *ch;
34218c2ecf20Sopenharmony_ci	struct ieee80211_supported_band *sband;
34228c2ecf20Sopenharmony_ci	struct ieee80211_channel *channels;
34238c2ecf20Sopenharmony_ci	struct ieee80211_channel *geo_ch;
34248c2ecf20Sopenharmony_ci	struct ieee80211_rate *rates;
34258c2ecf20Sopenharmony_ci	int i = 0;
34268c2ecf20Sopenharmony_ci	s8 max_tx_power = 0;
34278c2ecf20Sopenharmony_ci
34288c2ecf20Sopenharmony_ci	if (il->bands[NL80211_BAND_2GHZ].n_bitrates ||
34298c2ecf20Sopenharmony_ci	    il->bands[NL80211_BAND_5GHZ].n_bitrates) {
34308c2ecf20Sopenharmony_ci		D_INFO("Geography modes already initialized.\n");
34318c2ecf20Sopenharmony_ci		set_bit(S_GEO_CONFIGURED, &il->status);
34328c2ecf20Sopenharmony_ci		return 0;
34338c2ecf20Sopenharmony_ci	}
34348c2ecf20Sopenharmony_ci
34358c2ecf20Sopenharmony_ci	channels =
34368c2ecf20Sopenharmony_ci	    kcalloc(il->channel_count, sizeof(struct ieee80211_channel),
34378c2ecf20Sopenharmony_ci		    GFP_KERNEL);
34388c2ecf20Sopenharmony_ci	if (!channels)
34398c2ecf20Sopenharmony_ci		return -ENOMEM;
34408c2ecf20Sopenharmony_ci
34418c2ecf20Sopenharmony_ci	rates =
34428c2ecf20Sopenharmony_ci	    kzalloc((sizeof(struct ieee80211_rate) * RATE_COUNT_LEGACY),
34438c2ecf20Sopenharmony_ci		    GFP_KERNEL);
34448c2ecf20Sopenharmony_ci	if (!rates) {
34458c2ecf20Sopenharmony_ci		kfree(channels);
34468c2ecf20Sopenharmony_ci		return -ENOMEM;
34478c2ecf20Sopenharmony_ci	}
34488c2ecf20Sopenharmony_ci
34498c2ecf20Sopenharmony_ci	/* 5.2GHz channels start after the 2.4GHz channels */
34508c2ecf20Sopenharmony_ci	sband = &il->bands[NL80211_BAND_5GHZ];
34518c2ecf20Sopenharmony_ci	sband->channels = &channels[ARRAY_SIZE(il_eeprom_band_1)];
34528c2ecf20Sopenharmony_ci	/* just OFDM */
34538c2ecf20Sopenharmony_ci	sband->bitrates = &rates[IL_FIRST_OFDM_RATE];
34548c2ecf20Sopenharmony_ci	sband->n_bitrates = RATE_COUNT_LEGACY - IL_FIRST_OFDM_RATE;
34558c2ecf20Sopenharmony_ci
34568c2ecf20Sopenharmony_ci	if (il->cfg->sku & IL_SKU_N)
34578c2ecf20Sopenharmony_ci		il_init_ht_hw_capab(il, &sband->ht_cap, NL80211_BAND_5GHZ);
34588c2ecf20Sopenharmony_ci
34598c2ecf20Sopenharmony_ci	sband = &il->bands[NL80211_BAND_2GHZ];
34608c2ecf20Sopenharmony_ci	sband->channels = channels;
34618c2ecf20Sopenharmony_ci	/* OFDM & CCK */
34628c2ecf20Sopenharmony_ci	sband->bitrates = rates;
34638c2ecf20Sopenharmony_ci	sband->n_bitrates = RATE_COUNT_LEGACY;
34648c2ecf20Sopenharmony_ci
34658c2ecf20Sopenharmony_ci	if (il->cfg->sku & IL_SKU_N)
34668c2ecf20Sopenharmony_ci		il_init_ht_hw_capab(il, &sband->ht_cap, NL80211_BAND_2GHZ);
34678c2ecf20Sopenharmony_ci
34688c2ecf20Sopenharmony_ci	il->ieee_channels = channels;
34698c2ecf20Sopenharmony_ci	il->ieee_rates = rates;
34708c2ecf20Sopenharmony_ci
34718c2ecf20Sopenharmony_ci	for (i = 0; i < il->channel_count; i++) {
34728c2ecf20Sopenharmony_ci		ch = &il->channel_info[i];
34738c2ecf20Sopenharmony_ci
34748c2ecf20Sopenharmony_ci		if (!il_is_channel_valid(ch))
34758c2ecf20Sopenharmony_ci			continue;
34768c2ecf20Sopenharmony_ci
34778c2ecf20Sopenharmony_ci		sband = &il->bands[ch->band];
34788c2ecf20Sopenharmony_ci
34798c2ecf20Sopenharmony_ci		geo_ch = &sband->channels[sband->n_channels++];
34808c2ecf20Sopenharmony_ci
34818c2ecf20Sopenharmony_ci		geo_ch->center_freq =
34828c2ecf20Sopenharmony_ci		    ieee80211_channel_to_frequency(ch->channel, ch->band);
34838c2ecf20Sopenharmony_ci		geo_ch->max_power = ch->max_power_avg;
34848c2ecf20Sopenharmony_ci		geo_ch->max_antenna_gain = 0xff;
34858c2ecf20Sopenharmony_ci		geo_ch->hw_value = ch->channel;
34868c2ecf20Sopenharmony_ci
34878c2ecf20Sopenharmony_ci		if (il_is_channel_valid(ch)) {
34888c2ecf20Sopenharmony_ci			if (!(ch->flags & EEPROM_CHANNEL_IBSS))
34898c2ecf20Sopenharmony_ci				geo_ch->flags |= IEEE80211_CHAN_NO_IR;
34908c2ecf20Sopenharmony_ci
34918c2ecf20Sopenharmony_ci			if (!(ch->flags & EEPROM_CHANNEL_ACTIVE))
34928c2ecf20Sopenharmony_ci				geo_ch->flags |= IEEE80211_CHAN_NO_IR;
34938c2ecf20Sopenharmony_ci
34948c2ecf20Sopenharmony_ci			if (ch->flags & EEPROM_CHANNEL_RADAR)
34958c2ecf20Sopenharmony_ci				geo_ch->flags |= IEEE80211_CHAN_RADAR;
34968c2ecf20Sopenharmony_ci
34978c2ecf20Sopenharmony_ci			geo_ch->flags |= ch->ht40_extension_channel;
34988c2ecf20Sopenharmony_ci
34998c2ecf20Sopenharmony_ci			if (ch->max_power_avg > max_tx_power)
35008c2ecf20Sopenharmony_ci				max_tx_power = ch->max_power_avg;
35018c2ecf20Sopenharmony_ci		} else {
35028c2ecf20Sopenharmony_ci			geo_ch->flags |= IEEE80211_CHAN_DISABLED;
35038c2ecf20Sopenharmony_ci		}
35048c2ecf20Sopenharmony_ci
35058c2ecf20Sopenharmony_ci		D_INFO("Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", ch->channel,
35068c2ecf20Sopenharmony_ci		       geo_ch->center_freq,
35078c2ecf20Sopenharmony_ci		       il_is_channel_a_band(ch) ? "5.2" : "2.4",
35088c2ecf20Sopenharmony_ci		       geo_ch->
35098c2ecf20Sopenharmony_ci		       flags & IEEE80211_CHAN_DISABLED ? "restricted" : "valid",
35108c2ecf20Sopenharmony_ci		       geo_ch->flags);
35118c2ecf20Sopenharmony_ci	}
35128c2ecf20Sopenharmony_ci
35138c2ecf20Sopenharmony_ci	il->tx_power_device_lmt = max_tx_power;
35148c2ecf20Sopenharmony_ci	il->tx_power_user_lmt = max_tx_power;
35158c2ecf20Sopenharmony_ci	il->tx_power_next = max_tx_power;
35168c2ecf20Sopenharmony_ci
35178c2ecf20Sopenharmony_ci	if (il->bands[NL80211_BAND_5GHZ].n_channels == 0 &&
35188c2ecf20Sopenharmony_ci	    (il->cfg->sku & IL_SKU_A)) {
35198c2ecf20Sopenharmony_ci		IL_INFO("Incorrectly detected BG card as ABG. "
35208c2ecf20Sopenharmony_ci			"Please send your PCI ID 0x%04X:0x%04X to maintainer.\n",
35218c2ecf20Sopenharmony_ci			il->pci_dev->device, il->pci_dev->subsystem_device);
35228c2ecf20Sopenharmony_ci		il->cfg->sku &= ~IL_SKU_A;
35238c2ecf20Sopenharmony_ci	}
35248c2ecf20Sopenharmony_ci
35258c2ecf20Sopenharmony_ci	IL_INFO("Tunable channels: %d 802.11bg, %d 802.11a channels\n",
35268c2ecf20Sopenharmony_ci		il->bands[NL80211_BAND_2GHZ].n_channels,
35278c2ecf20Sopenharmony_ci		il->bands[NL80211_BAND_5GHZ].n_channels);
35288c2ecf20Sopenharmony_ci
35298c2ecf20Sopenharmony_ci	set_bit(S_GEO_CONFIGURED, &il->status);
35308c2ecf20Sopenharmony_ci
35318c2ecf20Sopenharmony_ci	return 0;
35328c2ecf20Sopenharmony_ci}
35338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_init_geos);
35348c2ecf20Sopenharmony_ci
35358c2ecf20Sopenharmony_ci/*
35368c2ecf20Sopenharmony_ci * il_free_geos - undo allocations in il_init_geos
35378c2ecf20Sopenharmony_ci */
35388c2ecf20Sopenharmony_civoid
35398c2ecf20Sopenharmony_ciil_free_geos(struct il_priv *il)
35408c2ecf20Sopenharmony_ci{
35418c2ecf20Sopenharmony_ci	kfree(il->ieee_channels);
35428c2ecf20Sopenharmony_ci	kfree(il->ieee_rates);
35438c2ecf20Sopenharmony_ci	clear_bit(S_GEO_CONFIGURED, &il->status);
35448c2ecf20Sopenharmony_ci}
35458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_free_geos);
35468c2ecf20Sopenharmony_ci
35478c2ecf20Sopenharmony_cistatic bool
35488c2ecf20Sopenharmony_ciil_is_channel_extension(struct il_priv *il, enum nl80211_band band,
35498c2ecf20Sopenharmony_ci			u16 channel, u8 extension_chan_offset)
35508c2ecf20Sopenharmony_ci{
35518c2ecf20Sopenharmony_ci	const struct il_channel_info *ch_info;
35528c2ecf20Sopenharmony_ci
35538c2ecf20Sopenharmony_ci	ch_info = il_get_channel_info(il, band, channel);
35548c2ecf20Sopenharmony_ci	if (!il_is_channel_valid(ch_info))
35558c2ecf20Sopenharmony_ci		return false;
35568c2ecf20Sopenharmony_ci
35578c2ecf20Sopenharmony_ci	if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE)
35588c2ecf20Sopenharmony_ci		return !(ch_info->
35598c2ecf20Sopenharmony_ci			 ht40_extension_channel & IEEE80211_CHAN_NO_HT40PLUS);
35608c2ecf20Sopenharmony_ci	else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW)
35618c2ecf20Sopenharmony_ci		return !(ch_info->
35628c2ecf20Sopenharmony_ci			 ht40_extension_channel & IEEE80211_CHAN_NO_HT40MINUS);
35638c2ecf20Sopenharmony_ci
35648c2ecf20Sopenharmony_ci	return false;
35658c2ecf20Sopenharmony_ci}
35668c2ecf20Sopenharmony_ci
35678c2ecf20Sopenharmony_cibool
35688c2ecf20Sopenharmony_ciil_is_ht40_tx_allowed(struct il_priv *il, struct ieee80211_sta_ht_cap *ht_cap)
35698c2ecf20Sopenharmony_ci{
35708c2ecf20Sopenharmony_ci	if (!il->ht.enabled || !il->ht.is_40mhz)
35718c2ecf20Sopenharmony_ci		return false;
35728c2ecf20Sopenharmony_ci
35738c2ecf20Sopenharmony_ci	/*
35748c2ecf20Sopenharmony_ci	 * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40
35758c2ecf20Sopenharmony_ci	 * the bit will not set if it is pure 40MHz case
35768c2ecf20Sopenharmony_ci	 */
35778c2ecf20Sopenharmony_ci	if (ht_cap && !ht_cap->ht_supported)
35788c2ecf20Sopenharmony_ci		return false;
35798c2ecf20Sopenharmony_ci
35808c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUGFS
35818c2ecf20Sopenharmony_ci	if (il->disable_ht40)
35828c2ecf20Sopenharmony_ci		return false;
35838c2ecf20Sopenharmony_ci#endif
35848c2ecf20Sopenharmony_ci
35858c2ecf20Sopenharmony_ci	return il_is_channel_extension(il, il->band,
35868c2ecf20Sopenharmony_ci				       le16_to_cpu(il->staging.channel),
35878c2ecf20Sopenharmony_ci				       il->ht.extension_chan_offset);
35888c2ecf20Sopenharmony_ci}
35898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_is_ht40_tx_allowed);
35908c2ecf20Sopenharmony_ci
35918c2ecf20Sopenharmony_cistatic u16 noinline
35928c2ecf20Sopenharmony_ciil_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val)
35938c2ecf20Sopenharmony_ci{
35948c2ecf20Sopenharmony_ci	u16 new_val;
35958c2ecf20Sopenharmony_ci	u16 beacon_factor;
35968c2ecf20Sopenharmony_ci
35978c2ecf20Sopenharmony_ci	/*
35988c2ecf20Sopenharmony_ci	 * If mac80211 hasn't given us a beacon interval, program
35998c2ecf20Sopenharmony_ci	 * the default into the device.
36008c2ecf20Sopenharmony_ci	 */
36018c2ecf20Sopenharmony_ci	if (!beacon_val)
36028c2ecf20Sopenharmony_ci		return DEFAULT_BEACON_INTERVAL;
36038c2ecf20Sopenharmony_ci
36048c2ecf20Sopenharmony_ci	/*
36058c2ecf20Sopenharmony_ci	 * If the beacon interval we obtained from the peer
36068c2ecf20Sopenharmony_ci	 * is too large, we'll have to wake up more often
36078c2ecf20Sopenharmony_ci	 * (and in IBSS case, we'll beacon too much)
36088c2ecf20Sopenharmony_ci	 *
36098c2ecf20Sopenharmony_ci	 * For example, if max_beacon_val is 4096, and the
36108c2ecf20Sopenharmony_ci	 * requested beacon interval is 7000, we'll have to
36118c2ecf20Sopenharmony_ci	 * use 3500 to be able to wake up on the beacons.
36128c2ecf20Sopenharmony_ci	 *
36138c2ecf20Sopenharmony_ci	 * This could badly influence beacon detection stats.
36148c2ecf20Sopenharmony_ci	 */
36158c2ecf20Sopenharmony_ci
36168c2ecf20Sopenharmony_ci	beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val;
36178c2ecf20Sopenharmony_ci	new_val = beacon_val / beacon_factor;
36188c2ecf20Sopenharmony_ci
36198c2ecf20Sopenharmony_ci	if (!new_val)
36208c2ecf20Sopenharmony_ci		new_val = max_beacon_val;
36218c2ecf20Sopenharmony_ci
36228c2ecf20Sopenharmony_ci	return new_val;
36238c2ecf20Sopenharmony_ci}
36248c2ecf20Sopenharmony_ci
36258c2ecf20Sopenharmony_ciint
36268c2ecf20Sopenharmony_ciil_send_rxon_timing(struct il_priv *il)
36278c2ecf20Sopenharmony_ci{
36288c2ecf20Sopenharmony_ci	u64 tsf;
36298c2ecf20Sopenharmony_ci	s32 interval_tm, rem;
36308c2ecf20Sopenharmony_ci	struct ieee80211_conf *conf = NULL;
36318c2ecf20Sopenharmony_ci	u16 beacon_int;
36328c2ecf20Sopenharmony_ci	struct ieee80211_vif *vif = il->vif;
36338c2ecf20Sopenharmony_ci
36348c2ecf20Sopenharmony_ci	conf = &il->hw->conf;
36358c2ecf20Sopenharmony_ci
36368c2ecf20Sopenharmony_ci	lockdep_assert_held(&il->mutex);
36378c2ecf20Sopenharmony_ci
36388c2ecf20Sopenharmony_ci	memset(&il->timing, 0, sizeof(struct il_rxon_time_cmd));
36398c2ecf20Sopenharmony_ci
36408c2ecf20Sopenharmony_ci	il->timing.timestamp = cpu_to_le64(il->timestamp);
36418c2ecf20Sopenharmony_ci	il->timing.listen_interval = cpu_to_le16(conf->listen_interval);
36428c2ecf20Sopenharmony_ci
36438c2ecf20Sopenharmony_ci	beacon_int = vif ? vif->bss_conf.beacon_int : 0;
36448c2ecf20Sopenharmony_ci
36458c2ecf20Sopenharmony_ci	/*
36468c2ecf20Sopenharmony_ci	 * TODO: For IBSS we need to get atim_win from mac80211,
36478c2ecf20Sopenharmony_ci	 *       for now just always use 0
36488c2ecf20Sopenharmony_ci	 */
36498c2ecf20Sopenharmony_ci	il->timing.atim_win = 0;
36508c2ecf20Sopenharmony_ci
36518c2ecf20Sopenharmony_ci	beacon_int =
36528c2ecf20Sopenharmony_ci	    il_adjust_beacon_interval(beacon_int,
36538c2ecf20Sopenharmony_ci				      il->hw_params.max_beacon_itrvl *
36548c2ecf20Sopenharmony_ci				      TIME_UNIT);
36558c2ecf20Sopenharmony_ci	il->timing.beacon_interval = cpu_to_le16(beacon_int);
36568c2ecf20Sopenharmony_ci
36578c2ecf20Sopenharmony_ci	tsf = il->timestamp;	/* tsf is modifed by do_div: copy it */
36588c2ecf20Sopenharmony_ci	interval_tm = beacon_int * TIME_UNIT;
36598c2ecf20Sopenharmony_ci	rem = do_div(tsf, interval_tm);
36608c2ecf20Sopenharmony_ci	il->timing.beacon_init_val = cpu_to_le32(interval_tm - rem);
36618c2ecf20Sopenharmony_ci
36628c2ecf20Sopenharmony_ci	il->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ? : 1) : 1;
36638c2ecf20Sopenharmony_ci
36648c2ecf20Sopenharmony_ci	D_ASSOC("beacon interval %d beacon timer %d beacon tim %d\n",
36658c2ecf20Sopenharmony_ci		le16_to_cpu(il->timing.beacon_interval),
36668c2ecf20Sopenharmony_ci		le32_to_cpu(il->timing.beacon_init_val),
36678c2ecf20Sopenharmony_ci		le16_to_cpu(il->timing.atim_win));
36688c2ecf20Sopenharmony_ci
36698c2ecf20Sopenharmony_ci	return il_send_cmd_pdu(il, C_RXON_TIMING, sizeof(il->timing),
36708c2ecf20Sopenharmony_ci			       &il->timing);
36718c2ecf20Sopenharmony_ci}
36728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_rxon_timing);
36738c2ecf20Sopenharmony_ci
36748c2ecf20Sopenharmony_civoid
36758c2ecf20Sopenharmony_ciil_set_rxon_hwcrypto(struct il_priv *il, int hw_decrypt)
36768c2ecf20Sopenharmony_ci{
36778c2ecf20Sopenharmony_ci	struct il_rxon_cmd *rxon = &il->staging;
36788c2ecf20Sopenharmony_ci
36798c2ecf20Sopenharmony_ci	if (hw_decrypt)
36808c2ecf20Sopenharmony_ci		rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK;
36818c2ecf20Sopenharmony_ci	else
36828c2ecf20Sopenharmony_ci		rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK;
36838c2ecf20Sopenharmony_ci
36848c2ecf20Sopenharmony_ci}
36858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_rxon_hwcrypto);
36868c2ecf20Sopenharmony_ci
36878c2ecf20Sopenharmony_ci/* validate RXON structure is valid */
36888c2ecf20Sopenharmony_ciint
36898c2ecf20Sopenharmony_ciil_check_rxon_cmd(struct il_priv *il)
36908c2ecf20Sopenharmony_ci{
36918c2ecf20Sopenharmony_ci	struct il_rxon_cmd *rxon = &il->staging;
36928c2ecf20Sopenharmony_ci	bool error = false;
36938c2ecf20Sopenharmony_ci
36948c2ecf20Sopenharmony_ci	if (rxon->flags & RXON_FLG_BAND_24G_MSK) {
36958c2ecf20Sopenharmony_ci		if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) {
36968c2ecf20Sopenharmony_ci			IL_WARN("check 2.4G: wrong narrow\n");
36978c2ecf20Sopenharmony_ci			error = true;
36988c2ecf20Sopenharmony_ci		}
36998c2ecf20Sopenharmony_ci		if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) {
37008c2ecf20Sopenharmony_ci			IL_WARN("check 2.4G: wrong radar\n");
37018c2ecf20Sopenharmony_ci			error = true;
37028c2ecf20Sopenharmony_ci		}
37038c2ecf20Sopenharmony_ci	} else {
37048c2ecf20Sopenharmony_ci		if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) {
37058c2ecf20Sopenharmony_ci			IL_WARN("check 5.2G: not short slot!\n");
37068c2ecf20Sopenharmony_ci			error = true;
37078c2ecf20Sopenharmony_ci		}
37088c2ecf20Sopenharmony_ci		if (rxon->flags & RXON_FLG_CCK_MSK) {
37098c2ecf20Sopenharmony_ci			IL_WARN("check 5.2G: CCK!\n");
37108c2ecf20Sopenharmony_ci			error = true;
37118c2ecf20Sopenharmony_ci		}
37128c2ecf20Sopenharmony_ci	}
37138c2ecf20Sopenharmony_ci	if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) {
37148c2ecf20Sopenharmony_ci		IL_WARN("mac/bssid mcast!\n");
37158c2ecf20Sopenharmony_ci		error = true;
37168c2ecf20Sopenharmony_ci	}
37178c2ecf20Sopenharmony_ci
37188c2ecf20Sopenharmony_ci	/* make sure basic rates 6Mbps and 1Mbps are supported */
37198c2ecf20Sopenharmony_ci	if ((rxon->ofdm_basic_rates & RATE_6M_MASK) == 0 &&
37208c2ecf20Sopenharmony_ci	    (rxon->cck_basic_rates & RATE_1M_MASK) == 0) {
37218c2ecf20Sopenharmony_ci		IL_WARN("neither 1 nor 6 are basic\n");
37228c2ecf20Sopenharmony_ci		error = true;
37238c2ecf20Sopenharmony_ci	}
37248c2ecf20Sopenharmony_ci
37258c2ecf20Sopenharmony_ci	if (le16_to_cpu(rxon->assoc_id) > 2007) {
37268c2ecf20Sopenharmony_ci		IL_WARN("aid > 2007\n");
37278c2ecf20Sopenharmony_ci		error = true;
37288c2ecf20Sopenharmony_ci	}
37298c2ecf20Sopenharmony_ci
37308c2ecf20Sopenharmony_ci	if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) ==
37318c2ecf20Sopenharmony_ci	    (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) {
37328c2ecf20Sopenharmony_ci		IL_WARN("CCK and short slot\n");
37338c2ecf20Sopenharmony_ci		error = true;
37348c2ecf20Sopenharmony_ci	}
37358c2ecf20Sopenharmony_ci
37368c2ecf20Sopenharmony_ci	if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) ==
37378c2ecf20Sopenharmony_ci	    (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) {
37388c2ecf20Sopenharmony_ci		IL_WARN("CCK and auto detect");
37398c2ecf20Sopenharmony_ci		error = true;
37408c2ecf20Sopenharmony_ci	}
37418c2ecf20Sopenharmony_ci
37428c2ecf20Sopenharmony_ci	if ((rxon->
37438c2ecf20Sopenharmony_ci	     flags & (RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK)) ==
37448c2ecf20Sopenharmony_ci	    RXON_FLG_TGG_PROTECT_MSK) {
37458c2ecf20Sopenharmony_ci		IL_WARN("TGg but no auto-detect\n");
37468c2ecf20Sopenharmony_ci		error = true;
37478c2ecf20Sopenharmony_ci	}
37488c2ecf20Sopenharmony_ci
37498c2ecf20Sopenharmony_ci	if (error)
37508c2ecf20Sopenharmony_ci		IL_WARN("Tuning to channel %d\n", le16_to_cpu(rxon->channel));
37518c2ecf20Sopenharmony_ci
37528c2ecf20Sopenharmony_ci	if (error) {
37538c2ecf20Sopenharmony_ci		IL_ERR("Invalid RXON\n");
37548c2ecf20Sopenharmony_ci		return -EINVAL;
37558c2ecf20Sopenharmony_ci	}
37568c2ecf20Sopenharmony_ci	return 0;
37578c2ecf20Sopenharmony_ci}
37588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_check_rxon_cmd);
37598c2ecf20Sopenharmony_ci
37608c2ecf20Sopenharmony_ci/*
37618c2ecf20Sopenharmony_ci * il_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed
37628c2ecf20Sopenharmony_ci * @il: staging_rxon is compared to active_rxon
37638c2ecf20Sopenharmony_ci *
37648c2ecf20Sopenharmony_ci * If the RXON structure is changing enough to require a new tune,
37658c2ecf20Sopenharmony_ci * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that
37668c2ecf20Sopenharmony_ci * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required.
37678c2ecf20Sopenharmony_ci */
37688c2ecf20Sopenharmony_ciint
37698c2ecf20Sopenharmony_ciil_full_rxon_required(struct il_priv *il)
37708c2ecf20Sopenharmony_ci{
37718c2ecf20Sopenharmony_ci	const struct il_rxon_cmd *staging = &il->staging;
37728c2ecf20Sopenharmony_ci	const struct il_rxon_cmd *active = &il->active;
37738c2ecf20Sopenharmony_ci
37748c2ecf20Sopenharmony_ci#define CHK(cond)							\
37758c2ecf20Sopenharmony_ci	if ((cond)) {							\
37768c2ecf20Sopenharmony_ci		D_INFO("need full RXON - " #cond "\n");	\
37778c2ecf20Sopenharmony_ci		return 1;						\
37788c2ecf20Sopenharmony_ci	}
37798c2ecf20Sopenharmony_ci
37808c2ecf20Sopenharmony_ci#define CHK_NEQ(c1, c2)						\
37818c2ecf20Sopenharmony_ci	if ((c1) != (c2)) {					\
37828c2ecf20Sopenharmony_ci		D_INFO("need full RXON - "	\
37838c2ecf20Sopenharmony_ci			       #c1 " != " #c2 " - %d != %d\n",	\
37848c2ecf20Sopenharmony_ci			       (c1), (c2));			\
37858c2ecf20Sopenharmony_ci		return 1;					\
37868c2ecf20Sopenharmony_ci	}
37878c2ecf20Sopenharmony_ci
37888c2ecf20Sopenharmony_ci	/* These items are only settable from the full RXON command */
37898c2ecf20Sopenharmony_ci	CHK(!il_is_associated(il));
37908c2ecf20Sopenharmony_ci	CHK(!ether_addr_equal_64bits(staging->bssid_addr, active->bssid_addr));
37918c2ecf20Sopenharmony_ci	CHK(!ether_addr_equal_64bits(staging->node_addr, active->node_addr));
37928c2ecf20Sopenharmony_ci	CHK(!ether_addr_equal_64bits(staging->wlap_bssid_addr,
37938c2ecf20Sopenharmony_ci				     active->wlap_bssid_addr));
37948c2ecf20Sopenharmony_ci	CHK_NEQ(staging->dev_type, active->dev_type);
37958c2ecf20Sopenharmony_ci	CHK_NEQ(staging->channel, active->channel);
37968c2ecf20Sopenharmony_ci	CHK_NEQ(staging->air_propagation, active->air_propagation);
37978c2ecf20Sopenharmony_ci	CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates,
37988c2ecf20Sopenharmony_ci		active->ofdm_ht_single_stream_basic_rates);
37998c2ecf20Sopenharmony_ci	CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates,
38008c2ecf20Sopenharmony_ci		active->ofdm_ht_dual_stream_basic_rates);
38018c2ecf20Sopenharmony_ci	CHK_NEQ(staging->assoc_id, active->assoc_id);
38028c2ecf20Sopenharmony_ci
38038c2ecf20Sopenharmony_ci	/* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can
38048c2ecf20Sopenharmony_ci	 * be updated with the RXON_ASSOC command -- however only some
38058c2ecf20Sopenharmony_ci	 * flag transitions are allowed using RXON_ASSOC */
38068c2ecf20Sopenharmony_ci
38078c2ecf20Sopenharmony_ci	/* Check if we are not switching bands */
38088c2ecf20Sopenharmony_ci	CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK,
38098c2ecf20Sopenharmony_ci		active->flags & RXON_FLG_BAND_24G_MSK);
38108c2ecf20Sopenharmony_ci
38118c2ecf20Sopenharmony_ci	/* Check if we are switching association toggle */
38128c2ecf20Sopenharmony_ci	CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK,
38138c2ecf20Sopenharmony_ci		active->filter_flags & RXON_FILTER_ASSOC_MSK);
38148c2ecf20Sopenharmony_ci
38158c2ecf20Sopenharmony_ci#undef CHK
38168c2ecf20Sopenharmony_ci#undef CHK_NEQ
38178c2ecf20Sopenharmony_ci
38188c2ecf20Sopenharmony_ci	return 0;
38198c2ecf20Sopenharmony_ci}
38208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_full_rxon_required);
38218c2ecf20Sopenharmony_ci
38228c2ecf20Sopenharmony_ciu8
38238c2ecf20Sopenharmony_ciil_get_lowest_plcp(struct il_priv *il)
38248c2ecf20Sopenharmony_ci{
38258c2ecf20Sopenharmony_ci	/*
38268c2ecf20Sopenharmony_ci	 * Assign the lowest rate -- should really get this from
38278c2ecf20Sopenharmony_ci	 * the beacon skb from mac80211.
38288c2ecf20Sopenharmony_ci	 */
38298c2ecf20Sopenharmony_ci	if (il->staging.flags & RXON_FLG_BAND_24G_MSK)
38308c2ecf20Sopenharmony_ci		return RATE_1M_PLCP;
38318c2ecf20Sopenharmony_ci	else
38328c2ecf20Sopenharmony_ci		return RATE_6M_PLCP;
38338c2ecf20Sopenharmony_ci}
38348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_lowest_plcp);
38358c2ecf20Sopenharmony_ci
38368c2ecf20Sopenharmony_cistatic void
38378c2ecf20Sopenharmony_ci_il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf)
38388c2ecf20Sopenharmony_ci{
38398c2ecf20Sopenharmony_ci	struct il_rxon_cmd *rxon = &il->staging;
38408c2ecf20Sopenharmony_ci
38418c2ecf20Sopenharmony_ci	if (!il->ht.enabled) {
38428c2ecf20Sopenharmony_ci		rxon->flags &=
38438c2ecf20Sopenharmony_ci		    ~(RXON_FLG_CHANNEL_MODE_MSK |
38448c2ecf20Sopenharmony_ci		      RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_HT40_PROT_MSK
38458c2ecf20Sopenharmony_ci		      | RXON_FLG_HT_PROT_MSK);
38468c2ecf20Sopenharmony_ci		return;
38478c2ecf20Sopenharmony_ci	}
38488c2ecf20Sopenharmony_ci
38498c2ecf20Sopenharmony_ci	rxon->flags |=
38508c2ecf20Sopenharmony_ci	    cpu_to_le32(il->ht.protection << RXON_FLG_HT_OPERATING_MODE_POS);
38518c2ecf20Sopenharmony_ci
38528c2ecf20Sopenharmony_ci	/* Set up channel bandwidth:
38538c2ecf20Sopenharmony_ci	 * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */
38548c2ecf20Sopenharmony_ci	/* clear the HT channel mode before set the mode */
38558c2ecf20Sopenharmony_ci	rxon->flags &=
38568c2ecf20Sopenharmony_ci	    ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
38578c2ecf20Sopenharmony_ci	if (il_is_ht40_tx_allowed(il, NULL)) {
38588c2ecf20Sopenharmony_ci		/* pure ht40 */
38598c2ecf20Sopenharmony_ci		if (il->ht.protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) {
38608c2ecf20Sopenharmony_ci			rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40;
38618c2ecf20Sopenharmony_ci			/* Note: control channel is opposite of extension channel */
38628c2ecf20Sopenharmony_ci			switch (il->ht.extension_chan_offset) {
38638c2ecf20Sopenharmony_ci			case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
38648c2ecf20Sopenharmony_ci				rxon->flags &=
38658c2ecf20Sopenharmony_ci				    ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
38668c2ecf20Sopenharmony_ci				break;
38678c2ecf20Sopenharmony_ci			case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
38688c2ecf20Sopenharmony_ci				rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
38698c2ecf20Sopenharmony_ci				break;
38708c2ecf20Sopenharmony_ci			}
38718c2ecf20Sopenharmony_ci		} else {
38728c2ecf20Sopenharmony_ci			/* Note: control channel is opposite of extension channel */
38738c2ecf20Sopenharmony_ci			switch (il->ht.extension_chan_offset) {
38748c2ecf20Sopenharmony_ci			case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
38758c2ecf20Sopenharmony_ci				rxon->flags &=
38768c2ecf20Sopenharmony_ci				    ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
38778c2ecf20Sopenharmony_ci				rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
38788c2ecf20Sopenharmony_ci				break;
38798c2ecf20Sopenharmony_ci			case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
38808c2ecf20Sopenharmony_ci				rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
38818c2ecf20Sopenharmony_ci				rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
38828c2ecf20Sopenharmony_ci				break;
38838c2ecf20Sopenharmony_ci			case IEEE80211_HT_PARAM_CHA_SEC_NONE:
38848c2ecf20Sopenharmony_ci			default:
38858c2ecf20Sopenharmony_ci				/* channel location only valid if in Mixed mode */
38868c2ecf20Sopenharmony_ci				IL_ERR("invalid extension channel offset\n");
38878c2ecf20Sopenharmony_ci				break;
38888c2ecf20Sopenharmony_ci			}
38898c2ecf20Sopenharmony_ci		}
38908c2ecf20Sopenharmony_ci	} else {
38918c2ecf20Sopenharmony_ci		rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY;
38928c2ecf20Sopenharmony_ci	}
38938c2ecf20Sopenharmony_ci
38948c2ecf20Sopenharmony_ci	if (il->ops->set_rxon_chain)
38958c2ecf20Sopenharmony_ci		il->ops->set_rxon_chain(il);
38968c2ecf20Sopenharmony_ci
38978c2ecf20Sopenharmony_ci	D_ASSOC("rxon flags 0x%X operation mode :0x%X "
38988c2ecf20Sopenharmony_ci		"extension channel offset 0x%x\n", le32_to_cpu(rxon->flags),
38998c2ecf20Sopenharmony_ci		il->ht.protection, il->ht.extension_chan_offset);
39008c2ecf20Sopenharmony_ci}
39018c2ecf20Sopenharmony_ci
39028c2ecf20Sopenharmony_civoid
39038c2ecf20Sopenharmony_ciil_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf)
39048c2ecf20Sopenharmony_ci{
39058c2ecf20Sopenharmony_ci	_il_set_rxon_ht(il, ht_conf);
39068c2ecf20Sopenharmony_ci}
39078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_rxon_ht);
39088c2ecf20Sopenharmony_ci
39098c2ecf20Sopenharmony_ci/* Return valid, unused, channel for a passive scan to reset the RF */
39108c2ecf20Sopenharmony_ciu8
39118c2ecf20Sopenharmony_ciil_get_single_channel_number(struct il_priv *il, enum nl80211_band band)
39128c2ecf20Sopenharmony_ci{
39138c2ecf20Sopenharmony_ci	const struct il_channel_info *ch_info;
39148c2ecf20Sopenharmony_ci	int i;
39158c2ecf20Sopenharmony_ci	u8 channel = 0;
39168c2ecf20Sopenharmony_ci	u8 min, max;
39178c2ecf20Sopenharmony_ci
39188c2ecf20Sopenharmony_ci	if (band == NL80211_BAND_5GHZ) {
39198c2ecf20Sopenharmony_ci		min = 14;
39208c2ecf20Sopenharmony_ci		max = il->channel_count;
39218c2ecf20Sopenharmony_ci	} else {
39228c2ecf20Sopenharmony_ci		min = 0;
39238c2ecf20Sopenharmony_ci		max = 14;
39248c2ecf20Sopenharmony_ci	}
39258c2ecf20Sopenharmony_ci
39268c2ecf20Sopenharmony_ci	for (i = min; i < max; i++) {
39278c2ecf20Sopenharmony_ci		channel = il->channel_info[i].channel;
39288c2ecf20Sopenharmony_ci		if (channel == le16_to_cpu(il->staging.channel))
39298c2ecf20Sopenharmony_ci			continue;
39308c2ecf20Sopenharmony_ci
39318c2ecf20Sopenharmony_ci		ch_info = il_get_channel_info(il, band, channel);
39328c2ecf20Sopenharmony_ci		if (il_is_channel_valid(ch_info))
39338c2ecf20Sopenharmony_ci			break;
39348c2ecf20Sopenharmony_ci	}
39358c2ecf20Sopenharmony_ci
39368c2ecf20Sopenharmony_ci	return channel;
39378c2ecf20Sopenharmony_ci}
39388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_get_single_channel_number);
39398c2ecf20Sopenharmony_ci
39408c2ecf20Sopenharmony_ci/*
39418c2ecf20Sopenharmony_ci * il_set_rxon_channel - Set the band and channel values in staging RXON
39428c2ecf20Sopenharmony_ci * @ch: requested channel as a pointer to struct ieee80211_channel
39438c2ecf20Sopenharmony_ci
39448c2ecf20Sopenharmony_ci * NOTE:  Does not commit to the hardware; it sets appropriate bit fields
39458c2ecf20Sopenharmony_ci * in the staging RXON flag structure based on the ch->band
39468c2ecf20Sopenharmony_ci */
39478c2ecf20Sopenharmony_ciint
39488c2ecf20Sopenharmony_ciil_set_rxon_channel(struct il_priv *il, struct ieee80211_channel *ch)
39498c2ecf20Sopenharmony_ci{
39508c2ecf20Sopenharmony_ci	enum nl80211_band band = ch->band;
39518c2ecf20Sopenharmony_ci	u16 channel = ch->hw_value;
39528c2ecf20Sopenharmony_ci
39538c2ecf20Sopenharmony_ci	if (le16_to_cpu(il->staging.channel) == channel && il->band == band)
39548c2ecf20Sopenharmony_ci		return 0;
39558c2ecf20Sopenharmony_ci
39568c2ecf20Sopenharmony_ci	il->staging.channel = cpu_to_le16(channel);
39578c2ecf20Sopenharmony_ci	if (band == NL80211_BAND_5GHZ)
39588c2ecf20Sopenharmony_ci		il->staging.flags &= ~RXON_FLG_BAND_24G_MSK;
39598c2ecf20Sopenharmony_ci	else
39608c2ecf20Sopenharmony_ci		il->staging.flags |= RXON_FLG_BAND_24G_MSK;
39618c2ecf20Sopenharmony_ci
39628c2ecf20Sopenharmony_ci	il->band = band;
39638c2ecf20Sopenharmony_ci
39648c2ecf20Sopenharmony_ci	D_INFO("Staging channel set to %d [%d]\n", channel, band);
39658c2ecf20Sopenharmony_ci
39668c2ecf20Sopenharmony_ci	return 0;
39678c2ecf20Sopenharmony_ci}
39688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_rxon_channel);
39698c2ecf20Sopenharmony_ci
39708c2ecf20Sopenharmony_civoid
39718c2ecf20Sopenharmony_ciil_set_flags_for_band(struct il_priv *il, enum nl80211_band band,
39728c2ecf20Sopenharmony_ci		      struct ieee80211_vif *vif)
39738c2ecf20Sopenharmony_ci{
39748c2ecf20Sopenharmony_ci	if (band == NL80211_BAND_5GHZ) {
39758c2ecf20Sopenharmony_ci		il->staging.flags &=
39768c2ecf20Sopenharmony_ci		    ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK |
39778c2ecf20Sopenharmony_ci		      RXON_FLG_CCK_MSK);
39788c2ecf20Sopenharmony_ci		il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
39798c2ecf20Sopenharmony_ci	} else {
39808c2ecf20Sopenharmony_ci		/* Copied from il_post_associate() */
39818c2ecf20Sopenharmony_ci		if (vif && vif->bss_conf.use_short_slot)
39828c2ecf20Sopenharmony_ci			il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
39838c2ecf20Sopenharmony_ci		else
39848c2ecf20Sopenharmony_ci			il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
39858c2ecf20Sopenharmony_ci
39868c2ecf20Sopenharmony_ci		il->staging.flags |= RXON_FLG_BAND_24G_MSK;
39878c2ecf20Sopenharmony_ci		il->staging.flags |= RXON_FLG_AUTO_DETECT_MSK;
39888c2ecf20Sopenharmony_ci		il->staging.flags &= ~RXON_FLG_CCK_MSK;
39898c2ecf20Sopenharmony_ci	}
39908c2ecf20Sopenharmony_ci}
39918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_flags_for_band);
39928c2ecf20Sopenharmony_ci
39938c2ecf20Sopenharmony_ci/*
39948c2ecf20Sopenharmony_ci * initialize rxon structure with default values from eeprom
39958c2ecf20Sopenharmony_ci */
39968c2ecf20Sopenharmony_civoid
39978c2ecf20Sopenharmony_ciil_connection_init_rx_config(struct il_priv *il)
39988c2ecf20Sopenharmony_ci{
39998c2ecf20Sopenharmony_ci	const struct il_channel_info *ch_info;
40008c2ecf20Sopenharmony_ci
40018c2ecf20Sopenharmony_ci	memset(&il->staging, 0, sizeof(il->staging));
40028c2ecf20Sopenharmony_ci
40038c2ecf20Sopenharmony_ci	switch (il->iw_mode) {
40048c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_UNSPECIFIED:
40058c2ecf20Sopenharmony_ci		il->staging.dev_type = RXON_DEV_TYPE_ESS;
40068c2ecf20Sopenharmony_ci		break;
40078c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
40088c2ecf20Sopenharmony_ci		il->staging.dev_type = RXON_DEV_TYPE_ESS;
40098c2ecf20Sopenharmony_ci		il->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK;
40108c2ecf20Sopenharmony_ci		break;
40118c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
40128c2ecf20Sopenharmony_ci		il->staging.dev_type = RXON_DEV_TYPE_IBSS;
40138c2ecf20Sopenharmony_ci		il->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK;
40148c2ecf20Sopenharmony_ci		il->staging.filter_flags =
40158c2ecf20Sopenharmony_ci		    RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK;
40168c2ecf20Sopenharmony_ci		break;
40178c2ecf20Sopenharmony_ci	default:
40188c2ecf20Sopenharmony_ci		IL_ERR("Unsupported interface type %d\n", il->vif->type);
40198c2ecf20Sopenharmony_ci		return;
40208c2ecf20Sopenharmony_ci	}
40218c2ecf20Sopenharmony_ci
40228c2ecf20Sopenharmony_ci#if 0
40238c2ecf20Sopenharmony_ci	/* TODO:  Figure out when short_preamble would be set and cache from
40248c2ecf20Sopenharmony_ci	 * that */
40258c2ecf20Sopenharmony_ci	if (!hw_to_local(il->hw)->short_preamble)
40268c2ecf20Sopenharmony_ci		il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
40278c2ecf20Sopenharmony_ci	else
40288c2ecf20Sopenharmony_ci		il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
40298c2ecf20Sopenharmony_ci#endif
40308c2ecf20Sopenharmony_ci
40318c2ecf20Sopenharmony_ci	ch_info =
40328c2ecf20Sopenharmony_ci	    il_get_channel_info(il, il->band, le16_to_cpu(il->active.channel));
40338c2ecf20Sopenharmony_ci
40348c2ecf20Sopenharmony_ci	if (!ch_info)
40358c2ecf20Sopenharmony_ci		ch_info = &il->channel_info[0];
40368c2ecf20Sopenharmony_ci
40378c2ecf20Sopenharmony_ci	il->staging.channel = cpu_to_le16(ch_info->channel);
40388c2ecf20Sopenharmony_ci	il->band = ch_info->band;
40398c2ecf20Sopenharmony_ci
40408c2ecf20Sopenharmony_ci	il_set_flags_for_band(il, il->band, il->vif);
40418c2ecf20Sopenharmony_ci
40428c2ecf20Sopenharmony_ci	il->staging.ofdm_basic_rates =
40438c2ecf20Sopenharmony_ci	    (IL_OFDM_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF;
40448c2ecf20Sopenharmony_ci	il->staging.cck_basic_rates =
40458c2ecf20Sopenharmony_ci	    (IL_CCK_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF;
40468c2ecf20Sopenharmony_ci
40478c2ecf20Sopenharmony_ci	/* clear both MIX and PURE40 mode flag */
40488c2ecf20Sopenharmony_ci	il->staging.flags &=
40498c2ecf20Sopenharmony_ci	    ~(RXON_FLG_CHANNEL_MODE_MIXED | RXON_FLG_CHANNEL_MODE_PURE_40);
40508c2ecf20Sopenharmony_ci	if (il->vif)
40518c2ecf20Sopenharmony_ci		memcpy(il->staging.node_addr, il->vif->addr, ETH_ALEN);
40528c2ecf20Sopenharmony_ci
40538c2ecf20Sopenharmony_ci	il->staging.ofdm_ht_single_stream_basic_rates = 0xff;
40548c2ecf20Sopenharmony_ci	il->staging.ofdm_ht_dual_stream_basic_rates = 0xff;
40558c2ecf20Sopenharmony_ci}
40568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_connection_init_rx_config);
40578c2ecf20Sopenharmony_ci
40588c2ecf20Sopenharmony_civoid
40598c2ecf20Sopenharmony_ciil_set_rate(struct il_priv *il)
40608c2ecf20Sopenharmony_ci{
40618c2ecf20Sopenharmony_ci	const struct ieee80211_supported_band *hw = NULL;
40628c2ecf20Sopenharmony_ci	struct ieee80211_rate *rate;
40638c2ecf20Sopenharmony_ci	int i;
40648c2ecf20Sopenharmony_ci
40658c2ecf20Sopenharmony_ci	hw = il_get_hw_mode(il, il->band);
40668c2ecf20Sopenharmony_ci	if (!hw) {
40678c2ecf20Sopenharmony_ci		IL_ERR("Failed to set rate: unable to get hw mode\n");
40688c2ecf20Sopenharmony_ci		return;
40698c2ecf20Sopenharmony_ci	}
40708c2ecf20Sopenharmony_ci
40718c2ecf20Sopenharmony_ci	il->active_rate = 0;
40728c2ecf20Sopenharmony_ci
40738c2ecf20Sopenharmony_ci	for (i = 0; i < hw->n_bitrates; i++) {
40748c2ecf20Sopenharmony_ci		rate = &(hw->bitrates[i]);
40758c2ecf20Sopenharmony_ci		if (rate->hw_value < RATE_COUNT_LEGACY)
40768c2ecf20Sopenharmony_ci			il->active_rate |= (1 << rate->hw_value);
40778c2ecf20Sopenharmony_ci	}
40788c2ecf20Sopenharmony_ci
40798c2ecf20Sopenharmony_ci	D_RATE("Set active_rate = %0x\n", il->active_rate);
40808c2ecf20Sopenharmony_ci
40818c2ecf20Sopenharmony_ci	il->staging.cck_basic_rates =
40828c2ecf20Sopenharmony_ci	    (IL_CCK_BASIC_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF;
40838c2ecf20Sopenharmony_ci
40848c2ecf20Sopenharmony_ci	il->staging.ofdm_basic_rates =
40858c2ecf20Sopenharmony_ci	    (IL_OFDM_BASIC_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF;
40868c2ecf20Sopenharmony_ci}
40878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_rate);
40888c2ecf20Sopenharmony_ci
40898c2ecf20Sopenharmony_civoid
40908c2ecf20Sopenharmony_ciil_chswitch_done(struct il_priv *il, bool is_success)
40918c2ecf20Sopenharmony_ci{
40928c2ecf20Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status))
40938c2ecf20Sopenharmony_ci		return;
40948c2ecf20Sopenharmony_ci
40958c2ecf20Sopenharmony_ci	if (test_and_clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status))
40968c2ecf20Sopenharmony_ci		ieee80211_chswitch_done(il->vif, is_success);
40978c2ecf20Sopenharmony_ci}
40988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_chswitch_done);
40998c2ecf20Sopenharmony_ci
41008c2ecf20Sopenharmony_civoid
41018c2ecf20Sopenharmony_ciil_hdl_csa(struct il_priv *il, struct il_rx_buf *rxb)
41028c2ecf20Sopenharmony_ci{
41038c2ecf20Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
41048c2ecf20Sopenharmony_ci	struct il_csa_notification *csa = &(pkt->u.csa_notif);
41058c2ecf20Sopenharmony_ci	struct il_rxon_cmd *rxon = (void *)&il->active;
41068c2ecf20Sopenharmony_ci
41078c2ecf20Sopenharmony_ci	if (!test_bit(S_CHANNEL_SWITCH_PENDING, &il->status))
41088c2ecf20Sopenharmony_ci		return;
41098c2ecf20Sopenharmony_ci
41108c2ecf20Sopenharmony_ci	if (!le32_to_cpu(csa->status) && csa->channel == il->switch_channel) {
41118c2ecf20Sopenharmony_ci		rxon->channel = csa->channel;
41128c2ecf20Sopenharmony_ci		il->staging.channel = csa->channel;
41138c2ecf20Sopenharmony_ci		D_11H("CSA notif: channel %d\n", le16_to_cpu(csa->channel));
41148c2ecf20Sopenharmony_ci		il_chswitch_done(il, true);
41158c2ecf20Sopenharmony_ci	} else {
41168c2ecf20Sopenharmony_ci		IL_ERR("CSA notif (fail) : channel %d\n",
41178c2ecf20Sopenharmony_ci		       le16_to_cpu(csa->channel));
41188c2ecf20Sopenharmony_ci		il_chswitch_done(il, false);
41198c2ecf20Sopenharmony_ci	}
41208c2ecf20Sopenharmony_ci}
41218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_hdl_csa);
41228c2ecf20Sopenharmony_ci
41238c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
41248c2ecf20Sopenharmony_civoid
41258c2ecf20Sopenharmony_ciil_print_rx_config_cmd(struct il_priv *il)
41268c2ecf20Sopenharmony_ci{
41278c2ecf20Sopenharmony_ci	struct il_rxon_cmd *rxon = &il->staging;
41288c2ecf20Sopenharmony_ci
41298c2ecf20Sopenharmony_ci	D_RADIO("RX CONFIG:\n");
41308c2ecf20Sopenharmony_ci	il_print_hex_dump(il, IL_DL_RADIO, (u8 *) rxon, sizeof(*rxon));
41318c2ecf20Sopenharmony_ci	D_RADIO("u16 channel: 0x%x\n", le16_to_cpu(rxon->channel));
41328c2ecf20Sopenharmony_ci	D_RADIO("u32 flags: 0x%08X\n", le32_to_cpu(rxon->flags));
41338c2ecf20Sopenharmony_ci	D_RADIO("u32 filter_flags: 0x%08x\n", le32_to_cpu(rxon->filter_flags));
41348c2ecf20Sopenharmony_ci	D_RADIO("u8 dev_type: 0x%x\n", rxon->dev_type);
41358c2ecf20Sopenharmony_ci	D_RADIO("u8 ofdm_basic_rates: 0x%02x\n", rxon->ofdm_basic_rates);
41368c2ecf20Sopenharmony_ci	D_RADIO("u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates);
41378c2ecf20Sopenharmony_ci	D_RADIO("u8[6] node_addr: %pM\n", rxon->node_addr);
41388c2ecf20Sopenharmony_ci	D_RADIO("u8[6] bssid_addr: %pM\n", rxon->bssid_addr);
41398c2ecf20Sopenharmony_ci	D_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id));
41408c2ecf20Sopenharmony_ci}
41418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_print_rx_config_cmd);
41428c2ecf20Sopenharmony_ci#endif
41438c2ecf20Sopenharmony_ci/*
41448c2ecf20Sopenharmony_ci * il_irq_handle_error - called for HW or SW error interrupt from card
41458c2ecf20Sopenharmony_ci */
41468c2ecf20Sopenharmony_civoid
41478c2ecf20Sopenharmony_ciil_irq_handle_error(struct il_priv *il)
41488c2ecf20Sopenharmony_ci{
41498c2ecf20Sopenharmony_ci	/* Set the FW error flag -- cleared on il_down */
41508c2ecf20Sopenharmony_ci	set_bit(S_FW_ERROR, &il->status);
41518c2ecf20Sopenharmony_ci
41528c2ecf20Sopenharmony_ci	/* Cancel currently queued command. */
41538c2ecf20Sopenharmony_ci	clear_bit(S_HCMD_ACTIVE, &il->status);
41548c2ecf20Sopenharmony_ci
41558c2ecf20Sopenharmony_ci	IL_ERR("Loaded firmware version: %s\n", il->hw->wiphy->fw_version);
41568c2ecf20Sopenharmony_ci
41578c2ecf20Sopenharmony_ci	il->ops->dump_nic_error_log(il);
41588c2ecf20Sopenharmony_ci	if (il->ops->dump_fh)
41598c2ecf20Sopenharmony_ci		il->ops->dump_fh(il, NULL, false);
41608c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
41618c2ecf20Sopenharmony_ci	if (il_get_debug_level(il) & IL_DL_FW_ERRORS)
41628c2ecf20Sopenharmony_ci		il_print_rx_config_cmd(il);
41638c2ecf20Sopenharmony_ci#endif
41648c2ecf20Sopenharmony_ci
41658c2ecf20Sopenharmony_ci	wake_up(&il->wait_command_queue);
41668c2ecf20Sopenharmony_ci
41678c2ecf20Sopenharmony_ci	/* Keep the restart process from trying to send host
41688c2ecf20Sopenharmony_ci	 * commands by clearing the INIT status bit */
41698c2ecf20Sopenharmony_ci	clear_bit(S_READY, &il->status);
41708c2ecf20Sopenharmony_ci
41718c2ecf20Sopenharmony_ci	if (!test_bit(S_EXIT_PENDING, &il->status)) {
41728c2ecf20Sopenharmony_ci		IL_DBG(IL_DL_FW_ERRORS,
41738c2ecf20Sopenharmony_ci		       "Restarting adapter due to uCode error.\n");
41748c2ecf20Sopenharmony_ci
41758c2ecf20Sopenharmony_ci		if (il->cfg->mod_params->restart_fw)
41768c2ecf20Sopenharmony_ci			queue_work(il->workqueue, &il->restart);
41778c2ecf20Sopenharmony_ci	}
41788c2ecf20Sopenharmony_ci}
41798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_irq_handle_error);
41808c2ecf20Sopenharmony_ci
41818c2ecf20Sopenharmony_cistatic int
41828c2ecf20Sopenharmony_ci_il_apm_stop_master(struct il_priv *il)
41838c2ecf20Sopenharmony_ci{
41848c2ecf20Sopenharmony_ci	int ret = 0;
41858c2ecf20Sopenharmony_ci
41868c2ecf20Sopenharmony_ci	/* stop device's busmaster DMA activity */
41878c2ecf20Sopenharmony_ci	_il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
41888c2ecf20Sopenharmony_ci
41898c2ecf20Sopenharmony_ci	ret =
41908c2ecf20Sopenharmony_ci	    _il_poll_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED,
41918c2ecf20Sopenharmony_ci			 CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
41928c2ecf20Sopenharmony_ci	if (ret < 0)
41938c2ecf20Sopenharmony_ci		IL_WARN("Master Disable Timed Out, 100 usec\n");
41948c2ecf20Sopenharmony_ci
41958c2ecf20Sopenharmony_ci	D_INFO("stop master\n");
41968c2ecf20Sopenharmony_ci
41978c2ecf20Sopenharmony_ci	return ret;
41988c2ecf20Sopenharmony_ci}
41998c2ecf20Sopenharmony_ci
42008c2ecf20Sopenharmony_civoid
42018c2ecf20Sopenharmony_ci_il_apm_stop(struct il_priv *il)
42028c2ecf20Sopenharmony_ci{
42038c2ecf20Sopenharmony_ci	lockdep_assert_held(&il->reg_lock);
42048c2ecf20Sopenharmony_ci
42058c2ecf20Sopenharmony_ci	D_INFO("Stop card, put in low power state\n");
42068c2ecf20Sopenharmony_ci
42078c2ecf20Sopenharmony_ci	/* Stop device's DMA activity */
42088c2ecf20Sopenharmony_ci	_il_apm_stop_master(il);
42098c2ecf20Sopenharmony_ci
42108c2ecf20Sopenharmony_ci	/* Reset the entire device */
42118c2ecf20Sopenharmony_ci	_il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
42128c2ecf20Sopenharmony_ci
42138c2ecf20Sopenharmony_ci	udelay(10);
42148c2ecf20Sopenharmony_ci
42158c2ecf20Sopenharmony_ci	/*
42168c2ecf20Sopenharmony_ci	 * Clear "initialization complete" bit to move adapter from
42178c2ecf20Sopenharmony_ci	 * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
42188c2ecf20Sopenharmony_ci	 */
42198c2ecf20Sopenharmony_ci	_il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
42208c2ecf20Sopenharmony_ci}
42218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(_il_apm_stop);
42228c2ecf20Sopenharmony_ci
42238c2ecf20Sopenharmony_civoid
42248c2ecf20Sopenharmony_ciil_apm_stop(struct il_priv *il)
42258c2ecf20Sopenharmony_ci{
42268c2ecf20Sopenharmony_ci	unsigned long flags;
42278c2ecf20Sopenharmony_ci
42288c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->reg_lock, flags);
42298c2ecf20Sopenharmony_ci	_il_apm_stop(il);
42308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->reg_lock, flags);
42318c2ecf20Sopenharmony_ci}
42328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_apm_stop);
42338c2ecf20Sopenharmony_ci
42348c2ecf20Sopenharmony_ci/*
42358c2ecf20Sopenharmony_ci * Start up NIC's basic functionality after it has been reset
42368c2ecf20Sopenharmony_ci * (e.g. after platform boot, or shutdown via il_apm_stop())
42378c2ecf20Sopenharmony_ci * NOTE:  This does not load uCode nor start the embedded processor
42388c2ecf20Sopenharmony_ci */
42398c2ecf20Sopenharmony_ciint
42408c2ecf20Sopenharmony_ciil_apm_init(struct il_priv *il)
42418c2ecf20Sopenharmony_ci{
42428c2ecf20Sopenharmony_ci	int ret = 0;
42438c2ecf20Sopenharmony_ci	u16 lctl;
42448c2ecf20Sopenharmony_ci
42458c2ecf20Sopenharmony_ci	D_INFO("Init card's basic functions\n");
42468c2ecf20Sopenharmony_ci
42478c2ecf20Sopenharmony_ci	/*
42488c2ecf20Sopenharmony_ci	 * Use "set_bit" below rather than "write", to preserve any hardware
42498c2ecf20Sopenharmony_ci	 * bits already set by default after reset.
42508c2ecf20Sopenharmony_ci	 */
42518c2ecf20Sopenharmony_ci
42528c2ecf20Sopenharmony_ci	/* Disable L0S exit timer (platform NMI Work/Around) */
42538c2ecf20Sopenharmony_ci	il_set_bit(il, CSR_GIO_CHICKEN_BITS,
42548c2ecf20Sopenharmony_ci		   CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
42558c2ecf20Sopenharmony_ci
42568c2ecf20Sopenharmony_ci	/*
42578c2ecf20Sopenharmony_ci	 * Disable L0s without affecting L1;
42588c2ecf20Sopenharmony_ci	 *  don't wait for ICH L0s (ICH bug W/A)
42598c2ecf20Sopenharmony_ci	 */
42608c2ecf20Sopenharmony_ci	il_set_bit(il, CSR_GIO_CHICKEN_BITS,
42618c2ecf20Sopenharmony_ci		   CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
42628c2ecf20Sopenharmony_ci
42638c2ecf20Sopenharmony_ci	/* Set FH wait threshold to maximum (HW error during stress W/A) */
42648c2ecf20Sopenharmony_ci	il_set_bit(il, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL);
42658c2ecf20Sopenharmony_ci
42668c2ecf20Sopenharmony_ci	/*
42678c2ecf20Sopenharmony_ci	 * Enable HAP INTA (interrupt from management bus) to
42688c2ecf20Sopenharmony_ci	 * wake device's PCI Express link L1a -> L0s
42698c2ecf20Sopenharmony_ci	 * NOTE:  This is no-op for 3945 (non-existent bit)
42708c2ecf20Sopenharmony_ci	 */
42718c2ecf20Sopenharmony_ci	il_set_bit(il, CSR_HW_IF_CONFIG_REG,
42728c2ecf20Sopenharmony_ci		   CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A);
42738c2ecf20Sopenharmony_ci
42748c2ecf20Sopenharmony_ci	/*
42758c2ecf20Sopenharmony_ci	 * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition.
42768c2ecf20Sopenharmony_ci	 * Check if BIOS (or OS) enabled L1-ASPM on this device.
42778c2ecf20Sopenharmony_ci	 * If so (likely), disable L0S, so device moves directly L0->L1;
42788c2ecf20Sopenharmony_ci	 *    costs negligible amount of power savings.
42798c2ecf20Sopenharmony_ci	 * If not (unlikely), enable L0S, so there is at least some
42808c2ecf20Sopenharmony_ci	 *    power savings, even without L1.
42818c2ecf20Sopenharmony_ci	 */
42828c2ecf20Sopenharmony_ci	if (il->cfg->set_l0s) {
42838c2ecf20Sopenharmony_ci		ret = pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl);
42848c2ecf20Sopenharmony_ci		if (!ret && (lctl & PCI_EXP_LNKCTL_ASPM_L1)) {
42858c2ecf20Sopenharmony_ci			/* L1-ASPM enabled; disable(!) L0S  */
42868c2ecf20Sopenharmony_ci			il_set_bit(il, CSR_GIO_REG,
42878c2ecf20Sopenharmony_ci				   CSR_GIO_REG_VAL_L0S_ENABLED);
42888c2ecf20Sopenharmony_ci			D_POWER("L1 Enabled; Disabling L0S\n");
42898c2ecf20Sopenharmony_ci		} else {
42908c2ecf20Sopenharmony_ci			/* L1-ASPM disabled; enable(!) L0S */
42918c2ecf20Sopenharmony_ci			il_clear_bit(il, CSR_GIO_REG,
42928c2ecf20Sopenharmony_ci				     CSR_GIO_REG_VAL_L0S_ENABLED);
42938c2ecf20Sopenharmony_ci			D_POWER("L1 Disabled; Enabling L0S\n");
42948c2ecf20Sopenharmony_ci		}
42958c2ecf20Sopenharmony_ci	}
42968c2ecf20Sopenharmony_ci
42978c2ecf20Sopenharmony_ci	/* Configure analog phase-lock-loop before activating to D0A */
42988c2ecf20Sopenharmony_ci	if (il->cfg->pll_cfg_val)
42998c2ecf20Sopenharmony_ci		il_set_bit(il, CSR_ANA_PLL_CFG,
43008c2ecf20Sopenharmony_ci			   il->cfg->pll_cfg_val);
43018c2ecf20Sopenharmony_ci
43028c2ecf20Sopenharmony_ci	/*
43038c2ecf20Sopenharmony_ci	 * Set "initialization complete" bit to move adapter from
43048c2ecf20Sopenharmony_ci	 * D0U* --> D0A* (powered-up active) state.
43058c2ecf20Sopenharmony_ci	 */
43068c2ecf20Sopenharmony_ci	il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
43078c2ecf20Sopenharmony_ci
43088c2ecf20Sopenharmony_ci	/*
43098c2ecf20Sopenharmony_ci	 * Wait for clock stabilization; once stabilized, access to
43108c2ecf20Sopenharmony_ci	 * device-internal resources is supported, e.g. il_wr_prph()
43118c2ecf20Sopenharmony_ci	 * and accesses to uCode SRAM.
43128c2ecf20Sopenharmony_ci	 */
43138c2ecf20Sopenharmony_ci	ret =
43148c2ecf20Sopenharmony_ci	    _il_poll_bit(il, CSR_GP_CNTRL,
43158c2ecf20Sopenharmony_ci			 CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
43168c2ecf20Sopenharmony_ci			 CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
43178c2ecf20Sopenharmony_ci	if (ret < 0) {
43188c2ecf20Sopenharmony_ci		D_INFO("Failed to init the card\n");
43198c2ecf20Sopenharmony_ci		goto out;
43208c2ecf20Sopenharmony_ci	}
43218c2ecf20Sopenharmony_ci
43228c2ecf20Sopenharmony_ci	/*
43238c2ecf20Sopenharmony_ci	 * Enable DMA and BSM (if used) clocks, wait for them to stabilize.
43248c2ecf20Sopenharmony_ci	 * BSM (Boostrap State Machine) is only in 3945 and 4965.
43258c2ecf20Sopenharmony_ci	 *
43268c2ecf20Sopenharmony_ci	 * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits
43278c2ecf20Sopenharmony_ci	 * do not disable clocks.  This preserves any hardware bits already
43288c2ecf20Sopenharmony_ci	 * set by default in "CLK_CTRL_REG" after reset.
43298c2ecf20Sopenharmony_ci	 */
43308c2ecf20Sopenharmony_ci	if (il->cfg->use_bsm)
43318c2ecf20Sopenharmony_ci		il_wr_prph(il, APMG_CLK_EN_REG,
43328c2ecf20Sopenharmony_ci			   APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT);
43338c2ecf20Sopenharmony_ci	else
43348c2ecf20Sopenharmony_ci		il_wr_prph(il, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
43358c2ecf20Sopenharmony_ci	udelay(20);
43368c2ecf20Sopenharmony_ci
43378c2ecf20Sopenharmony_ci	/* Disable L1-Active */
43388c2ecf20Sopenharmony_ci	il_set_bits_prph(il, APMG_PCIDEV_STT_REG,
43398c2ecf20Sopenharmony_ci			 APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
43408c2ecf20Sopenharmony_ci
43418c2ecf20Sopenharmony_ciout:
43428c2ecf20Sopenharmony_ci	return ret;
43438c2ecf20Sopenharmony_ci}
43448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_apm_init);
43458c2ecf20Sopenharmony_ci
43468c2ecf20Sopenharmony_ciint
43478c2ecf20Sopenharmony_ciil_set_tx_power(struct il_priv *il, s8 tx_power, bool force)
43488c2ecf20Sopenharmony_ci{
43498c2ecf20Sopenharmony_ci	int ret;
43508c2ecf20Sopenharmony_ci	s8 prev_tx_power;
43518c2ecf20Sopenharmony_ci	bool defer;
43528c2ecf20Sopenharmony_ci
43538c2ecf20Sopenharmony_ci	lockdep_assert_held(&il->mutex);
43548c2ecf20Sopenharmony_ci
43558c2ecf20Sopenharmony_ci	if (il->tx_power_user_lmt == tx_power && !force)
43568c2ecf20Sopenharmony_ci		return 0;
43578c2ecf20Sopenharmony_ci
43588c2ecf20Sopenharmony_ci	if (!il->ops->send_tx_power)
43598c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
43608c2ecf20Sopenharmony_ci
43618c2ecf20Sopenharmony_ci	/* 0 dBm mean 1 milliwatt */
43628c2ecf20Sopenharmony_ci	if (tx_power < 0) {
43638c2ecf20Sopenharmony_ci		IL_WARN("Requested user TXPOWER %d below 1 mW.\n", tx_power);
43648c2ecf20Sopenharmony_ci		return -EINVAL;
43658c2ecf20Sopenharmony_ci	}
43668c2ecf20Sopenharmony_ci
43678c2ecf20Sopenharmony_ci	if (tx_power > il->tx_power_device_lmt) {
43688c2ecf20Sopenharmony_ci		IL_WARN("Requested user TXPOWER %d above upper limit %d.\n",
43698c2ecf20Sopenharmony_ci			tx_power, il->tx_power_device_lmt);
43708c2ecf20Sopenharmony_ci		return -EINVAL;
43718c2ecf20Sopenharmony_ci	}
43728c2ecf20Sopenharmony_ci
43738c2ecf20Sopenharmony_ci	if (!il_is_ready_rf(il))
43748c2ecf20Sopenharmony_ci		return -EIO;
43758c2ecf20Sopenharmony_ci
43768c2ecf20Sopenharmony_ci	/* scan complete and commit_rxon use tx_power_next value,
43778c2ecf20Sopenharmony_ci	 * it always need to be updated for newest request */
43788c2ecf20Sopenharmony_ci	il->tx_power_next = tx_power;
43798c2ecf20Sopenharmony_ci
43808c2ecf20Sopenharmony_ci	/* do not set tx power when scanning or channel changing */
43818c2ecf20Sopenharmony_ci	defer = test_bit(S_SCANNING, &il->status) ||
43828c2ecf20Sopenharmony_ci	    memcmp(&il->active, &il->staging, sizeof(il->staging));
43838c2ecf20Sopenharmony_ci	if (defer && !force) {
43848c2ecf20Sopenharmony_ci		D_INFO("Deferring tx power set\n");
43858c2ecf20Sopenharmony_ci		return 0;
43868c2ecf20Sopenharmony_ci	}
43878c2ecf20Sopenharmony_ci
43888c2ecf20Sopenharmony_ci	prev_tx_power = il->tx_power_user_lmt;
43898c2ecf20Sopenharmony_ci	il->tx_power_user_lmt = tx_power;
43908c2ecf20Sopenharmony_ci
43918c2ecf20Sopenharmony_ci	ret = il->ops->send_tx_power(il);
43928c2ecf20Sopenharmony_ci
43938c2ecf20Sopenharmony_ci	/* if fail to set tx_power, restore the orig. tx power */
43948c2ecf20Sopenharmony_ci	if (ret) {
43958c2ecf20Sopenharmony_ci		il->tx_power_user_lmt = prev_tx_power;
43968c2ecf20Sopenharmony_ci		il->tx_power_next = prev_tx_power;
43978c2ecf20Sopenharmony_ci	}
43988c2ecf20Sopenharmony_ci	return ret;
43998c2ecf20Sopenharmony_ci}
44008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_set_tx_power);
44018c2ecf20Sopenharmony_ci
44028c2ecf20Sopenharmony_civoid
44038c2ecf20Sopenharmony_ciil_send_bt_config(struct il_priv *il)
44048c2ecf20Sopenharmony_ci{
44058c2ecf20Sopenharmony_ci	struct il_bt_cmd bt_cmd = {
44068c2ecf20Sopenharmony_ci		.lead_time = BT_LEAD_TIME_DEF,
44078c2ecf20Sopenharmony_ci		.max_kill = BT_MAX_KILL_DEF,
44088c2ecf20Sopenharmony_ci		.kill_ack_mask = 0,
44098c2ecf20Sopenharmony_ci		.kill_cts_mask = 0,
44108c2ecf20Sopenharmony_ci	};
44118c2ecf20Sopenharmony_ci
44128c2ecf20Sopenharmony_ci	if (!bt_coex_active)
44138c2ecf20Sopenharmony_ci		bt_cmd.flags = BT_COEX_DISABLE;
44148c2ecf20Sopenharmony_ci	else
44158c2ecf20Sopenharmony_ci		bt_cmd.flags = BT_COEX_ENABLE;
44168c2ecf20Sopenharmony_ci
44178c2ecf20Sopenharmony_ci	D_INFO("BT coex %s\n",
44188c2ecf20Sopenharmony_ci	       (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active");
44198c2ecf20Sopenharmony_ci
44208c2ecf20Sopenharmony_ci	if (il_send_cmd_pdu(il, C_BT_CONFIG, sizeof(struct il_bt_cmd), &bt_cmd))
44218c2ecf20Sopenharmony_ci		IL_ERR("failed to send BT Coex Config\n");
44228c2ecf20Sopenharmony_ci}
44238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_bt_config);
44248c2ecf20Sopenharmony_ci
44258c2ecf20Sopenharmony_ciint
44268c2ecf20Sopenharmony_ciil_send_stats_request(struct il_priv *il, u8 flags, bool clear)
44278c2ecf20Sopenharmony_ci{
44288c2ecf20Sopenharmony_ci	struct il_stats_cmd stats_cmd = {
44298c2ecf20Sopenharmony_ci		.configuration_flags = clear ? IL_STATS_CONF_CLEAR_STATS : 0,
44308c2ecf20Sopenharmony_ci	};
44318c2ecf20Sopenharmony_ci
44328c2ecf20Sopenharmony_ci	if (flags & CMD_ASYNC)
44338c2ecf20Sopenharmony_ci		return il_send_cmd_pdu_async(il, C_STATS, sizeof(struct il_stats_cmd),
44348c2ecf20Sopenharmony_ci					     &stats_cmd, NULL);
44358c2ecf20Sopenharmony_ci	else
44368c2ecf20Sopenharmony_ci		return il_send_cmd_pdu(il, C_STATS, sizeof(struct il_stats_cmd),
44378c2ecf20Sopenharmony_ci				       &stats_cmd);
44388c2ecf20Sopenharmony_ci}
44398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_send_stats_request);
44408c2ecf20Sopenharmony_ci
44418c2ecf20Sopenharmony_civoid
44428c2ecf20Sopenharmony_ciil_hdl_pm_sleep(struct il_priv *il, struct il_rx_buf *rxb)
44438c2ecf20Sopenharmony_ci{
44448c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLEGACY_DEBUG
44458c2ecf20Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
44468c2ecf20Sopenharmony_ci	struct il_sleep_notification *sleep = &(pkt->u.sleep_notif);
44478c2ecf20Sopenharmony_ci	D_RX("sleep mode: %d, src: %d\n",
44488c2ecf20Sopenharmony_ci	     sleep->pm_sleep_mode, sleep->pm_wakeup_src);
44498c2ecf20Sopenharmony_ci#endif
44508c2ecf20Sopenharmony_ci}
44518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_hdl_pm_sleep);
44528c2ecf20Sopenharmony_ci
44538c2ecf20Sopenharmony_civoid
44548c2ecf20Sopenharmony_ciil_hdl_pm_debug_stats(struct il_priv *il, struct il_rx_buf *rxb)
44558c2ecf20Sopenharmony_ci{
44568c2ecf20Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
44578c2ecf20Sopenharmony_ci	u32 len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK;
44588c2ecf20Sopenharmony_ci	D_RADIO("Dumping %d bytes of unhandled notification for %s:\n", len,
44598c2ecf20Sopenharmony_ci		il_get_cmd_string(pkt->hdr.cmd));
44608c2ecf20Sopenharmony_ci	il_print_hex_dump(il, IL_DL_RADIO, pkt->u.raw, len);
44618c2ecf20Sopenharmony_ci}
44628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_hdl_pm_debug_stats);
44638c2ecf20Sopenharmony_ci
44648c2ecf20Sopenharmony_civoid
44658c2ecf20Sopenharmony_ciil_hdl_error(struct il_priv *il, struct il_rx_buf *rxb)
44668c2ecf20Sopenharmony_ci{
44678c2ecf20Sopenharmony_ci	struct il_rx_pkt *pkt = rxb_addr(rxb);
44688c2ecf20Sopenharmony_ci
44698c2ecf20Sopenharmony_ci	IL_ERR("Error Reply type 0x%08X cmd %s (0x%02X) "
44708c2ecf20Sopenharmony_ci	       "seq 0x%04X ser 0x%08X\n",
44718c2ecf20Sopenharmony_ci	       le32_to_cpu(pkt->u.err_resp.error_type),
44728c2ecf20Sopenharmony_ci	       il_get_cmd_string(pkt->u.err_resp.cmd_id),
44738c2ecf20Sopenharmony_ci	       pkt->u.err_resp.cmd_id,
44748c2ecf20Sopenharmony_ci	       le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num),
44758c2ecf20Sopenharmony_ci	       le32_to_cpu(pkt->u.err_resp.error_info));
44768c2ecf20Sopenharmony_ci}
44778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_hdl_error);
44788c2ecf20Sopenharmony_ci
44798c2ecf20Sopenharmony_civoid
44808c2ecf20Sopenharmony_ciil_clear_isr_stats(struct il_priv *il)
44818c2ecf20Sopenharmony_ci{
44828c2ecf20Sopenharmony_ci	memset(&il->isr_stats, 0, sizeof(il->isr_stats));
44838c2ecf20Sopenharmony_ci}
44848c2ecf20Sopenharmony_ci
44858c2ecf20Sopenharmony_ciint
44868c2ecf20Sopenharmony_ciil_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
44878c2ecf20Sopenharmony_ci	       const struct ieee80211_tx_queue_params *params)
44888c2ecf20Sopenharmony_ci{
44898c2ecf20Sopenharmony_ci	struct il_priv *il = hw->priv;
44908c2ecf20Sopenharmony_ci	unsigned long flags;
44918c2ecf20Sopenharmony_ci	int q;
44928c2ecf20Sopenharmony_ci
44938c2ecf20Sopenharmony_ci	D_MAC80211("enter\n");
44948c2ecf20Sopenharmony_ci
44958c2ecf20Sopenharmony_ci	if (!il_is_ready_rf(il)) {
44968c2ecf20Sopenharmony_ci		D_MAC80211("leave - RF not ready\n");
44978c2ecf20Sopenharmony_ci		return -EIO;
44988c2ecf20Sopenharmony_ci	}
44998c2ecf20Sopenharmony_ci
45008c2ecf20Sopenharmony_ci	if (queue >= AC_NUM) {
45018c2ecf20Sopenharmony_ci		D_MAC80211("leave - queue >= AC_NUM %d\n", queue);
45028c2ecf20Sopenharmony_ci		return 0;
45038c2ecf20Sopenharmony_ci	}
45048c2ecf20Sopenharmony_ci
45058c2ecf20Sopenharmony_ci	q = AC_NUM - 1 - queue;
45068c2ecf20Sopenharmony_ci
45078c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
45088c2ecf20Sopenharmony_ci
45098c2ecf20Sopenharmony_ci	il->qos_data.def_qos_parm.ac[q].cw_min =
45108c2ecf20Sopenharmony_ci	    cpu_to_le16(params->cw_min);
45118c2ecf20Sopenharmony_ci	il->qos_data.def_qos_parm.ac[q].cw_max =
45128c2ecf20Sopenharmony_ci	    cpu_to_le16(params->cw_max);
45138c2ecf20Sopenharmony_ci	il->qos_data.def_qos_parm.ac[q].aifsn = params->aifs;
45148c2ecf20Sopenharmony_ci	il->qos_data.def_qos_parm.ac[q].edca_txop =
45158c2ecf20Sopenharmony_ci	    cpu_to_le16((params->txop * 32));
45168c2ecf20Sopenharmony_ci
45178c2ecf20Sopenharmony_ci	il->qos_data.def_qos_parm.ac[q].reserved1 = 0;
45188c2ecf20Sopenharmony_ci
45198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
45208c2ecf20Sopenharmony_ci
45218c2ecf20Sopenharmony_ci	D_MAC80211("leave\n");
45228c2ecf20Sopenharmony_ci	return 0;
45238c2ecf20Sopenharmony_ci}
45248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_conf_tx);
45258c2ecf20Sopenharmony_ci
45268c2ecf20Sopenharmony_ciint
45278c2ecf20Sopenharmony_ciil_mac_tx_last_beacon(struct ieee80211_hw *hw)
45288c2ecf20Sopenharmony_ci{
45298c2ecf20Sopenharmony_ci	struct il_priv *il = hw->priv;
45308c2ecf20Sopenharmony_ci	int ret;
45318c2ecf20Sopenharmony_ci
45328c2ecf20Sopenharmony_ci	D_MAC80211("enter\n");
45338c2ecf20Sopenharmony_ci
45348c2ecf20Sopenharmony_ci	ret = (il->ibss_manager == IL_IBSS_MANAGER);
45358c2ecf20Sopenharmony_ci
45368c2ecf20Sopenharmony_ci	D_MAC80211("leave ret %d\n", ret);
45378c2ecf20Sopenharmony_ci	return ret;
45388c2ecf20Sopenharmony_ci}
45398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(il_mac_tx_last_beacon);
45408c2ecf20Sopenharmony_ci
45418c2ecf20Sopenharmony_cistatic int
45428c2ecf20Sopenharmony_ciil_set_mode(struct il_priv *il)
45438c2ecf20Sopenharmony_ci{
45448c2ecf20Sopenharmony_ci	il_connection_init_rx_config(il);
45458c2ecf20Sopenharmony_ci
45468c2ecf20Sopenharmony_ci	if (il->ops->set_rxon_chain)
45478c2ecf20Sopenharmony_ci		il->ops->set_rxon_chain(il);
45488c2ecf20Sopenharmony_ci
45498c2ecf20Sopenharmony_ci	return il_commit_rxon(il);
45508c2ecf20Sopenharmony_ci}
45518c2ecf20Sopenharmony_ci
45528c2ecf20Sopenharmony_ciint
45538c2ecf20Sopenharmony_ciil_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
45548c2ecf20Sopenharmony_ci{
45558c2ecf20Sopenharmony_ci	struct il_priv *il = hw->priv;
45568c2ecf20Sopenharmony_ci	int err;
45578c2ecf20Sopenharmony_ci	bool reset;
45588c2ecf20Sopenharmony_ci
45598c2ecf20Sopenharmony_ci	mutex_lock(&il->mutex);
45608c2ecf20Sopenharmony_ci	D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr);
45618c2ecf20Sopenharmony_ci
45628c2ecf20Sopenharmony_ci	if (!il_is_ready_rf(il)) {
45638c2ecf20Sopenharmony_ci		IL_WARN("Try to add interface when device not ready\n");
45648c2ecf20Sopenharmony_ci		err = -EINVAL;
45658c2ecf20Sopenharmony_ci		goto out;
45668c2ecf20Sopenharmony_ci	}
45678c2ecf20Sopenharmony_ci
45688c2ecf20Sopenharmony_ci	/*
45698c2ecf20Sopenharmony_ci	 * We do not support multiple virtual interfaces, but on hardware reset
45708c2ecf20Sopenharmony_ci	 * we have to add the same interface again.
45718c2ecf20Sopenharmony_ci	 */
45728c2ecf20Sopenharmony_ci	reset = (il->vif == vif);
45738c2ecf20Sopenharmony_ci	if (il->vif && !reset) {
45748c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
45758c2ecf20Sopenharmony_ci		goto out;
45768c2ecf20Sopenharmony_ci	}
45778c2ecf20Sopenharmony_ci
45788c2ecf20Sopenharmony_ci	il->vif = vif;
45798c2ecf20Sopenharmony_ci	il->iw_mode = vif->type;
45808c2ecf20Sopenharmony_ci
45818c2ecf20Sopenharmony_ci	err = il_set_mode(il);
45828c2ecf20Sopenharmony_ci	if (err) {
45838c2ecf20Sopenharmony_ci		IL_WARN("Fail to set mode %d\n", vif->type);
45848c2ecf20Sopenharmony_ci		if (!reset) {
45858c2ecf20Sopenharmony_ci			il->vif = NULL;
45868c2ecf20Sopenharmony_ci			il->iw_mode = NL80211_IFTYPE_STATION;
45878c2ecf20Sopenharmony_ci		}
45888c2ecf20Sopenharmony_ci	}
45898c2ecf20Sopenharmony_ci
45908c2ecf20Sopenharmony_ciout:
45918c2ecf20Sopenharmony_ci	D_MAC80211("leave err %d\n", err);
45928c2ecf20Sopenharmony_ci	mutex_unlock(&il->mutex);
45938c2ecf20Sopenharmony_ci
45948c2ecf20Sopenharmony_ci	return err;
45958c2ecf20Sopenharmony_ci}
45968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_add_interface);
45978c2ecf20Sopenharmony_ci
45988c2ecf20Sopenharmony_cistatic void
45998c2ecf20Sopenharmony_ciil_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif)
46008c2ecf20Sopenharmony_ci{
46018c2ecf20Sopenharmony_ci	lockdep_assert_held(&il->mutex);
46028c2ecf20Sopenharmony_ci
46038c2ecf20Sopenharmony_ci	if (il->scan_vif == vif) {
46048c2ecf20Sopenharmony_ci		il_scan_cancel_timeout(il, 200);
46058c2ecf20Sopenharmony_ci		il_force_scan_end(il);
46068c2ecf20Sopenharmony_ci	}
46078c2ecf20Sopenharmony_ci
46088c2ecf20Sopenharmony_ci	il_set_mode(il);
46098c2ecf20Sopenharmony_ci}
46108c2ecf20Sopenharmony_ci
46118c2ecf20Sopenharmony_civoid
46128c2ecf20Sopenharmony_ciil_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
46138c2ecf20Sopenharmony_ci{
46148c2ecf20Sopenharmony_ci	struct il_priv *il = hw->priv;
46158c2ecf20Sopenharmony_ci
46168c2ecf20Sopenharmony_ci	mutex_lock(&il->mutex);
46178c2ecf20Sopenharmony_ci	D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr);
46188c2ecf20Sopenharmony_ci
46198c2ecf20Sopenharmony_ci	WARN_ON(il->vif != vif);
46208c2ecf20Sopenharmony_ci	il->vif = NULL;
46218c2ecf20Sopenharmony_ci	il->iw_mode = NL80211_IFTYPE_UNSPECIFIED;
46228c2ecf20Sopenharmony_ci	il_teardown_interface(il, vif);
46238c2ecf20Sopenharmony_ci	eth_zero_addr(il->bssid);
46248c2ecf20Sopenharmony_ci
46258c2ecf20Sopenharmony_ci	D_MAC80211("leave\n");
46268c2ecf20Sopenharmony_ci	mutex_unlock(&il->mutex);
46278c2ecf20Sopenharmony_ci}
46288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_remove_interface);
46298c2ecf20Sopenharmony_ci
46308c2ecf20Sopenharmony_ciint
46318c2ecf20Sopenharmony_ciil_alloc_txq_mem(struct il_priv *il)
46328c2ecf20Sopenharmony_ci{
46338c2ecf20Sopenharmony_ci	if (!il->txq)
46348c2ecf20Sopenharmony_ci		il->txq =
46358c2ecf20Sopenharmony_ci		    kcalloc(il->cfg->num_of_queues,
46368c2ecf20Sopenharmony_ci			    sizeof(struct il_tx_queue),
46378c2ecf20Sopenharmony_ci			    GFP_KERNEL);
46388c2ecf20Sopenharmony_ci	if (!il->txq) {
46398c2ecf20Sopenharmony_ci		IL_ERR("Not enough memory for txq\n");
46408c2ecf20Sopenharmony_ci		return -ENOMEM;
46418c2ecf20Sopenharmony_ci	}
46428c2ecf20Sopenharmony_ci	return 0;
46438c2ecf20Sopenharmony_ci}
46448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_alloc_txq_mem);
46458c2ecf20Sopenharmony_ci
46468c2ecf20Sopenharmony_civoid
46478c2ecf20Sopenharmony_ciil_free_txq_mem(struct il_priv *il)
46488c2ecf20Sopenharmony_ci{
46498c2ecf20Sopenharmony_ci	kfree(il->txq);
46508c2ecf20Sopenharmony_ci	il->txq = NULL;
46518c2ecf20Sopenharmony_ci}
46528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_free_txq_mem);
46538c2ecf20Sopenharmony_ci
46548c2ecf20Sopenharmony_ciint
46558c2ecf20Sopenharmony_ciil_force_reset(struct il_priv *il, bool external)
46568c2ecf20Sopenharmony_ci{
46578c2ecf20Sopenharmony_ci	struct il_force_reset *force_reset;
46588c2ecf20Sopenharmony_ci
46598c2ecf20Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status))
46608c2ecf20Sopenharmony_ci		return -EINVAL;
46618c2ecf20Sopenharmony_ci
46628c2ecf20Sopenharmony_ci	force_reset = &il->force_reset;
46638c2ecf20Sopenharmony_ci	force_reset->reset_request_count++;
46648c2ecf20Sopenharmony_ci	if (!external) {
46658c2ecf20Sopenharmony_ci		if (force_reset->last_force_reset_jiffies &&
46668c2ecf20Sopenharmony_ci		    time_after(force_reset->last_force_reset_jiffies +
46678c2ecf20Sopenharmony_ci			       force_reset->reset_duration, jiffies)) {
46688c2ecf20Sopenharmony_ci			D_INFO("force reset rejected\n");
46698c2ecf20Sopenharmony_ci			force_reset->reset_reject_count++;
46708c2ecf20Sopenharmony_ci			return -EAGAIN;
46718c2ecf20Sopenharmony_ci		}
46728c2ecf20Sopenharmony_ci	}
46738c2ecf20Sopenharmony_ci	force_reset->reset_success_count++;
46748c2ecf20Sopenharmony_ci	force_reset->last_force_reset_jiffies = jiffies;
46758c2ecf20Sopenharmony_ci
46768c2ecf20Sopenharmony_ci	/*
46778c2ecf20Sopenharmony_ci	 * if the request is from external(ex: debugfs),
46788c2ecf20Sopenharmony_ci	 * then always perform the request in regardless the module
46798c2ecf20Sopenharmony_ci	 * parameter setting
46808c2ecf20Sopenharmony_ci	 * if the request is from internal (uCode error or driver
46818c2ecf20Sopenharmony_ci	 * detect failure), then fw_restart module parameter
46828c2ecf20Sopenharmony_ci	 * need to be check before performing firmware reload
46838c2ecf20Sopenharmony_ci	 */
46848c2ecf20Sopenharmony_ci
46858c2ecf20Sopenharmony_ci	if (!external && !il->cfg->mod_params->restart_fw) {
46868c2ecf20Sopenharmony_ci		D_INFO("Cancel firmware reload based on "
46878c2ecf20Sopenharmony_ci		       "module parameter setting\n");
46888c2ecf20Sopenharmony_ci		return 0;
46898c2ecf20Sopenharmony_ci	}
46908c2ecf20Sopenharmony_ci
46918c2ecf20Sopenharmony_ci	IL_ERR("On demand firmware reload\n");
46928c2ecf20Sopenharmony_ci
46938c2ecf20Sopenharmony_ci	/* Set the FW error flag -- cleared on il_down */
46948c2ecf20Sopenharmony_ci	set_bit(S_FW_ERROR, &il->status);
46958c2ecf20Sopenharmony_ci	wake_up(&il->wait_command_queue);
46968c2ecf20Sopenharmony_ci	/*
46978c2ecf20Sopenharmony_ci	 * Keep the restart process from trying to send host
46988c2ecf20Sopenharmony_ci	 * commands by clearing the INIT status bit
46998c2ecf20Sopenharmony_ci	 */
47008c2ecf20Sopenharmony_ci	clear_bit(S_READY, &il->status);
47018c2ecf20Sopenharmony_ci	queue_work(il->workqueue, &il->restart);
47028c2ecf20Sopenharmony_ci
47038c2ecf20Sopenharmony_ci	return 0;
47048c2ecf20Sopenharmony_ci}
47058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_force_reset);
47068c2ecf20Sopenharmony_ci
47078c2ecf20Sopenharmony_ciint
47088c2ecf20Sopenharmony_ciil_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
47098c2ecf20Sopenharmony_ci			enum nl80211_iftype newtype, bool newp2p)
47108c2ecf20Sopenharmony_ci{
47118c2ecf20Sopenharmony_ci	struct il_priv *il = hw->priv;
47128c2ecf20Sopenharmony_ci	int err;
47138c2ecf20Sopenharmony_ci
47148c2ecf20Sopenharmony_ci	mutex_lock(&il->mutex);
47158c2ecf20Sopenharmony_ci	D_MAC80211("enter: type %d, addr %pM newtype %d newp2p %d\n",
47168c2ecf20Sopenharmony_ci		    vif->type, vif->addr, newtype, newp2p);
47178c2ecf20Sopenharmony_ci
47188c2ecf20Sopenharmony_ci	if (newp2p) {
47198c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
47208c2ecf20Sopenharmony_ci		goto out;
47218c2ecf20Sopenharmony_ci	}
47228c2ecf20Sopenharmony_ci
47238c2ecf20Sopenharmony_ci	if (!il->vif || !il_is_ready_rf(il)) {
47248c2ecf20Sopenharmony_ci		/*
47258c2ecf20Sopenharmony_ci		 * Huh? But wait ... this can maybe happen when
47268c2ecf20Sopenharmony_ci		 * we're in the middle of a firmware restart!
47278c2ecf20Sopenharmony_ci		 */
47288c2ecf20Sopenharmony_ci		err = -EBUSY;
47298c2ecf20Sopenharmony_ci		goto out;
47308c2ecf20Sopenharmony_ci	}
47318c2ecf20Sopenharmony_ci
47328c2ecf20Sopenharmony_ci	/* success */
47338c2ecf20Sopenharmony_ci	vif->type = newtype;
47348c2ecf20Sopenharmony_ci	vif->p2p = false;
47358c2ecf20Sopenharmony_ci	il->iw_mode = newtype;
47368c2ecf20Sopenharmony_ci	il_teardown_interface(il, vif);
47378c2ecf20Sopenharmony_ci	err = 0;
47388c2ecf20Sopenharmony_ci
47398c2ecf20Sopenharmony_ciout:
47408c2ecf20Sopenharmony_ci	D_MAC80211("leave err %d\n", err);
47418c2ecf20Sopenharmony_ci	mutex_unlock(&il->mutex);
47428c2ecf20Sopenharmony_ci
47438c2ecf20Sopenharmony_ci	return err;
47448c2ecf20Sopenharmony_ci}
47458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_change_interface);
47468c2ecf20Sopenharmony_ci
47478c2ecf20Sopenharmony_civoid il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
47488c2ecf20Sopenharmony_ci		  u32 queues, bool drop)
47498c2ecf20Sopenharmony_ci{
47508c2ecf20Sopenharmony_ci	struct il_priv *il = hw->priv;
47518c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(500);
47528c2ecf20Sopenharmony_ci	int i;
47538c2ecf20Sopenharmony_ci
47548c2ecf20Sopenharmony_ci	mutex_lock(&il->mutex);
47558c2ecf20Sopenharmony_ci	D_MAC80211("enter\n");
47568c2ecf20Sopenharmony_ci
47578c2ecf20Sopenharmony_ci	if (il->txq == NULL)
47588c2ecf20Sopenharmony_ci		goto out;
47598c2ecf20Sopenharmony_ci
47608c2ecf20Sopenharmony_ci	for (i = 0; i < il->hw_params.max_txq_num; i++) {
47618c2ecf20Sopenharmony_ci		struct il_queue *q;
47628c2ecf20Sopenharmony_ci
47638c2ecf20Sopenharmony_ci		if (i == il->cmd_queue)
47648c2ecf20Sopenharmony_ci			continue;
47658c2ecf20Sopenharmony_ci
47668c2ecf20Sopenharmony_ci		q = &il->txq[i].q;
47678c2ecf20Sopenharmony_ci		if (q->read_ptr == q->write_ptr)
47688c2ecf20Sopenharmony_ci			continue;
47698c2ecf20Sopenharmony_ci
47708c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout)) {
47718c2ecf20Sopenharmony_ci			IL_ERR("Failed to flush queue %d\n", q->id);
47728c2ecf20Sopenharmony_ci			break;
47738c2ecf20Sopenharmony_ci		}
47748c2ecf20Sopenharmony_ci
47758c2ecf20Sopenharmony_ci		msleep(20);
47768c2ecf20Sopenharmony_ci	}
47778c2ecf20Sopenharmony_ciout:
47788c2ecf20Sopenharmony_ci	D_MAC80211("leave\n");
47798c2ecf20Sopenharmony_ci	mutex_unlock(&il->mutex);
47808c2ecf20Sopenharmony_ci}
47818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_flush);
47828c2ecf20Sopenharmony_ci
47838c2ecf20Sopenharmony_ci/*
47848c2ecf20Sopenharmony_ci * On every watchdog tick we check (latest) time stamp. If it does not
47858c2ecf20Sopenharmony_ci * change during timeout period and queue is not empty we reset firmware.
47868c2ecf20Sopenharmony_ci */
47878c2ecf20Sopenharmony_cistatic int
47888c2ecf20Sopenharmony_ciil_check_stuck_queue(struct il_priv *il, int cnt)
47898c2ecf20Sopenharmony_ci{
47908c2ecf20Sopenharmony_ci	struct il_tx_queue *txq = &il->txq[cnt];
47918c2ecf20Sopenharmony_ci	struct il_queue *q = &txq->q;
47928c2ecf20Sopenharmony_ci	unsigned long timeout;
47938c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
47948c2ecf20Sopenharmony_ci	int ret;
47958c2ecf20Sopenharmony_ci
47968c2ecf20Sopenharmony_ci	if (q->read_ptr == q->write_ptr) {
47978c2ecf20Sopenharmony_ci		txq->time_stamp = now;
47988c2ecf20Sopenharmony_ci		return 0;
47998c2ecf20Sopenharmony_ci	}
48008c2ecf20Sopenharmony_ci
48018c2ecf20Sopenharmony_ci	timeout =
48028c2ecf20Sopenharmony_ci	    txq->time_stamp +
48038c2ecf20Sopenharmony_ci	    msecs_to_jiffies(il->cfg->wd_timeout);
48048c2ecf20Sopenharmony_ci
48058c2ecf20Sopenharmony_ci	if (time_after(now, timeout)) {
48068c2ecf20Sopenharmony_ci		IL_ERR("Queue %d stuck for %u ms.\n", q->id,
48078c2ecf20Sopenharmony_ci		       jiffies_to_msecs(now - txq->time_stamp));
48088c2ecf20Sopenharmony_ci		ret = il_force_reset(il, false);
48098c2ecf20Sopenharmony_ci		return (ret == -EAGAIN) ? 0 : 1;
48108c2ecf20Sopenharmony_ci	}
48118c2ecf20Sopenharmony_ci
48128c2ecf20Sopenharmony_ci	return 0;
48138c2ecf20Sopenharmony_ci}
48148c2ecf20Sopenharmony_ci
48158c2ecf20Sopenharmony_ci/*
48168c2ecf20Sopenharmony_ci * Making watchdog tick be a quarter of timeout assure we will
48178c2ecf20Sopenharmony_ci * discover the queue hung between timeout and 1.25*timeout
48188c2ecf20Sopenharmony_ci */
48198c2ecf20Sopenharmony_ci#define IL_WD_TICK(timeout) ((timeout) / 4)
48208c2ecf20Sopenharmony_ci
48218c2ecf20Sopenharmony_ci/*
48228c2ecf20Sopenharmony_ci * Watchdog timer callback, we check each tx queue for stuck, if if hung
48238c2ecf20Sopenharmony_ci * we reset the firmware. If everything is fine just rearm the timer.
48248c2ecf20Sopenharmony_ci */
48258c2ecf20Sopenharmony_civoid
48268c2ecf20Sopenharmony_ciil_bg_watchdog(struct timer_list *t)
48278c2ecf20Sopenharmony_ci{
48288c2ecf20Sopenharmony_ci	struct il_priv *il = from_timer(il, t, watchdog);
48298c2ecf20Sopenharmony_ci	int cnt;
48308c2ecf20Sopenharmony_ci	unsigned long timeout;
48318c2ecf20Sopenharmony_ci
48328c2ecf20Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status))
48338c2ecf20Sopenharmony_ci		return;
48348c2ecf20Sopenharmony_ci
48358c2ecf20Sopenharmony_ci	timeout = il->cfg->wd_timeout;
48368c2ecf20Sopenharmony_ci	if (timeout == 0)
48378c2ecf20Sopenharmony_ci		return;
48388c2ecf20Sopenharmony_ci
48398c2ecf20Sopenharmony_ci	/* monitor and check for stuck cmd queue */
48408c2ecf20Sopenharmony_ci	if (il_check_stuck_queue(il, il->cmd_queue))
48418c2ecf20Sopenharmony_ci		return;
48428c2ecf20Sopenharmony_ci
48438c2ecf20Sopenharmony_ci	/* monitor and check for other stuck queues */
48448c2ecf20Sopenharmony_ci	for (cnt = 0; cnt < il->hw_params.max_txq_num; cnt++) {
48458c2ecf20Sopenharmony_ci		/* skip as we already checked the command queue */
48468c2ecf20Sopenharmony_ci		if (cnt == il->cmd_queue)
48478c2ecf20Sopenharmony_ci			continue;
48488c2ecf20Sopenharmony_ci		if (il_check_stuck_queue(il, cnt))
48498c2ecf20Sopenharmony_ci			return;
48508c2ecf20Sopenharmony_ci	}
48518c2ecf20Sopenharmony_ci
48528c2ecf20Sopenharmony_ci	mod_timer(&il->watchdog,
48538c2ecf20Sopenharmony_ci		  jiffies + msecs_to_jiffies(IL_WD_TICK(timeout)));
48548c2ecf20Sopenharmony_ci}
48558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_bg_watchdog);
48568c2ecf20Sopenharmony_ci
48578c2ecf20Sopenharmony_civoid
48588c2ecf20Sopenharmony_ciil_setup_watchdog(struct il_priv *il)
48598c2ecf20Sopenharmony_ci{
48608c2ecf20Sopenharmony_ci	unsigned int timeout = il->cfg->wd_timeout;
48618c2ecf20Sopenharmony_ci
48628c2ecf20Sopenharmony_ci	if (timeout)
48638c2ecf20Sopenharmony_ci		mod_timer(&il->watchdog,
48648c2ecf20Sopenharmony_ci			  jiffies + msecs_to_jiffies(IL_WD_TICK(timeout)));
48658c2ecf20Sopenharmony_ci	else
48668c2ecf20Sopenharmony_ci		del_timer(&il->watchdog);
48678c2ecf20Sopenharmony_ci}
48688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_setup_watchdog);
48698c2ecf20Sopenharmony_ci
48708c2ecf20Sopenharmony_ci/*
48718c2ecf20Sopenharmony_ci * extended beacon time format
48728c2ecf20Sopenharmony_ci * time in usec will be changed into a 32-bit value in extended:internal format
48738c2ecf20Sopenharmony_ci * the extended part is the beacon counts
48748c2ecf20Sopenharmony_ci * the internal part is the time in usec within one beacon interval
48758c2ecf20Sopenharmony_ci */
48768c2ecf20Sopenharmony_ciu32
48778c2ecf20Sopenharmony_ciil_usecs_to_beacons(struct il_priv *il, u32 usec, u32 beacon_interval)
48788c2ecf20Sopenharmony_ci{
48798c2ecf20Sopenharmony_ci	u32 quot;
48808c2ecf20Sopenharmony_ci	u32 rem;
48818c2ecf20Sopenharmony_ci	u32 interval = beacon_interval * TIME_UNIT;
48828c2ecf20Sopenharmony_ci
48838c2ecf20Sopenharmony_ci	if (!interval || !usec)
48848c2ecf20Sopenharmony_ci		return 0;
48858c2ecf20Sopenharmony_ci
48868c2ecf20Sopenharmony_ci	quot =
48878c2ecf20Sopenharmony_ci	    (usec /
48888c2ecf20Sopenharmony_ci	     interval) & (il_beacon_time_mask_high(il,
48898c2ecf20Sopenharmony_ci						   il->hw_params.
48908c2ecf20Sopenharmony_ci						   beacon_time_tsf_bits) >> il->
48918c2ecf20Sopenharmony_ci			  hw_params.beacon_time_tsf_bits);
48928c2ecf20Sopenharmony_ci	rem =
48938c2ecf20Sopenharmony_ci	    (usec % interval) & il_beacon_time_mask_low(il,
48948c2ecf20Sopenharmony_ci							il->hw_params.
48958c2ecf20Sopenharmony_ci							beacon_time_tsf_bits);
48968c2ecf20Sopenharmony_ci
48978c2ecf20Sopenharmony_ci	return (quot << il->hw_params.beacon_time_tsf_bits) + rem;
48988c2ecf20Sopenharmony_ci}
48998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_usecs_to_beacons);
49008c2ecf20Sopenharmony_ci
49018c2ecf20Sopenharmony_ci/* base is usually what we get from ucode with each received frame,
49028c2ecf20Sopenharmony_ci * the same as HW timer counter counting down
49038c2ecf20Sopenharmony_ci */
49048c2ecf20Sopenharmony_ci__le32
49058c2ecf20Sopenharmony_ciil_add_beacon_time(struct il_priv *il, u32 base, u32 addon,
49068c2ecf20Sopenharmony_ci		   u32 beacon_interval)
49078c2ecf20Sopenharmony_ci{
49088c2ecf20Sopenharmony_ci	u32 base_low = base & il_beacon_time_mask_low(il,
49098c2ecf20Sopenharmony_ci						      il->hw_params.
49108c2ecf20Sopenharmony_ci						      beacon_time_tsf_bits);
49118c2ecf20Sopenharmony_ci	u32 addon_low = addon & il_beacon_time_mask_low(il,
49128c2ecf20Sopenharmony_ci							il->hw_params.
49138c2ecf20Sopenharmony_ci							beacon_time_tsf_bits);
49148c2ecf20Sopenharmony_ci	u32 interval = beacon_interval * TIME_UNIT;
49158c2ecf20Sopenharmony_ci	u32 res = (base & il_beacon_time_mask_high(il,
49168c2ecf20Sopenharmony_ci						   il->hw_params.
49178c2ecf20Sopenharmony_ci						   beacon_time_tsf_bits)) +
49188c2ecf20Sopenharmony_ci	    (addon & il_beacon_time_mask_high(il,
49198c2ecf20Sopenharmony_ci					      il->hw_params.
49208c2ecf20Sopenharmony_ci					      beacon_time_tsf_bits));
49218c2ecf20Sopenharmony_ci
49228c2ecf20Sopenharmony_ci	if (base_low > addon_low)
49238c2ecf20Sopenharmony_ci		res += base_low - addon_low;
49248c2ecf20Sopenharmony_ci	else if (base_low < addon_low) {
49258c2ecf20Sopenharmony_ci		res += interval + base_low - addon_low;
49268c2ecf20Sopenharmony_ci		res += (1 << il->hw_params.beacon_time_tsf_bits);
49278c2ecf20Sopenharmony_ci	} else
49288c2ecf20Sopenharmony_ci		res += (1 << il->hw_params.beacon_time_tsf_bits);
49298c2ecf20Sopenharmony_ci
49308c2ecf20Sopenharmony_ci	return cpu_to_le32(res);
49318c2ecf20Sopenharmony_ci}
49328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_add_beacon_time);
49338c2ecf20Sopenharmony_ci
49348c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
49358c2ecf20Sopenharmony_ci
49368c2ecf20Sopenharmony_cistatic int
49378c2ecf20Sopenharmony_ciil_pci_suspend(struct device *device)
49388c2ecf20Sopenharmony_ci{
49398c2ecf20Sopenharmony_ci	struct il_priv *il = dev_get_drvdata(device);
49408c2ecf20Sopenharmony_ci
49418c2ecf20Sopenharmony_ci	/*
49428c2ecf20Sopenharmony_ci	 * This function is called when system goes into suspend state
49438c2ecf20Sopenharmony_ci	 * mac80211 will call il_mac_stop() from the mac80211 suspend function
49448c2ecf20Sopenharmony_ci	 * first but since il_mac_stop() has no knowledge of who the caller is,
49458c2ecf20Sopenharmony_ci	 * it will not call apm_ops.stop() to stop the DMA operation.
49468c2ecf20Sopenharmony_ci	 * Calling apm_ops.stop here to make sure we stop the DMA.
49478c2ecf20Sopenharmony_ci	 */
49488c2ecf20Sopenharmony_ci	il_apm_stop(il);
49498c2ecf20Sopenharmony_ci
49508c2ecf20Sopenharmony_ci	return 0;
49518c2ecf20Sopenharmony_ci}
49528c2ecf20Sopenharmony_ci
49538c2ecf20Sopenharmony_cistatic int
49548c2ecf20Sopenharmony_ciil_pci_resume(struct device *device)
49558c2ecf20Sopenharmony_ci{
49568c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(device);
49578c2ecf20Sopenharmony_ci	struct il_priv *il = pci_get_drvdata(pdev);
49588c2ecf20Sopenharmony_ci	bool hw_rfkill = false;
49598c2ecf20Sopenharmony_ci
49608c2ecf20Sopenharmony_ci	/*
49618c2ecf20Sopenharmony_ci	 * We disable the RETRY_TIMEOUT register (0x41) to keep
49628c2ecf20Sopenharmony_ci	 * PCI Tx retries from interfering with C3 CPU state.
49638c2ecf20Sopenharmony_ci	 */
49648c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
49658c2ecf20Sopenharmony_ci
49668c2ecf20Sopenharmony_ci	il_enable_interrupts(il);
49678c2ecf20Sopenharmony_ci
49688c2ecf20Sopenharmony_ci	if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
49698c2ecf20Sopenharmony_ci		hw_rfkill = true;
49708c2ecf20Sopenharmony_ci
49718c2ecf20Sopenharmony_ci	if (hw_rfkill)
49728c2ecf20Sopenharmony_ci		set_bit(S_RFKILL, &il->status);
49738c2ecf20Sopenharmony_ci	else
49748c2ecf20Sopenharmony_ci		clear_bit(S_RFKILL, &il->status);
49758c2ecf20Sopenharmony_ci
49768c2ecf20Sopenharmony_ci	wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rfkill);
49778c2ecf20Sopenharmony_ci
49788c2ecf20Sopenharmony_ci	return 0;
49798c2ecf20Sopenharmony_ci}
49808c2ecf20Sopenharmony_ci
49818c2ecf20Sopenharmony_ciSIMPLE_DEV_PM_OPS(il_pm_ops, il_pci_suspend, il_pci_resume);
49828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_pm_ops);
49838c2ecf20Sopenharmony_ci
49848c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
49858c2ecf20Sopenharmony_ci
49868c2ecf20Sopenharmony_cistatic void
49878c2ecf20Sopenharmony_ciil_update_qos(struct il_priv *il)
49888c2ecf20Sopenharmony_ci{
49898c2ecf20Sopenharmony_ci	if (test_bit(S_EXIT_PENDING, &il->status))
49908c2ecf20Sopenharmony_ci		return;
49918c2ecf20Sopenharmony_ci
49928c2ecf20Sopenharmony_ci	il->qos_data.def_qos_parm.qos_flags = 0;
49938c2ecf20Sopenharmony_ci
49948c2ecf20Sopenharmony_ci	if (il->qos_data.qos_active)
49958c2ecf20Sopenharmony_ci		il->qos_data.def_qos_parm.qos_flags |=
49968c2ecf20Sopenharmony_ci		    QOS_PARAM_FLG_UPDATE_EDCA_MSK;
49978c2ecf20Sopenharmony_ci
49988c2ecf20Sopenharmony_ci	if (il->ht.enabled)
49998c2ecf20Sopenharmony_ci		il->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK;
50008c2ecf20Sopenharmony_ci
50018c2ecf20Sopenharmony_ci	D_QOS("send QoS cmd with Qos active=%d FLAGS=0x%X\n",
50028c2ecf20Sopenharmony_ci	      il->qos_data.qos_active, il->qos_data.def_qos_parm.qos_flags);
50038c2ecf20Sopenharmony_ci
50048c2ecf20Sopenharmony_ci	il_send_cmd_pdu_async(il, C_QOS_PARAM, sizeof(struct il_qosparam_cmd),
50058c2ecf20Sopenharmony_ci			      &il->qos_data.def_qos_parm, NULL);
50068c2ecf20Sopenharmony_ci}
50078c2ecf20Sopenharmony_ci
50088c2ecf20Sopenharmony_ci/*
50098c2ecf20Sopenharmony_ci * il_mac_config - mac80211 config callback
50108c2ecf20Sopenharmony_ci */
50118c2ecf20Sopenharmony_ciint
50128c2ecf20Sopenharmony_ciil_mac_config(struct ieee80211_hw *hw, u32 changed)
50138c2ecf20Sopenharmony_ci{
50148c2ecf20Sopenharmony_ci	struct il_priv *il = hw->priv;
50158c2ecf20Sopenharmony_ci	const struct il_channel_info *ch_info;
50168c2ecf20Sopenharmony_ci	struct ieee80211_conf *conf = &hw->conf;
50178c2ecf20Sopenharmony_ci	struct ieee80211_channel *channel = conf->chandef.chan;
50188c2ecf20Sopenharmony_ci	struct il_ht_config *ht_conf = &il->current_ht_config;
50198c2ecf20Sopenharmony_ci	unsigned long flags = 0;
50208c2ecf20Sopenharmony_ci	int ret = 0;
50218c2ecf20Sopenharmony_ci	u16 ch;
50228c2ecf20Sopenharmony_ci	int scan_active = 0;
50238c2ecf20Sopenharmony_ci	bool ht_changed = false;
50248c2ecf20Sopenharmony_ci
50258c2ecf20Sopenharmony_ci	mutex_lock(&il->mutex);
50268c2ecf20Sopenharmony_ci	D_MAC80211("enter: channel %d changed 0x%X\n", channel->hw_value,
50278c2ecf20Sopenharmony_ci		   changed);
50288c2ecf20Sopenharmony_ci
50298c2ecf20Sopenharmony_ci	if (unlikely(test_bit(S_SCANNING, &il->status))) {
50308c2ecf20Sopenharmony_ci		scan_active = 1;
50318c2ecf20Sopenharmony_ci		D_MAC80211("scan active\n");
50328c2ecf20Sopenharmony_ci	}
50338c2ecf20Sopenharmony_ci
50348c2ecf20Sopenharmony_ci	if (changed &
50358c2ecf20Sopenharmony_ci	    (IEEE80211_CONF_CHANGE_SMPS | IEEE80211_CONF_CHANGE_CHANNEL)) {
50368c2ecf20Sopenharmony_ci		/* mac80211 uses static for non-HT which is what we want */
50378c2ecf20Sopenharmony_ci		il->current_ht_config.smps = conf->smps_mode;
50388c2ecf20Sopenharmony_ci
50398c2ecf20Sopenharmony_ci		/*
50408c2ecf20Sopenharmony_ci		 * Recalculate chain counts.
50418c2ecf20Sopenharmony_ci		 *
50428c2ecf20Sopenharmony_ci		 * If monitor mode is enabled then mac80211 will
50438c2ecf20Sopenharmony_ci		 * set up the SM PS mode to OFF if an HT channel is
50448c2ecf20Sopenharmony_ci		 * configured.
50458c2ecf20Sopenharmony_ci		 */
50468c2ecf20Sopenharmony_ci		if (il->ops->set_rxon_chain)
50478c2ecf20Sopenharmony_ci			il->ops->set_rxon_chain(il);
50488c2ecf20Sopenharmony_ci	}
50498c2ecf20Sopenharmony_ci
50508c2ecf20Sopenharmony_ci	/* during scanning mac80211 will delay channel setting until
50518c2ecf20Sopenharmony_ci	 * scan finish with changed = 0
50528c2ecf20Sopenharmony_ci	 */
50538c2ecf20Sopenharmony_ci	if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
50548c2ecf20Sopenharmony_ci
50558c2ecf20Sopenharmony_ci		if (scan_active)
50568c2ecf20Sopenharmony_ci			goto set_ch_out;
50578c2ecf20Sopenharmony_ci
50588c2ecf20Sopenharmony_ci		ch = channel->hw_value;
50598c2ecf20Sopenharmony_ci		ch_info = il_get_channel_info(il, channel->band, ch);
50608c2ecf20Sopenharmony_ci		if (!il_is_channel_valid(ch_info)) {
50618c2ecf20Sopenharmony_ci			D_MAC80211("leave - invalid channel\n");
50628c2ecf20Sopenharmony_ci			ret = -EINVAL;
50638c2ecf20Sopenharmony_ci			goto set_ch_out;
50648c2ecf20Sopenharmony_ci		}
50658c2ecf20Sopenharmony_ci
50668c2ecf20Sopenharmony_ci		if (il->iw_mode == NL80211_IFTYPE_ADHOC &&
50678c2ecf20Sopenharmony_ci		    !il_is_channel_ibss(ch_info)) {
50688c2ecf20Sopenharmony_ci			D_MAC80211("leave - not IBSS channel\n");
50698c2ecf20Sopenharmony_ci			ret = -EINVAL;
50708c2ecf20Sopenharmony_ci			goto set_ch_out;
50718c2ecf20Sopenharmony_ci		}
50728c2ecf20Sopenharmony_ci
50738c2ecf20Sopenharmony_ci		spin_lock_irqsave(&il->lock, flags);
50748c2ecf20Sopenharmony_ci
50758c2ecf20Sopenharmony_ci		/* Configure HT40 channels */
50768c2ecf20Sopenharmony_ci		if (il->ht.enabled != conf_is_ht(conf)) {
50778c2ecf20Sopenharmony_ci			il->ht.enabled = conf_is_ht(conf);
50788c2ecf20Sopenharmony_ci			ht_changed = true;
50798c2ecf20Sopenharmony_ci		}
50808c2ecf20Sopenharmony_ci		if (il->ht.enabled) {
50818c2ecf20Sopenharmony_ci			if (conf_is_ht40_minus(conf)) {
50828c2ecf20Sopenharmony_ci				il->ht.extension_chan_offset =
50838c2ecf20Sopenharmony_ci				    IEEE80211_HT_PARAM_CHA_SEC_BELOW;
50848c2ecf20Sopenharmony_ci				il->ht.is_40mhz = true;
50858c2ecf20Sopenharmony_ci			} else if (conf_is_ht40_plus(conf)) {
50868c2ecf20Sopenharmony_ci				il->ht.extension_chan_offset =
50878c2ecf20Sopenharmony_ci				    IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
50888c2ecf20Sopenharmony_ci				il->ht.is_40mhz = true;
50898c2ecf20Sopenharmony_ci			} else {
50908c2ecf20Sopenharmony_ci				il->ht.extension_chan_offset =
50918c2ecf20Sopenharmony_ci				    IEEE80211_HT_PARAM_CHA_SEC_NONE;
50928c2ecf20Sopenharmony_ci				il->ht.is_40mhz = false;
50938c2ecf20Sopenharmony_ci			}
50948c2ecf20Sopenharmony_ci		} else
50958c2ecf20Sopenharmony_ci			il->ht.is_40mhz = false;
50968c2ecf20Sopenharmony_ci
50978c2ecf20Sopenharmony_ci		/*
50988c2ecf20Sopenharmony_ci		 * Default to no protection. Protection mode will
50998c2ecf20Sopenharmony_ci		 * later be set from BSS config in il_ht_conf
51008c2ecf20Sopenharmony_ci		 */
51018c2ecf20Sopenharmony_ci		il->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
51028c2ecf20Sopenharmony_ci
51038c2ecf20Sopenharmony_ci		/* if we are switching from ht to 2.4 clear flags
51048c2ecf20Sopenharmony_ci		 * from any ht related info since 2.4 does not
51058c2ecf20Sopenharmony_ci		 * support ht */
51068c2ecf20Sopenharmony_ci		if ((le16_to_cpu(il->staging.channel) != ch))
51078c2ecf20Sopenharmony_ci			il->staging.flags = 0;
51088c2ecf20Sopenharmony_ci
51098c2ecf20Sopenharmony_ci		il_set_rxon_channel(il, channel);
51108c2ecf20Sopenharmony_ci		il_set_rxon_ht(il, ht_conf);
51118c2ecf20Sopenharmony_ci
51128c2ecf20Sopenharmony_ci		il_set_flags_for_band(il, channel->band, il->vif);
51138c2ecf20Sopenharmony_ci
51148c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&il->lock, flags);
51158c2ecf20Sopenharmony_ci
51168c2ecf20Sopenharmony_ci		if (il->ops->update_bcast_stations)
51178c2ecf20Sopenharmony_ci			ret = il->ops->update_bcast_stations(il);
51188c2ecf20Sopenharmony_ci
51198c2ecf20Sopenharmony_ciset_ch_out:
51208c2ecf20Sopenharmony_ci		/* The list of supported rates and rate mask can be different
51218c2ecf20Sopenharmony_ci		 * for each band; since the band may have changed, reset
51228c2ecf20Sopenharmony_ci		 * the rate mask to what mac80211 lists */
51238c2ecf20Sopenharmony_ci		il_set_rate(il);
51248c2ecf20Sopenharmony_ci	}
51258c2ecf20Sopenharmony_ci
51268c2ecf20Sopenharmony_ci	if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) {
51278c2ecf20Sopenharmony_ci		il->power_data.ps_disabled = !(conf->flags & IEEE80211_CONF_PS);
51288c2ecf20Sopenharmony_ci		if (!il->power_data.ps_disabled)
51298c2ecf20Sopenharmony_ci			IL_WARN_ONCE("Enabling power save might cause firmware crashes\n");
51308c2ecf20Sopenharmony_ci		ret = il_power_update_mode(il, false);
51318c2ecf20Sopenharmony_ci		if (ret)
51328c2ecf20Sopenharmony_ci			D_MAC80211("Error setting sleep level\n");
51338c2ecf20Sopenharmony_ci	}
51348c2ecf20Sopenharmony_ci
51358c2ecf20Sopenharmony_ci	if (changed & IEEE80211_CONF_CHANGE_POWER) {
51368c2ecf20Sopenharmony_ci		D_MAC80211("TX Power old=%d new=%d\n", il->tx_power_user_lmt,
51378c2ecf20Sopenharmony_ci			   conf->power_level);
51388c2ecf20Sopenharmony_ci
51398c2ecf20Sopenharmony_ci		il_set_tx_power(il, conf->power_level, false);
51408c2ecf20Sopenharmony_ci	}
51418c2ecf20Sopenharmony_ci
51428c2ecf20Sopenharmony_ci	if (!il_is_ready(il)) {
51438c2ecf20Sopenharmony_ci		D_MAC80211("leave - not ready\n");
51448c2ecf20Sopenharmony_ci		goto out;
51458c2ecf20Sopenharmony_ci	}
51468c2ecf20Sopenharmony_ci
51478c2ecf20Sopenharmony_ci	if (scan_active)
51488c2ecf20Sopenharmony_ci		goto out;
51498c2ecf20Sopenharmony_ci
51508c2ecf20Sopenharmony_ci	if (memcmp(&il->active, &il->staging, sizeof(il->staging)))
51518c2ecf20Sopenharmony_ci		il_commit_rxon(il);
51528c2ecf20Sopenharmony_ci	else
51538c2ecf20Sopenharmony_ci		D_INFO("Not re-sending same RXON configuration.\n");
51548c2ecf20Sopenharmony_ci	if (ht_changed)
51558c2ecf20Sopenharmony_ci		il_update_qos(il);
51568c2ecf20Sopenharmony_ci
51578c2ecf20Sopenharmony_ciout:
51588c2ecf20Sopenharmony_ci	D_MAC80211("leave ret %d\n", ret);
51598c2ecf20Sopenharmony_ci	mutex_unlock(&il->mutex);
51608c2ecf20Sopenharmony_ci
51618c2ecf20Sopenharmony_ci	return ret;
51628c2ecf20Sopenharmony_ci}
51638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_config);
51648c2ecf20Sopenharmony_ci
51658c2ecf20Sopenharmony_civoid
51668c2ecf20Sopenharmony_ciil_mac_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
51678c2ecf20Sopenharmony_ci{
51688c2ecf20Sopenharmony_ci	struct il_priv *il = hw->priv;
51698c2ecf20Sopenharmony_ci	unsigned long flags;
51708c2ecf20Sopenharmony_ci
51718c2ecf20Sopenharmony_ci	mutex_lock(&il->mutex);
51728c2ecf20Sopenharmony_ci	D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr);
51738c2ecf20Sopenharmony_ci
51748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
51758c2ecf20Sopenharmony_ci
51768c2ecf20Sopenharmony_ci	memset(&il->current_ht_config, 0, sizeof(struct il_ht_config));
51778c2ecf20Sopenharmony_ci
51788c2ecf20Sopenharmony_ci	/* new association get rid of ibss beacon skb */
51798c2ecf20Sopenharmony_ci	dev_consume_skb_irq(il->beacon_skb);
51808c2ecf20Sopenharmony_ci	il->beacon_skb = NULL;
51818c2ecf20Sopenharmony_ci	il->timestamp = 0;
51828c2ecf20Sopenharmony_ci
51838c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
51848c2ecf20Sopenharmony_ci
51858c2ecf20Sopenharmony_ci	il_scan_cancel_timeout(il, 100);
51868c2ecf20Sopenharmony_ci	if (!il_is_ready_rf(il)) {
51878c2ecf20Sopenharmony_ci		D_MAC80211("leave - not ready\n");
51888c2ecf20Sopenharmony_ci		mutex_unlock(&il->mutex);
51898c2ecf20Sopenharmony_ci		return;
51908c2ecf20Sopenharmony_ci	}
51918c2ecf20Sopenharmony_ci
51928c2ecf20Sopenharmony_ci	/* we are restarting association process */
51938c2ecf20Sopenharmony_ci	il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
51948c2ecf20Sopenharmony_ci	il_commit_rxon(il);
51958c2ecf20Sopenharmony_ci
51968c2ecf20Sopenharmony_ci	il_set_rate(il);
51978c2ecf20Sopenharmony_ci
51988c2ecf20Sopenharmony_ci	D_MAC80211("leave\n");
51998c2ecf20Sopenharmony_ci	mutex_unlock(&il->mutex);
52008c2ecf20Sopenharmony_ci}
52018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_reset_tsf);
52028c2ecf20Sopenharmony_ci
52038c2ecf20Sopenharmony_cistatic void
52048c2ecf20Sopenharmony_ciil_ht_conf(struct il_priv *il, struct ieee80211_vif *vif)
52058c2ecf20Sopenharmony_ci{
52068c2ecf20Sopenharmony_ci	struct il_ht_config *ht_conf = &il->current_ht_config;
52078c2ecf20Sopenharmony_ci	struct ieee80211_sta *sta;
52088c2ecf20Sopenharmony_ci	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
52098c2ecf20Sopenharmony_ci
52108c2ecf20Sopenharmony_ci	D_ASSOC("enter:\n");
52118c2ecf20Sopenharmony_ci
52128c2ecf20Sopenharmony_ci	if (!il->ht.enabled)
52138c2ecf20Sopenharmony_ci		return;
52148c2ecf20Sopenharmony_ci
52158c2ecf20Sopenharmony_ci	il->ht.protection =
52168c2ecf20Sopenharmony_ci	    bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION;
52178c2ecf20Sopenharmony_ci	il->ht.non_gf_sta_present =
52188c2ecf20Sopenharmony_ci	    !!(bss_conf->
52198c2ecf20Sopenharmony_ci	       ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
52208c2ecf20Sopenharmony_ci
52218c2ecf20Sopenharmony_ci	ht_conf->single_chain_sufficient = false;
52228c2ecf20Sopenharmony_ci
52238c2ecf20Sopenharmony_ci	switch (vif->type) {
52248c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
52258c2ecf20Sopenharmony_ci		rcu_read_lock();
52268c2ecf20Sopenharmony_ci		sta = ieee80211_find_sta(vif, bss_conf->bssid);
52278c2ecf20Sopenharmony_ci		if (sta) {
52288c2ecf20Sopenharmony_ci			struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
52298c2ecf20Sopenharmony_ci			int maxstreams;
52308c2ecf20Sopenharmony_ci
52318c2ecf20Sopenharmony_ci			maxstreams =
52328c2ecf20Sopenharmony_ci			    (ht_cap->mcs.
52338c2ecf20Sopenharmony_ci			     tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
52348c2ecf20Sopenharmony_ci			    >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
52358c2ecf20Sopenharmony_ci			maxstreams += 1;
52368c2ecf20Sopenharmony_ci
52378c2ecf20Sopenharmony_ci			if (ht_cap->mcs.rx_mask[1] == 0 &&
52388c2ecf20Sopenharmony_ci			    ht_cap->mcs.rx_mask[2] == 0)
52398c2ecf20Sopenharmony_ci				ht_conf->single_chain_sufficient = true;
52408c2ecf20Sopenharmony_ci			if (maxstreams <= 1)
52418c2ecf20Sopenharmony_ci				ht_conf->single_chain_sufficient = true;
52428c2ecf20Sopenharmony_ci		} else {
52438c2ecf20Sopenharmony_ci			/*
52448c2ecf20Sopenharmony_ci			 * If at all, this can only happen through a race
52458c2ecf20Sopenharmony_ci			 * when the AP disconnects us while we're still
52468c2ecf20Sopenharmony_ci			 * setting up the connection, in that case mac80211
52478c2ecf20Sopenharmony_ci			 * will soon tell us about that.
52488c2ecf20Sopenharmony_ci			 */
52498c2ecf20Sopenharmony_ci			ht_conf->single_chain_sufficient = true;
52508c2ecf20Sopenharmony_ci		}
52518c2ecf20Sopenharmony_ci		rcu_read_unlock();
52528c2ecf20Sopenharmony_ci		break;
52538c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
52548c2ecf20Sopenharmony_ci		ht_conf->single_chain_sufficient = true;
52558c2ecf20Sopenharmony_ci		break;
52568c2ecf20Sopenharmony_ci	default:
52578c2ecf20Sopenharmony_ci		break;
52588c2ecf20Sopenharmony_ci	}
52598c2ecf20Sopenharmony_ci
52608c2ecf20Sopenharmony_ci	D_ASSOC("leave\n");
52618c2ecf20Sopenharmony_ci}
52628c2ecf20Sopenharmony_ci
52638c2ecf20Sopenharmony_cistatic inline void
52648c2ecf20Sopenharmony_ciil_set_no_assoc(struct il_priv *il, struct ieee80211_vif *vif)
52658c2ecf20Sopenharmony_ci{
52668c2ecf20Sopenharmony_ci	/*
52678c2ecf20Sopenharmony_ci	 * inform the ucode that there is no longer an
52688c2ecf20Sopenharmony_ci	 * association and that no more packets should be
52698c2ecf20Sopenharmony_ci	 * sent
52708c2ecf20Sopenharmony_ci	 */
52718c2ecf20Sopenharmony_ci	il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
52728c2ecf20Sopenharmony_ci	il->staging.assoc_id = 0;
52738c2ecf20Sopenharmony_ci	il_commit_rxon(il);
52748c2ecf20Sopenharmony_ci}
52758c2ecf20Sopenharmony_ci
52768c2ecf20Sopenharmony_cistatic void
52778c2ecf20Sopenharmony_ciil_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
52788c2ecf20Sopenharmony_ci{
52798c2ecf20Sopenharmony_ci	struct il_priv *il = hw->priv;
52808c2ecf20Sopenharmony_ci	unsigned long flags;
52818c2ecf20Sopenharmony_ci	__le64 timestamp;
52828c2ecf20Sopenharmony_ci	struct sk_buff *skb = ieee80211_beacon_get(hw, vif);
52838c2ecf20Sopenharmony_ci
52848c2ecf20Sopenharmony_ci	if (!skb)
52858c2ecf20Sopenharmony_ci		return;
52868c2ecf20Sopenharmony_ci
52878c2ecf20Sopenharmony_ci	D_MAC80211("enter\n");
52888c2ecf20Sopenharmony_ci
52898c2ecf20Sopenharmony_ci	lockdep_assert_held(&il->mutex);
52908c2ecf20Sopenharmony_ci
52918c2ecf20Sopenharmony_ci	if (!il->beacon_enabled) {
52928c2ecf20Sopenharmony_ci		IL_ERR("update beacon with no beaconing enabled\n");
52938c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
52948c2ecf20Sopenharmony_ci		return;
52958c2ecf20Sopenharmony_ci	}
52968c2ecf20Sopenharmony_ci
52978c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
52988c2ecf20Sopenharmony_ci	dev_consume_skb_irq(il->beacon_skb);
52998c2ecf20Sopenharmony_ci	il->beacon_skb = skb;
53008c2ecf20Sopenharmony_ci
53018c2ecf20Sopenharmony_ci	timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp;
53028c2ecf20Sopenharmony_ci	il->timestamp = le64_to_cpu(timestamp);
53038c2ecf20Sopenharmony_ci
53048c2ecf20Sopenharmony_ci	D_MAC80211("leave\n");
53058c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
53068c2ecf20Sopenharmony_ci
53078c2ecf20Sopenharmony_ci	if (!il_is_ready_rf(il)) {
53088c2ecf20Sopenharmony_ci		D_MAC80211("leave - RF not ready\n");
53098c2ecf20Sopenharmony_ci		return;
53108c2ecf20Sopenharmony_ci	}
53118c2ecf20Sopenharmony_ci
53128c2ecf20Sopenharmony_ci	il->ops->post_associate(il);
53138c2ecf20Sopenharmony_ci}
53148c2ecf20Sopenharmony_ci
53158c2ecf20Sopenharmony_civoid
53168c2ecf20Sopenharmony_ciil_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
53178c2ecf20Sopenharmony_ci			struct ieee80211_bss_conf *bss_conf, u32 changes)
53188c2ecf20Sopenharmony_ci{
53198c2ecf20Sopenharmony_ci	struct il_priv *il = hw->priv;
53208c2ecf20Sopenharmony_ci	int ret;
53218c2ecf20Sopenharmony_ci
53228c2ecf20Sopenharmony_ci	mutex_lock(&il->mutex);
53238c2ecf20Sopenharmony_ci	D_MAC80211("enter: changes 0x%x\n", changes);
53248c2ecf20Sopenharmony_ci
53258c2ecf20Sopenharmony_ci	if (!il_is_alive(il)) {
53268c2ecf20Sopenharmony_ci		D_MAC80211("leave - not alive\n");
53278c2ecf20Sopenharmony_ci		mutex_unlock(&il->mutex);
53288c2ecf20Sopenharmony_ci		return;
53298c2ecf20Sopenharmony_ci	}
53308c2ecf20Sopenharmony_ci
53318c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_QOS) {
53328c2ecf20Sopenharmony_ci		unsigned long flags;
53338c2ecf20Sopenharmony_ci
53348c2ecf20Sopenharmony_ci		spin_lock_irqsave(&il->lock, flags);
53358c2ecf20Sopenharmony_ci		il->qos_data.qos_active = bss_conf->qos;
53368c2ecf20Sopenharmony_ci		il_update_qos(il);
53378c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&il->lock, flags);
53388c2ecf20Sopenharmony_ci	}
53398c2ecf20Sopenharmony_ci
53408c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_BEACON_ENABLED) {
53418c2ecf20Sopenharmony_ci		/* FIXME: can we remove beacon_enabled ? */
53428c2ecf20Sopenharmony_ci		if (vif->bss_conf.enable_beacon)
53438c2ecf20Sopenharmony_ci			il->beacon_enabled = true;
53448c2ecf20Sopenharmony_ci		else
53458c2ecf20Sopenharmony_ci			il->beacon_enabled = false;
53468c2ecf20Sopenharmony_ci	}
53478c2ecf20Sopenharmony_ci
53488c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_BSSID) {
53498c2ecf20Sopenharmony_ci		D_MAC80211("BSSID %pM\n", bss_conf->bssid);
53508c2ecf20Sopenharmony_ci
53518c2ecf20Sopenharmony_ci		/*
53528c2ecf20Sopenharmony_ci		 * On passive channel we wait with blocked queues to see if
53538c2ecf20Sopenharmony_ci		 * there is traffic on that channel. If no frame will be
53548c2ecf20Sopenharmony_ci		 * received (what is very unlikely since scan detects AP on
53558c2ecf20Sopenharmony_ci		 * that channel, but theoretically possible), mac80211 associate
53568c2ecf20Sopenharmony_ci		 * procedure will time out and mac80211 will call us with NULL
53578c2ecf20Sopenharmony_ci		 * bssid. We have to unblock queues on such condition.
53588c2ecf20Sopenharmony_ci		 */
53598c2ecf20Sopenharmony_ci		if (is_zero_ether_addr(bss_conf->bssid))
53608c2ecf20Sopenharmony_ci			il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
53618c2ecf20Sopenharmony_ci
53628c2ecf20Sopenharmony_ci		/*
53638c2ecf20Sopenharmony_ci		 * If there is currently a HW scan going on in the background,
53648c2ecf20Sopenharmony_ci		 * then we need to cancel it, otherwise sometimes we are not
53658c2ecf20Sopenharmony_ci		 * able to authenticate (FIXME: why ?)
53668c2ecf20Sopenharmony_ci		 */
53678c2ecf20Sopenharmony_ci		if (il_scan_cancel_timeout(il, 100)) {
53688c2ecf20Sopenharmony_ci			D_MAC80211("leave - scan abort failed\n");
53698c2ecf20Sopenharmony_ci			mutex_unlock(&il->mutex);
53708c2ecf20Sopenharmony_ci			return;
53718c2ecf20Sopenharmony_ci		}
53728c2ecf20Sopenharmony_ci
53738c2ecf20Sopenharmony_ci		/* mac80211 only sets assoc when in STATION mode */
53748c2ecf20Sopenharmony_ci		memcpy(il->staging.bssid_addr, bss_conf->bssid, ETH_ALEN);
53758c2ecf20Sopenharmony_ci
53768c2ecf20Sopenharmony_ci		/* FIXME: currently needed in a few places */
53778c2ecf20Sopenharmony_ci		memcpy(il->bssid, bss_conf->bssid, ETH_ALEN);
53788c2ecf20Sopenharmony_ci	}
53798c2ecf20Sopenharmony_ci
53808c2ecf20Sopenharmony_ci	/*
53818c2ecf20Sopenharmony_ci	 * This needs to be after setting the BSSID in case
53828c2ecf20Sopenharmony_ci	 * mac80211 decides to do both changes at once because
53838c2ecf20Sopenharmony_ci	 * it will invoke post_associate.
53848c2ecf20Sopenharmony_ci	 */
53858c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_ADHOC && (changes & BSS_CHANGED_BEACON))
53868c2ecf20Sopenharmony_ci		il_beacon_update(hw, vif);
53878c2ecf20Sopenharmony_ci
53888c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_ERP_PREAMBLE) {
53898c2ecf20Sopenharmony_ci		D_MAC80211("ERP_PREAMBLE %d\n", bss_conf->use_short_preamble);
53908c2ecf20Sopenharmony_ci		if (bss_conf->use_short_preamble)
53918c2ecf20Sopenharmony_ci			il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
53928c2ecf20Sopenharmony_ci		else
53938c2ecf20Sopenharmony_ci			il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
53948c2ecf20Sopenharmony_ci	}
53958c2ecf20Sopenharmony_ci
53968c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_ERP_CTS_PROT) {
53978c2ecf20Sopenharmony_ci		D_MAC80211("ERP_CTS %d\n", bss_conf->use_cts_prot);
53988c2ecf20Sopenharmony_ci		if (bss_conf->use_cts_prot && il->band != NL80211_BAND_5GHZ)
53998c2ecf20Sopenharmony_ci			il->staging.flags |= RXON_FLG_TGG_PROTECT_MSK;
54008c2ecf20Sopenharmony_ci		else
54018c2ecf20Sopenharmony_ci			il->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK;
54028c2ecf20Sopenharmony_ci		if (bss_conf->use_cts_prot)
54038c2ecf20Sopenharmony_ci			il->staging.flags |= RXON_FLG_SELF_CTS_EN;
54048c2ecf20Sopenharmony_ci		else
54058c2ecf20Sopenharmony_ci			il->staging.flags &= ~RXON_FLG_SELF_CTS_EN;
54068c2ecf20Sopenharmony_ci	}
54078c2ecf20Sopenharmony_ci
54088c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_BASIC_RATES) {
54098c2ecf20Sopenharmony_ci		/* XXX use this information
54108c2ecf20Sopenharmony_ci		 *
54118c2ecf20Sopenharmony_ci		 * To do that, remove code from il_set_rate() and put something
54128c2ecf20Sopenharmony_ci		 * like this here:
54138c2ecf20Sopenharmony_ci		 *
54148c2ecf20Sopenharmony_ci		 if (A-band)
54158c2ecf20Sopenharmony_ci		 il->staging.ofdm_basic_rates =
54168c2ecf20Sopenharmony_ci		 bss_conf->basic_rates;
54178c2ecf20Sopenharmony_ci		 else
54188c2ecf20Sopenharmony_ci		 il->staging.ofdm_basic_rates =
54198c2ecf20Sopenharmony_ci		 bss_conf->basic_rates >> 4;
54208c2ecf20Sopenharmony_ci		 il->staging.cck_basic_rates =
54218c2ecf20Sopenharmony_ci		 bss_conf->basic_rates & 0xF;
54228c2ecf20Sopenharmony_ci		 */
54238c2ecf20Sopenharmony_ci	}
54248c2ecf20Sopenharmony_ci
54258c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_HT) {
54268c2ecf20Sopenharmony_ci		il_ht_conf(il, vif);
54278c2ecf20Sopenharmony_ci
54288c2ecf20Sopenharmony_ci		if (il->ops->set_rxon_chain)
54298c2ecf20Sopenharmony_ci			il->ops->set_rxon_chain(il);
54308c2ecf20Sopenharmony_ci	}
54318c2ecf20Sopenharmony_ci
54328c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_ASSOC) {
54338c2ecf20Sopenharmony_ci		D_MAC80211("ASSOC %d\n", bss_conf->assoc);
54348c2ecf20Sopenharmony_ci		if (bss_conf->assoc) {
54358c2ecf20Sopenharmony_ci			il->timestamp = bss_conf->sync_tsf;
54368c2ecf20Sopenharmony_ci
54378c2ecf20Sopenharmony_ci			if (!il_is_rfkill(il))
54388c2ecf20Sopenharmony_ci				il->ops->post_associate(il);
54398c2ecf20Sopenharmony_ci		} else
54408c2ecf20Sopenharmony_ci			il_set_no_assoc(il, vif);
54418c2ecf20Sopenharmony_ci	}
54428c2ecf20Sopenharmony_ci
54438c2ecf20Sopenharmony_ci	if (changes && il_is_associated(il) && bss_conf->aid) {
54448c2ecf20Sopenharmony_ci		D_MAC80211("Changes (%#x) while associated\n", changes);
54458c2ecf20Sopenharmony_ci		ret = il_send_rxon_assoc(il);
54468c2ecf20Sopenharmony_ci		if (!ret) {
54478c2ecf20Sopenharmony_ci			/* Sync active_rxon with latest change. */
54488c2ecf20Sopenharmony_ci			memcpy((void *)&il->active, &il->staging,
54498c2ecf20Sopenharmony_ci			       sizeof(struct il_rxon_cmd));
54508c2ecf20Sopenharmony_ci		}
54518c2ecf20Sopenharmony_ci	}
54528c2ecf20Sopenharmony_ci
54538c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_BEACON_ENABLED) {
54548c2ecf20Sopenharmony_ci		if (vif->bss_conf.enable_beacon) {
54558c2ecf20Sopenharmony_ci			memcpy(il->staging.bssid_addr, bss_conf->bssid,
54568c2ecf20Sopenharmony_ci			       ETH_ALEN);
54578c2ecf20Sopenharmony_ci			memcpy(il->bssid, bss_conf->bssid, ETH_ALEN);
54588c2ecf20Sopenharmony_ci			il->ops->config_ap(il);
54598c2ecf20Sopenharmony_ci		} else
54608c2ecf20Sopenharmony_ci			il_set_no_assoc(il, vif);
54618c2ecf20Sopenharmony_ci	}
54628c2ecf20Sopenharmony_ci
54638c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_IBSS) {
54648c2ecf20Sopenharmony_ci		ret = il->ops->manage_ibss_station(il, vif,
54658c2ecf20Sopenharmony_ci						   bss_conf->ibss_joined);
54668c2ecf20Sopenharmony_ci		if (ret)
54678c2ecf20Sopenharmony_ci			IL_ERR("failed to %s IBSS station %pM\n",
54688c2ecf20Sopenharmony_ci			       bss_conf->ibss_joined ? "add" : "remove",
54698c2ecf20Sopenharmony_ci			       bss_conf->bssid);
54708c2ecf20Sopenharmony_ci	}
54718c2ecf20Sopenharmony_ci
54728c2ecf20Sopenharmony_ci	D_MAC80211("leave\n");
54738c2ecf20Sopenharmony_ci	mutex_unlock(&il->mutex);
54748c2ecf20Sopenharmony_ci}
54758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_mac_bss_info_changed);
54768c2ecf20Sopenharmony_ci
54778c2ecf20Sopenharmony_ciirqreturn_t
54788c2ecf20Sopenharmony_ciil_isr(int irq, void *data)
54798c2ecf20Sopenharmony_ci{
54808c2ecf20Sopenharmony_ci	struct il_priv *il = data;
54818c2ecf20Sopenharmony_ci	u32 inta, inta_mask;
54828c2ecf20Sopenharmony_ci	u32 inta_fh;
54838c2ecf20Sopenharmony_ci	unsigned long flags;
54848c2ecf20Sopenharmony_ci	if (!il)
54858c2ecf20Sopenharmony_ci		return IRQ_NONE;
54868c2ecf20Sopenharmony_ci
54878c2ecf20Sopenharmony_ci	spin_lock_irqsave(&il->lock, flags);
54888c2ecf20Sopenharmony_ci
54898c2ecf20Sopenharmony_ci	/* Disable (but don't clear!) interrupts here to avoid
54908c2ecf20Sopenharmony_ci	 *    back-to-back ISRs and sporadic interrupts from our NIC.
54918c2ecf20Sopenharmony_ci	 * If we have something to service, the tasklet will re-enable ints.
54928c2ecf20Sopenharmony_ci	 * If we *don't* have something, we'll re-enable before leaving here. */
54938c2ecf20Sopenharmony_ci	inta_mask = _il_rd(il, CSR_INT_MASK);	/* just for debug */
54948c2ecf20Sopenharmony_ci	_il_wr(il, CSR_INT_MASK, 0x00000000);
54958c2ecf20Sopenharmony_ci
54968c2ecf20Sopenharmony_ci	/* Discover which interrupts are active/pending */
54978c2ecf20Sopenharmony_ci	inta = _il_rd(il, CSR_INT);
54988c2ecf20Sopenharmony_ci	inta_fh = _il_rd(il, CSR_FH_INT_STATUS);
54998c2ecf20Sopenharmony_ci
55008c2ecf20Sopenharmony_ci	/* Ignore interrupt if there's nothing in NIC to service.
55018c2ecf20Sopenharmony_ci	 * This may be due to IRQ shared with another device,
55028c2ecf20Sopenharmony_ci	 * or due to sporadic interrupts thrown from our NIC. */
55038c2ecf20Sopenharmony_ci	if (!inta && !inta_fh) {
55048c2ecf20Sopenharmony_ci		D_ISR("Ignore interrupt, inta == 0, inta_fh == 0\n");
55058c2ecf20Sopenharmony_ci		goto none;
55068c2ecf20Sopenharmony_ci	}
55078c2ecf20Sopenharmony_ci
55088c2ecf20Sopenharmony_ci	if (inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0) {
55098c2ecf20Sopenharmony_ci		/* Hardware disappeared. It might have already raised
55108c2ecf20Sopenharmony_ci		 * an interrupt */
55118c2ecf20Sopenharmony_ci		IL_WARN("HARDWARE GONE?? INTA == 0x%08x\n", inta);
55128c2ecf20Sopenharmony_ci		goto unplugged;
55138c2ecf20Sopenharmony_ci	}
55148c2ecf20Sopenharmony_ci
55158c2ecf20Sopenharmony_ci	D_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, inta_mask,
55168c2ecf20Sopenharmony_ci	      inta_fh);
55178c2ecf20Sopenharmony_ci
55188c2ecf20Sopenharmony_ci	inta &= ~CSR_INT_BIT_SCD;
55198c2ecf20Sopenharmony_ci
55208c2ecf20Sopenharmony_ci	/* il_irq_tasklet() will service interrupts and re-enable them */
55218c2ecf20Sopenharmony_ci	if (likely(inta || inta_fh))
55228c2ecf20Sopenharmony_ci		tasklet_schedule(&il->irq_tasklet);
55238c2ecf20Sopenharmony_ci
55248c2ecf20Sopenharmony_ciunplugged:
55258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
55268c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
55278c2ecf20Sopenharmony_ci
55288c2ecf20Sopenharmony_cinone:
55298c2ecf20Sopenharmony_ci	/* re-enable interrupts here since we don't have anything to service. */
55308c2ecf20Sopenharmony_ci	/* only Re-enable if disabled by irq */
55318c2ecf20Sopenharmony_ci	if (test_bit(S_INT_ENABLED, &il->status))
55328c2ecf20Sopenharmony_ci		il_enable_interrupts(il);
55338c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&il->lock, flags);
55348c2ecf20Sopenharmony_ci	return IRQ_NONE;
55358c2ecf20Sopenharmony_ci}
55368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_isr);
55378c2ecf20Sopenharmony_ci
55388c2ecf20Sopenharmony_ci/*
55398c2ecf20Sopenharmony_ci *  il_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this
55408c2ecf20Sopenharmony_ci *  function.
55418c2ecf20Sopenharmony_ci */
55428c2ecf20Sopenharmony_civoid
55438c2ecf20Sopenharmony_ciil_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info,
55448c2ecf20Sopenharmony_ci		     __le16 fc, __le32 *tx_flags)
55458c2ecf20Sopenharmony_ci{
55468c2ecf20Sopenharmony_ci	if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
55478c2ecf20Sopenharmony_ci		*tx_flags |= TX_CMD_FLG_RTS_MSK;
55488c2ecf20Sopenharmony_ci		*tx_flags &= ~TX_CMD_FLG_CTS_MSK;
55498c2ecf20Sopenharmony_ci		*tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
55508c2ecf20Sopenharmony_ci
55518c2ecf20Sopenharmony_ci		if (!ieee80211_is_mgmt(fc))
55528c2ecf20Sopenharmony_ci			return;
55538c2ecf20Sopenharmony_ci
55548c2ecf20Sopenharmony_ci		switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) {
55558c2ecf20Sopenharmony_ci		case cpu_to_le16(IEEE80211_STYPE_AUTH):
55568c2ecf20Sopenharmony_ci		case cpu_to_le16(IEEE80211_STYPE_DEAUTH):
55578c2ecf20Sopenharmony_ci		case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ):
55588c2ecf20Sopenharmony_ci		case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ):
55598c2ecf20Sopenharmony_ci			*tx_flags &= ~TX_CMD_FLG_RTS_MSK;
55608c2ecf20Sopenharmony_ci			*tx_flags |= TX_CMD_FLG_CTS_MSK;
55618c2ecf20Sopenharmony_ci			break;
55628c2ecf20Sopenharmony_ci		}
55638c2ecf20Sopenharmony_ci	} else if (info->control.rates[0].
55648c2ecf20Sopenharmony_ci		   flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
55658c2ecf20Sopenharmony_ci		*tx_flags &= ~TX_CMD_FLG_RTS_MSK;
55668c2ecf20Sopenharmony_ci		*tx_flags |= TX_CMD_FLG_CTS_MSK;
55678c2ecf20Sopenharmony_ci		*tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
55688c2ecf20Sopenharmony_ci	}
55698c2ecf20Sopenharmony_ci}
55708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(il_tx_cmd_protection);
5571