4754 lines · 125.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz> * Copyright 2007	Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014  Intel Mobile Communications GmbH * Copyright (C) 2015-2017	Intel Deutschland GmbH * Copyright (C) 2018-2026 Intel Corporation * * utilities for mac80211 */ #include <net/mac80211.h>#include <linux/netdevice.h>#include <linux/export.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/skbuff.h>#include <linux/etherdevice.h>#include <linux/if_arp.h>#include <linux/bitmap.h>#include <linux/crc32.h>#include <net/net_namespace.h>#include <net/cfg80211.h>#include <net/rtnetlink.h>#include <kunit/visibility.h> #include "ieee80211_i.h"#include "driver-ops.h"#include "rate.h"#include "mesh.h"#include "wme.h"#include "led.h"#include "wep.h" /* privid for wiphys to determine whether they belong to us or not */const void *const mac80211_wiphy_privid = &mac80211_wiphy_privid; struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy){	struct ieee80211_local *local; 	local = wiphy_priv(wiphy);	return &local->hw;}EXPORT_SYMBOL(wiphy_to_ieee80211_hw); const struct ieee80211_conn_settings ieee80211_conn_settings_unlimited = {	.mode = IEEE80211_CONN_MODE_EHT,	.bw_limit = IEEE80211_CONN_BW_LIMIT_320,}; u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,			enum nl80211_iftype type){	__le16 fc = hdr->frame_control; 	if (ieee80211_is_data(fc)) {		if (len < 24) /* drop incorrect hdr len (data) */			return NULL; 		if (ieee80211_has_a4(fc))			return NULL;		if (ieee80211_has_tods(fc))			return hdr->addr1;		if (ieee80211_has_fromds(fc))			return hdr->addr2; 		return hdr->addr3;	} 	if (ieee80211_is_s1g_beacon(fc)) {		struct ieee80211_ext *ext = (void *) hdr; 		if (len < offsetofend(struct ieee80211_ext, u.s1g_beacon.sa))			return NULL; 		return ext->u.s1g_beacon.sa;	} 	if (ieee80211_is_mgmt(fc)) {		if (len < 24) /* drop incorrect hdr len (mgmt) */			return NULL;		return hdr->addr3;	} 	if (ieee80211_is_ctl(fc)) {		if (ieee80211_is_pspoll(fc))			return hdr->addr1; 		if (ieee80211_is_back_req(fc)) {			switch (type) {			case NL80211_IFTYPE_STATION:				return hdr->addr2;			case NL80211_IFTYPE_AP:			case NL80211_IFTYPE_AP_VLAN:				return hdr->addr1;			default:				break; /* fall through to the return */			}		}	} 	return NULL;} void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx){	struct sk_buff *skb;	struct ieee80211_hdr *hdr; 	skb_queue_walk(&tx->skbs, skb) {		hdr = (struct ieee80211_hdr *) skb->data;		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);	}} int ieee80211_frame_duration(enum nl80211_band band, size_t len,			     int rate, int erp, int short_preamble){	int dur; 	/* calculate duration (in microseconds, rounded up to next higher	 * integer if it includes a fractional microsecond) to send frame of	 * len bytes (does not include FCS) at the given rate. Duration will	 * also include SIFS.	 *	 * rate is in 100 kbps, so divident is multiplied by 10 in the	 * DIV_ROUND_UP() operations.	 */ 	if (band == NL80211_BAND_5GHZ || erp) {		/*		 * OFDM:		 *		 * N_DBPS = DATARATE x 4		 * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS)		 *	(16 = SIGNAL time, 6 = tail bits)		 * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext		 *		 * T_SYM = 4 usec		 * 802.11a - 18.5.2: aSIFSTime = 16 usec		 * 802.11g - 19.8.4: aSIFSTime = 10 usec +		 *	signal ext = 6 usec		 */		dur = 16; /* SIFS + signal ext */		dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */		dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */ 		/* rates should already consider the channel bandwidth,		 * don't apply divisor again.		 */		dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,					4 * rate); /* T_SYM x N_SYM */	} else {		/*		 * 802.11b or 802.11g with 802.11b compatibility:		 * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime +		 * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0.		 *		 * 802.11 (DS): 15.3.3, 802.11b: 18.3.4		 * aSIFSTime = 10 usec		 * aPreambleLength = 144 usec or 72 usec with short preamble		 * aPLCPHeaderLength = 48 usec or 24 usec with short preamble		 */		dur = 10; /* aSIFSTime = 10 usec */		dur += short_preamble ? (72 + 24) : (144 + 48); 		dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate);	} 	return dur;} /* Exported duration function for driver use */__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,					struct ieee80211_vif *vif,					enum nl80211_band band,					size_t frame_len,					struct ieee80211_rate *rate){	struct ieee80211_sub_if_data *sdata;	u16 dur;	int erp;	bool short_preamble = false; 	erp = 0;	if (vif) {		sdata = vif_to_sdata(vif);		short_preamble = sdata->vif.bss_conf.use_short_preamble;		if (sdata->deflink.operating_11g_mode)			erp = rate->flags & IEEE80211_RATE_ERP_G;	} 	dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp,				       short_preamble); 	return cpu_to_le16(dur);}EXPORT_SYMBOL(ieee80211_generic_frame_duration); __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,			      struct ieee80211_vif *vif, size_t frame_len,			      const struct ieee80211_tx_info *frame_txctl){	struct ieee80211_local *local = hw_to_local(hw);	struct ieee80211_rate *rate;	struct ieee80211_sub_if_data *sdata;	bool short_preamble;	int erp, bitrate;	u16 dur;	struct ieee80211_supported_band *sband; 	sband = local->hw.wiphy->bands[frame_txctl->band]; 	short_preamble = false; 	rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx]; 	erp = 0;	if (vif) {		sdata = vif_to_sdata(vif);		short_preamble = sdata->vif.bss_conf.use_short_preamble;		if (sdata->deflink.operating_11g_mode)			erp = rate->flags & IEEE80211_RATE_ERP_G;	} 	bitrate = rate->bitrate; 	/* CTS duration */	dur = ieee80211_frame_duration(sband->band, 10, bitrate,				       erp, short_preamble);	/* Data frame duration */	dur += ieee80211_frame_duration(sband->band, frame_len, bitrate,					erp, short_preamble);	/* ACK duration */	dur += ieee80211_frame_duration(sband->band, 10, bitrate,					erp, short_preamble); 	return cpu_to_le16(dur);}EXPORT_SYMBOL(ieee80211_rts_duration); __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,				    struct ieee80211_vif *vif,				    size_t frame_len,				    const struct ieee80211_tx_info *frame_txctl){	struct ieee80211_local *local = hw_to_local(hw);	struct ieee80211_rate *rate;	struct ieee80211_sub_if_data *sdata;	bool short_preamble;	int erp, bitrate;	u16 dur;	struct ieee80211_supported_band *sband; 	sband = local->hw.wiphy->bands[frame_txctl->band]; 	short_preamble = false; 	rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];	erp = 0;	if (vif) {		sdata = vif_to_sdata(vif);		short_preamble = sdata->vif.bss_conf.use_short_preamble;		if (sdata->deflink.operating_11g_mode)			erp = rate->flags & IEEE80211_RATE_ERP_G;	} 	bitrate = rate->bitrate; 	/* Data frame duration */	dur = ieee80211_frame_duration(sband->band, frame_len, bitrate,				       erp, short_preamble);	if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {		/* ACK duration */		dur += ieee80211_frame_duration(sband->band, 10, bitrate,						erp, short_preamble);	} 	return cpu_to_le16(dur);}EXPORT_SYMBOL(ieee80211_ctstoself_duration); static void wake_tx_push_queue(struct ieee80211_local *local,			       struct ieee80211_sub_if_data *sdata,			       struct ieee80211_txq *queue){	struct ieee80211_tx_control control = {		.sta = queue->sta,	};	struct sk_buff *skb; 	while (1) {		skb = ieee80211_tx_dequeue(&local->hw, queue);		if (!skb)			break; 		drv_tx(local, &control, skb);	}} /* wake_tx_queue handler for driver not implementing a custom one*/void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,				    struct ieee80211_txq *txq){	struct ieee80211_local *local = hw_to_local(hw);	struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);	struct ieee80211_txq *queue; 	spin_lock(&local->handle_wake_tx_queue_lock); 	/* Use ieee80211_next_txq() for airtime fairness accounting */	ieee80211_txq_schedule_start(hw, txq->ac);	while ((queue = ieee80211_next_txq(hw, txq->ac))) {		wake_tx_push_queue(local, sdata, queue);		ieee80211_return_txq(hw, queue, false);	}	ieee80211_txq_schedule_end(hw, txq->ac);	spin_unlock(&local->handle_wake_tx_queue_lock);}EXPORT_SYMBOL(ieee80211_handle_wake_tx_queue); static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac){	struct ieee80211_local *local = sdata->local;	struct ieee80211_vif *vif = &sdata->vif;	struct fq *fq = &local->fq;	struct ps_data *ps = NULL;	struct txq_info *txqi = NULL;	struct sta_info *sta;	int i; 	local_bh_disable();	spin_lock(&fq->lock); 	if (!test_bit(SDATA_STATE_RUNNING, &sdata->state))		goto out; 	if (sdata->vif.type == NL80211_IFTYPE_AP)		ps = &sdata->bss->ps; 	list_for_each_entry_rcu(sta, &local->sta_list, list) {		if (sdata != sta->sdata)			continue; 		for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {			struct ieee80211_txq *txq = sta->sta.txq[i];			struct txq_info *sta_txqi; 			if (!txq)				continue; 			sta_txqi = to_txq_info(txq); 			if (ac != txq->ac)				continue; 			if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY,						&sta_txqi->flags))				continue; 			spin_unlock(&fq->lock);			drv_wake_tx_queue(local, sta_txqi);			spin_lock(&fq->lock);		}	} 	if (vif->txq) {		txqi = to_txq_info(vif->txq); 		/* txq and txq_mgmt are mutually exclusive */		WARN_ON_ONCE(vif->txq_mgmt); 		if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ||		    (ps && atomic_read(&ps->num_sta_ps)) ||		    ac != vif->txq->ac)			txqi = NULL;	} else if (vif->txq_mgmt) {		txqi = to_txq_info(vif->txq_mgmt); 		if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ||		    ac != vif->txq_mgmt->ac)			txqi = NULL;	} 	spin_unlock(&fq->lock); 	if (txqi)		drv_wake_tx_queue(local, txqi); 	local_bh_enable();	return;out:	spin_unlock(&fq->lock);	local_bh_enable();} static void__releases(&local->queue_stop_reason_lock)__acquires(&local->queue_stop_reason_lock)_ieee80211_wake_txqs(struct ieee80211_local *local, unsigned long *flags){	struct ieee80211_sub_if_data *sdata;	int n_acs = IEEE80211_NUM_ACS;	int i; 	rcu_read_lock(); 	if (local->hw.queues < IEEE80211_NUM_ACS)		n_acs = 1; 	for (i = 0; i < local->hw.queues; i++) {		if (local->queue_stop_reasons[i])			continue; 		spin_unlock_irqrestore(&local->queue_stop_reason_lock, *flags);		list_for_each_entry_rcu(sdata, &local->interfaces, list) {			int ac; 			for (ac = 0; ac < n_acs; ac++) {				int ac_queue = sdata->vif.hw_queue[ac]; 				if (ac_queue == i ||				    sdata->vif.cab_queue == i)					__ieee80211_wake_txqs(sdata, ac);			}		}		spin_lock_irqsave(&local->queue_stop_reason_lock, *flags);	} 	rcu_read_unlock();} void ieee80211_wake_txqs(struct tasklet_struct *t){	struct ieee80211_local *local = from_tasklet(local, t,						     wake_txqs_tasklet);	unsigned long flags; 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);	_ieee80211_wake_txqs(local, &flags);	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);} static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,				   enum queue_stop_reason reason,				   bool refcounted,				   unsigned long *flags){	struct ieee80211_local *local = hw_to_local(hw); 	if (WARN_ON(queue >= hw->queues))		return; 	if (!test_bit(reason, &local->queue_stop_reasons[queue]))		return; 	if (!refcounted) {		local->q_stop_reasons[queue][reason] = 0;	} else {		local->q_stop_reasons[queue][reason]--;		if (WARN_ON(local->q_stop_reasons[queue][reason] < 0))			local->q_stop_reasons[queue][reason] = 0;	} 	if (local->q_stop_reasons[queue][reason] == 0)		__clear_bit(reason, &local->queue_stop_reasons[queue]); 	trace_wake_queue(local, queue, reason,			 local->q_stop_reasons[queue][reason]); 	if (local->queue_stop_reasons[queue] != 0)		/* someone still has this queue stopped */		return; 	if (!skb_queue_empty(&local->pending[queue]))		tasklet_schedule(&local->tx_pending_tasklet); 	/*	 * Calling _ieee80211_wake_txqs here can be a problem because it may	 * release queue_stop_reason_lock which has been taken by	 * __ieee80211_wake_queue's caller. It is certainly not very nice to	 * release someone's lock, but it is fine because all the callers of	 * __ieee80211_wake_queue call it right before releasing the lock.	 */	if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)		tasklet_schedule(&local->wake_txqs_tasklet);	else		_ieee80211_wake_txqs(local, flags);} void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,				    enum queue_stop_reason reason,				    bool refcounted){	struct ieee80211_local *local = hw_to_local(hw);	unsigned long flags; 

Lines 1–500 of 4754.