162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Some IBSS support code for cfg80211. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> 662306a36Sopenharmony_ci * Copyright (C) 2020-2022 Intel Corporation 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/etherdevice.h> 1062306a36Sopenharmony_ci#include <linux/if_arp.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/export.h> 1362306a36Sopenharmony_ci#include <net/cfg80211.h> 1462306a36Sopenharmony_ci#include "wext-compat.h" 1562306a36Sopenharmony_ci#include "nl80211.h" 1662306a36Sopenharmony_ci#include "rdev-ops.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_civoid __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, 2062306a36Sopenharmony_ci struct ieee80211_channel *channel) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 2362306a36Sopenharmony_ci struct cfg80211_bss *bss; 2462306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 2562306a36Sopenharmony_ci union iwreq_data wrqu; 2662306a36Sopenharmony_ci#endif 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 2962306a36Sopenharmony_ci return; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (!wdev->u.ibss.ssid_len) 3262306a36Sopenharmony_ci return; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0, 3562306a36Sopenharmony_ci IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (WARN_ON(!bss)) 3862306a36Sopenharmony_ci return; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (wdev->u.ibss.current_bss) { 4162306a36Sopenharmony_ci cfg80211_unhold_bss(wdev->u.ibss.current_bss); 4262306a36Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, &wdev->u.ibss.current_bss->pub); 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci cfg80211_hold_bss(bss_from_pub(bss)); 4662306a36Sopenharmony_ci wdev->u.ibss.current_bss = bss_from_pub(bss); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci cfg80211_upload_connect_keys(wdev); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid, 5162306a36Sopenharmony_ci GFP_KERNEL); 5262306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 5362306a36Sopenharmony_ci memset(&wrqu, 0, sizeof(wrqu)); 5462306a36Sopenharmony_ci memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); 5562306a36Sopenharmony_ci wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 5662306a36Sopenharmony_ci#endif 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_civoid cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, 6062306a36Sopenharmony_ci struct ieee80211_channel *channel, gfp_t gfp) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 6362306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 6462306a36Sopenharmony_ci struct cfg80211_event *ev; 6562306a36Sopenharmony_ci unsigned long flags; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci trace_cfg80211_ibss_joined(dev, bssid, channel); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (WARN_ON(!channel)) 7062306a36Sopenharmony_ci return; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci ev = kzalloc(sizeof(*ev), gfp); 7362306a36Sopenharmony_ci if (!ev) 7462306a36Sopenharmony_ci return; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci ev->type = EVENT_IBSS_JOINED; 7762306a36Sopenharmony_ci memcpy(ev->ij.bssid, bssid, ETH_ALEN); 7862306a36Sopenharmony_ci ev->ij.channel = channel; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci spin_lock_irqsave(&wdev->event_lock, flags); 8162306a36Sopenharmony_ci list_add_tail(&ev->list, &wdev->event_list); 8262306a36Sopenharmony_ci spin_unlock_irqrestore(&wdev->event_lock, flags); 8362306a36Sopenharmony_ci queue_work(cfg80211_wq, &rdev->event_work); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_ibss_joined); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ciint __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, 8862306a36Sopenharmony_ci struct net_device *dev, 8962306a36Sopenharmony_ci struct cfg80211_ibss_params *params, 9062306a36Sopenharmony_ci struct cfg80211_cached_keys *connkeys) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 9362306a36Sopenharmony_ci int err; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci lockdep_assert_held(&rdev->wiphy.mtx); 9662306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (wdev->u.ibss.ssid_len) 9962306a36Sopenharmony_ci return -EALREADY; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (!params->basic_rates) { 10262306a36Sopenharmony_ci /* 10362306a36Sopenharmony_ci * If no rates were explicitly configured, 10462306a36Sopenharmony_ci * use the mandatory rate set for 11b or 10562306a36Sopenharmony_ci * 11a for maximum compatibility. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 10862306a36Sopenharmony_ci enum nl80211_band band; 10962306a36Sopenharmony_ci u32 flag; 11062306a36Sopenharmony_ci int j; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci band = params->chandef.chan->band; 11362306a36Sopenharmony_ci if (band == NL80211_BAND_5GHZ || 11462306a36Sopenharmony_ci band == NL80211_BAND_6GHZ) 11562306a36Sopenharmony_ci flag = IEEE80211_RATE_MANDATORY_A; 11662306a36Sopenharmony_ci else 11762306a36Sopenharmony_ci flag = IEEE80211_RATE_MANDATORY_B; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci sband = rdev->wiphy.bands[band]; 12062306a36Sopenharmony_ci for (j = 0; j < sband->n_bitrates; j++) { 12162306a36Sopenharmony_ci if (sband->bitrates[j].flags & flag) 12262306a36Sopenharmony_ci params->basic_rates |= BIT(j); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (WARN_ON(connkeys && connkeys->def < 0)) 12762306a36Sopenharmony_ci return -EINVAL; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (WARN_ON(wdev->connect_keys)) 13062306a36Sopenharmony_ci kfree_sensitive(wdev->connect_keys); 13162306a36Sopenharmony_ci wdev->connect_keys = connkeys; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci wdev->u.ibss.chandef = params->chandef; 13462306a36Sopenharmony_ci if (connkeys) { 13562306a36Sopenharmony_ci params->wep_keys = connkeys->params; 13662306a36Sopenharmony_ci params->wep_tx_key = connkeys->def; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 14062306a36Sopenharmony_ci wdev->wext.ibss.chandef = params->chandef; 14162306a36Sopenharmony_ci#endif 14262306a36Sopenharmony_ci err = rdev_join_ibss(rdev, dev, params); 14362306a36Sopenharmony_ci if (err) { 14462306a36Sopenharmony_ci wdev->connect_keys = NULL; 14562306a36Sopenharmony_ci return err; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci memcpy(wdev->u.ibss.ssid, params->ssid, params->ssid_len); 14962306a36Sopenharmony_ci wdev->u.ibss.ssid_len = params->ssid_len; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 15762306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 15862306a36Sopenharmony_ci int i; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci kfree_sensitive(wdev->connect_keys); 16362306a36Sopenharmony_ci wdev->connect_keys = NULL; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci rdev_set_qos_map(rdev, dev, NULL); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* 16862306a36Sopenharmony_ci * Delete all the keys ... pairwise keys can't really 16962306a36Sopenharmony_ci * exist any more anyway, but default keys might. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci if (rdev->ops->del_key) 17262306a36Sopenharmony_ci for (i = 0; i < 6; i++) 17362306a36Sopenharmony_ci rdev_del_key(rdev, dev, -1, i, false, NULL); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (wdev->u.ibss.current_bss) { 17662306a36Sopenharmony_ci cfg80211_unhold_bss(wdev->u.ibss.current_bss); 17762306a36Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, &wdev->u.ibss.current_bss->pub); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci wdev->u.ibss.current_bss = NULL; 18162306a36Sopenharmony_ci wdev->u.ibss.ssid_len = 0; 18262306a36Sopenharmony_ci memset(&wdev->u.ibss.chandef, 0, sizeof(wdev->u.ibss.chandef)); 18362306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 18462306a36Sopenharmony_ci if (!nowext) 18562306a36Sopenharmony_ci wdev->wext.ibss.ssid_len = 0; 18662306a36Sopenharmony_ci#endif 18762306a36Sopenharmony_ci cfg80211_sched_dfs_chan_update(rdev); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_civoid cfg80211_clear_ibss(struct net_device *dev, bool nowext) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci wdev_lock(wdev); 19562306a36Sopenharmony_ci __cfg80211_clear_ibss(dev, nowext); 19662306a36Sopenharmony_ci wdev_unlock(wdev); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciint __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, 20062306a36Sopenharmony_ci struct net_device *dev, bool nowext) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 20362306a36Sopenharmony_ci int err; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!wdev->u.ibss.ssid_len) 20862306a36Sopenharmony_ci return -ENOLINK; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci err = rdev_leave_ibss(rdev, dev); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (err) 21362306a36Sopenharmony_ci return err; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci wdev->conn_owner_nlportid = 0; 21662306a36Sopenharmony_ci __cfg80211_clear_ibss(dev, nowext); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ciint cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, 22262306a36Sopenharmony_ci struct net_device *dev, bool nowext) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 22562306a36Sopenharmony_ci int err; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci wdev_lock(wdev); 22862306a36Sopenharmony_ci err = __cfg80211_leave_ibss(rdev, dev, nowext); 22962306a36Sopenharmony_ci wdev_unlock(wdev); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return err; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 23562306a36Sopenharmony_ciint cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, 23662306a36Sopenharmony_ci struct wireless_dev *wdev) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct cfg80211_cached_keys *ck = NULL; 23962306a36Sopenharmony_ci enum nl80211_band band; 24062306a36Sopenharmony_ci int i, err; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (!wdev->wext.ibss.beacon_interval) 24562306a36Sopenharmony_ci wdev->wext.ibss.beacon_interval = 100; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* try to find an IBSS channel if none requested ... */ 24862306a36Sopenharmony_ci if (!wdev->wext.ibss.chandef.chan) { 24962306a36Sopenharmony_ci struct ieee80211_channel *new_chan = NULL; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci for (band = 0; band < NUM_NL80211_BANDS; band++) { 25262306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 25362306a36Sopenharmony_ci struct ieee80211_channel *chan; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci sband = rdev->wiphy.bands[band]; 25662306a36Sopenharmony_ci if (!sband) 25762306a36Sopenharmony_ci continue; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 26062306a36Sopenharmony_ci chan = &sband->channels[i]; 26162306a36Sopenharmony_ci if (chan->flags & IEEE80211_CHAN_NO_IR) 26262306a36Sopenharmony_ci continue; 26362306a36Sopenharmony_ci if (chan->flags & IEEE80211_CHAN_DISABLED) 26462306a36Sopenharmony_ci continue; 26562306a36Sopenharmony_ci new_chan = chan; 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (new_chan) 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (!new_chan) 27462306a36Sopenharmony_ci return -EINVAL; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci cfg80211_chandef_create(&wdev->wext.ibss.chandef, new_chan, 27762306a36Sopenharmony_ci NL80211_CHAN_NO_HT); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* don't join -- SSID is not there */ 28162306a36Sopenharmony_ci if (!wdev->wext.ibss.ssid_len) 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (!netif_running(wdev->netdev)) 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (wdev->wext.keys) 28862306a36Sopenharmony_ci wdev->wext.keys->def = wdev->wext.default_key; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci wdev->wext.ibss.privacy = wdev->wext.default_key != -1; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (wdev->wext.keys && wdev->wext.keys->def != -1) { 29362306a36Sopenharmony_ci ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); 29462306a36Sopenharmony_ci if (!ck) 29562306a36Sopenharmony_ci return -ENOMEM; 29662306a36Sopenharmony_ci for (i = 0; i < 4; i++) 29762306a36Sopenharmony_ci ck->params[i].key = ck->data[i]; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci err = __cfg80211_join_ibss(rdev, wdev->netdev, 30062306a36Sopenharmony_ci &wdev->wext.ibss, ck); 30162306a36Sopenharmony_ci if (err) 30262306a36Sopenharmony_ci kfree(ck); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return err; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ciint cfg80211_ibss_wext_siwfreq(struct net_device *dev, 30862306a36Sopenharmony_ci struct iw_request_info *info, 30962306a36Sopenharmony_ci struct iw_freq *wextfreq, char *extra) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 31262306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 31362306a36Sopenharmony_ci struct ieee80211_channel *chan = NULL; 31462306a36Sopenharmony_ci int err, freq; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* call only for ibss! */ 31762306a36Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 31862306a36Sopenharmony_ci return -EINVAL; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (!rdev->ops->join_ibss) 32162306a36Sopenharmony_ci return -EOPNOTSUPP; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci freq = cfg80211_wext_freq(wextfreq); 32462306a36Sopenharmony_ci if (freq < 0) 32562306a36Sopenharmony_ci return freq; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (freq) { 32862306a36Sopenharmony_ci chan = ieee80211_get_channel(wdev->wiphy, freq); 32962306a36Sopenharmony_ci if (!chan) 33062306a36Sopenharmony_ci return -EINVAL; 33162306a36Sopenharmony_ci if (chan->flags & IEEE80211_CHAN_NO_IR || 33262306a36Sopenharmony_ci chan->flags & IEEE80211_CHAN_DISABLED) 33362306a36Sopenharmony_ci return -EINVAL; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (wdev->wext.ibss.chandef.chan == chan) 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci wdev_lock(wdev); 34062306a36Sopenharmony_ci err = 0; 34162306a36Sopenharmony_ci if (wdev->u.ibss.ssid_len) 34262306a36Sopenharmony_ci err = __cfg80211_leave_ibss(rdev, dev, true); 34362306a36Sopenharmony_ci wdev_unlock(wdev); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (err) 34662306a36Sopenharmony_ci return err; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (chan) { 34962306a36Sopenharmony_ci cfg80211_chandef_create(&wdev->wext.ibss.chandef, chan, 35062306a36Sopenharmony_ci NL80211_CHAN_NO_HT); 35162306a36Sopenharmony_ci wdev->wext.ibss.channel_fixed = true; 35262306a36Sopenharmony_ci } else { 35362306a36Sopenharmony_ci /* cfg80211_ibss_wext_join will pick one if needed */ 35462306a36Sopenharmony_ci wdev->wext.ibss.channel_fixed = false; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci wdev_lock(wdev); 35862306a36Sopenharmony_ci err = cfg80211_ibss_wext_join(rdev, wdev); 35962306a36Sopenharmony_ci wdev_unlock(wdev); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return err; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ciint cfg80211_ibss_wext_giwfreq(struct net_device *dev, 36562306a36Sopenharmony_ci struct iw_request_info *info, 36662306a36Sopenharmony_ci struct iw_freq *freq, char *extra) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 36962306a36Sopenharmony_ci struct ieee80211_channel *chan = NULL; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* call only for ibss! */ 37262306a36Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 37362306a36Sopenharmony_ci return -EINVAL; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci wdev_lock(wdev); 37662306a36Sopenharmony_ci if (wdev->u.ibss.current_bss) 37762306a36Sopenharmony_ci chan = wdev->u.ibss.current_bss->pub.channel; 37862306a36Sopenharmony_ci else if (wdev->wext.ibss.chandef.chan) 37962306a36Sopenharmony_ci chan = wdev->wext.ibss.chandef.chan; 38062306a36Sopenharmony_ci wdev_unlock(wdev); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (chan) { 38362306a36Sopenharmony_ci freq->m = chan->center_freq; 38462306a36Sopenharmony_ci freq->e = 6; 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* no channel if not joining */ 38962306a36Sopenharmony_ci return -EINVAL; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ciint cfg80211_ibss_wext_siwessid(struct net_device *dev, 39362306a36Sopenharmony_ci struct iw_request_info *info, 39462306a36Sopenharmony_ci struct iw_point *data, char *ssid) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 39762306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 39862306a36Sopenharmony_ci size_t len = data->length; 39962306a36Sopenharmony_ci int err; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* call only for ibss! */ 40262306a36Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 40362306a36Sopenharmony_ci return -EINVAL; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!rdev->ops->join_ibss) 40662306a36Sopenharmony_ci return -EOPNOTSUPP; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci wdev_lock(wdev); 40962306a36Sopenharmony_ci err = 0; 41062306a36Sopenharmony_ci if (wdev->u.ibss.ssid_len) 41162306a36Sopenharmony_ci err = __cfg80211_leave_ibss(rdev, dev, true); 41262306a36Sopenharmony_ci wdev_unlock(wdev); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (err) 41562306a36Sopenharmony_ci return err; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* iwconfig uses nul termination in SSID.. */ 41862306a36Sopenharmony_ci if (len > 0 && ssid[len - 1] == '\0') 41962306a36Sopenharmony_ci len--; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci memcpy(wdev->u.ibss.ssid, ssid, len); 42262306a36Sopenharmony_ci wdev->wext.ibss.ssid = wdev->u.ibss.ssid; 42362306a36Sopenharmony_ci wdev->wext.ibss.ssid_len = len; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci wdev_lock(wdev); 42662306a36Sopenharmony_ci err = cfg80211_ibss_wext_join(rdev, wdev); 42762306a36Sopenharmony_ci wdev_unlock(wdev); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return err; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ciint cfg80211_ibss_wext_giwessid(struct net_device *dev, 43362306a36Sopenharmony_ci struct iw_request_info *info, 43462306a36Sopenharmony_ci struct iw_point *data, char *ssid) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* call only for ibss! */ 43962306a36Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 44062306a36Sopenharmony_ci return -EINVAL; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci data->flags = 0; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci wdev_lock(wdev); 44562306a36Sopenharmony_ci if (wdev->u.ibss.ssid_len) { 44662306a36Sopenharmony_ci data->flags = 1; 44762306a36Sopenharmony_ci data->length = wdev->u.ibss.ssid_len; 44862306a36Sopenharmony_ci memcpy(ssid, wdev->u.ibss.ssid, data->length); 44962306a36Sopenharmony_ci } else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) { 45062306a36Sopenharmony_ci data->flags = 1; 45162306a36Sopenharmony_ci data->length = wdev->wext.ibss.ssid_len; 45262306a36Sopenharmony_ci memcpy(ssid, wdev->wext.ibss.ssid, data->length); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci wdev_unlock(wdev); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci return 0; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ciint cfg80211_ibss_wext_siwap(struct net_device *dev, 46062306a36Sopenharmony_ci struct iw_request_info *info, 46162306a36Sopenharmony_ci struct sockaddr *ap_addr, char *extra) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 46462306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 46562306a36Sopenharmony_ci u8 *bssid = ap_addr->sa_data; 46662306a36Sopenharmony_ci int err; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* call only for ibss! */ 46962306a36Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 47062306a36Sopenharmony_ci return -EINVAL; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (!rdev->ops->join_ibss) 47362306a36Sopenharmony_ci return -EOPNOTSUPP; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (ap_addr->sa_family != ARPHRD_ETHER) 47662306a36Sopenharmony_ci return -EINVAL; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* automatic mode */ 47962306a36Sopenharmony_ci if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) 48062306a36Sopenharmony_ci bssid = NULL; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (bssid && !is_valid_ether_addr(bssid)) 48362306a36Sopenharmony_ci return -EINVAL; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* both automatic */ 48662306a36Sopenharmony_ci if (!bssid && !wdev->wext.ibss.bssid) 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* fixed already - and no change */ 49062306a36Sopenharmony_ci if (wdev->wext.ibss.bssid && bssid && 49162306a36Sopenharmony_ci ether_addr_equal(bssid, wdev->wext.ibss.bssid)) 49262306a36Sopenharmony_ci return 0; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci wdev_lock(wdev); 49562306a36Sopenharmony_ci err = 0; 49662306a36Sopenharmony_ci if (wdev->u.ibss.ssid_len) 49762306a36Sopenharmony_ci err = __cfg80211_leave_ibss(rdev, dev, true); 49862306a36Sopenharmony_ci wdev_unlock(wdev); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (err) 50162306a36Sopenharmony_ci return err; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (bssid) { 50462306a36Sopenharmony_ci memcpy(wdev->wext.bssid, bssid, ETH_ALEN); 50562306a36Sopenharmony_ci wdev->wext.ibss.bssid = wdev->wext.bssid; 50662306a36Sopenharmony_ci } else 50762306a36Sopenharmony_ci wdev->wext.ibss.bssid = NULL; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci wdev_lock(wdev); 51062306a36Sopenharmony_ci err = cfg80211_ibss_wext_join(rdev, wdev); 51162306a36Sopenharmony_ci wdev_unlock(wdev); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return err; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ciint cfg80211_ibss_wext_giwap(struct net_device *dev, 51762306a36Sopenharmony_ci struct iw_request_info *info, 51862306a36Sopenharmony_ci struct sockaddr *ap_addr, char *extra) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* call only for ibss! */ 52362306a36Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 52462306a36Sopenharmony_ci return -EINVAL; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci ap_addr->sa_family = ARPHRD_ETHER; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci wdev_lock(wdev); 52962306a36Sopenharmony_ci if (wdev->u.ibss.current_bss) 53062306a36Sopenharmony_ci memcpy(ap_addr->sa_data, wdev->u.ibss.current_bss->pub.bssid, 53162306a36Sopenharmony_ci ETH_ALEN); 53262306a36Sopenharmony_ci else if (wdev->wext.ibss.bssid) 53362306a36Sopenharmony_ci memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); 53462306a36Sopenharmony_ci else 53562306a36Sopenharmony_ci eth_zero_addr(ap_addr->sa_data); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci wdev_unlock(wdev); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci#endif 542