162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * IBSS mode implementation
462306a36Sopenharmony_ci * Copyright 2003-2008, Jouni Malinen <j@w1.fi>
562306a36Sopenharmony_ci * Copyright 2004, Instant802 Networks, Inc.
662306a36Sopenharmony_ci * Copyright 2005, Devicescape Software, Inc.
762306a36Sopenharmony_ci * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
862306a36Sopenharmony_ci * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
962306a36Sopenharmony_ci * Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
1062306a36Sopenharmony_ci * Copyright 2013-2014  Intel Mobile Communications GmbH
1162306a36Sopenharmony_ci * Copyright(c) 2016 Intel Deutschland GmbH
1262306a36Sopenharmony_ci * Copyright(c) 2018-2023 Intel Corporation
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/if_ether.h>
1862306a36Sopenharmony_ci#include <linux/skbuff.h>
1962306a36Sopenharmony_ci#include <linux/if_arp.h>
2062306a36Sopenharmony_ci#include <linux/etherdevice.h>
2162306a36Sopenharmony_ci#include <linux/rtnetlink.h>
2262306a36Sopenharmony_ci#include <net/mac80211.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "ieee80211_i.h"
2562306a36Sopenharmony_ci#include "driver-ops.h"
2662306a36Sopenharmony_ci#include "rate.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define IEEE80211_SCAN_INTERVAL (2 * HZ)
2962306a36Sopenharmony_ci#define IEEE80211_IBSS_JOIN_TIMEOUT (7 * HZ)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)
3262306a36Sopenharmony_ci#define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ)
3362306a36Sopenharmony_ci#define IEEE80211_IBSS_RSN_INACTIVITY_LIMIT (10 * HZ)
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define IEEE80211_IBSS_MAX_STA_ENTRIES 128
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic struct beacon_data *
3862306a36Sopenharmony_ciieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
3962306a36Sopenharmony_ci			   const int beacon_int, const u32 basic_rates,
4062306a36Sopenharmony_ci			   const u16 capability, u64 tsf,
4162306a36Sopenharmony_ci			   struct cfg80211_chan_def *chandef,
4262306a36Sopenharmony_ci			   bool *have_higher_than_11mbit,
4362306a36Sopenharmony_ci			   struct cfg80211_csa_settings *csa_settings)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
4662306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
4762306a36Sopenharmony_ci	int rates_n = 0, i, ri;
4862306a36Sopenharmony_ci	struct ieee80211_mgmt *mgmt;
4962306a36Sopenharmony_ci	u8 *pos;
5062306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
5162306a36Sopenharmony_ci	u32 rate_flags, rates = 0, rates_added = 0;
5262306a36Sopenharmony_ci	struct beacon_data *presp;
5362306a36Sopenharmony_ci	int frame_len;
5462306a36Sopenharmony_ci	int shift;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* Build IBSS probe response */
5762306a36Sopenharmony_ci	frame_len = sizeof(struct ieee80211_hdr_3addr) +
5862306a36Sopenharmony_ci		    12 /* struct ieee80211_mgmt.u.beacon */ +
5962306a36Sopenharmony_ci		    2 + IEEE80211_MAX_SSID_LEN /* max SSID */ +
6062306a36Sopenharmony_ci		    2 + 8 /* max Supported Rates */ +
6162306a36Sopenharmony_ci		    3 /* max DS params */ +
6262306a36Sopenharmony_ci		    4 /* IBSS params */ +
6362306a36Sopenharmony_ci		    5 /* Channel Switch Announcement */ +
6462306a36Sopenharmony_ci		    2 + (IEEE80211_MAX_SUPP_RATES - 8) +
6562306a36Sopenharmony_ci		    2 + sizeof(struct ieee80211_ht_cap) +
6662306a36Sopenharmony_ci		    2 + sizeof(struct ieee80211_ht_operation) +
6762306a36Sopenharmony_ci		    2 + sizeof(struct ieee80211_vht_cap) +
6862306a36Sopenharmony_ci		    2 + sizeof(struct ieee80211_vht_operation) +
6962306a36Sopenharmony_ci		    ifibss->ie_len;
7062306a36Sopenharmony_ci	presp = kzalloc(sizeof(*presp) + frame_len, GFP_KERNEL);
7162306a36Sopenharmony_ci	if (!presp)
7262306a36Sopenharmony_ci		return NULL;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	presp->head = (void *)(presp + 1);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	mgmt = (void *) presp->head;
7762306a36Sopenharmony_ci	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
7862306a36Sopenharmony_ci					  IEEE80211_STYPE_PROBE_RESP);
7962306a36Sopenharmony_ci	eth_broadcast_addr(mgmt->da);
8062306a36Sopenharmony_ci	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
8162306a36Sopenharmony_ci	memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
8262306a36Sopenharmony_ci	mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_int);
8362306a36Sopenharmony_ci	mgmt->u.beacon.timestamp = cpu_to_le64(tsf);
8462306a36Sopenharmony_ci	mgmt->u.beacon.capab_info = cpu_to_le16(capability);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	pos = (u8 *)mgmt + offsetof(struct ieee80211_mgmt, u.beacon.variable);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	*pos++ = WLAN_EID_SSID;
8962306a36Sopenharmony_ci	*pos++ = ifibss->ssid_len;
9062306a36Sopenharmony_ci	memcpy(pos, ifibss->ssid, ifibss->ssid_len);
9162306a36Sopenharmony_ci	pos += ifibss->ssid_len;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	sband = local->hw.wiphy->bands[chandef->chan->band];
9462306a36Sopenharmony_ci	rate_flags = ieee80211_chandef_rate_flags(chandef);
9562306a36Sopenharmony_ci	shift = ieee80211_chandef_get_shift(chandef);
9662306a36Sopenharmony_ci	rates_n = 0;
9762306a36Sopenharmony_ci	if (have_higher_than_11mbit)
9862306a36Sopenharmony_ci		*have_higher_than_11mbit = false;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	for (i = 0; i < sband->n_bitrates; i++) {
10162306a36Sopenharmony_ci		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
10262306a36Sopenharmony_ci			continue;
10362306a36Sopenharmony_ci		if (sband->bitrates[i].bitrate > 110 &&
10462306a36Sopenharmony_ci		    have_higher_than_11mbit)
10562306a36Sopenharmony_ci			*have_higher_than_11mbit = true;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		rates |= BIT(i);
10862306a36Sopenharmony_ci		rates_n++;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	*pos++ = WLAN_EID_SUPP_RATES;
11262306a36Sopenharmony_ci	*pos++ = min_t(int, 8, rates_n);
11362306a36Sopenharmony_ci	for (ri = 0; ri < sband->n_bitrates; ri++) {
11462306a36Sopenharmony_ci		int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
11562306a36Sopenharmony_ci					5 * (1 << shift));
11662306a36Sopenharmony_ci		u8 basic = 0;
11762306a36Sopenharmony_ci		if (!(rates & BIT(ri)))
11862306a36Sopenharmony_ci			continue;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		if (basic_rates & BIT(ri))
12162306a36Sopenharmony_ci			basic = 0x80;
12262306a36Sopenharmony_ci		*pos++ = basic | (u8) rate;
12362306a36Sopenharmony_ci		if (++rates_added == 8) {
12462306a36Sopenharmony_ci			ri++; /* continue at next rate for EXT_SUPP_RATES */
12562306a36Sopenharmony_ci			break;
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (sband->band == NL80211_BAND_2GHZ) {
13062306a36Sopenharmony_ci		*pos++ = WLAN_EID_DS_PARAMS;
13162306a36Sopenharmony_ci		*pos++ = 1;
13262306a36Sopenharmony_ci		*pos++ = ieee80211_frequency_to_channel(
13362306a36Sopenharmony_ci				chandef->chan->center_freq);
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	*pos++ = WLAN_EID_IBSS_PARAMS;
13762306a36Sopenharmony_ci	*pos++ = 2;
13862306a36Sopenharmony_ci	/* FIX: set ATIM window based on scan results */
13962306a36Sopenharmony_ci	*pos++ = 0;
14062306a36Sopenharmony_ci	*pos++ = 0;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (csa_settings) {
14362306a36Sopenharmony_ci		*pos++ = WLAN_EID_CHANNEL_SWITCH;
14462306a36Sopenharmony_ci		*pos++ = 3;
14562306a36Sopenharmony_ci		*pos++ = csa_settings->block_tx ? 1 : 0;
14662306a36Sopenharmony_ci		*pos++ = ieee80211_frequency_to_channel(
14762306a36Sopenharmony_ci				csa_settings->chandef.chan->center_freq);
14862306a36Sopenharmony_ci		presp->cntdwn_counter_offsets[0] = (pos - presp->head);
14962306a36Sopenharmony_ci		*pos++ = csa_settings->count;
15062306a36Sopenharmony_ci		presp->cntdwn_current_counter = csa_settings->count;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */
15462306a36Sopenharmony_ci	if (rates_n > 8) {
15562306a36Sopenharmony_ci		*pos++ = WLAN_EID_EXT_SUPP_RATES;
15662306a36Sopenharmony_ci		*pos++ = rates_n - 8;
15762306a36Sopenharmony_ci		for (; ri < sband->n_bitrates; ri++) {
15862306a36Sopenharmony_ci			int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
15962306a36Sopenharmony_ci						5 * (1 << shift));
16062306a36Sopenharmony_ci			u8 basic = 0;
16162306a36Sopenharmony_ci			if (!(rates & BIT(ri)))
16262306a36Sopenharmony_ci				continue;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci			if (basic_rates & BIT(ri))
16562306a36Sopenharmony_ci				basic = 0x80;
16662306a36Sopenharmony_ci			*pos++ = basic | (u8) rate;
16762306a36Sopenharmony_ci		}
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (ifibss->ie_len) {
17162306a36Sopenharmony_ci		memcpy(pos, ifibss->ie, ifibss->ie_len);
17262306a36Sopenharmony_ci		pos += ifibss->ie_len;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* add HT capability and information IEs */
17662306a36Sopenharmony_ci	if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT &&
17762306a36Sopenharmony_ci	    chandef->width != NL80211_CHAN_WIDTH_5 &&
17862306a36Sopenharmony_ci	    chandef->width != NL80211_CHAN_WIDTH_10 &&
17962306a36Sopenharmony_ci	    sband->ht_cap.ht_supported) {
18062306a36Sopenharmony_ci		struct ieee80211_sta_ht_cap ht_cap;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
18362306a36Sopenharmony_ci		ieee80211_apply_htcap_overrides(sdata, &ht_cap);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		pos = ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
18662306a36Sopenharmony_ci		/*
18762306a36Sopenharmony_ci		 * Note: According to 802.11n-2009 9.13.3.1, HT Protection
18862306a36Sopenharmony_ci		 * field and RIFS Mode are reserved in IBSS mode, therefore
18962306a36Sopenharmony_ci		 * keep them at 0
19062306a36Sopenharmony_ci		 */
19162306a36Sopenharmony_ci		pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
19262306a36Sopenharmony_ci						 chandef, 0, false);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		/* add VHT capability and information IEs */
19562306a36Sopenharmony_ci		if (chandef->width != NL80211_CHAN_WIDTH_20 &&
19662306a36Sopenharmony_ci		    chandef->width != NL80211_CHAN_WIDTH_40 &&
19762306a36Sopenharmony_ci		    sband->vht_cap.vht_supported) {
19862306a36Sopenharmony_ci			pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
19962306a36Sopenharmony_ci							 sband->vht_cap.cap);
20062306a36Sopenharmony_ci			pos = ieee80211_ie_build_vht_oper(pos, &sband->vht_cap,
20162306a36Sopenharmony_ci							  chandef);
20262306a36Sopenharmony_ci		}
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (local->hw.queues >= IEEE80211_NUM_ACS)
20662306a36Sopenharmony_ci		pos = ieee80211_add_wmm_info_ie(pos, 0); /* U-APSD not in use */
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	presp->head_len = pos - presp->head;
20962306a36Sopenharmony_ci	if (WARN_ON(presp->head_len > frame_len))
21062306a36Sopenharmony_ci		goto error;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	return presp;
21362306a36Sopenharmony_cierror:
21462306a36Sopenharmony_ci	kfree(presp);
21562306a36Sopenharmony_ci	return NULL;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
21962306a36Sopenharmony_ci				      const u8 *bssid, const int beacon_int,
22062306a36Sopenharmony_ci				      struct cfg80211_chan_def *req_chandef,
22162306a36Sopenharmony_ci				      const u32 basic_rates,
22262306a36Sopenharmony_ci				      const u16 capability, u64 tsf,
22362306a36Sopenharmony_ci				      bool creator)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
22662306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
22762306a36Sopenharmony_ci	struct ieee80211_mgmt *mgmt;
22862306a36Sopenharmony_ci	struct cfg80211_bss *bss;
22962306a36Sopenharmony_ci	u64 bss_change;
23062306a36Sopenharmony_ci	struct cfg80211_chan_def chandef;
23162306a36Sopenharmony_ci	struct ieee80211_channel *chan;
23262306a36Sopenharmony_ci	struct beacon_data *presp;
23362306a36Sopenharmony_ci	struct cfg80211_inform_bss bss_meta = {};
23462306a36Sopenharmony_ci	bool have_higher_than_11mbit;
23562306a36Sopenharmony_ci	bool radar_required;
23662306a36Sopenharmony_ci	int err;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	sdata_assert_lock(sdata);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/* Reset own TSF to allow time synchronization work. */
24162306a36Sopenharmony_ci	drv_reset_tsf(local, sdata);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (!ether_addr_equal(ifibss->bssid, bssid))
24462306a36Sopenharmony_ci		sta_info_flush(sdata);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* if merging, indicate to driver that we leave the old IBSS */
24762306a36Sopenharmony_ci	if (sdata->vif.cfg.ibss_joined) {
24862306a36Sopenharmony_ci		sdata->vif.cfg.ibss_joined = false;
24962306a36Sopenharmony_ci		sdata->vif.cfg.ibss_creator = false;
25062306a36Sopenharmony_ci		sdata->vif.bss_conf.enable_beacon = false;
25162306a36Sopenharmony_ci		netif_carrier_off(sdata->dev);
25262306a36Sopenharmony_ci		ieee80211_bss_info_change_notify(sdata,
25362306a36Sopenharmony_ci						 BSS_CHANGED_IBSS |
25462306a36Sopenharmony_ci						 BSS_CHANGED_BEACON_ENABLED);
25562306a36Sopenharmony_ci		drv_leave_ibss(local, sdata);
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	presp = sdata_dereference(ifibss->presp, sdata);
25962306a36Sopenharmony_ci	RCU_INIT_POINTER(ifibss->presp, NULL);
26062306a36Sopenharmony_ci	if (presp)
26162306a36Sopenharmony_ci		kfree_rcu(presp, rcu_head);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/* make a copy of the chandef, it could be modified below. */
26462306a36Sopenharmony_ci	chandef = *req_chandef;
26562306a36Sopenharmony_ci	chan = chandef.chan;
26662306a36Sopenharmony_ci	if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef,
26762306a36Sopenharmony_ci				     NL80211_IFTYPE_ADHOC)) {
26862306a36Sopenharmony_ci		if (chandef.width == NL80211_CHAN_WIDTH_5 ||
26962306a36Sopenharmony_ci		    chandef.width == NL80211_CHAN_WIDTH_10 ||
27062306a36Sopenharmony_ci		    chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
27162306a36Sopenharmony_ci		    chandef.width == NL80211_CHAN_WIDTH_20) {
27262306a36Sopenharmony_ci			sdata_info(sdata,
27362306a36Sopenharmony_ci				   "Failed to join IBSS, beacons forbidden\n");
27462306a36Sopenharmony_ci			return;
27562306a36Sopenharmony_ci		}
27662306a36Sopenharmony_ci		chandef.width = NL80211_CHAN_WIDTH_20;
27762306a36Sopenharmony_ci		chandef.center_freq1 = chan->center_freq;
27862306a36Sopenharmony_ci		/* check again for downgraded chandef */
27962306a36Sopenharmony_ci		if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef,
28062306a36Sopenharmony_ci					     NL80211_IFTYPE_ADHOC)) {
28162306a36Sopenharmony_ci			sdata_info(sdata,
28262306a36Sopenharmony_ci				   "Failed to join IBSS, beacons forbidden\n");
28362306a36Sopenharmony_ci			return;
28462306a36Sopenharmony_ci		}
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
28862306a36Sopenharmony_ci					    &chandef, NL80211_IFTYPE_ADHOC);
28962306a36Sopenharmony_ci	if (err < 0) {
29062306a36Sopenharmony_ci		sdata_info(sdata,
29162306a36Sopenharmony_ci			   "Failed to join IBSS, invalid chandef\n");
29262306a36Sopenharmony_ci		return;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci	if (err > 0 && !ifibss->userspace_handles_dfs) {
29562306a36Sopenharmony_ci		sdata_info(sdata,
29662306a36Sopenharmony_ci			   "Failed to join IBSS, DFS channel without control program\n");
29762306a36Sopenharmony_ci		return;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	radar_required = err;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	mutex_lock(&local->mtx);
30362306a36Sopenharmony_ci	if (ieee80211_link_use_channel(&sdata->deflink, &chandef,
30462306a36Sopenharmony_ci				       ifibss->fixed_channel ?
30562306a36Sopenharmony_ci					IEEE80211_CHANCTX_SHARED :
30662306a36Sopenharmony_ci					IEEE80211_CHANCTX_EXCLUSIVE)) {
30762306a36Sopenharmony_ci		sdata_info(sdata, "Failed to join IBSS, no channel context\n");
30862306a36Sopenharmony_ci		mutex_unlock(&local->mtx);
30962306a36Sopenharmony_ci		return;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci	sdata->deflink.radar_required = radar_required;
31262306a36Sopenharmony_ci	mutex_unlock(&local->mtx);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	memcpy(ifibss->bssid, bssid, ETH_ALEN);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates,
31762306a36Sopenharmony_ci					   capability, tsf, &chandef,
31862306a36Sopenharmony_ci					   &have_higher_than_11mbit, NULL);
31962306a36Sopenharmony_ci	if (!presp)
32062306a36Sopenharmony_ci		return;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	rcu_assign_pointer(ifibss->presp, presp);
32362306a36Sopenharmony_ci	mgmt = (void *)presp->head;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	sdata->vif.bss_conf.enable_beacon = true;
32662306a36Sopenharmony_ci	sdata->vif.bss_conf.beacon_int = beacon_int;
32762306a36Sopenharmony_ci	sdata->vif.bss_conf.basic_rates = basic_rates;
32862306a36Sopenharmony_ci	sdata->vif.cfg.ssid_len = ifibss->ssid_len;
32962306a36Sopenharmony_ci	memcpy(sdata->vif.cfg.ssid, ifibss->ssid, ifibss->ssid_len);
33062306a36Sopenharmony_ci	bss_change = BSS_CHANGED_BEACON_INT;
33162306a36Sopenharmony_ci	bss_change |= ieee80211_reset_erp_info(sdata);
33262306a36Sopenharmony_ci	bss_change |= BSS_CHANGED_BSSID;
33362306a36Sopenharmony_ci	bss_change |= BSS_CHANGED_BEACON;
33462306a36Sopenharmony_ci	bss_change |= BSS_CHANGED_BEACON_ENABLED;
33562306a36Sopenharmony_ci	bss_change |= BSS_CHANGED_BASIC_RATES;
33662306a36Sopenharmony_ci	bss_change |= BSS_CHANGED_HT;
33762306a36Sopenharmony_ci	bss_change |= BSS_CHANGED_IBSS;
33862306a36Sopenharmony_ci	bss_change |= BSS_CHANGED_SSID;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/*
34162306a36Sopenharmony_ci	 * In 5 GHz/802.11a, we can always use short slot time.
34262306a36Sopenharmony_ci	 * (IEEE 802.11-2012 18.3.8.7)
34362306a36Sopenharmony_ci	 *
34462306a36Sopenharmony_ci	 * In 2.4GHz, we must always use long slots in IBSS for compatibility
34562306a36Sopenharmony_ci	 * reasons.
34662306a36Sopenharmony_ci	 * (IEEE 802.11-2012 19.4.5)
34762306a36Sopenharmony_ci	 *
34862306a36Sopenharmony_ci	 * HT follows these specifications (IEEE 802.11-2012 20.3.18)
34962306a36Sopenharmony_ci	 */
35062306a36Sopenharmony_ci	sdata->vif.bss_conf.use_short_slot = chan->band == NL80211_BAND_5GHZ;
35162306a36Sopenharmony_ci	bss_change |= BSS_CHANGED_ERP_SLOT;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/* cf. IEEE 802.11 9.2.12 */
35462306a36Sopenharmony_ci	sdata->deflink.operating_11g_mode =
35562306a36Sopenharmony_ci		chan->band == NL80211_BAND_2GHZ && have_higher_than_11mbit;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	ieee80211_set_wmm_default(&sdata->deflink, true, false);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	sdata->vif.cfg.ibss_joined = true;
36062306a36Sopenharmony_ci	sdata->vif.cfg.ibss_creator = creator;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	err = drv_join_ibss(local, sdata);
36362306a36Sopenharmony_ci	if (err) {
36462306a36Sopenharmony_ci		sdata->vif.cfg.ibss_joined = false;
36562306a36Sopenharmony_ci		sdata->vif.cfg.ibss_creator = false;
36662306a36Sopenharmony_ci		sdata->vif.bss_conf.enable_beacon = false;
36762306a36Sopenharmony_ci		sdata->vif.cfg.ssid_len = 0;
36862306a36Sopenharmony_ci		RCU_INIT_POINTER(ifibss->presp, NULL);
36962306a36Sopenharmony_ci		kfree_rcu(presp, rcu_head);
37062306a36Sopenharmony_ci		mutex_lock(&local->mtx);
37162306a36Sopenharmony_ci		ieee80211_link_release_channel(&sdata->deflink);
37262306a36Sopenharmony_ci		mutex_unlock(&local->mtx);
37362306a36Sopenharmony_ci		sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n",
37462306a36Sopenharmony_ci			   err);
37562306a36Sopenharmony_ci		return;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	ieee80211_bss_info_change_notify(sdata, bss_change);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	ifibss->state = IEEE80211_IBSS_MLME_JOINED;
38162306a36Sopenharmony_ci	mod_timer(&ifibss->timer,
38262306a36Sopenharmony_ci		  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	bss_meta.chan = chan;
38562306a36Sopenharmony_ci	bss_meta.scan_width = cfg80211_chandef_to_scan_width(&chandef);
38662306a36Sopenharmony_ci	bss = cfg80211_inform_bss_frame_data(local->hw.wiphy, &bss_meta, mgmt,
38762306a36Sopenharmony_ci					     presp->head_len, GFP_KERNEL);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	cfg80211_put_bss(local->hw.wiphy, bss);
39062306a36Sopenharmony_ci	netif_carrier_on(sdata->dev);
39162306a36Sopenharmony_ci	cfg80211_ibss_joined(sdata->dev, ifibss->bssid, chan, GFP_KERNEL);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
39562306a36Sopenharmony_ci				    struct ieee80211_bss *bss)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct cfg80211_bss *cbss =
39862306a36Sopenharmony_ci		container_of((void *)bss, struct cfg80211_bss, priv);
39962306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
40062306a36Sopenharmony_ci	struct cfg80211_chan_def chandef;
40162306a36Sopenharmony_ci	u32 basic_rates;
40262306a36Sopenharmony_ci	int i, j;
40362306a36Sopenharmony_ci	u16 beacon_int = cbss->beacon_interval;
40462306a36Sopenharmony_ci	const struct cfg80211_bss_ies *ies;
40562306a36Sopenharmony_ci	enum nl80211_channel_type chan_type;
40662306a36Sopenharmony_ci	u64 tsf;
40762306a36Sopenharmony_ci	u32 rate_flags;
40862306a36Sopenharmony_ci	int shift;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	sdata_assert_lock(sdata);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (beacon_int < 10)
41362306a36Sopenharmony_ci		beacon_int = 10;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	switch (sdata->u.ibss.chandef.width) {
41662306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_20_NOHT:
41762306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_20:
41862306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_40:
41962306a36Sopenharmony_ci		chan_type = cfg80211_get_chandef_type(&sdata->u.ibss.chandef);
42062306a36Sopenharmony_ci		cfg80211_chandef_create(&chandef, cbss->channel, chan_type);
42162306a36Sopenharmony_ci		break;
42262306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_5:
42362306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_10:
42462306a36Sopenharmony_ci		cfg80211_chandef_create(&chandef, cbss->channel,
42562306a36Sopenharmony_ci					NL80211_CHAN_NO_HT);
42662306a36Sopenharmony_ci		chandef.width = sdata->u.ibss.chandef.width;
42762306a36Sopenharmony_ci		break;
42862306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_80:
42962306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_80P80:
43062306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_160:
43162306a36Sopenharmony_ci		chandef = sdata->u.ibss.chandef;
43262306a36Sopenharmony_ci		chandef.chan = cbss->channel;
43362306a36Sopenharmony_ci		break;
43462306a36Sopenharmony_ci	default:
43562306a36Sopenharmony_ci		/* fall back to 20 MHz for unsupported modes */
43662306a36Sopenharmony_ci		cfg80211_chandef_create(&chandef, cbss->channel,
43762306a36Sopenharmony_ci					NL80211_CHAN_NO_HT);
43862306a36Sopenharmony_ci		break;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
44262306a36Sopenharmony_ci	rate_flags = ieee80211_chandef_rate_flags(&sdata->u.ibss.chandef);
44362306a36Sopenharmony_ci	shift = ieee80211_vif_get_shift(&sdata->vif);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	basic_rates = 0;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	for (i = 0; i < bss->supp_rates_len; i++) {
44862306a36Sopenharmony_ci		int rate = bss->supp_rates[i] & 0x7f;
44962306a36Sopenharmony_ci		bool is_basic = !!(bss->supp_rates[i] & 0x80);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		for (j = 0; j < sband->n_bitrates; j++) {
45262306a36Sopenharmony_ci			int brate;
45362306a36Sopenharmony_ci			if ((rate_flags & sband->bitrates[j].flags)
45462306a36Sopenharmony_ci			    != rate_flags)
45562306a36Sopenharmony_ci				continue;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci			brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
45862306a36Sopenharmony_ci					     5 * (1 << shift));
45962306a36Sopenharmony_ci			if (brate == rate) {
46062306a36Sopenharmony_ci				if (is_basic)
46162306a36Sopenharmony_ci					basic_rates |= BIT(j);
46262306a36Sopenharmony_ci				break;
46362306a36Sopenharmony_ci			}
46462306a36Sopenharmony_ci		}
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	rcu_read_lock();
46862306a36Sopenharmony_ci	ies = rcu_dereference(cbss->ies);
46962306a36Sopenharmony_ci	tsf = ies->tsf;
47062306a36Sopenharmony_ci	rcu_read_unlock();
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	__ieee80211_sta_join_ibss(sdata, cbss->bssid,
47362306a36Sopenharmony_ci				  beacon_int,
47462306a36Sopenharmony_ci				  &chandef,
47562306a36Sopenharmony_ci				  basic_rates,
47662306a36Sopenharmony_ci				  cbss->capability,
47762306a36Sopenharmony_ci				  tsf, false);
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ciint ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
48162306a36Sopenharmony_ci			      struct cfg80211_csa_settings *csa_settings,
48262306a36Sopenharmony_ci			      u64 *changed)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
48562306a36Sopenharmony_ci	struct beacon_data *presp, *old_presp;
48662306a36Sopenharmony_ci	struct cfg80211_bss *cbss;
48762306a36Sopenharmony_ci	const struct cfg80211_bss_ies *ies;
48862306a36Sopenharmony_ci	u16 capability = WLAN_CAPABILITY_IBSS;
48962306a36Sopenharmony_ci	u64 tsf;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	sdata_assert_lock(sdata);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (ifibss->privacy)
49462306a36Sopenharmony_ci		capability |= WLAN_CAPABILITY_PRIVACY;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan,
49762306a36Sopenharmony_ci				ifibss->bssid, ifibss->ssid,
49862306a36Sopenharmony_ci				ifibss->ssid_len, IEEE80211_BSS_TYPE_IBSS,
49962306a36Sopenharmony_ci				IEEE80211_PRIVACY(ifibss->privacy));
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (WARN_ON(!cbss))
50262306a36Sopenharmony_ci		return -EINVAL;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	rcu_read_lock();
50562306a36Sopenharmony_ci	ies = rcu_dereference(cbss->ies);
50662306a36Sopenharmony_ci	tsf = ies->tsf;
50762306a36Sopenharmony_ci	rcu_read_unlock();
50862306a36Sopenharmony_ci	cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	old_presp = sdata_dereference(ifibss->presp, sdata);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	presp = ieee80211_ibss_build_presp(sdata,
51362306a36Sopenharmony_ci					   sdata->vif.bss_conf.beacon_int,
51462306a36Sopenharmony_ci					   sdata->vif.bss_conf.basic_rates,
51562306a36Sopenharmony_ci					   capability, tsf, &ifibss->chandef,
51662306a36Sopenharmony_ci					   NULL, csa_settings);
51762306a36Sopenharmony_ci	if (!presp)
51862306a36Sopenharmony_ci		return -ENOMEM;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	rcu_assign_pointer(ifibss->presp, presp);
52162306a36Sopenharmony_ci	if (old_presp)
52262306a36Sopenharmony_ci		kfree_rcu(old_presp, rcu_head);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	*changed |= BSS_CHANGED_BEACON;
52562306a36Sopenharmony_ci	return 0;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ciint ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata, u64 *changed)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
53162306a36Sopenharmony_ci	struct cfg80211_bss *cbss;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	sdata_assert_lock(sdata);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* When not connected/joined, sending CSA doesn't make sense. */
53662306a36Sopenharmony_ci	if (ifibss->state != IEEE80211_IBSS_MLME_JOINED)
53762306a36Sopenharmony_ci		return -ENOLINK;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	/* update cfg80211 bss information with the new channel */
54062306a36Sopenharmony_ci	if (!is_zero_ether_addr(ifibss->bssid)) {
54162306a36Sopenharmony_ci		cbss = cfg80211_get_bss(sdata->local->hw.wiphy,
54262306a36Sopenharmony_ci					ifibss->chandef.chan,
54362306a36Sopenharmony_ci					ifibss->bssid, ifibss->ssid,
54462306a36Sopenharmony_ci					ifibss->ssid_len,
54562306a36Sopenharmony_ci					IEEE80211_BSS_TYPE_IBSS,
54662306a36Sopenharmony_ci					IEEE80211_PRIVACY(ifibss->privacy));
54762306a36Sopenharmony_ci		/* XXX: should not really modify cfg80211 data */
54862306a36Sopenharmony_ci		if (cbss) {
54962306a36Sopenharmony_ci			cbss->channel = sdata->deflink.csa_chandef.chan;
55062306a36Sopenharmony_ci			cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
55162306a36Sopenharmony_ci		}
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	ifibss->chandef = sdata->deflink.csa_chandef;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* generate the beacon */
55762306a36Sopenharmony_ci	return ieee80211_ibss_csa_beacon(sdata, NULL, changed);
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_civoid ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	wiphy_work_cancel(sdata->local->hw.wiphy,
56562306a36Sopenharmony_ci			  &ifibss->csa_connection_drop_work);
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
56962306a36Sopenharmony_ci	__acquires(RCU)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = sta->sdata;
57262306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	memcpy(addr, sta->sta.addr, ETH_ALEN);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	ibss_dbg(sdata, "Adding new IBSS station %pM\n", addr);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
57962306a36Sopenharmony_ci	sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
58062306a36Sopenharmony_ci	/* authorize the station only if the network is not RSN protected. If
58162306a36Sopenharmony_ci	 * not wait for the userspace to authorize it */
58262306a36Sopenharmony_ci	if (!sta->sdata->u.ibss.control_port)
58362306a36Sopenharmony_ci		sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	rate_control_rate_init(sta);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	/* If it fails, maybe we raced another insertion? */
58862306a36Sopenharmony_ci	if (sta_info_insert_rcu(sta))
58962306a36Sopenharmony_ci		return sta_info_get(sdata, addr);
59062306a36Sopenharmony_ci	return sta;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic struct sta_info *
59462306a36Sopenharmony_ciieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
59562306a36Sopenharmony_ci		       const u8 *addr, u32 supp_rates)
59662306a36Sopenharmony_ci	__acquires(RCU)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
59962306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
60062306a36Sopenharmony_ci	struct sta_info *sta;
60162306a36Sopenharmony_ci	struct ieee80211_chanctx_conf *chanctx_conf;
60262306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
60362306a36Sopenharmony_ci	enum nl80211_bss_scan_width scan_width;
60462306a36Sopenharmony_ci	int band;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/*
60762306a36Sopenharmony_ci	 * XXX: Consider removing the least recently used entry and
60862306a36Sopenharmony_ci	 * 	allow new one to be added.
60962306a36Sopenharmony_ci	 */
61062306a36Sopenharmony_ci	if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
61162306a36Sopenharmony_ci		net_info_ratelimited("%s: No room for a new IBSS STA entry %pM\n",
61262306a36Sopenharmony_ci				    sdata->name, addr);
61362306a36Sopenharmony_ci		rcu_read_lock();
61462306a36Sopenharmony_ci		return NULL;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH) {
61862306a36Sopenharmony_ci		rcu_read_lock();
61962306a36Sopenharmony_ci		return NULL;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	if (!ether_addr_equal(bssid, sdata->u.ibss.bssid)) {
62362306a36Sopenharmony_ci		rcu_read_lock();
62462306a36Sopenharmony_ci		return NULL;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	rcu_read_lock();
62862306a36Sopenharmony_ci	chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
62962306a36Sopenharmony_ci	if (WARN_ON_ONCE(!chanctx_conf))
63062306a36Sopenharmony_ci		return NULL;
63162306a36Sopenharmony_ci	band = chanctx_conf->def.chan->band;
63262306a36Sopenharmony_ci	scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
63362306a36Sopenharmony_ci	rcu_read_unlock();
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
63662306a36Sopenharmony_ci	if (!sta) {
63762306a36Sopenharmony_ci		rcu_read_lock();
63862306a36Sopenharmony_ci		return NULL;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	/* make sure mandatory rates are always added */
64262306a36Sopenharmony_ci	sband = local->hw.wiphy->bands[band];
64362306a36Sopenharmony_ci	sta->sta.deflink.supp_rates[band] = supp_rates |
64462306a36Sopenharmony_ci			ieee80211_mandatory_rates(sband, scan_width);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	return ieee80211_ibss_finish_sta(sta);
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
65262306a36Sopenharmony_ci	int active = 0;
65362306a36Sopenharmony_ci	struct sta_info *sta;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	sdata_assert_lock(sdata);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	rcu_read_lock();
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	list_for_each_entry_rcu(sta, &local->sta_list, list) {
66062306a36Sopenharmony_ci		unsigned long last_active = ieee80211_sta_last_active(sta);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		if (sta->sdata == sdata &&
66362306a36Sopenharmony_ci		    time_is_after_jiffies(last_active +
66462306a36Sopenharmony_ci					  IEEE80211_IBSS_MERGE_INTERVAL)) {
66562306a36Sopenharmony_ci			active++;
66662306a36Sopenharmony_ci			break;
66762306a36Sopenharmony_ci		}
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	rcu_read_unlock();
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	return active;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
67862306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
67962306a36Sopenharmony_ci	struct cfg80211_bss *cbss;
68062306a36Sopenharmony_ci	struct beacon_data *presp;
68162306a36Sopenharmony_ci	struct sta_info *sta;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	if (!is_zero_ether_addr(ifibss->bssid)) {
68462306a36Sopenharmony_ci		cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
68562306a36Sopenharmony_ci					ifibss->bssid, ifibss->ssid,
68662306a36Sopenharmony_ci					ifibss->ssid_len,
68762306a36Sopenharmony_ci					IEEE80211_BSS_TYPE_IBSS,
68862306a36Sopenharmony_ci					IEEE80211_PRIVACY(ifibss->privacy));
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci		if (cbss) {
69162306a36Sopenharmony_ci			cfg80211_unlink_bss(local->hw.wiphy, cbss);
69262306a36Sopenharmony_ci			cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
69362306a36Sopenharmony_ci		}
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	sta_info_flush(sdata);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	spin_lock_bh(&ifibss->incomplete_lock);
70162306a36Sopenharmony_ci	while (!list_empty(&ifibss->incomplete_stations)) {
70262306a36Sopenharmony_ci		sta = list_first_entry(&ifibss->incomplete_stations,
70362306a36Sopenharmony_ci				       struct sta_info, list);
70462306a36Sopenharmony_ci		list_del(&sta->list);
70562306a36Sopenharmony_ci		spin_unlock_bh(&ifibss->incomplete_lock);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci		sta_info_free(local, sta);
70862306a36Sopenharmony_ci		spin_lock_bh(&ifibss->incomplete_lock);
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci	spin_unlock_bh(&ifibss->incomplete_lock);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	netif_carrier_off(sdata->dev);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	sdata->vif.cfg.ibss_joined = false;
71562306a36Sopenharmony_ci	sdata->vif.cfg.ibss_creator = false;
71662306a36Sopenharmony_ci	sdata->vif.bss_conf.enable_beacon = false;
71762306a36Sopenharmony_ci	sdata->vif.cfg.ssid_len = 0;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/* remove beacon */
72062306a36Sopenharmony_ci	presp = sdata_dereference(ifibss->presp, sdata);
72162306a36Sopenharmony_ci	RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
72262306a36Sopenharmony_ci	if (presp)
72362306a36Sopenharmony_ci		kfree_rcu(presp, rcu_head);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
72662306a36Sopenharmony_ci	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
72762306a36Sopenharmony_ci						BSS_CHANGED_IBSS);
72862306a36Sopenharmony_ci	drv_leave_ibss(local, sdata);
72962306a36Sopenharmony_ci	mutex_lock(&local->mtx);
73062306a36Sopenharmony_ci	ieee80211_link_release_channel(&sdata->deflink);
73162306a36Sopenharmony_ci	mutex_unlock(&local->mtx);
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistatic void ieee80211_csa_connection_drop_work(struct wiphy *wiphy,
73562306a36Sopenharmony_ci					       struct wiphy_work *work)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata =
73862306a36Sopenharmony_ci		container_of(work, struct ieee80211_sub_if_data,
73962306a36Sopenharmony_ci			     u.ibss.csa_connection_drop_work);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	sdata_lock(sdata);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	ieee80211_ibss_disconnect(sdata);
74462306a36Sopenharmony_ci	synchronize_rcu();
74562306a36Sopenharmony_ci	skb_queue_purge(&sdata->skb_queue);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	/* trigger a scan to find another IBSS network to join */
74862306a36Sopenharmony_ci	wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	sdata_unlock(sdata);
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistatic void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
75662306a36Sopenharmony_ci	int err;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	/* if the current channel is a DFS channel, mark the channel as
75962306a36Sopenharmony_ci	 * unavailable.
76062306a36Sopenharmony_ci	 */
76162306a36Sopenharmony_ci	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
76262306a36Sopenharmony_ci					    &ifibss->chandef,
76362306a36Sopenharmony_ci					    NL80211_IFTYPE_ADHOC);
76462306a36Sopenharmony_ci	if (err > 0)
76562306a36Sopenharmony_ci		cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef,
76662306a36Sopenharmony_ci				     GFP_ATOMIC);
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic bool
77062306a36Sopenharmony_ciieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
77162306a36Sopenharmony_ci				  struct ieee802_11_elems *elems,
77262306a36Sopenharmony_ci				  bool beacon)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct cfg80211_csa_settings params;
77562306a36Sopenharmony_ci	struct ieee80211_csa_ie csa_ie;
77662306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
77762306a36Sopenharmony_ci	enum nl80211_channel_type ch_type;
77862306a36Sopenharmony_ci	int err;
77962306a36Sopenharmony_ci	ieee80211_conn_flags_t conn_flags;
78062306a36Sopenharmony_ci	u32 vht_cap_info = 0;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	sdata_assert_lock(sdata);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	conn_flags = IEEE80211_CONN_DISABLE_VHT;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	switch (ifibss->chandef.width) {
78762306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_5:
78862306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_10:
78962306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_20_NOHT:
79062306a36Sopenharmony_ci		conn_flags |= IEEE80211_CONN_DISABLE_HT;
79162306a36Sopenharmony_ci		fallthrough;
79262306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_20:
79362306a36Sopenharmony_ci		conn_flags |= IEEE80211_CONN_DISABLE_40MHZ;
79462306a36Sopenharmony_ci		break;
79562306a36Sopenharmony_ci	default:
79662306a36Sopenharmony_ci		break;
79762306a36Sopenharmony_ci	}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	if (elems->vht_cap_elem)
80062306a36Sopenharmony_ci		vht_cap_info = le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	memset(&params, 0, sizeof(params));
80362306a36Sopenharmony_ci	err = ieee80211_parse_ch_switch_ie(sdata, elems,
80462306a36Sopenharmony_ci					   ifibss->chandef.chan->band,
80562306a36Sopenharmony_ci					   vht_cap_info,
80662306a36Sopenharmony_ci					   conn_flags, ifibss->bssid, &csa_ie);
80762306a36Sopenharmony_ci	/* can't switch to destination channel, fail */
80862306a36Sopenharmony_ci	if (err < 0)
80962306a36Sopenharmony_ci		goto disconnect;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	/* did not contain a CSA */
81262306a36Sopenharmony_ci	if (err)
81362306a36Sopenharmony_ci		return false;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	/* channel switch is not supported, disconnect */
81662306a36Sopenharmony_ci	if (!(sdata->local->hw.wiphy->flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
81762306a36Sopenharmony_ci		goto disconnect;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	params.count = csa_ie.count;
82062306a36Sopenharmony_ci	params.chandef = csa_ie.chandef;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	switch (ifibss->chandef.width) {
82362306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_20_NOHT:
82462306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_20:
82562306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_40:
82662306a36Sopenharmony_ci		/* keep our current HT mode (HT20/HT40+/HT40-), even if
82762306a36Sopenharmony_ci		 * another mode  has been announced. The mode is not adopted
82862306a36Sopenharmony_ci		 * within the beacon while doing CSA and we should therefore
82962306a36Sopenharmony_ci		 * keep the mode which we announce.
83062306a36Sopenharmony_ci		 */
83162306a36Sopenharmony_ci		ch_type = cfg80211_get_chandef_type(&ifibss->chandef);
83262306a36Sopenharmony_ci		cfg80211_chandef_create(&params.chandef, params.chandef.chan,
83362306a36Sopenharmony_ci					ch_type);
83462306a36Sopenharmony_ci		break;
83562306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_5:
83662306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_10:
83762306a36Sopenharmony_ci		if (params.chandef.width != ifibss->chandef.width) {
83862306a36Sopenharmony_ci			sdata_info(sdata,
83962306a36Sopenharmony_ci				   "IBSS %pM received channel switch from incompatible channel width (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
84062306a36Sopenharmony_ci				   ifibss->bssid,
84162306a36Sopenharmony_ci				   params.chandef.chan->center_freq,
84262306a36Sopenharmony_ci				   params.chandef.width,
84362306a36Sopenharmony_ci				   params.chandef.center_freq1,
84462306a36Sopenharmony_ci				   params.chandef.center_freq2);
84562306a36Sopenharmony_ci			goto disconnect;
84662306a36Sopenharmony_ci		}
84762306a36Sopenharmony_ci		break;
84862306a36Sopenharmony_ci	default:
84962306a36Sopenharmony_ci		/* should not happen, conn_flags should prevent VHT modes. */
85062306a36Sopenharmony_ci		WARN_ON(1);
85162306a36Sopenharmony_ci		goto disconnect;
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, &params.chandef,
85562306a36Sopenharmony_ci				     NL80211_IFTYPE_ADHOC)) {
85662306a36Sopenharmony_ci		sdata_info(sdata,
85762306a36Sopenharmony_ci			   "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
85862306a36Sopenharmony_ci			   ifibss->bssid,
85962306a36Sopenharmony_ci			   params.chandef.chan->center_freq,
86062306a36Sopenharmony_ci			   params.chandef.width,
86162306a36Sopenharmony_ci			   params.chandef.center_freq1,
86262306a36Sopenharmony_ci			   params.chandef.center_freq2);
86362306a36Sopenharmony_ci		goto disconnect;
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
86762306a36Sopenharmony_ci					    &params.chandef,
86862306a36Sopenharmony_ci					    NL80211_IFTYPE_ADHOC);
86962306a36Sopenharmony_ci	if (err < 0)
87062306a36Sopenharmony_ci		goto disconnect;
87162306a36Sopenharmony_ci	if (err > 0 && !ifibss->userspace_handles_dfs) {
87262306a36Sopenharmony_ci		/* IBSS-DFS only allowed with a control program */
87362306a36Sopenharmony_ci		goto disconnect;
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	params.radar_required = err;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (cfg80211_chandef_identical(&params.chandef,
87962306a36Sopenharmony_ci				       &sdata->vif.bss_conf.chandef)) {
88062306a36Sopenharmony_ci		ibss_dbg(sdata,
88162306a36Sopenharmony_ci			 "received csa with an identical chandef, ignoring\n");
88262306a36Sopenharmony_ci		return true;
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	/* all checks done, now perform the channel switch. */
88662306a36Sopenharmony_ci	ibss_dbg(sdata,
88762306a36Sopenharmony_ci		 "received channel switch announcement to go to channel %d MHz\n",
88862306a36Sopenharmony_ci		 params.chandef.chan->center_freq);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	params.block_tx = !!csa_ie.mode;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev,
89362306a36Sopenharmony_ci				     &params))
89462306a36Sopenharmony_ci		goto disconnect;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	ieee80211_ibss_csa_mark_radar(sdata);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	return true;
89962306a36Sopenharmony_cidisconnect:
90062306a36Sopenharmony_ci	ibss_dbg(sdata, "Can't handle channel switch, disconnect\n");
90162306a36Sopenharmony_ci	wiphy_work_queue(sdata->local->hw.wiphy,
90262306a36Sopenharmony_ci			 &ifibss->csa_connection_drop_work);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	ieee80211_ibss_csa_mark_radar(sdata);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	return true;
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic void
91062306a36Sopenharmony_ciieee80211_rx_mgmt_spectrum_mgmt(struct ieee80211_sub_if_data *sdata,
91162306a36Sopenharmony_ci				struct ieee80211_mgmt *mgmt, size_t len,
91262306a36Sopenharmony_ci				struct ieee80211_rx_status *rx_status,
91362306a36Sopenharmony_ci				struct ieee802_11_elems *elems)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	int required_len;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	if (len < IEEE80211_MIN_ACTION_SIZE + 1)
91862306a36Sopenharmony_ci		return;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	/* CSA is the only action we handle for now */
92162306a36Sopenharmony_ci	if (mgmt->u.action.u.measurement.action_code !=
92262306a36Sopenharmony_ci	    WLAN_ACTION_SPCT_CHL_SWITCH)
92362306a36Sopenharmony_ci		return;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	required_len = IEEE80211_MIN_ACTION_SIZE +
92662306a36Sopenharmony_ci		       sizeof(mgmt->u.action.u.chan_switch);
92762306a36Sopenharmony_ci	if (len < required_len)
92862306a36Sopenharmony_ci		return;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (!sdata->vif.bss_conf.csa_active)
93162306a36Sopenharmony_ci		ieee80211_ibss_process_chanswitch(sdata, elems, false);
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
93562306a36Sopenharmony_ci					  struct ieee80211_mgmt *mgmt,
93662306a36Sopenharmony_ci					  size_t len)
93762306a36Sopenharmony_ci{
93862306a36Sopenharmony_ci	u16 reason = le16_to_cpu(mgmt->u.deauth.reason_code);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (len < IEEE80211_DEAUTH_FRAME_LEN)
94162306a36Sopenharmony_ci		return;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	ibss_dbg(sdata, "RX DeAuth SA=%pM DA=%pM\n", mgmt->sa, mgmt->da);
94462306a36Sopenharmony_ci	ibss_dbg(sdata, "\tBSSID=%pM (reason: %d)\n", mgmt->bssid, reason);
94562306a36Sopenharmony_ci	sta_info_destroy_addr(sdata, mgmt->sa);
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
94962306a36Sopenharmony_ci					struct ieee80211_mgmt *mgmt,
95062306a36Sopenharmony_ci					size_t len)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci	u16 auth_alg, auth_transaction;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	sdata_assert_lock(sdata);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	if (len < 24 + 6)
95762306a36Sopenharmony_ci		return;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
96062306a36Sopenharmony_ci	auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	ibss_dbg(sdata, "RX Auth SA=%pM DA=%pM\n", mgmt->sa, mgmt->da);
96362306a36Sopenharmony_ci	ibss_dbg(sdata, "\tBSSID=%pM (auth_transaction=%d)\n",
96462306a36Sopenharmony_ci		 mgmt->bssid, auth_transaction);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1)
96762306a36Sopenharmony_ci		return;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	/*
97062306a36Sopenharmony_ci	 * IEEE 802.11 standard does not require authentication in IBSS
97162306a36Sopenharmony_ci	 * networks and most implementations do not seem to use it.
97262306a36Sopenharmony_ci	 * However, try to reply to authentication attempts if someone
97362306a36Sopenharmony_ci	 * has actually implemented this.
97462306a36Sopenharmony_ci	 */
97562306a36Sopenharmony_ci	ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, 0, NULL, 0,
97662306a36Sopenharmony_ci			    mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0, 0);
97762306a36Sopenharmony_ci}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_cistatic void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
98062306a36Sopenharmony_ci				      struct ieee80211_mgmt *mgmt, size_t len,
98162306a36Sopenharmony_ci				      struct ieee80211_rx_status *rx_status,
98262306a36Sopenharmony_ci				      struct ieee802_11_elems *elems,
98362306a36Sopenharmony_ci				      struct ieee80211_channel *channel)
98462306a36Sopenharmony_ci{
98562306a36Sopenharmony_ci	struct sta_info *sta;
98662306a36Sopenharmony_ci	enum nl80211_band band = rx_status->band;
98762306a36Sopenharmony_ci	enum nl80211_bss_scan_width scan_width;
98862306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
98962306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
99062306a36Sopenharmony_ci	bool rates_updated = false;
99162306a36Sopenharmony_ci	u32 supp_rates = 0;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
99462306a36Sopenharmony_ci		return;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (!ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid))
99762306a36Sopenharmony_ci		return;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	sband = local->hw.wiphy->bands[band];
100062306a36Sopenharmony_ci	if (WARN_ON(!sband))
100162306a36Sopenharmony_ci		return;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	rcu_read_lock();
100462306a36Sopenharmony_ci	sta = sta_info_get(sdata, mgmt->sa);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	if (elems->supp_rates) {
100762306a36Sopenharmony_ci		supp_rates = ieee80211_sta_get_rates(sdata, elems,
100862306a36Sopenharmony_ci						     band, NULL);
100962306a36Sopenharmony_ci		if (sta) {
101062306a36Sopenharmony_ci			u32 prev_rates;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci			prev_rates = sta->sta.deflink.supp_rates[band];
101362306a36Sopenharmony_ci			/* make sure mandatory rates are always added */
101462306a36Sopenharmony_ci			scan_width = NL80211_BSS_CHAN_WIDTH_20;
101562306a36Sopenharmony_ci			if (rx_status->bw == RATE_INFO_BW_5)
101662306a36Sopenharmony_ci				scan_width = NL80211_BSS_CHAN_WIDTH_5;
101762306a36Sopenharmony_ci			else if (rx_status->bw == RATE_INFO_BW_10)
101862306a36Sopenharmony_ci				scan_width = NL80211_BSS_CHAN_WIDTH_10;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci			sta->sta.deflink.supp_rates[band] = supp_rates |
102162306a36Sopenharmony_ci				ieee80211_mandatory_rates(sband, scan_width);
102262306a36Sopenharmony_ci			if (sta->sta.deflink.supp_rates[band] != prev_rates) {
102362306a36Sopenharmony_ci				ibss_dbg(sdata,
102462306a36Sopenharmony_ci					 "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
102562306a36Sopenharmony_ci					 sta->sta.addr, prev_rates,
102662306a36Sopenharmony_ci					 sta->sta.deflink.supp_rates[band]);
102762306a36Sopenharmony_ci				rates_updated = true;
102862306a36Sopenharmony_ci			}
102962306a36Sopenharmony_ci		} else {
103062306a36Sopenharmony_ci			rcu_read_unlock();
103162306a36Sopenharmony_ci			sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
103262306a36Sopenharmony_ci						     mgmt->sa, supp_rates);
103362306a36Sopenharmony_ci		}
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	if (sta && !sta->sta.wme &&
103762306a36Sopenharmony_ci	    (elems->wmm_info || elems->s1g_capab) &&
103862306a36Sopenharmony_ci	    local->hw.queues >= IEEE80211_NUM_ACS) {
103962306a36Sopenharmony_ci		sta->sta.wme = true;
104062306a36Sopenharmony_ci		ieee80211_check_fast_xmit(sta);
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	if (sta && elems->ht_operation && elems->ht_cap_elem &&
104462306a36Sopenharmony_ci	    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
104562306a36Sopenharmony_ci	    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 &&
104662306a36Sopenharmony_ci	    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) {
104762306a36Sopenharmony_ci		/* we both use HT */
104862306a36Sopenharmony_ci		struct ieee80211_ht_cap htcap_ie;
104962306a36Sopenharmony_ci		struct cfg80211_chan_def chandef;
105062306a36Sopenharmony_ci		enum ieee80211_sta_rx_bandwidth bw = sta->sta.deflink.bandwidth;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
105362306a36Sopenharmony_ci		ieee80211_chandef_ht_oper(elems->ht_operation, &chandef);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci		memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
105662306a36Sopenharmony_ci		rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
105762306a36Sopenharmony_ci								   &htcap_ie,
105862306a36Sopenharmony_ci								   &sta->deflink);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci		if (elems->vht_operation && elems->vht_cap_elem &&
106162306a36Sopenharmony_ci		    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20 &&
106262306a36Sopenharmony_ci		    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_40) {
106362306a36Sopenharmony_ci			/* we both use VHT */
106462306a36Sopenharmony_ci			struct ieee80211_vht_cap cap_ie;
106562306a36Sopenharmony_ci			struct ieee80211_sta_vht_cap cap = sta->sta.deflink.vht_cap;
106662306a36Sopenharmony_ci			u32 vht_cap_info =
106762306a36Sopenharmony_ci				le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci			ieee80211_chandef_vht_oper(&local->hw, vht_cap_info,
107062306a36Sopenharmony_ci						   elems->vht_operation,
107162306a36Sopenharmony_ci						   elems->ht_operation,
107262306a36Sopenharmony_ci						   &chandef);
107362306a36Sopenharmony_ci			memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie));
107462306a36Sopenharmony_ci			ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
107562306a36Sopenharmony_ci							    &cap_ie, NULL,
107662306a36Sopenharmony_ci							    &sta->deflink);
107762306a36Sopenharmony_ci			if (memcmp(&cap, &sta->sta.deflink.vht_cap, sizeof(cap)))
107862306a36Sopenharmony_ci				rates_updated |= true;
107962306a36Sopenharmony_ci		}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci		if (bw != sta->sta.deflink.bandwidth)
108262306a36Sopenharmony_ci			rates_updated |= true;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci		if (!cfg80211_chandef_compatible(&sdata->u.ibss.chandef,
108562306a36Sopenharmony_ci						 &chandef))
108662306a36Sopenharmony_ci			WARN_ON_ONCE(1);
108762306a36Sopenharmony_ci	}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	if (sta && rates_updated) {
109062306a36Sopenharmony_ci		u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
109162306a36Sopenharmony_ci		u8 rx_nss = sta->sta.deflink.rx_nss;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci		/* Force rx_nss recalculation */
109462306a36Sopenharmony_ci		sta->sta.deflink.rx_nss = 0;
109562306a36Sopenharmony_ci		rate_control_rate_init(sta);
109662306a36Sopenharmony_ci		if (sta->sta.deflink.rx_nss != rx_nss)
109762306a36Sopenharmony_ci			changed |= IEEE80211_RC_NSS_CHANGED;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci		drv_sta_rc_update(local, sdata, &sta->sta, changed);
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	rcu_read_unlock();
110362306a36Sopenharmony_ci}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_cistatic void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
110662306a36Sopenharmony_ci				  struct ieee80211_mgmt *mgmt, size_t len,
110762306a36Sopenharmony_ci				  struct ieee80211_rx_status *rx_status,
110862306a36Sopenharmony_ci				  struct ieee802_11_elems *elems)
110962306a36Sopenharmony_ci{
111062306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
111162306a36Sopenharmony_ci	struct cfg80211_bss *cbss;
111262306a36Sopenharmony_ci	struct ieee80211_bss *bss;
111362306a36Sopenharmony_ci	struct ieee80211_channel *channel;
111462306a36Sopenharmony_ci	u64 beacon_timestamp, rx_timestamp;
111562306a36Sopenharmony_ci	u32 supp_rates = 0;
111662306a36Sopenharmony_ci	enum nl80211_band band = rx_status->band;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
111962306a36Sopenharmony_ci	if (!channel)
112062306a36Sopenharmony_ci		return;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	ieee80211_update_sta_info(sdata, mgmt, len, rx_status, elems, channel);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, channel);
112562306a36Sopenharmony_ci	if (!bss)
112662306a36Sopenharmony_ci		return;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	cbss = container_of((void *)bss, struct cfg80211_bss, priv);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	/* same for beacon and probe response */
113162306a36Sopenharmony_ci	beacon_timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	/* check if we need to merge IBSS */
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	/* not an IBSS */
113662306a36Sopenharmony_ci	if (!(cbss->capability & WLAN_CAPABILITY_IBSS))
113762306a36Sopenharmony_ci		goto put_bss;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	/* different channel */
114062306a36Sopenharmony_ci	if (sdata->u.ibss.fixed_channel &&
114162306a36Sopenharmony_ci	    sdata->u.ibss.chandef.chan != cbss->channel)
114262306a36Sopenharmony_ci		goto put_bss;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	/* different SSID */
114562306a36Sopenharmony_ci	if (elems->ssid_len != sdata->u.ibss.ssid_len ||
114662306a36Sopenharmony_ci	    memcmp(elems->ssid, sdata->u.ibss.ssid,
114762306a36Sopenharmony_ci				sdata->u.ibss.ssid_len))
114862306a36Sopenharmony_ci		goto put_bss;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	/* process channel switch */
115162306a36Sopenharmony_ci	if (sdata->vif.bss_conf.csa_active ||
115262306a36Sopenharmony_ci	    ieee80211_ibss_process_chanswitch(sdata, elems, true))
115362306a36Sopenharmony_ci		goto put_bss;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	/* same BSSID */
115662306a36Sopenharmony_ci	if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid))
115762306a36Sopenharmony_ci		goto put_bss;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	/* we use a fixed BSSID */
116062306a36Sopenharmony_ci	if (sdata->u.ibss.fixed_bssid)
116162306a36Sopenharmony_ci		goto put_bss;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (ieee80211_have_rx_timestamp(rx_status)) {
116462306a36Sopenharmony_ci		/* time when timestamp field was received */
116562306a36Sopenharmony_ci		rx_timestamp =
116662306a36Sopenharmony_ci			ieee80211_calculate_rx_timestamp(local, rx_status,
116762306a36Sopenharmony_ci							 len + FCS_LEN, 24);
116862306a36Sopenharmony_ci	} else {
116962306a36Sopenharmony_ci		/*
117062306a36Sopenharmony_ci		 * second best option: get current TSF
117162306a36Sopenharmony_ci		 * (will return -1 if not supported)
117262306a36Sopenharmony_ci		 */
117362306a36Sopenharmony_ci		rx_timestamp = drv_get_tsf(local, sdata);
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	ibss_dbg(sdata, "RX beacon SA=%pM BSSID=%pM TSF=0x%llx\n",
117762306a36Sopenharmony_ci		 mgmt->sa, mgmt->bssid,
117862306a36Sopenharmony_ci		 (unsigned long long)rx_timestamp);
117962306a36Sopenharmony_ci	ibss_dbg(sdata, "\tBCN=0x%llx diff=%lld @%lu\n",
118062306a36Sopenharmony_ci		 (unsigned long long)beacon_timestamp,
118162306a36Sopenharmony_ci		 (unsigned long long)(rx_timestamp - beacon_timestamp),
118262306a36Sopenharmony_ci		 jiffies);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	if (beacon_timestamp > rx_timestamp) {
118562306a36Sopenharmony_ci		ibss_dbg(sdata,
118662306a36Sopenharmony_ci			 "beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n",
118762306a36Sopenharmony_ci			 mgmt->bssid);
118862306a36Sopenharmony_ci		ieee80211_sta_join_ibss(sdata, bss);
118962306a36Sopenharmony_ci		supp_rates = ieee80211_sta_get_rates(sdata, elems, band, NULL);
119062306a36Sopenharmony_ci		ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
119162306a36Sopenharmony_ci				       supp_rates);
119262306a36Sopenharmony_ci		rcu_read_unlock();
119362306a36Sopenharmony_ci	}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci put_bss:
119662306a36Sopenharmony_ci	ieee80211_rx_bss_put(local, bss);
119762306a36Sopenharmony_ci}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_civoid ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
120062306a36Sopenharmony_ci			      const u8 *bssid, const u8 *addr,
120162306a36Sopenharmony_ci			      u32 supp_rates)
120262306a36Sopenharmony_ci{
120362306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
120462306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
120562306a36Sopenharmony_ci	struct sta_info *sta;
120662306a36Sopenharmony_ci	struct ieee80211_chanctx_conf *chanctx_conf;
120762306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
120862306a36Sopenharmony_ci	enum nl80211_bss_scan_width scan_width;
120962306a36Sopenharmony_ci	int band;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	/*
121262306a36Sopenharmony_ci	 * XXX: Consider removing the least recently used entry and
121362306a36Sopenharmony_ci	 * 	allow new one to be added.
121462306a36Sopenharmony_ci	 */
121562306a36Sopenharmony_ci	if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
121662306a36Sopenharmony_ci		net_info_ratelimited("%s: No room for a new IBSS STA entry %pM\n",
121762306a36Sopenharmony_ci				    sdata->name, addr);
121862306a36Sopenharmony_ci		return;
121962306a36Sopenharmony_ci	}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH)
122262306a36Sopenharmony_ci		return;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	if (!ether_addr_equal(bssid, sdata->u.ibss.bssid))
122562306a36Sopenharmony_ci		return;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	rcu_read_lock();
122862306a36Sopenharmony_ci	chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
122962306a36Sopenharmony_ci	if (WARN_ON_ONCE(!chanctx_conf)) {
123062306a36Sopenharmony_ci		rcu_read_unlock();
123162306a36Sopenharmony_ci		return;
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci	band = chanctx_conf->def.chan->band;
123462306a36Sopenharmony_ci	scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
123562306a36Sopenharmony_ci	rcu_read_unlock();
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
123862306a36Sopenharmony_ci	if (!sta)
123962306a36Sopenharmony_ci		return;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	/* make sure mandatory rates are always added */
124262306a36Sopenharmony_ci	sband = local->hw.wiphy->bands[band];
124362306a36Sopenharmony_ci	sta->sta.deflink.supp_rates[band] = supp_rates |
124462306a36Sopenharmony_ci			ieee80211_mandatory_rates(sband, scan_width);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	spin_lock(&ifibss->incomplete_lock);
124762306a36Sopenharmony_ci	list_add(&sta->list, &ifibss->incomplete_stations);
124862306a36Sopenharmony_ci	spin_unlock(&ifibss->incomplete_lock);
124962306a36Sopenharmony_ci	wiphy_work_queue(local->hw.wiphy, &sdata->work);
125062306a36Sopenharmony_ci}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_cistatic void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
125362306a36Sopenharmony_ci{
125462306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
125562306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
125662306a36Sopenharmony_ci	struct sta_info *sta, *tmp;
125762306a36Sopenharmony_ci	unsigned long exp_time = IEEE80211_IBSS_INACTIVITY_LIMIT;
125862306a36Sopenharmony_ci	unsigned long exp_rsn = IEEE80211_IBSS_RSN_INACTIVITY_LIMIT;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	mutex_lock(&local->sta_mtx);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
126362306a36Sopenharmony_ci		unsigned long last_active = ieee80211_sta_last_active(sta);
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci		if (sdata != sta->sdata)
126662306a36Sopenharmony_ci			continue;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci		if (time_is_before_jiffies(last_active + exp_time) ||
126962306a36Sopenharmony_ci		    (time_is_before_jiffies(last_active + exp_rsn) &&
127062306a36Sopenharmony_ci		     sta->sta_state != IEEE80211_STA_AUTHORIZED)) {
127162306a36Sopenharmony_ci			u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci			sta_dbg(sta->sdata, "expiring inactive %sSTA %pM\n",
127462306a36Sopenharmony_ci				sta->sta_state != IEEE80211_STA_AUTHORIZED ?
127562306a36Sopenharmony_ci				"not authorized " : "", sta->sta.addr);
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci			ieee80211_send_deauth_disassoc(sdata, sta->sta.addr,
127862306a36Sopenharmony_ci						       ifibss->bssid,
127962306a36Sopenharmony_ci						       IEEE80211_STYPE_DEAUTH,
128062306a36Sopenharmony_ci						       WLAN_REASON_DEAUTH_LEAVING,
128162306a36Sopenharmony_ci						       true, frame_buf);
128262306a36Sopenharmony_ci			WARN_ON(__sta_info_destroy(sta));
128362306a36Sopenharmony_ci		}
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	mutex_unlock(&local->sta_mtx);
128762306a36Sopenharmony_ci}
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci/*
129062306a36Sopenharmony_ci * This function is called with state == IEEE80211_IBSS_MLME_JOINED
129162306a36Sopenharmony_ci */
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_cistatic void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
129462306a36Sopenharmony_ci{
129562306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
129662306a36Sopenharmony_ci	enum nl80211_bss_scan_width scan_width;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	sdata_assert_lock(sdata);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	mod_timer(&ifibss->timer,
130162306a36Sopenharmony_ci		  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	ieee80211_ibss_sta_expire(sdata);
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	if (time_before(jiffies, ifibss->last_scan_completed +
130662306a36Sopenharmony_ci		       IEEE80211_IBSS_MERGE_INTERVAL))
130762306a36Sopenharmony_ci		return;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	if (ieee80211_sta_active_ibss(sdata))
131062306a36Sopenharmony_ci		return;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	if (ifibss->fixed_channel)
131362306a36Sopenharmony_ci		return;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	sdata_info(sdata,
131662306a36Sopenharmony_ci		   "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n");
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
131962306a36Sopenharmony_ci	ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
132062306a36Sopenharmony_ci				    NULL, 0, scan_width);
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_cistatic void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
132662306a36Sopenharmony_ci	u8 bssid[ETH_ALEN];
132762306a36Sopenharmony_ci	u16 capability;
132862306a36Sopenharmony_ci	int i;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	sdata_assert_lock(sdata);
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	if (ifibss->fixed_bssid) {
133362306a36Sopenharmony_ci		memcpy(bssid, ifibss->bssid, ETH_ALEN);
133462306a36Sopenharmony_ci	} else {
133562306a36Sopenharmony_ci		/* Generate random, not broadcast, locally administered BSSID. Mix in
133662306a36Sopenharmony_ci		 * own MAC address to make sure that devices that do not have proper
133762306a36Sopenharmony_ci		 * random number generator get different BSSID. */
133862306a36Sopenharmony_ci		get_random_bytes(bssid, ETH_ALEN);
133962306a36Sopenharmony_ci		for (i = 0; i < ETH_ALEN; i++)
134062306a36Sopenharmony_ci			bssid[i] ^= sdata->vif.addr[i];
134162306a36Sopenharmony_ci		bssid[0] &= ~0x01;
134262306a36Sopenharmony_ci		bssid[0] |= 0x02;
134362306a36Sopenharmony_ci	}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	sdata_info(sdata, "Creating new IBSS network, BSSID %pM\n", bssid);
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	capability = WLAN_CAPABILITY_IBSS;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	if (ifibss->privacy)
135062306a36Sopenharmony_ci		capability |= WLAN_CAPABILITY_PRIVACY;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	__ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int,
135362306a36Sopenharmony_ci				  &ifibss->chandef, ifibss->basic_rates,
135462306a36Sopenharmony_ci				  capability, 0, true);
135562306a36Sopenharmony_ci}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_cistatic unsigned int ibss_setup_channels(struct wiphy *wiphy,
135862306a36Sopenharmony_ci					struct ieee80211_channel **channels,
135962306a36Sopenharmony_ci					unsigned int channels_max,
136062306a36Sopenharmony_ci					u32 center_freq, u32 width)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	struct ieee80211_channel *chan = NULL;
136362306a36Sopenharmony_ci	unsigned int n_chan = 0;
136462306a36Sopenharmony_ci	u32 start_freq, end_freq, freq;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	if (width <= 20) {
136762306a36Sopenharmony_ci		start_freq = center_freq;
136862306a36Sopenharmony_ci		end_freq = center_freq;
136962306a36Sopenharmony_ci	} else {
137062306a36Sopenharmony_ci		start_freq = center_freq - width / 2 + 10;
137162306a36Sopenharmony_ci		end_freq = center_freq + width / 2 - 10;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	for (freq = start_freq; freq <= end_freq; freq += 20) {
137562306a36Sopenharmony_ci		chan = ieee80211_get_channel(wiphy, freq);
137662306a36Sopenharmony_ci		if (!chan)
137762306a36Sopenharmony_ci			continue;
137862306a36Sopenharmony_ci		if (n_chan >= channels_max)
137962306a36Sopenharmony_ci			return n_chan;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci		channels[n_chan] = chan;
138262306a36Sopenharmony_ci		n_chan++;
138362306a36Sopenharmony_ci	}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	return n_chan;
138662306a36Sopenharmony_ci}
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_cistatic unsigned int
138962306a36Sopenharmony_ciieee80211_ibss_setup_scan_channels(struct wiphy *wiphy,
139062306a36Sopenharmony_ci				   const struct cfg80211_chan_def *chandef,
139162306a36Sopenharmony_ci				   struct ieee80211_channel **channels,
139262306a36Sopenharmony_ci				   unsigned int channels_max)
139362306a36Sopenharmony_ci{
139462306a36Sopenharmony_ci	unsigned int n_chan = 0;
139562306a36Sopenharmony_ci	u32 width, cf1, cf2 = 0;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	switch (chandef->width) {
139862306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_40:
139962306a36Sopenharmony_ci		width = 40;
140062306a36Sopenharmony_ci		break;
140162306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_80P80:
140262306a36Sopenharmony_ci		cf2 = chandef->center_freq2;
140362306a36Sopenharmony_ci		fallthrough;
140462306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_80:
140562306a36Sopenharmony_ci		width = 80;
140662306a36Sopenharmony_ci		break;
140762306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_160:
140862306a36Sopenharmony_ci		width = 160;
140962306a36Sopenharmony_ci		break;
141062306a36Sopenharmony_ci	default:
141162306a36Sopenharmony_ci		width = 20;
141262306a36Sopenharmony_ci		break;
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	cf1 = chandef->center_freq1;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	n_chan = ibss_setup_channels(wiphy, channels, channels_max, cf1, width);
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	if (cf2)
142062306a36Sopenharmony_ci		n_chan += ibss_setup_channels(wiphy, &channels[n_chan],
142162306a36Sopenharmony_ci					      channels_max - n_chan, cf2,
142262306a36Sopenharmony_ci					      width);
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	return n_chan;
142562306a36Sopenharmony_ci}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci/*
142862306a36Sopenharmony_ci * This function is called with state == IEEE80211_IBSS_MLME_SEARCH
142962306a36Sopenharmony_ci */
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_cistatic void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
143262306a36Sopenharmony_ci{
143362306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
143462306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
143562306a36Sopenharmony_ci	struct cfg80211_bss *cbss;
143662306a36Sopenharmony_ci	struct ieee80211_channel *chan = NULL;
143762306a36Sopenharmony_ci	const u8 *bssid = NULL;
143862306a36Sopenharmony_ci	enum nl80211_bss_scan_width scan_width;
143962306a36Sopenharmony_ci	int active_ibss;
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	sdata_assert_lock(sdata);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	active_ibss = ieee80211_sta_active_ibss(sdata);
144462306a36Sopenharmony_ci	ibss_dbg(sdata, "sta_find_ibss (active_ibss=%d)\n", active_ibss);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	if (active_ibss)
144762306a36Sopenharmony_ci		return;
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	if (ifibss->fixed_bssid)
145062306a36Sopenharmony_ci		bssid = ifibss->bssid;
145162306a36Sopenharmony_ci	if (ifibss->fixed_channel)
145262306a36Sopenharmony_ci		chan = ifibss->chandef.chan;
145362306a36Sopenharmony_ci	if (!is_zero_ether_addr(ifibss->bssid))
145462306a36Sopenharmony_ci		bssid = ifibss->bssid;
145562306a36Sopenharmony_ci	cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid,
145662306a36Sopenharmony_ci				ifibss->ssid, ifibss->ssid_len,
145762306a36Sopenharmony_ci				IEEE80211_BSS_TYPE_IBSS,
145862306a36Sopenharmony_ci				IEEE80211_PRIVACY(ifibss->privacy));
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	if (cbss) {
146162306a36Sopenharmony_ci		struct ieee80211_bss *bss;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci		bss = (void *)cbss->priv;
146462306a36Sopenharmony_ci		ibss_dbg(sdata,
146562306a36Sopenharmony_ci			 "sta_find_ibss: selected %pM current %pM\n",
146662306a36Sopenharmony_ci			 cbss->bssid, ifibss->bssid);
146762306a36Sopenharmony_ci		sdata_info(sdata,
146862306a36Sopenharmony_ci			   "Selected IBSS BSSID %pM based on configured SSID\n",
146962306a36Sopenharmony_ci			   cbss->bssid);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci		ieee80211_sta_join_ibss(sdata, bss);
147262306a36Sopenharmony_ci		ieee80211_rx_bss_put(local, bss);
147362306a36Sopenharmony_ci		return;
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	/* if a fixed bssid and a fixed freq have been provided create the IBSS
147762306a36Sopenharmony_ci	 * directly and do not waste time scanning
147862306a36Sopenharmony_ci	 */
147962306a36Sopenharmony_ci	if (ifibss->fixed_bssid && ifibss->fixed_channel) {
148062306a36Sopenharmony_ci		sdata_info(sdata, "Created IBSS using preconfigured BSSID %pM\n",
148162306a36Sopenharmony_ci			   bssid);
148262306a36Sopenharmony_ci		ieee80211_sta_create_ibss(sdata);
148362306a36Sopenharmony_ci		return;
148462306a36Sopenharmony_ci	}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	ibss_dbg(sdata, "sta_find_ibss: did not try to join ibss\n");
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	/* Selected IBSS not found in current scan results - try to scan */
149062306a36Sopenharmony_ci	if (time_after(jiffies, ifibss->last_scan_completed +
149162306a36Sopenharmony_ci					IEEE80211_SCAN_INTERVAL)) {
149262306a36Sopenharmony_ci		struct ieee80211_channel *channels[8];
149362306a36Sopenharmony_ci		unsigned int num;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci		sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci		scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci		if (ifibss->fixed_channel) {
150062306a36Sopenharmony_ci			num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy,
150162306a36Sopenharmony_ci								 &ifibss->chandef,
150262306a36Sopenharmony_ci								 channels,
150362306a36Sopenharmony_ci								 ARRAY_SIZE(channels));
150462306a36Sopenharmony_ci			ieee80211_request_ibss_scan(sdata, ifibss->ssid,
150562306a36Sopenharmony_ci						    ifibss->ssid_len, channels,
150662306a36Sopenharmony_ci						    num, scan_width);
150762306a36Sopenharmony_ci		} else {
150862306a36Sopenharmony_ci			ieee80211_request_ibss_scan(sdata, ifibss->ssid,
150962306a36Sopenharmony_ci						    ifibss->ssid_len, NULL,
151062306a36Sopenharmony_ci						    0, scan_width);
151162306a36Sopenharmony_ci		}
151262306a36Sopenharmony_ci	} else {
151362306a36Sopenharmony_ci		int interval = IEEE80211_SCAN_INTERVAL;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci		if (time_after(jiffies, ifibss->ibss_join_req +
151662306a36Sopenharmony_ci			       IEEE80211_IBSS_JOIN_TIMEOUT))
151762306a36Sopenharmony_ci			ieee80211_sta_create_ibss(sdata);
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci		mod_timer(&ifibss->timer,
152062306a36Sopenharmony_ci			  round_jiffies(jiffies + interval));
152162306a36Sopenharmony_ci	}
152262306a36Sopenharmony_ci}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_cistatic void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
152562306a36Sopenharmony_ci					struct sk_buff *req)
152662306a36Sopenharmony_ci{
152762306a36Sopenharmony_ci	struct ieee80211_mgmt *mgmt = (void *)req->data;
152862306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
152962306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
153062306a36Sopenharmony_ci	int tx_last_beacon, len = req->len;
153162306a36Sopenharmony_ci	struct sk_buff *skb;
153262306a36Sopenharmony_ci	struct beacon_data *presp;
153362306a36Sopenharmony_ci	u8 *pos, *end;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	sdata_assert_lock(sdata);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	presp = sdata_dereference(ifibss->presp, sdata);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	if (ifibss->state != IEEE80211_IBSS_MLME_JOINED ||
154062306a36Sopenharmony_ci	    len < 24 + 2 || !presp)
154162306a36Sopenharmony_ci		return;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	tx_last_beacon = drv_tx_last_beacon(local);
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	ibss_dbg(sdata, "RX ProbeReq SA=%pM DA=%pM\n", mgmt->sa, mgmt->da);
154662306a36Sopenharmony_ci	ibss_dbg(sdata, "\tBSSID=%pM (tx_last_beacon=%d)\n",
154762306a36Sopenharmony_ci		 mgmt->bssid, tx_last_beacon);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	if (!tx_last_beacon && is_multicast_ether_addr(mgmt->da))
155062306a36Sopenharmony_ci		return;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	if (!ether_addr_equal(mgmt->bssid, ifibss->bssid) &&
155362306a36Sopenharmony_ci	    !is_broadcast_ether_addr(mgmt->bssid))
155462306a36Sopenharmony_ci		return;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	end = ((u8 *) mgmt) + len;
155762306a36Sopenharmony_ci	pos = mgmt->u.probe_req.variable;
155862306a36Sopenharmony_ci	if (pos[0] != WLAN_EID_SSID ||
155962306a36Sopenharmony_ci	    pos + 2 + pos[1] > end) {
156062306a36Sopenharmony_ci		ibss_dbg(sdata, "Invalid SSID IE in ProbeReq from %pM\n",
156162306a36Sopenharmony_ci			 mgmt->sa);
156262306a36Sopenharmony_ci		return;
156362306a36Sopenharmony_ci	}
156462306a36Sopenharmony_ci	if (pos[1] != 0 &&
156562306a36Sopenharmony_ci	    (pos[1] != ifibss->ssid_len ||
156662306a36Sopenharmony_ci	     memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len))) {
156762306a36Sopenharmony_ci		/* Ignore ProbeReq for foreign SSID */
156862306a36Sopenharmony_ci		return;
156962306a36Sopenharmony_ci	}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	/* Reply with ProbeResp */
157262306a36Sopenharmony_ci	skb = dev_alloc_skb(local->tx_headroom + presp->head_len);
157362306a36Sopenharmony_ci	if (!skb)
157462306a36Sopenharmony_ci		return;
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	skb_reserve(skb, local->tx_headroom);
157762306a36Sopenharmony_ci	skb_put_data(skb, presp->head, presp->head_len);
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	memcpy(((struct ieee80211_mgmt *) skb->data)->da, mgmt->sa, ETH_ALEN);
158062306a36Sopenharmony_ci	ibss_dbg(sdata, "Sending ProbeResp to %pM\n", mgmt->sa);
158162306a36Sopenharmony_ci	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	/* avoid excessive retries for probe request to wildcard SSIDs */
158462306a36Sopenharmony_ci	if (pos[1] == 0)
158562306a36Sopenharmony_ci		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_NO_ACK;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	ieee80211_tx_skb(sdata, skb);
158862306a36Sopenharmony_ci}
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_cistatic
159162306a36Sopenharmony_civoid ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
159262306a36Sopenharmony_ci				    struct ieee80211_mgmt *mgmt, size_t len,
159362306a36Sopenharmony_ci				    struct ieee80211_rx_status *rx_status)
159462306a36Sopenharmony_ci{
159562306a36Sopenharmony_ci	size_t baselen;
159662306a36Sopenharmony_ci	struct ieee802_11_elems *elems;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(mgmt->u.probe_resp), variable) !=
159962306a36Sopenharmony_ci		     offsetof(typeof(mgmt->u.beacon), variable));
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	/*
160262306a36Sopenharmony_ci	 * either beacon or probe_resp but the variable field is at the
160362306a36Sopenharmony_ci	 * same offset
160462306a36Sopenharmony_ci	 */
160562306a36Sopenharmony_ci	baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
160662306a36Sopenharmony_ci	if (baselen > len)
160762306a36Sopenharmony_ci		return;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
161062306a36Sopenharmony_ci				       len - baselen, false, NULL);
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	if (elems) {
161362306a36Sopenharmony_ci		ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, elems);
161462306a36Sopenharmony_ci		kfree(elems);
161562306a36Sopenharmony_ci	}
161662306a36Sopenharmony_ci}
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_civoid ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
161962306a36Sopenharmony_ci				   struct sk_buff *skb)
162062306a36Sopenharmony_ci{
162162306a36Sopenharmony_ci	struct ieee80211_rx_status *rx_status;
162262306a36Sopenharmony_ci	struct ieee80211_mgmt *mgmt;
162362306a36Sopenharmony_ci	u16 fc;
162462306a36Sopenharmony_ci	struct ieee802_11_elems *elems;
162562306a36Sopenharmony_ci	int ies_len;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	rx_status = IEEE80211_SKB_RXCB(skb);
162862306a36Sopenharmony_ci	mgmt = (struct ieee80211_mgmt *) skb->data;
162962306a36Sopenharmony_ci	fc = le16_to_cpu(mgmt->frame_control);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	sdata_lock(sdata);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	if (!sdata->u.ibss.ssid_len)
163462306a36Sopenharmony_ci		goto mgmt_out; /* not ready to merge yet */
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	switch (fc & IEEE80211_FCTL_STYPE) {
163762306a36Sopenharmony_ci	case IEEE80211_STYPE_PROBE_REQ:
163862306a36Sopenharmony_ci		ieee80211_rx_mgmt_probe_req(sdata, skb);
163962306a36Sopenharmony_ci		break;
164062306a36Sopenharmony_ci	case IEEE80211_STYPE_PROBE_RESP:
164162306a36Sopenharmony_ci	case IEEE80211_STYPE_BEACON:
164262306a36Sopenharmony_ci		ieee80211_rx_mgmt_probe_beacon(sdata, mgmt, skb->len,
164362306a36Sopenharmony_ci					       rx_status);
164462306a36Sopenharmony_ci		break;
164562306a36Sopenharmony_ci	case IEEE80211_STYPE_AUTH:
164662306a36Sopenharmony_ci		ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len);
164762306a36Sopenharmony_ci		break;
164862306a36Sopenharmony_ci	case IEEE80211_STYPE_DEAUTH:
164962306a36Sopenharmony_ci		ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len);
165062306a36Sopenharmony_ci		break;
165162306a36Sopenharmony_ci	case IEEE80211_STYPE_ACTION:
165262306a36Sopenharmony_ci		switch (mgmt->u.action.category) {
165362306a36Sopenharmony_ci		case WLAN_CATEGORY_SPECTRUM_MGMT:
165462306a36Sopenharmony_ci			ies_len = skb->len -
165562306a36Sopenharmony_ci				  offsetof(struct ieee80211_mgmt,
165662306a36Sopenharmony_ci					   u.action.u.chan_switch.variable);
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci			if (ies_len < 0)
165962306a36Sopenharmony_ci				break;
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci			elems = ieee802_11_parse_elems(
166262306a36Sopenharmony_ci				mgmt->u.action.u.chan_switch.variable,
166362306a36Sopenharmony_ci				ies_len, true, NULL);
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci			if (elems && !elems->parse_error)
166662306a36Sopenharmony_ci				ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt,
166762306a36Sopenharmony_ci								skb->len,
166862306a36Sopenharmony_ci								rx_status,
166962306a36Sopenharmony_ci								elems);
167062306a36Sopenharmony_ci			kfree(elems);
167162306a36Sopenharmony_ci			break;
167262306a36Sopenharmony_ci		}
167362306a36Sopenharmony_ci	}
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci mgmt_out:
167662306a36Sopenharmony_ci	sdata_unlock(sdata);
167762306a36Sopenharmony_ci}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_civoid ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
168062306a36Sopenharmony_ci{
168162306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
168262306a36Sopenharmony_ci	struct sta_info *sta;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	sdata_lock(sdata);
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	/*
168762306a36Sopenharmony_ci	 * Work could be scheduled after scan or similar
168862306a36Sopenharmony_ci	 * when we aren't even joined (or trying) with a
168962306a36Sopenharmony_ci	 * network.
169062306a36Sopenharmony_ci	 */
169162306a36Sopenharmony_ci	if (!ifibss->ssid_len)
169262306a36Sopenharmony_ci		goto out;
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	spin_lock_bh(&ifibss->incomplete_lock);
169562306a36Sopenharmony_ci	while (!list_empty(&ifibss->incomplete_stations)) {
169662306a36Sopenharmony_ci		sta = list_first_entry(&ifibss->incomplete_stations,
169762306a36Sopenharmony_ci				       struct sta_info, list);
169862306a36Sopenharmony_ci		list_del(&sta->list);
169962306a36Sopenharmony_ci		spin_unlock_bh(&ifibss->incomplete_lock);
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci		ieee80211_ibss_finish_sta(sta);
170262306a36Sopenharmony_ci		rcu_read_unlock();
170362306a36Sopenharmony_ci		spin_lock_bh(&ifibss->incomplete_lock);
170462306a36Sopenharmony_ci	}
170562306a36Sopenharmony_ci	spin_unlock_bh(&ifibss->incomplete_lock);
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	switch (ifibss->state) {
170862306a36Sopenharmony_ci	case IEEE80211_IBSS_MLME_SEARCH:
170962306a36Sopenharmony_ci		ieee80211_sta_find_ibss(sdata);
171062306a36Sopenharmony_ci		break;
171162306a36Sopenharmony_ci	case IEEE80211_IBSS_MLME_JOINED:
171262306a36Sopenharmony_ci		ieee80211_sta_merge_ibss(sdata);
171362306a36Sopenharmony_ci		break;
171462306a36Sopenharmony_ci	default:
171562306a36Sopenharmony_ci		WARN_ON(1);
171662306a36Sopenharmony_ci		break;
171762306a36Sopenharmony_ci	}
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci out:
172062306a36Sopenharmony_ci	sdata_unlock(sdata);
172162306a36Sopenharmony_ci}
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_cistatic void ieee80211_ibss_timer(struct timer_list *t)
172462306a36Sopenharmony_ci{
172562306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata =
172662306a36Sopenharmony_ci		from_timer(sdata, t, u.ibss.timer);
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work);
172962306a36Sopenharmony_ci}
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_civoid ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
173262306a36Sopenharmony_ci{
173362306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	timer_setup(&ifibss->timer, ieee80211_ibss_timer, 0);
173662306a36Sopenharmony_ci	INIT_LIST_HEAD(&ifibss->incomplete_stations);
173762306a36Sopenharmony_ci	spin_lock_init(&ifibss->incomplete_lock);
173862306a36Sopenharmony_ci	wiphy_work_init(&ifibss->csa_connection_drop_work,
173962306a36Sopenharmony_ci			ieee80211_csa_connection_drop_work);
174062306a36Sopenharmony_ci}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci/* scan finished notification */
174362306a36Sopenharmony_civoid ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local)
174462306a36Sopenharmony_ci{
174562306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	mutex_lock(&local->iflist_mtx);
174862306a36Sopenharmony_ci	list_for_each_entry(sdata, &local->interfaces, list) {
174962306a36Sopenharmony_ci		if (!ieee80211_sdata_running(sdata))
175062306a36Sopenharmony_ci			continue;
175162306a36Sopenharmony_ci		if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
175262306a36Sopenharmony_ci			continue;
175362306a36Sopenharmony_ci		sdata->u.ibss.last_scan_completed = jiffies;
175462306a36Sopenharmony_ci	}
175562306a36Sopenharmony_ci	mutex_unlock(&local->iflist_mtx);
175662306a36Sopenharmony_ci}
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ciint ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
175962306a36Sopenharmony_ci			struct cfg80211_ibss_params *params)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	u64 changed = 0;
176262306a36Sopenharmony_ci	u32 rate_flags;
176362306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
176462306a36Sopenharmony_ci	enum ieee80211_chanctx_mode chanmode;
176562306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
176662306a36Sopenharmony_ci	int radar_detect_width = 0;
176762306a36Sopenharmony_ci	int i;
176862306a36Sopenharmony_ci	int ret;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	if (params->chandef.chan->freq_offset) {
177162306a36Sopenharmony_ci		/* this may work, but is untested */
177262306a36Sopenharmony_ci		return -EOPNOTSUPP;
177362306a36Sopenharmony_ci	}
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
177662306a36Sopenharmony_ci					    &params->chandef,
177762306a36Sopenharmony_ci					    sdata->wdev.iftype);
177862306a36Sopenharmony_ci	if (ret < 0)
177962306a36Sopenharmony_ci		return ret;
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	if (ret > 0) {
178262306a36Sopenharmony_ci		if (!params->userspace_handles_dfs)
178362306a36Sopenharmony_ci			return -EINVAL;
178462306a36Sopenharmony_ci		radar_detect_width = BIT(params->chandef.width);
178562306a36Sopenharmony_ci	}
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	chanmode = (params->channel_fixed && !ret) ?
178862306a36Sopenharmony_ci		IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE;
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	mutex_lock(&local->chanctx_mtx);
179162306a36Sopenharmony_ci	ret = ieee80211_check_combinations(sdata, &params->chandef, chanmode,
179262306a36Sopenharmony_ci					   radar_detect_width);
179362306a36Sopenharmony_ci	mutex_unlock(&local->chanctx_mtx);
179462306a36Sopenharmony_ci	if (ret < 0)
179562306a36Sopenharmony_ci		return ret;
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	if (params->bssid) {
179862306a36Sopenharmony_ci		memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
179962306a36Sopenharmony_ci		sdata->u.ibss.fixed_bssid = true;
180062306a36Sopenharmony_ci	} else
180162306a36Sopenharmony_ci		sdata->u.ibss.fixed_bssid = false;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	sdata->u.ibss.privacy = params->privacy;
180462306a36Sopenharmony_ci	sdata->u.ibss.control_port = params->control_port;
180562306a36Sopenharmony_ci	sdata->u.ibss.userspace_handles_dfs = params->userspace_handles_dfs;
180662306a36Sopenharmony_ci	sdata->u.ibss.basic_rates = params->basic_rates;
180762306a36Sopenharmony_ci	sdata->u.ibss.last_scan_completed = jiffies;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	/* fix basic_rates if channel does not support these rates */
181062306a36Sopenharmony_ci	rate_flags = ieee80211_chandef_rate_flags(&params->chandef);
181162306a36Sopenharmony_ci	sband = local->hw.wiphy->bands[params->chandef.chan->band];
181262306a36Sopenharmony_ci	for (i = 0; i < sband->n_bitrates; i++) {
181362306a36Sopenharmony_ci		if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
181462306a36Sopenharmony_ci			sdata->u.ibss.basic_rates &= ~BIT(i);
181562306a36Sopenharmony_ci	}
181662306a36Sopenharmony_ci	memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate,
181762306a36Sopenharmony_ci	       sizeof(params->mcast_rate));
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	sdata->vif.bss_conf.beacon_int = params->beacon_interval;
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	sdata->u.ibss.chandef = params->chandef;
182262306a36Sopenharmony_ci	sdata->u.ibss.fixed_channel = params->channel_fixed;
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	if (params->ie) {
182562306a36Sopenharmony_ci		sdata->u.ibss.ie = kmemdup(params->ie, params->ie_len,
182662306a36Sopenharmony_ci					   GFP_KERNEL);
182762306a36Sopenharmony_ci		if (sdata->u.ibss.ie)
182862306a36Sopenharmony_ci			sdata->u.ibss.ie_len = params->ie_len;
182962306a36Sopenharmony_ci	}
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH;
183262306a36Sopenharmony_ci	sdata->u.ibss.ibss_join_req = jiffies;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len);
183562306a36Sopenharmony_ci	sdata->u.ibss.ssid_len = params->ssid_len;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	memcpy(&sdata->u.ibss.ht_capa, &params->ht_capa,
183862306a36Sopenharmony_ci	       sizeof(sdata->u.ibss.ht_capa));
183962306a36Sopenharmony_ci	memcpy(&sdata->u.ibss.ht_capa_mask, &params->ht_capa_mask,
184062306a36Sopenharmony_ci	       sizeof(sdata->u.ibss.ht_capa_mask));
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci	/*
184362306a36Sopenharmony_ci	 * 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is
184462306a36Sopenharmony_ci	 * reserved, but an HT STA shall protect HT transmissions as though
184562306a36Sopenharmony_ci	 * the HT Protection field were set to non-HT mixed mode.
184662306a36Sopenharmony_ci	 *
184762306a36Sopenharmony_ci	 * In an IBSS, the RIFS Mode field of the HT Operation element is
184862306a36Sopenharmony_ci	 * also reserved, but an HT STA shall operate as though this field
184962306a36Sopenharmony_ci	 * were set to 1.
185062306a36Sopenharmony_ci	 */
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	sdata->vif.bss_conf.ht_operation_mode |=
185362306a36Sopenharmony_ci		  IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED
185462306a36Sopenharmony_ci		| IEEE80211_HT_PARAM_RIFS_MODE;
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	changed |= BSS_CHANGED_HT | BSS_CHANGED_MCAST_RATE;
185762306a36Sopenharmony_ci	ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed);
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
186062306a36Sopenharmony_ci	sdata->deflink.needed_rx_chains = local->rx_chains;
186162306a36Sopenharmony_ci	sdata->control_port_over_nl80211 = params->control_port_over_nl80211;
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	wiphy_work_queue(local->hw.wiphy, &sdata->work);
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	return 0;
186662306a36Sopenharmony_ci}
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ciint ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
186962306a36Sopenharmony_ci{
187062306a36Sopenharmony_ci	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	ieee80211_ibss_disconnect(sdata);
187362306a36Sopenharmony_ci	ifibss->ssid_len = 0;
187462306a36Sopenharmony_ci	eth_zero_addr(ifibss->bssid);
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	/* remove beacon */
187762306a36Sopenharmony_ci	kfree(sdata->u.ibss.ie);
187862306a36Sopenharmony_ci	sdata->u.ibss.ie = NULL;
187962306a36Sopenharmony_ci	sdata->u.ibss.ie_len = 0;
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	/* on the next join, re-program HT parameters */
188262306a36Sopenharmony_ci	memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
188362306a36Sopenharmony_ci	memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask));
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	synchronize_rcu();
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	skb_queue_purge(&sdata->skb_queue);
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	del_timer_sync(&sdata->u.ibss.timer);
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	return 0;
189262306a36Sopenharmony_ci}
1893