xref: /openbmc/linux/net/wireless/ibss.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
204a773adSJohannes Berg /*
304a773adSJohannes Berg  * Some IBSS support code for cfg80211.
404a773adSJohannes Berg  *
504a773adSJohannes Berg  * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
634c9a0e7SJohannes Berg  * Copyright (C) 2020-2022 Intel Corporation
704a773adSJohannes Berg  */
804a773adSJohannes Berg 
904a773adSJohannes Berg #include <linux/etherdevice.h>
1004a773adSJohannes Berg #include <linux/if_arp.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
12bc3b2d7fSPaul Gortmaker #include <linux/export.h>
1304a773adSJohannes Berg #include <net/cfg80211.h>
140e82ffe3SJohannes Berg #include "wext-compat.h"
1504a773adSJohannes Berg #include "nl80211.h"
16e35e4d28SHila Gonen #include "rdev-ops.h"
1704a773adSJohannes Berg 
1804a773adSJohannes Berg 
__cfg80211_ibss_joined(struct net_device * dev,const u8 * bssid,struct ieee80211_channel * channel)19fe94f3a4SAntonio Quartulli void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
20fe94f3a4SAntonio Quartulli 			    struct ieee80211_channel *channel)
2104a773adSJohannes Berg {
2204a773adSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
2304a773adSJohannes Berg 	struct cfg80211_bss *bss;
243d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
2504a773adSJohannes Berg 	union iwreq_data wrqu;
2604a773adSJohannes Berg #endif
2704a773adSJohannes Berg 
2804a773adSJohannes Berg 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
2904a773adSJohannes Berg 		return;
3004a773adSJohannes Berg 
317b0a0e3cSJohannes Berg 	if (!wdev->u.ibss.ssid_len)
3204a773adSJohannes Berg 		return;
3304a773adSJohannes Berg 
34fe94f3a4SAntonio Quartulli 	bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
356eb18137SDedy Lansky 			       IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
3604a773adSJohannes Berg 
3704a773adSJohannes Berg 	if (WARN_ON(!bss))
3804a773adSJohannes Berg 		return;
3904a773adSJohannes Berg 
407b0a0e3cSJohannes Berg 	if (wdev->u.ibss.current_bss) {
417b0a0e3cSJohannes Berg 		cfg80211_unhold_bss(wdev->u.ibss.current_bss);
427b0a0e3cSJohannes Berg 		cfg80211_put_bss(wdev->wiphy, &wdev->u.ibss.current_bss->pub);
4304a773adSJohannes Berg 	}
4404a773adSJohannes Berg 
4519957bb3SJohannes Berg 	cfg80211_hold_bss(bss_from_pub(bss));
467b0a0e3cSJohannes Berg 	wdev->u.ibss.current_bss = bss_from_pub(bss);
4704a773adSJohannes Berg 
48fffd0934SJohannes Berg 	cfg80211_upload_connect_keys(wdev);
49fffd0934SJohannes Berg 
50f26cbf40SZhao, Gang 	nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid,
51667503ddSJohannes Berg 				GFP_KERNEL);
523d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
5304a773adSJohannes Berg 	memset(&wrqu, 0, sizeof(wrqu));
5404a773adSJohannes Berg 	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
5504a773adSJohannes Berg 	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
5604a773adSJohannes Berg #endif
5704a773adSJohannes Berg }
58667503ddSJohannes Berg 
cfg80211_ibss_joined(struct net_device * dev,const u8 * bssid,struct ieee80211_channel * channel,gfp_t gfp)59fe94f3a4SAntonio Quartulli void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
60fe94f3a4SAntonio Quartulli 			  struct ieee80211_channel *channel, gfp_t gfp)
61667503ddSJohannes Berg {
62667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
63f26cbf40SZhao, Gang 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
64667503ddSJohannes Berg 	struct cfg80211_event *ev;
65667503ddSJohannes Berg 	unsigned long flags;
66667503ddSJohannes Berg 
67fe94f3a4SAntonio Quartulli 	trace_cfg80211_ibss_joined(dev, bssid, channel);
68fe94f3a4SAntonio Quartulli 
69fe94f3a4SAntonio Quartulli 	if (WARN_ON(!channel))
70fe94f3a4SAntonio Quartulli 		return;
714ee3e063SBeni Lev 
72667503ddSJohannes Berg 	ev = kzalloc(sizeof(*ev), gfp);
73667503ddSJohannes Berg 	if (!ev)
74667503ddSJohannes Berg 		return;
75667503ddSJohannes Berg 
76667503ddSJohannes Berg 	ev->type = EVENT_IBSS_JOINED;
77fe94f3a4SAntonio Quartulli 	memcpy(ev->ij.bssid, bssid, ETH_ALEN);
78fe94f3a4SAntonio Quartulli 	ev->ij.channel = channel;
79667503ddSJohannes Berg 
80667503ddSJohannes Berg 	spin_lock_irqsave(&wdev->event_lock, flags);
81667503ddSJohannes Berg 	list_add_tail(&ev->list, &wdev->event_list);
82667503ddSJohannes Berg 	spin_unlock_irqrestore(&wdev->event_lock, flags);
83e60d7443SAlban Browaeys 	queue_work(cfg80211_wq, &rdev->event_work);
84667503ddSJohannes Berg }
8504a773adSJohannes Berg EXPORT_SYMBOL(cfg80211_ibss_joined);
8604a773adSJohannes Berg 
__cfg80211_join_ibss(struct cfg80211_registered_device * rdev,struct net_device * dev,struct cfg80211_ibss_params * params,struct cfg80211_cached_keys * connkeys)87f8d16d3eSDenis Kenzior int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
8804a773adSJohannes Berg 			 struct net_device *dev,
89fffd0934SJohannes Berg 			 struct cfg80211_ibss_params *params,
90fffd0934SJohannes Berg 			 struct cfg80211_cached_keys *connkeys)
9104a773adSJohannes Berg {
9204a773adSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
9304a773adSJohannes Berg 	int err;
9404a773adSJohannes Berg 
95a05829a7SJohannes Berg 	lockdep_assert_held(&rdev->wiphy.mtx);
96667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
97667503ddSJohannes Berg 
987b0a0e3cSJohannes Berg 	if (wdev->u.ibss.ssid_len)
9904a773adSJohannes Berg 		return -EALREADY;
10004a773adSJohannes Berg 
10193b05238SJohannes Berg 	if (!params->basic_rates) {
10293b05238SJohannes Berg 		/*
10393b05238SJohannes Berg 		* If no rates were explicitly configured,
10493b05238SJohannes Berg 		* use the mandatory rate set for 11b or
10593b05238SJohannes Berg 		* 11a for maximum compatibility.
10693b05238SJohannes Berg 		*/
1075ea4e780SArend van Spriel 		struct ieee80211_supported_band *sband;
1085ea4e780SArend van Spriel 		enum nl80211_band band;
1095ea4e780SArend van Spriel 		u32 flag;
11093b05238SJohannes Berg 		int j;
11193b05238SJohannes Berg 
1125ea4e780SArend van Spriel 		band = params->chandef.chan->band;
1135ea4e780SArend van Spriel 		if (band == NL80211_BAND_5GHZ ||
1145ea4e780SArend van Spriel 		    band == NL80211_BAND_6GHZ)
1155ea4e780SArend van Spriel 			flag = IEEE80211_RATE_MANDATORY_A;
1165ea4e780SArend van Spriel 		else
1175ea4e780SArend van Spriel 			flag = IEEE80211_RATE_MANDATORY_B;
1185ea4e780SArend van Spriel 
1195ea4e780SArend van Spriel 		sband = rdev->wiphy.bands[band];
12093b05238SJohannes Berg 		for (j = 0; j < sband->n_bitrates; j++) {
12193b05238SJohannes Berg 			if (sband->bitrates[j].flags & flag)
12293b05238SJohannes Berg 				params->basic_rates |= BIT(j);
12393b05238SJohannes Berg 		}
12493b05238SJohannes Berg 	}
12593b05238SJohannes Berg 
126f1c1f17aSJohannes Berg 	if (WARN_ON(connkeys && connkeys->def < 0))
127f1c1f17aSJohannes Berg 		return -EINVAL;
128f1c1f17aSJohannes Berg 
129fffd0934SJohannes Berg 	if (WARN_ON(wdev->connect_keys))
130453431a5SWaiman Long 		kfree_sensitive(wdev->connect_keys);
131fffd0934SJohannes Berg 	wdev->connect_keys = connkeys;
132fffd0934SJohannes Berg 
1337b0a0e3cSJohannes Berg 	wdev->u.ibss.chandef = params->chandef;
1349ae3b172STova Mussai 	if (connkeys) {
1359ae3b172STova Mussai 		params->wep_keys = connkeys->params;
1369ae3b172STova Mussai 		params->wep_tx_key = connkeys->def;
1379ae3b172STova Mussai 	}
1389ae3b172STova Mussai 
1393d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
140683b6d3bSJohannes Berg 	wdev->wext.ibss.chandef = params->chandef;
14104a773adSJohannes Berg #endif
142e35e4d28SHila Gonen 	err = rdev_join_ibss(rdev, dev, params);
143fffd0934SJohannes Berg 	if (err) {
144fffd0934SJohannes Berg 		wdev->connect_keys = NULL;
14504a773adSJohannes Berg 		return err;
146fffd0934SJohannes Berg 	}
14704a773adSJohannes Berg 
1487b0a0e3cSJohannes Berg 	memcpy(wdev->u.ibss.ssid, params->ssid, params->ssid_len);
1497b0a0e3cSJohannes Berg 	wdev->u.ibss.ssid_len = params->ssid_len;
15004a773adSJohannes Berg 
15104a773adSJohannes Berg 	return 0;
15204a773adSJohannes Berg }
15304a773adSJohannes Berg 
__cfg80211_clear_ibss(struct net_device * dev,bool nowext)154667503ddSJohannes Berg static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
155667503ddSJohannes Berg {
156667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
157f26cbf40SZhao, Gang 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
158fffd0934SJohannes Berg 	int i;
159667503ddSJohannes Berg 
160667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
16104a773adSJohannes Berg 
162453431a5SWaiman Long 	kfree_sensitive(wdev->connect_keys);
163fffd0934SJohannes Berg 	wdev->connect_keys = NULL;
164fffd0934SJohannes Berg 
165fa9ffc74SKyeyoon Park 	rdev_set_qos_map(rdev, dev, NULL);
166fa9ffc74SKyeyoon Park 
167fffd0934SJohannes Berg 	/*
168fffd0934SJohannes Berg 	 * Delete all the keys ... pairwise keys can't really
169fffd0934SJohannes Berg 	 * exist any more anyway, but default keys might.
170fffd0934SJohannes Berg 	 */
171fffd0934SJohannes Berg 	if (rdev->ops->del_key)
172fffd0934SJohannes Berg 		for (i = 0; i < 6; i++)
173e7a7b84eSVeerendranath Jakkam 			rdev_del_key(rdev, dev, -1, i, false, NULL);
174fffd0934SJohannes Berg 
1757b0a0e3cSJohannes Berg 	if (wdev->u.ibss.current_bss) {
1767b0a0e3cSJohannes Berg 		cfg80211_unhold_bss(wdev->u.ibss.current_bss);
1777b0a0e3cSJohannes Berg 		cfg80211_put_bss(wdev->wiphy, &wdev->u.ibss.current_bss->pub);
17804a773adSJohannes Berg 	}
17904a773adSJohannes Berg 
1807b0a0e3cSJohannes Berg 	wdev->u.ibss.current_bss = NULL;
1817b0a0e3cSJohannes Berg 	wdev->u.ibss.ssid_len = 0;
1827b0a0e3cSJohannes Berg 	memset(&wdev->u.ibss.chandef, 0, sizeof(wdev->u.ibss.chandef));
1833d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
1849d308429SJohannes Berg 	if (!nowext)
185cbe8fa9cSJohannes Berg 		wdev->wext.ibss.ssid_len = 0;
1869d308429SJohannes Berg #endif
187b35a51c7SVasanthakumar Thiagarajan 	cfg80211_sched_dfs_chan_update(rdev);
18804a773adSJohannes Berg }
18904a773adSJohannes Berg 
cfg80211_clear_ibss(struct net_device * dev,bool nowext)190667503ddSJohannes Berg void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
191667503ddSJohannes Berg {
192667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
193667503ddSJohannes Berg 
194667503ddSJohannes Berg 	wdev_lock(wdev);
195667503ddSJohannes Berg 	__cfg80211_clear_ibss(dev, nowext);
196667503ddSJohannes Berg 	wdev_unlock(wdev);
197667503ddSJohannes Berg }
198667503ddSJohannes Berg 
__cfg80211_leave_ibss(struct cfg80211_registered_device * rdev,struct net_device * dev,bool nowext)19998d3a7caSJohannes Berg int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
2009d308429SJohannes Berg 			  struct net_device *dev, bool nowext)
20104a773adSJohannes Berg {
20278485475SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
20304a773adSJohannes Berg 	int err;
20404a773adSJohannes Berg 
205667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
206667503ddSJohannes Berg 
2077b0a0e3cSJohannes Berg 	if (!wdev->u.ibss.ssid_len)
20878485475SJohannes Berg 		return -ENOLINK;
20978485475SJohannes Berg 
210e35e4d28SHila Gonen 	err = rdev_leave_ibss(rdev, dev);
21104a773adSJohannes Berg 
21204a773adSJohannes Berg 	if (err)
21304a773adSJohannes Berg 		return err;
21404a773adSJohannes Berg 
215f8d16d3eSDenis Kenzior 	wdev->conn_owner_nlportid = 0;
216667503ddSJohannes Berg 	__cfg80211_clear_ibss(dev, nowext);
21704a773adSJohannes Berg 
21804a773adSJohannes Berg 	return 0;
21904a773adSJohannes Berg }
22004a773adSJohannes Berg 
cfg80211_leave_ibss(struct cfg80211_registered_device * rdev,struct net_device * dev,bool nowext)221667503ddSJohannes Berg int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
222667503ddSJohannes Berg 			struct net_device *dev, bool nowext)
223667503ddSJohannes Berg {
224667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
225667503ddSJohannes Berg 	int err;
226667503ddSJohannes Berg 
227667503ddSJohannes Berg 	wdev_lock(wdev);
228667503ddSJohannes Berg 	err = __cfg80211_leave_ibss(rdev, dev, nowext);
229667503ddSJohannes Berg 	wdev_unlock(wdev);
230667503ddSJohannes Berg 
231667503ddSJohannes Berg 	return err;
232667503ddSJohannes Berg }
233667503ddSJohannes Berg 
2343d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
cfg80211_ibss_wext_join(struct cfg80211_registered_device * rdev,struct wireless_dev * wdev)235fffd0934SJohannes Berg int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
23604a773adSJohannes Berg 			    struct wireless_dev *wdev)
23704a773adSJohannes Berg {
238fffd0934SJohannes Berg 	struct cfg80211_cached_keys *ck = NULL;
23957fbcce3SJohannes Berg 	enum nl80211_band band;
240fffd0934SJohannes Berg 	int i, err;
241fffd0934SJohannes Berg 
242fffd0934SJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
24304a773adSJohannes Berg 
244cbe8fa9cSJohannes Berg 	if (!wdev->wext.ibss.beacon_interval)
245cbe8fa9cSJohannes Berg 		wdev->wext.ibss.beacon_interval = 100;
2468e30bc55SJohannes Berg 
24704a773adSJohannes Berg 	/* try to find an IBSS channel if none requested ... */
248683b6d3bSJohannes Berg 	if (!wdev->wext.ibss.chandef.chan) {
2491fe4517cSSimon Wunderlich 		struct ieee80211_channel *new_chan = NULL;
250683b6d3bSJohannes Berg 
25157fbcce3SJohannes Berg 		for (band = 0; band < NUM_NL80211_BANDS; band++) {
25204a773adSJohannes Berg 			struct ieee80211_supported_band *sband;
25304a773adSJohannes Berg 			struct ieee80211_channel *chan;
25404a773adSJohannes Berg 
25504a773adSJohannes Berg 			sband = rdev->wiphy.bands[band];
25604a773adSJohannes Berg 			if (!sband)
25704a773adSJohannes Berg 				continue;
25804a773adSJohannes Berg 
25904a773adSJohannes Berg 			for (i = 0; i < sband->n_channels; i++) {
26004a773adSJohannes Berg 				chan = &sband->channels[i];
2618fe02e16SLuis R. Rodriguez 				if (chan->flags & IEEE80211_CHAN_NO_IR)
26204a773adSJohannes Berg 					continue;
26304a773adSJohannes Berg 				if (chan->flags & IEEE80211_CHAN_DISABLED)
26404a773adSJohannes Berg 					continue;
2651fe4517cSSimon Wunderlich 				new_chan = chan;
26604a773adSJohannes Berg 				break;
26704a773adSJohannes Berg 			}
26804a773adSJohannes Berg 
2691fe4517cSSimon Wunderlich 			if (new_chan)
27004a773adSJohannes Berg 				break;
27104a773adSJohannes Berg 		}
27204a773adSJohannes Berg 
2731fe4517cSSimon Wunderlich 		if (!new_chan)
27404a773adSJohannes Berg 			return -EINVAL;
2751fe4517cSSimon Wunderlich 
2761fe4517cSSimon Wunderlich 		cfg80211_chandef_create(&wdev->wext.ibss.chandef, new_chan,
2771fe4517cSSimon Wunderlich 					NL80211_CHAN_NO_HT);
27804a773adSJohannes Berg 	}
27904a773adSJohannes Berg 
28004a773adSJohannes Berg 	/* don't join -- SSID is not there */
281cbe8fa9cSJohannes Berg 	if (!wdev->wext.ibss.ssid_len)
28204a773adSJohannes Berg 		return 0;
28304a773adSJohannes Berg 
28404a773adSJohannes Berg 	if (!netif_running(wdev->netdev))
28504a773adSJohannes Berg 		return 0;
28604a773adSJohannes Berg 
28789b706fbSJohannes Berg 	if (wdev->wext.keys)
288fffd0934SJohannes Berg 		wdev->wext.keys->def = wdev->wext.default_key;
289fffd0934SJohannes Berg 
290fffd0934SJohannes Berg 	wdev->wext.ibss.privacy = wdev->wext.default_key != -1;
291fffd0934SJohannes Berg 
292f1c1f17aSJohannes Berg 	if (wdev->wext.keys && wdev->wext.keys->def != -1) {
293fffd0934SJohannes Berg 		ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
294fffd0934SJohannes Berg 		if (!ck)
295fffd0934SJohannes Berg 			return -ENOMEM;
296*585b6e13SJohannes Berg 		for (i = 0; i < 4; i++)
297fffd0934SJohannes Berg 			ck->params[i].key = ck->data[i];
298fffd0934SJohannes Berg 	}
299fffd0934SJohannes Berg 	err = __cfg80211_join_ibss(rdev, wdev->netdev,
300fffd0934SJohannes Berg 				   &wdev->wext.ibss, ck);
301fffd0934SJohannes Berg 	if (err)
302fffd0934SJohannes Berg 		kfree(ck);
303fffd0934SJohannes Berg 
304fffd0934SJohannes Berg 	return err;
30504a773adSJohannes Berg }
30604a773adSJohannes Berg 
cfg80211_ibss_wext_siwfreq(struct net_device * dev,struct iw_request_info * info,struct iw_freq * wextfreq,char * extra)30704a773adSJohannes Berg int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
30804a773adSJohannes Berg 			       struct iw_request_info *info,
30959bbb6f7SJohannes Berg 			       struct iw_freq *wextfreq, char *extra)
31004a773adSJohannes Berg {
31104a773adSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
312f26cbf40SZhao, Gang 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
31359bbb6f7SJohannes Berg 	struct ieee80211_channel *chan = NULL;
31459bbb6f7SJohannes Berg 	int err, freq;
31504a773adSJohannes Berg 
31604a773adSJohannes Berg 	/* call only for ibss! */
31704a773adSJohannes Berg 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
31804a773adSJohannes Berg 		return -EINVAL;
31904a773adSJohannes Berg 
32059bbb6f7SJohannes Berg 	if (!rdev->ops->join_ibss)
32104a773adSJohannes Berg 		return -EOPNOTSUPP;
32204a773adSJohannes Berg 
32396998e3aSZhao, Gang 	freq = cfg80211_wext_freq(wextfreq);
32459bbb6f7SJohannes Berg 	if (freq < 0)
32559bbb6f7SJohannes Berg 		return freq;
32604a773adSJohannes Berg 
32759bbb6f7SJohannes Berg 	if (freq) {
32859bbb6f7SJohannes Berg 		chan = ieee80211_get_channel(wdev->wiphy, freq);
32959bbb6f7SJohannes Berg 		if (!chan)
33004a773adSJohannes Berg 			return -EINVAL;
3318fe02e16SLuis R. Rodriguez 		if (chan->flags & IEEE80211_CHAN_NO_IR ||
33259bbb6f7SJohannes Berg 		    chan->flags & IEEE80211_CHAN_DISABLED)
33359bbb6f7SJohannes Berg 			return -EINVAL;
33459bbb6f7SJohannes Berg 	}
33504a773adSJohannes Berg 
336683b6d3bSJohannes Berg 	if (wdev->wext.ibss.chandef.chan == chan)
33704a773adSJohannes Berg 		return 0;
33804a773adSJohannes Berg 
339667503ddSJohannes Berg 	wdev_lock(wdev);
340667503ddSJohannes Berg 	err = 0;
3417b0a0e3cSJohannes Berg 	if (wdev->u.ibss.ssid_len)
34259bbb6f7SJohannes Berg 		err = __cfg80211_leave_ibss(rdev, dev, true);
343667503ddSJohannes Berg 	wdev_unlock(wdev);
344667503ddSJohannes Berg 
34504a773adSJohannes Berg 	if (err)
34604a773adSJohannes Berg 		return err;
34704a773adSJohannes Berg 
34804a773adSJohannes Berg 	if (chan) {
3491fe4517cSSimon Wunderlich 		cfg80211_chandef_create(&wdev->wext.ibss.chandef, chan,
3501fe4517cSSimon Wunderlich 					NL80211_CHAN_NO_HT);
351cbe8fa9cSJohannes Berg 		wdev->wext.ibss.channel_fixed = true;
35204a773adSJohannes Berg 	} else {
35304a773adSJohannes Berg 		/* cfg80211_ibss_wext_join will pick one if needed */
354cbe8fa9cSJohannes Berg 		wdev->wext.ibss.channel_fixed = false;
35504a773adSJohannes Berg 	}
35604a773adSJohannes Berg 
357fffd0934SJohannes Berg 	wdev_lock(wdev);
35859bbb6f7SJohannes Berg 	err = cfg80211_ibss_wext_join(rdev, wdev);
359fffd0934SJohannes Berg 	wdev_unlock(wdev);
360fffd0934SJohannes Berg 
361fffd0934SJohannes Berg 	return err;
36204a773adSJohannes Berg }
36304a773adSJohannes Berg 
cfg80211_ibss_wext_giwfreq(struct net_device * dev,struct iw_request_info * info,struct iw_freq * freq,char * extra)36404a773adSJohannes Berg int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
36504a773adSJohannes Berg 			       struct iw_request_info *info,
36604a773adSJohannes Berg 			       struct iw_freq *freq, char *extra)
36704a773adSJohannes Berg {
36804a773adSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
36904a773adSJohannes Berg 	struct ieee80211_channel *chan = NULL;
37004a773adSJohannes Berg 
37104a773adSJohannes Berg 	/* call only for ibss! */
37204a773adSJohannes Berg 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
37304a773adSJohannes Berg 		return -EINVAL;
37404a773adSJohannes Berg 
375667503ddSJohannes Berg 	wdev_lock(wdev);
3767b0a0e3cSJohannes Berg 	if (wdev->u.ibss.current_bss)
3777b0a0e3cSJohannes Berg 		chan = wdev->u.ibss.current_bss->pub.channel;
378683b6d3bSJohannes Berg 	else if (wdev->wext.ibss.chandef.chan)
379683b6d3bSJohannes Berg 		chan = wdev->wext.ibss.chandef.chan;
380667503ddSJohannes Berg 	wdev_unlock(wdev);
38104a773adSJohannes Berg 
38204a773adSJohannes Berg 	if (chan) {
38304a773adSJohannes Berg 		freq->m = chan->center_freq;
38404a773adSJohannes Berg 		freq->e = 6;
38504a773adSJohannes Berg 		return 0;
38604a773adSJohannes Berg 	}
38704a773adSJohannes Berg 
38804a773adSJohannes Berg 	/* no channel if not joining */
38904a773adSJohannes Berg 	return -EINVAL;
39004a773adSJohannes Berg }
39104a773adSJohannes Berg 
cfg80211_ibss_wext_siwessid(struct net_device * dev,struct iw_request_info * info,struct iw_point * data,char * ssid)39204a773adSJohannes Berg int cfg80211_ibss_wext_siwessid(struct net_device *dev,
39304a773adSJohannes Berg 				struct iw_request_info *info,
39404a773adSJohannes Berg 				struct iw_point *data, char *ssid)
39504a773adSJohannes Berg {
39604a773adSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
397f26cbf40SZhao, Gang 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
39804a773adSJohannes Berg 	size_t len = data->length;
39904a773adSJohannes Berg 	int err;
40004a773adSJohannes Berg 
40104a773adSJohannes Berg 	/* call only for ibss! */
40204a773adSJohannes Berg 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
40304a773adSJohannes Berg 		return -EINVAL;
40404a773adSJohannes Berg 
40559bbb6f7SJohannes Berg 	if (!rdev->ops->join_ibss)
40604a773adSJohannes Berg 		return -EOPNOTSUPP;
40704a773adSJohannes Berg 
408667503ddSJohannes Berg 	wdev_lock(wdev);
409667503ddSJohannes Berg 	err = 0;
4107b0a0e3cSJohannes Berg 	if (wdev->u.ibss.ssid_len)
41159bbb6f7SJohannes Berg 		err = __cfg80211_leave_ibss(rdev, dev, true);
412667503ddSJohannes Berg 	wdev_unlock(wdev);
413667503ddSJohannes Berg 
41404a773adSJohannes Berg 	if (err)
41504a773adSJohannes Berg 		return err;
41604a773adSJohannes Berg 
41704a773adSJohannes Berg 	/* iwconfig uses nul termination in SSID.. */
41804a773adSJohannes Berg 	if (len > 0 && ssid[len - 1] == '\0')
41904a773adSJohannes Berg 		len--;
42004a773adSJohannes Berg 
4217b0a0e3cSJohannes Berg 	memcpy(wdev->u.ibss.ssid, ssid, len);
4227b0a0e3cSJohannes Berg 	wdev->wext.ibss.ssid = wdev->u.ibss.ssid;
423cbe8fa9cSJohannes Berg 	wdev->wext.ibss.ssid_len = len;
42404a773adSJohannes Berg 
425fffd0934SJohannes Berg 	wdev_lock(wdev);
42659bbb6f7SJohannes Berg 	err = cfg80211_ibss_wext_join(rdev, wdev);
427fffd0934SJohannes Berg 	wdev_unlock(wdev);
428fffd0934SJohannes Berg 
429fffd0934SJohannes Berg 	return err;
43004a773adSJohannes Berg }
43104a773adSJohannes Berg 
cfg80211_ibss_wext_giwessid(struct net_device * dev,struct iw_request_info * info,struct iw_point * data,char * ssid)43204a773adSJohannes Berg int cfg80211_ibss_wext_giwessid(struct net_device *dev,
43304a773adSJohannes Berg 				struct iw_request_info *info,
43404a773adSJohannes Berg 				struct iw_point *data, char *ssid)
43504a773adSJohannes Berg {
43604a773adSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
43704a773adSJohannes Berg 
43804a773adSJohannes Berg 	/* call only for ibss! */
43904a773adSJohannes Berg 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
44004a773adSJohannes Berg 		return -EINVAL;
44104a773adSJohannes Berg 
44204a773adSJohannes Berg 	data->flags = 0;
44304a773adSJohannes Berg 
444667503ddSJohannes Berg 	wdev_lock(wdev);
4457b0a0e3cSJohannes Berg 	if (wdev->u.ibss.ssid_len) {
44604a773adSJohannes Berg 		data->flags = 1;
4477b0a0e3cSJohannes Berg 		data->length = wdev->u.ibss.ssid_len;
4487b0a0e3cSJohannes Berg 		memcpy(ssid, wdev->u.ibss.ssid, data->length);
449cbe8fa9cSJohannes Berg 	} else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) {
45004a773adSJohannes Berg 		data->flags = 1;
451cbe8fa9cSJohannes Berg 		data->length = wdev->wext.ibss.ssid_len;
452cbe8fa9cSJohannes Berg 		memcpy(ssid, wdev->wext.ibss.ssid, data->length);
45304a773adSJohannes Berg 	}
454667503ddSJohannes Berg 	wdev_unlock(wdev);
45504a773adSJohannes Berg 
45604a773adSJohannes Berg 	return 0;
45704a773adSJohannes Berg }
45804a773adSJohannes Berg 
cfg80211_ibss_wext_siwap(struct net_device * dev,struct iw_request_info * info,struct sockaddr * ap_addr,char * extra)45904a773adSJohannes Berg int cfg80211_ibss_wext_siwap(struct net_device *dev,
46004a773adSJohannes Berg 			     struct iw_request_info *info,
46104a773adSJohannes Berg 			     struct sockaddr *ap_addr, char *extra)
46204a773adSJohannes Berg {
46304a773adSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
464f26cbf40SZhao, Gang 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
46504a773adSJohannes Berg 	u8 *bssid = ap_addr->sa_data;
46604a773adSJohannes Berg 	int err;
46704a773adSJohannes Berg 
46804a773adSJohannes Berg 	/* call only for ibss! */
46904a773adSJohannes Berg 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
47004a773adSJohannes Berg 		return -EINVAL;
47104a773adSJohannes Berg 
47259bbb6f7SJohannes Berg 	if (!rdev->ops->join_ibss)
47304a773adSJohannes Berg 		return -EOPNOTSUPP;
47404a773adSJohannes Berg 
47504a773adSJohannes Berg 	if (ap_addr->sa_family != ARPHRD_ETHER)
47604a773adSJohannes Berg 		return -EINVAL;
47704a773adSJohannes Berg 
47804a773adSJohannes Berg 	/* automatic mode */
47904a773adSJohannes Berg 	if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
48004a773adSJohannes Berg 		bssid = NULL;
48104a773adSJohannes Berg 
48274f82741SJohannes Berg 	if (bssid && !is_valid_ether_addr(bssid))
48374f82741SJohannes Berg 		return -EINVAL;
48474f82741SJohannes Berg 
48504a773adSJohannes Berg 	/* both automatic */
486cbe8fa9cSJohannes Berg 	if (!bssid && !wdev->wext.ibss.bssid)
48704a773adSJohannes Berg 		return 0;
48804a773adSJohannes Berg 
48904a773adSJohannes Berg 	/* fixed already - and no change */
490cbe8fa9cSJohannes Berg 	if (wdev->wext.ibss.bssid && bssid &&
491ac422d3cSJoe Perches 	    ether_addr_equal(bssid, wdev->wext.ibss.bssid))
49204a773adSJohannes Berg 		return 0;
49304a773adSJohannes Berg 
494667503ddSJohannes Berg 	wdev_lock(wdev);
495667503ddSJohannes Berg 	err = 0;
4967b0a0e3cSJohannes Berg 	if (wdev->u.ibss.ssid_len)
49759bbb6f7SJohannes Berg 		err = __cfg80211_leave_ibss(rdev, dev, true);
498667503ddSJohannes Berg 	wdev_unlock(wdev);
499667503ddSJohannes Berg 
50004a773adSJohannes Berg 	if (err)
50104a773adSJohannes Berg 		return err;
50204a773adSJohannes Berg 
50304a773adSJohannes Berg 	if (bssid) {
504cbe8fa9cSJohannes Berg 		memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
505cbe8fa9cSJohannes Berg 		wdev->wext.ibss.bssid = wdev->wext.bssid;
50604a773adSJohannes Berg 	} else
507cbe8fa9cSJohannes Berg 		wdev->wext.ibss.bssid = NULL;
50804a773adSJohannes Berg 
509fffd0934SJohannes Berg 	wdev_lock(wdev);
51059bbb6f7SJohannes Berg 	err = cfg80211_ibss_wext_join(rdev, wdev);
511fffd0934SJohannes Berg 	wdev_unlock(wdev);
512fffd0934SJohannes Berg 
513fffd0934SJohannes Berg 	return err;
51404a773adSJohannes Berg }
51504a773adSJohannes Berg 
cfg80211_ibss_wext_giwap(struct net_device * dev,struct iw_request_info * info,struct sockaddr * ap_addr,char * extra)51604a773adSJohannes Berg int cfg80211_ibss_wext_giwap(struct net_device *dev,
51704a773adSJohannes Berg 			     struct iw_request_info *info,
51804a773adSJohannes Berg 			     struct sockaddr *ap_addr, char *extra)
51904a773adSJohannes Berg {
52004a773adSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
52104a773adSJohannes Berg 
52204a773adSJohannes Berg 	/* call only for ibss! */
52304a773adSJohannes Berg 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
52404a773adSJohannes Berg 		return -EINVAL;
52504a773adSJohannes Berg 
52604a773adSJohannes Berg 	ap_addr->sa_family = ARPHRD_ETHER;
52704a773adSJohannes Berg 
528667503ddSJohannes Berg 	wdev_lock(wdev);
5297b0a0e3cSJohannes Berg 	if (wdev->u.ibss.current_bss)
5307b0a0e3cSJohannes Berg 		memcpy(ap_addr->sa_data, wdev->u.ibss.current_bss->pub.bssid,
5317b0a0e3cSJohannes Berg 		       ETH_ALEN);
53280e5b06aSZhu Yi 	else if (wdev->wext.ibss.bssid)
533cbe8fa9cSJohannes Berg 		memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN);
53480e5b06aSZhu Yi 	else
535d2beae10SJoe Perches 		eth_zero_addr(ap_addr->sa_data);
53680e5b06aSZhu Yi 
537667503ddSJohannes Berg 	wdev_unlock(wdev);
538667503ddSJohannes Berg 
53904a773adSJohannes Berg 	return 0;
54004a773adSJohannes Berg }
54104a773adSJohannes Berg #endif
542