18c2ecf20Sopenharmony_ci/*-
28c2ecf20Sopenharmony_ci * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
38c2ecf20Sopenharmony_ci * Copyright (c) 2004-2005 Atheros Communications, Inc.
48c2ecf20Sopenharmony_ci * Copyright (c) 2006 Devicescape Software, Inc.
58c2ecf20Sopenharmony_ci * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
68c2ecf20Sopenharmony_ci * Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
78c2ecf20Sopenharmony_ci * Copyright (c) 2010 Bruno Randolf <br1@einfach.org>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * All rights reserved.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
128c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
138c2ecf20Sopenharmony_ci * are met:
148c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
158c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer,
168c2ecf20Sopenharmony_ci *    without modification.
178c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce at minimum a disclaimer
188c2ecf20Sopenharmony_ci *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
198c2ecf20Sopenharmony_ci *    redistribution must be conditioned upon including a substantially
208c2ecf20Sopenharmony_ci *    similar Disclaimer requirement for further binary redistribution.
218c2ecf20Sopenharmony_ci * 3. Neither the names of the above-listed copyright holders nor the names
228c2ecf20Sopenharmony_ci *    of any contributors may be used to endorse or promote products derived
238c2ecf20Sopenharmony_ci *    from this software without specific prior written permission.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the
268c2ecf20Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free
278c2ecf20Sopenharmony_ci * Software Foundation.
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * NO WARRANTY
308c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
318c2ecf20Sopenharmony_ci * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
328c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
338c2ecf20Sopenharmony_ci * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
348c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
358c2ecf20Sopenharmony_ci * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
368c2ecf20Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
378c2ecf20Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
388c2ecf20Sopenharmony_ci * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
398c2ecf20Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
408c2ecf20Sopenharmony_ci * THE POSSIBILITY OF SUCH DAMAGES.
418c2ecf20Sopenharmony_ci *
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#include <net/mac80211.h>
478c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#include "ath5k.h"
508c2ecf20Sopenharmony_ci#include "base.h"
518c2ecf20Sopenharmony_ci#include "reg.h"
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/********************\
548c2ecf20Sopenharmony_ci* Mac80211 functions *
558c2ecf20Sopenharmony_ci\********************/
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void
588c2ecf20Sopenharmony_ciath5k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
598c2ecf20Sopenharmony_ci	 struct sk_buff *skb)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
628c2ecf20Sopenharmony_ci	u16 qnum = skb_get_queue_mapping(skb);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (WARN_ON(qnum >= ah->ah_capabilities.cap_queues.q_tx_num)) {
658c2ecf20Sopenharmony_ci		ieee80211_free_txskb(hw, skb);
668c2ecf20Sopenharmony_ci		return;
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	ath5k_tx_queue(hw, skb, &ah->txqs[qnum], control);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic int
748c2ecf20Sopenharmony_ciath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
778c2ecf20Sopenharmony_ci	int ret;
788c2ecf20Sopenharmony_ci	struct ath5k_vif *avf = (void *)vif->drv_priv;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	mutex_lock(&ah->lock);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if ((vif->type == NL80211_IFTYPE_AP ||
838c2ecf20Sopenharmony_ci	     vif->type == NL80211_IFTYPE_ADHOC)
848c2ecf20Sopenharmony_ci	    && (ah->num_ap_vifs + ah->num_adhoc_vifs) >= ATH_BCBUF) {
858c2ecf20Sopenharmony_ci		ret = -ELNRNG;
868c2ecf20Sopenharmony_ci		goto end;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* Don't allow other interfaces if one ad-hoc is configured.
908c2ecf20Sopenharmony_ci	 * TODO: Fix the problems with ad-hoc and multiple other interfaces.
918c2ecf20Sopenharmony_ci	 * We would need to operate the HW in ad-hoc mode to allow TSF updates
928c2ecf20Sopenharmony_ci	 * for the IBSS, but this breaks with additional AP or STA interfaces
938c2ecf20Sopenharmony_ci	 * at the moment. */
948c2ecf20Sopenharmony_ci	if (ah->num_adhoc_vifs ||
958c2ecf20Sopenharmony_ci	    (ah->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
968c2ecf20Sopenharmony_ci		ATH5K_ERR(ah, "Only one single ad-hoc interface is allowed.\n");
978c2ecf20Sopenharmony_ci		ret = -ELNRNG;
988c2ecf20Sopenharmony_ci		goto end;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	switch (vif->type) {
1028c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP:
1038c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
1048c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
1058c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT:
1068c2ecf20Sopenharmony_ci		avf->opmode = vif->type;
1078c2ecf20Sopenharmony_ci		break;
1088c2ecf20Sopenharmony_ci	default:
1098c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
1108c2ecf20Sopenharmony_ci		goto end;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	ah->nvifs++;
1148c2ecf20Sopenharmony_ci	ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "add interface mode %d\n", avf->opmode);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* Assign the vap/adhoc to a beacon xmit slot. */
1178c2ecf20Sopenharmony_ci	if ((avf->opmode == NL80211_IFTYPE_AP) ||
1188c2ecf20Sopenharmony_ci	    (avf->opmode == NL80211_IFTYPE_ADHOC) ||
1198c2ecf20Sopenharmony_ci	    (avf->opmode == NL80211_IFTYPE_MESH_POINT)) {
1208c2ecf20Sopenharmony_ci		int slot;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		WARN_ON(list_empty(&ah->bcbuf));
1238c2ecf20Sopenharmony_ci		avf->bbuf = list_first_entry(&ah->bcbuf, struct ath5k_buf,
1248c2ecf20Sopenharmony_ci					     list);
1258c2ecf20Sopenharmony_ci		list_del(&avf->bbuf->list);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		avf->bslot = 0;
1288c2ecf20Sopenharmony_ci		for (slot = 0; slot < ATH_BCBUF; slot++) {
1298c2ecf20Sopenharmony_ci			if (!ah->bslot[slot]) {
1308c2ecf20Sopenharmony_ci				avf->bslot = slot;
1318c2ecf20Sopenharmony_ci				break;
1328c2ecf20Sopenharmony_ci			}
1338c2ecf20Sopenharmony_ci		}
1348c2ecf20Sopenharmony_ci		BUG_ON(ah->bslot[avf->bslot] != NULL);
1358c2ecf20Sopenharmony_ci		ah->bslot[avf->bslot] = vif;
1368c2ecf20Sopenharmony_ci		if (avf->opmode == NL80211_IFTYPE_AP)
1378c2ecf20Sopenharmony_ci			ah->num_ap_vifs++;
1388c2ecf20Sopenharmony_ci		else if (avf->opmode == NL80211_IFTYPE_ADHOC)
1398c2ecf20Sopenharmony_ci			ah->num_adhoc_vifs++;
1408c2ecf20Sopenharmony_ci		else if (avf->opmode == NL80211_IFTYPE_MESH_POINT)
1418c2ecf20Sopenharmony_ci			ah->num_mesh_vifs++;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* Any MAC address is fine, all others are included through the
1458c2ecf20Sopenharmony_ci	 * filter.
1468c2ecf20Sopenharmony_ci	 */
1478c2ecf20Sopenharmony_ci	ath5k_hw_set_lladdr(ah, vif->addr);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	ath5k_update_bssid_mask_and_opmode(ah, vif);
1508c2ecf20Sopenharmony_ci	ret = 0;
1518c2ecf20Sopenharmony_ciend:
1528c2ecf20Sopenharmony_ci	mutex_unlock(&ah->lock);
1538c2ecf20Sopenharmony_ci	return ret;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic void
1588c2ecf20Sopenharmony_ciath5k_remove_interface(struct ieee80211_hw *hw,
1598c2ecf20Sopenharmony_ci		       struct ieee80211_vif *vif)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
1628c2ecf20Sopenharmony_ci	struct ath5k_vif *avf = (void *)vif->drv_priv;
1638c2ecf20Sopenharmony_ci	unsigned int i;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	mutex_lock(&ah->lock);
1668c2ecf20Sopenharmony_ci	ah->nvifs--;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (avf->bbuf) {
1698c2ecf20Sopenharmony_ci		ath5k_txbuf_free_skb(ah, avf->bbuf);
1708c2ecf20Sopenharmony_ci		list_add_tail(&avf->bbuf->list, &ah->bcbuf);
1718c2ecf20Sopenharmony_ci		for (i = 0; i < ATH_BCBUF; i++) {
1728c2ecf20Sopenharmony_ci			if (ah->bslot[i] == vif) {
1738c2ecf20Sopenharmony_ci				ah->bslot[i] = NULL;
1748c2ecf20Sopenharmony_ci				break;
1758c2ecf20Sopenharmony_ci			}
1768c2ecf20Sopenharmony_ci		}
1778c2ecf20Sopenharmony_ci		avf->bbuf = NULL;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci	if (avf->opmode == NL80211_IFTYPE_AP)
1808c2ecf20Sopenharmony_ci		ah->num_ap_vifs--;
1818c2ecf20Sopenharmony_ci	else if (avf->opmode == NL80211_IFTYPE_ADHOC)
1828c2ecf20Sopenharmony_ci		ah->num_adhoc_vifs--;
1838c2ecf20Sopenharmony_ci	else if (avf->opmode == NL80211_IFTYPE_MESH_POINT)
1848c2ecf20Sopenharmony_ci		ah->num_mesh_vifs--;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	ath5k_update_bssid_mask_and_opmode(ah, NULL);
1878c2ecf20Sopenharmony_ci	mutex_unlock(&ah->lock);
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/*
1928c2ecf20Sopenharmony_ci * TODO: Phy disable/diversity etc
1938c2ecf20Sopenharmony_ci */
1948c2ecf20Sopenharmony_cistatic int
1958c2ecf20Sopenharmony_ciath5k_config(struct ieee80211_hw *hw, u32 changed)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
1988c2ecf20Sopenharmony_ci	struct ieee80211_conf *conf = &hw->conf;
1998c2ecf20Sopenharmony_ci	int ret = 0;
2008c2ecf20Sopenharmony_ci	int i;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	mutex_lock(&ah->lock);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
2058c2ecf20Sopenharmony_ci		ret = ath5k_chan_set(ah, &conf->chandef);
2068c2ecf20Sopenharmony_ci		if (ret < 0)
2078c2ecf20Sopenharmony_ci			goto unlock;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if ((changed & IEEE80211_CONF_CHANGE_POWER) &&
2118c2ecf20Sopenharmony_ci	(ah->ah_txpower.txp_requested != conf->power_level)) {
2128c2ecf20Sopenharmony_ci		ah->ah_txpower.txp_requested = conf->power_level;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		/* Half dB steps */
2158c2ecf20Sopenharmony_ci		ath5k_hw_set_txpower_limit(ah, (conf->power_level * 2));
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
2198c2ecf20Sopenharmony_ci		ah->ah_retry_long = conf->long_frame_max_tx_count;
2208c2ecf20Sopenharmony_ci		ah->ah_retry_short = conf->short_frame_max_tx_count;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++)
2238c2ecf20Sopenharmony_ci			ath5k_hw_set_tx_retry_limits(ah, i);
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* TODO:
2278c2ecf20Sopenharmony_ci	 * 1) Move this on config_interface and handle each case
2288c2ecf20Sopenharmony_ci	 * separately eg. when we have only one STA vif, use
2298c2ecf20Sopenharmony_ci	 * AR5K_ANTMODE_SINGLE_AP
2308c2ecf20Sopenharmony_ci	 *
2318c2ecf20Sopenharmony_ci	 * 2) Allow the user to change antenna mode eg. when only
2328c2ecf20Sopenharmony_ci	 * one antenna is present
2338c2ecf20Sopenharmony_ci	 *
2348c2ecf20Sopenharmony_ci	 * 3) Allow the user to set default/tx antenna when possible
2358c2ecf20Sopenharmony_ci	 *
2368c2ecf20Sopenharmony_ci	 * 4) Default mode should handle 90% of the cases, together
2378c2ecf20Sopenharmony_ci	 * with fixed a/b and single AP modes we should be able to
2388c2ecf20Sopenharmony_ci	 * handle 99%. Sectored modes are extreme cases and i still
2398c2ecf20Sopenharmony_ci	 * haven't found a usage for them. If we decide to support them,
2408c2ecf20Sopenharmony_ci	 * then we must allow the user to set how many tx antennas we
2418c2ecf20Sopenharmony_ci	 * have available
2428c2ecf20Sopenharmony_ci	 */
2438c2ecf20Sopenharmony_ci	ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ciunlock:
2468c2ecf20Sopenharmony_ci	mutex_unlock(&ah->lock);
2478c2ecf20Sopenharmony_ci	return ret;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic void
2528c2ecf20Sopenharmony_ciath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
2538c2ecf20Sopenharmony_ci		       struct ieee80211_bss_conf *bss_conf, u32 changes)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct ath5k_vif *avf = (void *)vif->drv_priv;
2568c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
2578c2ecf20Sopenharmony_ci	struct ath_common *common = ath5k_hw_common(ah);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	mutex_lock(&ah->lock);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_BSSID) {
2628c2ecf20Sopenharmony_ci		/* Cache for later use during resets */
2638c2ecf20Sopenharmony_ci		memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
2648c2ecf20Sopenharmony_ci		common->curaid = 0;
2658c2ecf20Sopenharmony_ci		ath5k_hw_set_bssid(ah);
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_BEACON_INT)
2698c2ecf20Sopenharmony_ci		ah->bintval = bss_conf->beacon_int;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_ERP_SLOT) {
2728c2ecf20Sopenharmony_ci		int slot_time;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		ah->ah_short_slot = bss_conf->use_short_slot;
2758c2ecf20Sopenharmony_ci		slot_time = ath5k_hw_get_default_slottime(ah) +
2768c2ecf20Sopenharmony_ci			    3 * ah->ah_coverage_class;
2778c2ecf20Sopenharmony_ci		ath5k_hw_set_ifs_intervals(ah, slot_time);
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_ASSOC) {
2818c2ecf20Sopenharmony_ci		avf->assoc = bss_conf->assoc;
2828c2ecf20Sopenharmony_ci		if (bss_conf->assoc)
2838c2ecf20Sopenharmony_ci			ah->assoc = bss_conf->assoc;
2848c2ecf20Sopenharmony_ci		else
2858c2ecf20Sopenharmony_ci			ah->assoc = ath5k_any_vif_assoc(ah);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		if (ah->opmode == NL80211_IFTYPE_STATION)
2888c2ecf20Sopenharmony_ci			ath5k_set_beacon_filter(hw, ah->assoc);
2898c2ecf20Sopenharmony_ci		ath5k_hw_set_ledstate(ah, ah->assoc ?
2908c2ecf20Sopenharmony_ci			AR5K_LED_ASSOC : AR5K_LED_INIT);
2918c2ecf20Sopenharmony_ci		if (bss_conf->assoc) {
2928c2ecf20Sopenharmony_ci			ATH5K_DBG(ah, ATH5K_DEBUG_ANY,
2938c2ecf20Sopenharmony_ci				  "Bss Info ASSOC %d, bssid: %pM\n",
2948c2ecf20Sopenharmony_ci				  bss_conf->aid, common->curbssid);
2958c2ecf20Sopenharmony_ci			common->curaid = bss_conf->aid;
2968c2ecf20Sopenharmony_ci			ath5k_hw_set_bssid(ah);
2978c2ecf20Sopenharmony_ci			/* Once ANI is available you would start it here */
2988c2ecf20Sopenharmony_ci		}
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_BEACON) {
3028c2ecf20Sopenharmony_ci		spin_lock_bh(&ah->block);
3038c2ecf20Sopenharmony_ci		ath5k_beacon_update(hw, vif);
3048c2ecf20Sopenharmony_ci		spin_unlock_bh(&ah->block);
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_BEACON_ENABLED)
3088c2ecf20Sopenharmony_ci		ah->enable_beacon = bss_conf->enable_beacon;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED |
3118c2ecf20Sopenharmony_ci		       BSS_CHANGED_BEACON_INT))
3128c2ecf20Sopenharmony_ci		ath5k_beacon_config(ah);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	mutex_unlock(&ah->lock);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic u64
3198c2ecf20Sopenharmony_ciath5k_prepare_multicast(struct ieee80211_hw *hw,
3208c2ecf20Sopenharmony_ci			struct netdev_hw_addr_list *mc_list)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	u32 mfilt[2], val;
3238c2ecf20Sopenharmony_ci	u8 pos;
3248c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	mfilt[0] = 0;
3278c2ecf20Sopenharmony_ci	mfilt[1] = 0;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	netdev_hw_addr_list_for_each(ha, mc_list) {
3308c2ecf20Sopenharmony_ci		/* calculate XOR of eight 6-bit values */
3318c2ecf20Sopenharmony_ci		val = get_unaligned_le32(ha->addr + 0);
3328c2ecf20Sopenharmony_ci		pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
3338c2ecf20Sopenharmony_ci		val = get_unaligned_le32(ha->addr + 3);
3348c2ecf20Sopenharmony_ci		pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
3358c2ecf20Sopenharmony_ci		pos &= 0x3f;
3368c2ecf20Sopenharmony_ci		mfilt[pos / 32] |= (1 << (pos % 32));
3378c2ecf20Sopenharmony_ci		/* XXX: we might be able to just do this instead,
3388c2ecf20Sopenharmony_ci		* but not sure, needs testing, if we do use this we'd
3398c2ecf20Sopenharmony_ci		* need to inform below not to reset the mcast */
3408c2ecf20Sopenharmony_ci		/* ath5k_hw_set_mcast_filterindex(ah,
3418c2ecf20Sopenharmony_ci		 *      ha->addr[5]); */
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	return ((u64)(mfilt[1]) << 32) | mfilt[0];
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci/*
3498c2ecf20Sopenharmony_ci * o always accept unicast, broadcast, and multicast traffic
3508c2ecf20Sopenharmony_ci * o multicast traffic for all BSSIDs will be enabled if mac80211
3518c2ecf20Sopenharmony_ci *   says it should be
3528c2ecf20Sopenharmony_ci * o maintain current state of phy ofdm or phy cck error reception.
3538c2ecf20Sopenharmony_ci *   If the hardware detects any of these type of errors then
3548c2ecf20Sopenharmony_ci *   ath5k_hw_get_rx_filter() will pass to us the respective
3558c2ecf20Sopenharmony_ci *   hardware filters to be able to receive these type of frames.
3568c2ecf20Sopenharmony_ci * o probe request frames are accepted only when operating in
3578c2ecf20Sopenharmony_ci *   hostap, adhoc, or monitor modes
3588c2ecf20Sopenharmony_ci * o enable promiscuous mode according to the interface state
3598c2ecf20Sopenharmony_ci * o accept beacons:
3608c2ecf20Sopenharmony_ci *   - when operating in adhoc mode so the 802.11 layer creates
3618c2ecf20Sopenharmony_ci *     node table entries for peers,
3628c2ecf20Sopenharmony_ci *   - when operating in station mode for collecting rssi data when
3638c2ecf20Sopenharmony_ci *     the station is otherwise quiet, or
3648c2ecf20Sopenharmony_ci *   - when scanning
3658c2ecf20Sopenharmony_ci */
3668c2ecf20Sopenharmony_cistatic void
3678c2ecf20Sopenharmony_ciath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
3688c2ecf20Sopenharmony_ci		       unsigned int *new_flags, u64 multicast)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci#define SUPPORTED_FIF_FLAGS \
3718c2ecf20Sopenharmony_ci	(FIF_ALLMULTI | FIF_FCSFAIL | \
3728c2ecf20Sopenharmony_ci	FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \
3738c2ecf20Sopenharmony_ci	FIF_BCN_PRBRESP_PROMISC)
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
3768c2ecf20Sopenharmony_ci	u32 mfilt[2], rfilt;
3778c2ecf20Sopenharmony_ci	struct ath5k_vif_iter_data iter_data; /* to count STA interfaces */
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	mutex_lock(&ah->lock);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	mfilt[0] = multicast;
3828c2ecf20Sopenharmony_ci	mfilt[1] = multicast >> 32;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* Only deal with supported flags */
3858c2ecf20Sopenharmony_ci	changed_flags &= SUPPORTED_FIF_FLAGS;
3868c2ecf20Sopenharmony_ci	*new_flags &= SUPPORTED_FIF_FLAGS;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/* If HW detects any phy or radar errors, leave those filters on.
3898c2ecf20Sopenharmony_ci	 * Also, always enable Unicast, Broadcasts and Multicast
3908c2ecf20Sopenharmony_ci	 * XXX: move unicast, bssid broadcasts and multicast to mac80211 */
3918c2ecf20Sopenharmony_ci	rfilt = (ath5k_hw_get_rx_filter(ah) & (AR5K_RX_FILTER_PHYERR)) |
3928c2ecf20Sopenharmony_ci		(AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST |
3938c2ecf20Sopenharmony_ci		AR5K_RX_FILTER_MCAST);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/* Note, AR5K_RX_FILTER_MCAST is already enabled */
3968c2ecf20Sopenharmony_ci	if (*new_flags & FIF_ALLMULTI) {
3978c2ecf20Sopenharmony_ci		mfilt[0] =  ~0;
3988c2ecf20Sopenharmony_ci		mfilt[1] =  ~0;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/* This is the best we can do */
4028c2ecf20Sopenharmony_ci	if (*new_flags & (FIF_FCSFAIL | FIF_PLCPFAIL))
4038c2ecf20Sopenharmony_ci		rfilt |= AR5K_RX_FILTER_PHYERR;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/* FIF_BCN_PRBRESP_PROMISC really means to enable beacons
4068c2ecf20Sopenharmony_ci	* and probes for any BSSID */
4078c2ecf20Sopenharmony_ci	if ((*new_flags & FIF_BCN_PRBRESP_PROMISC) || (ah->nvifs > 1))
4088c2ecf20Sopenharmony_ci		rfilt |= AR5K_RX_FILTER_BEACON;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* FIF_CONTROL doc says we should only pass on control frames for this
4118c2ecf20Sopenharmony_ci	 * station. This needs testing. I believe right now this
4128c2ecf20Sopenharmony_ci	 * enables *all* control frames, which is OK.. but
4138c2ecf20Sopenharmony_ci	 * but we should see if we can improve on granularity */
4148c2ecf20Sopenharmony_ci	if (*new_flags & FIF_CONTROL)
4158c2ecf20Sopenharmony_ci		rfilt |= AR5K_RX_FILTER_CONTROL;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/* Additional settings per mode -- this is per ath5k */
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	/* XXX move these to mac80211, and add a beacon IFF flag to mac80211 */
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	switch (ah->opmode) {
4228c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT:
4238c2ecf20Sopenharmony_ci		rfilt |= AR5K_RX_FILTER_CONTROL |
4248c2ecf20Sopenharmony_ci			 AR5K_RX_FILTER_BEACON |
4258c2ecf20Sopenharmony_ci			 AR5K_RX_FILTER_PROBEREQ |
4268c2ecf20Sopenharmony_ci			 AR5K_RX_FILTER_PROM;
4278c2ecf20Sopenharmony_ci		break;
4288c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP:
4298c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
4308c2ecf20Sopenharmony_ci		rfilt |= AR5K_RX_FILTER_PROBEREQ |
4318c2ecf20Sopenharmony_ci			 AR5K_RX_FILTER_BEACON;
4328c2ecf20Sopenharmony_ci		break;
4338c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
4348c2ecf20Sopenharmony_ci		if (ah->assoc)
4358c2ecf20Sopenharmony_ci			rfilt |= AR5K_RX_FILTER_BEACON;
4368c2ecf20Sopenharmony_ci	default:
4378c2ecf20Sopenharmony_ci		break;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	iter_data.hw_macaddr = NULL;
4418c2ecf20Sopenharmony_ci	iter_data.n_stas = 0;
4428c2ecf20Sopenharmony_ci	iter_data.need_set_hw_addr = false;
4438c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(
4448c2ecf20Sopenharmony_ci		ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
4458c2ecf20Sopenharmony_ci		ath5k_vif_iter, &iter_data);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	/* Set up RX Filter */
4488c2ecf20Sopenharmony_ci	if (iter_data.n_stas > 1) {
4498c2ecf20Sopenharmony_ci		/* If you have multiple STA interfaces connected to
4508c2ecf20Sopenharmony_ci		 * different APs, ARPs are not received (most of the time?)
4518c2ecf20Sopenharmony_ci		 * Enabling PROMISC appears to fix that problem.
4528c2ecf20Sopenharmony_ci		 */
4538c2ecf20Sopenharmony_ci		rfilt |= AR5K_RX_FILTER_PROM;
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/* Set filters */
4578c2ecf20Sopenharmony_ci	ath5k_hw_set_rx_filter(ah, rfilt);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	/* Set multicast bits */
4608c2ecf20Sopenharmony_ci	ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]);
4618c2ecf20Sopenharmony_ci	/* Set the cached hw filter flags, this will later actually
4628c2ecf20Sopenharmony_ci	 * be set in HW */
4638c2ecf20Sopenharmony_ci	ah->filter_flags = rfilt;
4648c2ecf20Sopenharmony_ci	/* Store current FIF filter flags */
4658c2ecf20Sopenharmony_ci	ah->fif_filter_flags = *new_flags;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	mutex_unlock(&ah->lock);
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic int
4728c2ecf20Sopenharmony_ciath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
4738c2ecf20Sopenharmony_ci	      struct ieee80211_vif *vif, struct ieee80211_sta *sta,
4748c2ecf20Sopenharmony_ci	      struct ieee80211_key_conf *key)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
4778c2ecf20Sopenharmony_ci	struct ath_common *common = ath5k_hw_common(ah);
4788c2ecf20Sopenharmony_ci	int ret = 0;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (ath5k_modparam_nohwcrypt)
4818c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT)
4848c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_ADHOC &&
4878c2ecf20Sopenharmony_ci	    (key->cipher == WLAN_CIPHER_SUITE_TKIP ||
4888c2ecf20Sopenharmony_ci	     key->cipher == WLAN_CIPHER_SUITE_CCMP) &&
4898c2ecf20Sopenharmony_ci	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
4908c2ecf20Sopenharmony_ci		/* don't program group keys when using IBSS_RSN */
4918c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	switch (key->cipher) {
4958c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
4968c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
4978c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
4988c2ecf20Sopenharmony_ci		break;
4998c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
5008c2ecf20Sopenharmony_ci		if (common->crypt_caps & ATH_CRYPT_CAP_CIPHER_AESCCM)
5018c2ecf20Sopenharmony_ci			break;
5028c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5038c2ecf20Sopenharmony_ci	default:
5048c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	mutex_lock(&ah->lock);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	switch (cmd) {
5108c2ecf20Sopenharmony_ci	case SET_KEY:
5118c2ecf20Sopenharmony_ci		ret = ath_key_config(common, vif, sta, key);
5128c2ecf20Sopenharmony_ci		if (ret >= 0) {
5138c2ecf20Sopenharmony_ci			key->hw_key_idx = ret;
5148c2ecf20Sopenharmony_ci			/* push IV and Michael MIC generation to stack */
5158c2ecf20Sopenharmony_ci			key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
5168c2ecf20Sopenharmony_ci			if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
5178c2ecf20Sopenharmony_ci				key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
5188c2ecf20Sopenharmony_ci			if (key->cipher == WLAN_CIPHER_SUITE_CCMP)
5198c2ecf20Sopenharmony_ci				key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
5208c2ecf20Sopenharmony_ci			ret = 0;
5218c2ecf20Sopenharmony_ci		}
5228c2ecf20Sopenharmony_ci		break;
5238c2ecf20Sopenharmony_ci	case DISABLE_KEY:
5248c2ecf20Sopenharmony_ci		ath_key_delete(common, key->hw_key_idx);
5258c2ecf20Sopenharmony_ci		break;
5268c2ecf20Sopenharmony_ci	default:
5278c2ecf20Sopenharmony_ci		ret = -EINVAL;
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	mutex_unlock(&ah->lock);
5318c2ecf20Sopenharmony_ci	return ret;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic void
5368c2ecf20Sopenharmony_ciath5k_sw_scan_start(struct ieee80211_hw *hw,
5378c2ecf20Sopenharmony_ci		    struct ieee80211_vif *vif,
5388c2ecf20Sopenharmony_ci		    const u8 *mac_addr)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
5418c2ecf20Sopenharmony_ci	if (!ah->assoc)
5428c2ecf20Sopenharmony_ci		ath5k_hw_set_ledstate(ah, AR5K_LED_SCAN);
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic void
5478c2ecf20Sopenharmony_ciath5k_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
5508c2ecf20Sopenharmony_ci	ath5k_hw_set_ledstate(ah, ah->assoc ?
5518c2ecf20Sopenharmony_ci		AR5K_LED_ASSOC : AR5K_LED_INIT);
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic int
5568c2ecf20Sopenharmony_ciath5k_get_stats(struct ieee80211_hw *hw,
5578c2ecf20Sopenharmony_ci		struct ieee80211_low_level_stats *stats)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	/* Force update */
5628c2ecf20Sopenharmony_ci	ath5k_hw_update_mib_counters(ah);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	stats->dot11ACKFailureCount = ah->stats.ack_fail;
5658c2ecf20Sopenharmony_ci	stats->dot11RTSFailureCount = ah->stats.rts_fail;
5668c2ecf20Sopenharmony_ci	stats->dot11RTSSuccessCount = ah->stats.rts_ok;
5678c2ecf20Sopenharmony_ci	stats->dot11FCSErrorCount = ah->stats.fcs_error;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	return 0;
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_cistatic int
5748c2ecf20Sopenharmony_ciath5k_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
5758c2ecf20Sopenharmony_ci	      const struct ieee80211_tx_queue_params *params)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
5788c2ecf20Sopenharmony_ci	struct ath5k_txq_info qi;
5798c2ecf20Sopenharmony_ci	int ret = 0;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	if (queue >= ah->ah_capabilities.cap_queues.q_tx_num)
5828c2ecf20Sopenharmony_ci		return 0;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	mutex_lock(&ah->lock);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	ath5k_hw_get_tx_queueprops(ah, queue, &qi);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	qi.tqi_aifs = params->aifs;
5898c2ecf20Sopenharmony_ci	qi.tqi_cw_min = params->cw_min;
5908c2ecf20Sopenharmony_ci	qi.tqi_cw_max = params->cw_max;
5918c2ecf20Sopenharmony_ci	qi.tqi_burst_time = params->txop * 32;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	ATH5K_DBG(ah, ATH5K_DEBUG_ANY,
5948c2ecf20Sopenharmony_ci		  "Configure tx [queue %d],  "
5958c2ecf20Sopenharmony_ci		  "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
5968c2ecf20Sopenharmony_ci		  queue, params->aifs, params->cw_min,
5978c2ecf20Sopenharmony_ci		  params->cw_max, params->txop);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	if (ath5k_hw_set_tx_queueprops(ah, queue, &qi)) {
6008c2ecf20Sopenharmony_ci		ATH5K_ERR(ah,
6018c2ecf20Sopenharmony_ci			  "Unable to update hardware queue %u!\n", queue);
6028c2ecf20Sopenharmony_ci		ret = -EIO;
6038c2ecf20Sopenharmony_ci	} else
6048c2ecf20Sopenharmony_ci		ath5k_hw_reset_tx_queue(ah, queue);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	mutex_unlock(&ah->lock);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	return ret;
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic u64
6138c2ecf20Sopenharmony_ciath5k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	return ath5k_hw_get_tsf64(ah);
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_cistatic void
6228c2ecf20Sopenharmony_ciath5k_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf)
6238c2ecf20Sopenharmony_ci{
6248c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	ath5k_hw_set_tsf64(ah, tsf);
6278c2ecf20Sopenharmony_ci}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic void
6318c2ecf20Sopenharmony_ciath5k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	/*
6368c2ecf20Sopenharmony_ci	 * in IBSS mode we need to update the beacon timers too.
6378c2ecf20Sopenharmony_ci	 * this will also reset the TSF if we call it with 0
6388c2ecf20Sopenharmony_ci	 */
6398c2ecf20Sopenharmony_ci	if (ah->opmode == NL80211_IFTYPE_ADHOC)
6408c2ecf20Sopenharmony_ci		ath5k_beacon_update_timers(ah, 0);
6418c2ecf20Sopenharmony_ci	else
6428c2ecf20Sopenharmony_ci		ath5k_hw_reset_tsf(ah);
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic int
6478c2ecf20Sopenharmony_ciath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
6508c2ecf20Sopenharmony_ci	struct ieee80211_conf *conf = &hw->conf;
6518c2ecf20Sopenharmony_ci	struct ath_common *common = ath5k_hw_common(ah);
6528c2ecf20Sopenharmony_ci	struct ath_cycle_counters *cc = &common->cc_survey;
6538c2ecf20Sopenharmony_ci	unsigned int div = common->clockrate * 1000;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	if (idx != 0)
6568c2ecf20Sopenharmony_ci		return -ENOENT;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	spin_lock_bh(&common->cc_lock);
6598c2ecf20Sopenharmony_ci	ath_hw_cycle_counters_update(common);
6608c2ecf20Sopenharmony_ci	if (cc->cycles > 0) {
6618c2ecf20Sopenharmony_ci		ah->survey.time += cc->cycles / div;
6628c2ecf20Sopenharmony_ci		ah->survey.time_busy += cc->rx_busy / div;
6638c2ecf20Sopenharmony_ci		ah->survey.time_rx += cc->rx_frame / div;
6648c2ecf20Sopenharmony_ci		ah->survey.time_tx += cc->tx_frame / div;
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci	memset(cc, 0, sizeof(*cc));
6678c2ecf20Sopenharmony_ci	spin_unlock_bh(&common->cc_lock);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	memcpy(survey, &ah->survey, sizeof(*survey));
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	survey->channel = conf->chandef.chan;
6728c2ecf20Sopenharmony_ci	survey->noise = ah->ah_noise_floor;
6738c2ecf20Sopenharmony_ci	survey->filled = SURVEY_INFO_NOISE_DBM |
6748c2ecf20Sopenharmony_ci			SURVEY_INFO_IN_USE |
6758c2ecf20Sopenharmony_ci			SURVEY_INFO_TIME |
6768c2ecf20Sopenharmony_ci			SURVEY_INFO_TIME_BUSY |
6778c2ecf20Sopenharmony_ci			SURVEY_INFO_TIME_RX |
6788c2ecf20Sopenharmony_ci			SURVEY_INFO_TIME_TX;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	return 0;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci/**
6858c2ecf20Sopenharmony_ci * ath5k_set_coverage_class - Set IEEE 802.11 coverage class
6868c2ecf20Sopenharmony_ci *
6878c2ecf20Sopenharmony_ci * @hw: struct ieee80211_hw pointer
6888c2ecf20Sopenharmony_ci * @coverage_class: IEEE 802.11 coverage class number
6898c2ecf20Sopenharmony_ci *
6908c2ecf20Sopenharmony_ci * Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for given
6918c2ecf20Sopenharmony_ci * coverage class. The values are persistent, they are restored after device
6928c2ecf20Sopenharmony_ci * reset.
6938c2ecf20Sopenharmony_ci */
6948c2ecf20Sopenharmony_cistatic void
6958c2ecf20Sopenharmony_ciath5k_set_coverage_class(struct ieee80211_hw *hw, s16 coverage_class)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	mutex_lock(&ah->lock);
7008c2ecf20Sopenharmony_ci	ath5k_hw_set_coverage_class(ah, coverage_class);
7018c2ecf20Sopenharmony_ci	mutex_unlock(&ah->lock);
7028c2ecf20Sopenharmony_ci}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_cistatic int
7068c2ecf20Sopenharmony_ciath5k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	if (tx_ant == 1 && rx_ant == 1)
7118c2ecf20Sopenharmony_ci		ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_A);
7128c2ecf20Sopenharmony_ci	else if (tx_ant == 2 && rx_ant == 2)
7138c2ecf20Sopenharmony_ci		ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_B);
7148c2ecf20Sopenharmony_ci	else if ((tx_ant & 3) == 3 && (rx_ant & 3) == 3)
7158c2ecf20Sopenharmony_ci		ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT);
7168c2ecf20Sopenharmony_ci	else
7178c2ecf20Sopenharmony_ci		return -EINVAL;
7188c2ecf20Sopenharmony_ci	return 0;
7198c2ecf20Sopenharmony_ci}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_cistatic int
7238c2ecf20Sopenharmony_ciath5k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
7248c2ecf20Sopenharmony_ci{
7258c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	switch (ah->ah_ant_mode) {
7288c2ecf20Sopenharmony_ci	case AR5K_ANTMODE_FIXED_A:
7298c2ecf20Sopenharmony_ci		*tx_ant = 1; *rx_ant = 1; break;
7308c2ecf20Sopenharmony_ci	case AR5K_ANTMODE_FIXED_B:
7318c2ecf20Sopenharmony_ci		*tx_ant = 2; *rx_ant = 2; break;
7328c2ecf20Sopenharmony_ci	case AR5K_ANTMODE_DEFAULT:
7338c2ecf20Sopenharmony_ci		*tx_ant = 3; *rx_ant = 3; break;
7348c2ecf20Sopenharmony_ci	}
7358c2ecf20Sopenharmony_ci	return 0;
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_cistatic void ath5k_get_ringparam(struct ieee80211_hw *hw,
7408c2ecf20Sopenharmony_ci				u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	*tx = ah->txqs[AR5K_TX_QUEUE_ID_DATA_MIN].txq_max;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	*tx_max = ATH5K_TXQ_LEN_MAX;
7478c2ecf20Sopenharmony_ci	*rx = *rx_max = ATH_RXBUF;
7488c2ecf20Sopenharmony_ci}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_cistatic int ath5k_set_ringparam(struct ieee80211_hw *hw, u32 tx, u32 rx)
7528c2ecf20Sopenharmony_ci{
7538c2ecf20Sopenharmony_ci	struct ath5k_hw *ah = hw->priv;
7548c2ecf20Sopenharmony_ci	u16 qnum;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	/* only support setting tx ring size for now */
7578c2ecf20Sopenharmony_ci	if (rx != ATH_RXBUF)
7588c2ecf20Sopenharmony_ci		return -EINVAL;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	/* restrict tx ring size min/max */
7618c2ecf20Sopenharmony_ci	if (!tx || tx > ATH5K_TXQ_LEN_MAX)
7628c2ecf20Sopenharmony_ci		return -EINVAL;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	for (qnum = 0; qnum < ARRAY_SIZE(ah->txqs); qnum++) {
7658c2ecf20Sopenharmony_ci		if (!ah->txqs[qnum].setup)
7668c2ecf20Sopenharmony_ci			continue;
7678c2ecf20Sopenharmony_ci		if (ah->txqs[qnum].qnum < AR5K_TX_QUEUE_ID_DATA_MIN ||
7688c2ecf20Sopenharmony_ci		    ah->txqs[qnum].qnum > AR5K_TX_QUEUE_ID_DATA_MAX)
7698c2ecf20Sopenharmony_ci			continue;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci		ah->txqs[qnum].txq_max = tx;
7728c2ecf20Sopenharmony_ci		if (ah->txqs[qnum].txq_len >= ah->txqs[qnum].txq_max)
7738c2ecf20Sopenharmony_ci			ieee80211_stop_queue(hw, ah->txqs[qnum].qnum);
7748c2ecf20Sopenharmony_ci	}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	return 0;
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ciconst struct ieee80211_ops ath5k_hw_ops = {
7818c2ecf20Sopenharmony_ci	.tx			= ath5k_tx,
7828c2ecf20Sopenharmony_ci	.start			= ath5k_start,
7838c2ecf20Sopenharmony_ci	.stop			= ath5k_stop,
7848c2ecf20Sopenharmony_ci	.add_interface		= ath5k_add_interface,
7858c2ecf20Sopenharmony_ci	/* .change_interface	= not implemented */
7868c2ecf20Sopenharmony_ci	.remove_interface	= ath5k_remove_interface,
7878c2ecf20Sopenharmony_ci	.config			= ath5k_config,
7888c2ecf20Sopenharmony_ci	.bss_info_changed	= ath5k_bss_info_changed,
7898c2ecf20Sopenharmony_ci	.prepare_multicast	= ath5k_prepare_multicast,
7908c2ecf20Sopenharmony_ci	.configure_filter	= ath5k_configure_filter,
7918c2ecf20Sopenharmony_ci	/* .set_tim		= not implemented */
7928c2ecf20Sopenharmony_ci	.set_key		= ath5k_set_key,
7938c2ecf20Sopenharmony_ci	/* .update_tkip_key	= not implemented */
7948c2ecf20Sopenharmony_ci	/* .hw_scan		= not implemented */
7958c2ecf20Sopenharmony_ci	.sw_scan_start		= ath5k_sw_scan_start,
7968c2ecf20Sopenharmony_ci	.sw_scan_complete	= ath5k_sw_scan_complete,
7978c2ecf20Sopenharmony_ci	.get_stats		= ath5k_get_stats,
7988c2ecf20Sopenharmony_ci	/* .set_frag_threshold	= not implemented */
7998c2ecf20Sopenharmony_ci	/* .set_rts_threshold	= not implemented */
8008c2ecf20Sopenharmony_ci	/* .sta_add		= not implemented */
8018c2ecf20Sopenharmony_ci	/* .sta_remove		= not implemented */
8028c2ecf20Sopenharmony_ci	/* .sta_notify		= not implemented */
8038c2ecf20Sopenharmony_ci	.conf_tx		= ath5k_conf_tx,
8048c2ecf20Sopenharmony_ci	.get_tsf		= ath5k_get_tsf,
8058c2ecf20Sopenharmony_ci	.set_tsf		= ath5k_set_tsf,
8068c2ecf20Sopenharmony_ci	.reset_tsf		= ath5k_reset_tsf,
8078c2ecf20Sopenharmony_ci	/* .tx_last_beacon	= not implemented */
8088c2ecf20Sopenharmony_ci	/* .ampdu_action	= not needed */
8098c2ecf20Sopenharmony_ci	.get_survey		= ath5k_get_survey,
8108c2ecf20Sopenharmony_ci	.set_coverage_class	= ath5k_set_coverage_class,
8118c2ecf20Sopenharmony_ci	/* .rfkill_poll		= not implemented */
8128c2ecf20Sopenharmony_ci	/* .flush		= not implemented */
8138c2ecf20Sopenharmony_ci	/* .channel_switch	= not implemented */
8148c2ecf20Sopenharmony_ci	/* .napi_poll		= not implemented */
8158c2ecf20Sopenharmony_ci	.set_antenna		= ath5k_set_antenna,
8168c2ecf20Sopenharmony_ci	.get_antenna		= ath5k_get_antenna,
8178c2ecf20Sopenharmony_ci	.set_ringparam		= ath5k_set_ringparam,
8188c2ecf20Sopenharmony_ci	.get_ringparam		= ath5k_get_ringparam,
8198c2ecf20Sopenharmony_ci};
820