mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	cfg80211/nl80211: scanning (and mac80211 update to use it)
This patch adds basic scan capability to cfg80211/nl80211 and changes mac80211 to use it. The BSS list that cfg80211 maintains is made driver-accessible with a private area in each BSS struct, but mac80211 doesn't yet use it. That's another large project. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
		
							parent
							
								
									849b796781
								
							
						
					
					
						commit
						2a51931192
					
				
					 21 changed files with 1546 additions and 383 deletions
				
			
		| 
						 | 
				
			
			@ -2678,11 +2678,19 @@ static void iwl_bss_info_changed(struct ieee80211_hw *hw,
 | 
			
		|||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int iwl_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t ssid_len)
 | 
			
		||||
static int iwl_mac_hw_scan(struct ieee80211_hw *hw,
 | 
			
		||||
			   struct cfg80211_scan_request *req)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	struct iwl_priv *priv = hw->priv;
 | 
			
		||||
	int ret;
 | 
			
		||||
	u8 *ssid = NULL;
 | 
			
		||||
	size_t ssid_len = 0;
 | 
			
		||||
 | 
			
		||||
	if (req->n_ssids) {
 | 
			
		||||
		ssid = req->ssids[0].ssid;
 | 
			
		||||
		ssid_len = req->ssids[0].ssid_len;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	IWL_DEBUG_MAC80211(priv, "enter\n");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2718,7 +2726,7 @@ static int iwl_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t ssid_len)
 | 
			
		|||
 | 
			
		||||
	if (ssid_len) {
 | 
			
		||||
		priv->one_direct_scan = 1;
 | 
			
		||||
		priv->direct_ssid_len =  min_t(u8, ssid_len, IW_ESSID_MAX_SIZE);
 | 
			
		||||
		priv->direct_ssid_len = ssid_len;
 | 
			
		||||
		memcpy(priv->direct_ssid, ssid, priv->direct_ssid_len);
 | 
			
		||||
	} else {
 | 
			
		||||
		priv->one_direct_scan = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1271,6 +1271,7 @@ int iwl_setup_mac(struct iwl_priv *priv)
 | 
			
		|||
		BIT(NL80211_IFTYPE_ADHOC);
 | 
			
		||||
 | 
			
		||||
	hw->wiphy->custom_regulatory = true;
 | 
			
		||||
	hw->wiphy->max_scan_ssids = 1;
 | 
			
		||||
 | 
			
		||||
	/* Default value; 4 EDCA QOS priorities */
 | 
			
		||||
	hw->queues = 4;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -860,7 +860,7 @@ void iwl_bg_scan_completed(struct work_struct *work)
 | 
			
		|||
	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	ieee80211_scan_completed(priv->hw);
 | 
			
		||||
	ieee80211_scan_completed(priv->hw, false);
 | 
			
		||||
 | 
			
		||||
	/* Since setting the TXPOWER may have been deferred while
 | 
			
		||||
	 * performing the scan, fire one off */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4442,15 +4442,23 @@ static void iwl3945_bss_info_changed(struct ieee80211_hw *hw,
 | 
			
		|||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len)
 | 
			
		||||
static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw,
 | 
			
		||||
			       struct cfg80211_scan_request *req)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	struct iwl_priv *priv = hw->priv;
 | 
			
		||||
	size_t len = 0;
 | 
			
		||||
	u8 *ssid = NULL;
 | 
			
		||||
	DECLARE_SSID_BUF(ssid_buf);
 | 
			
		||||
 | 
			
		||||
	IWL_DEBUG_MAC80211(priv, "enter\n");
 | 
			
		||||
 | 
			
		||||
	if (req->n_ssids) {
 | 
			
		||||
		ssid = req->ssids[0].ssid;
 | 
			
		||||
		len = req->ssids[0].ssid_len;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&priv->mutex);
 | 
			
		||||
	spin_lock_irqsave(&priv->lock, flags);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4478,9 +4486,8 @@ static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len)
 | 
			
		|||
			       print_ssid(ssid_buf, ssid, len), len);
 | 
			
		||||
 | 
			
		||||
		priv->one_direct_scan = 1;
 | 
			
		||||
		priv->direct_ssid_len = (u8)
 | 
			
		||||
		    min((u8) len, (u8) IW_ESSID_MAX_SIZE);
 | 
			
		||||
		memcpy(priv->direct_ssid, ssid, priv->direct_ssid_len);
 | 
			
		||||
		priv->direct_ssid_len = len;
 | 
			
		||||
		memcpy(priv->direct_ssid, ssid, len);
 | 
			
		||||
	} else
 | 
			
		||||
		priv->one_direct_scan = 0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5412,6 +5419,8 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 | 
			
		|||
 | 
			
		||||
	hw->wiphy->custom_regulatory = true;
 | 
			
		||||
 | 
			
		||||
	hw->wiphy->max_scan_ssids = 1;
 | 
			
		||||
 | 
			
		||||
	/* 4 EDCA QOS priorities */
 | 
			
		||||
	hw->queues = 4;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -143,6 +143,13 @@
 | 
			
		|||
 *	added to all specified management frames generated by
 | 
			
		||||
 *	kernel/firmware/driver.
 | 
			
		||||
 *
 | 
			
		||||
 * @NL80211_CMD_GET_SCAN: get scan results
 | 
			
		||||
 * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
 | 
			
		||||
 * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
 | 
			
		||||
 *	NL80211_CMD_GET_SCAN and on the "scan" multicast group)
 | 
			
		||||
 * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
 | 
			
		||||
 *	partial scan results may be available
 | 
			
		||||
 *
 | 
			
		||||
 * @NL80211_CMD_MAX: highest used command number
 | 
			
		||||
 * @__NL80211_CMD_AFTER_LAST: internal use
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -192,6 +199,11 @@ enum nl80211_commands {
 | 
			
		|||
 | 
			
		||||
	NL80211_CMD_GET_REG,
 | 
			
		||||
 | 
			
		||||
	NL80211_CMD_GET_SCAN,
 | 
			
		||||
	NL80211_CMD_TRIGGER_SCAN,
 | 
			
		||||
	NL80211_CMD_NEW_SCAN_RESULTS,
 | 
			
		||||
	NL80211_CMD_SCAN_ABORTED,
 | 
			
		||||
 | 
			
		||||
	/* add new commands above here */
 | 
			
		||||
 | 
			
		||||
	/* used to define NL80211_CMD_MAX below */
 | 
			
		||||
| 
						 | 
				
			
			@ -305,6 +317,18 @@ enum nl80211_commands {
 | 
			
		|||
 * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with
 | 
			
		||||
 *	%NL80211_CMD_SET_MGMT_EXTRA_IE).
 | 
			
		||||
 *
 | 
			
		||||
 * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with
 | 
			
		||||
 *	a single scan request, a wiphy attribute.
 | 
			
		||||
 *
 | 
			
		||||
 * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz)
 | 
			
		||||
 * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive
 | 
			
		||||
 *	scanning and include a zero-length SSID (wildcard) for wildcard scan
 | 
			
		||||
 * @NL80211_ATTR_SCAN_GENERATION: the scan generation increases whenever the
 | 
			
		||||
 *	scan result list changes (BSS expired or added) so that applications
 | 
			
		||||
 *	can verify that they got a single, consistent snapshot (when all dump
 | 
			
		||||
 *	messages carried the same generation number)
 | 
			
		||||
 * @NL80211_ATTR_BSS: scan result BSS
 | 
			
		||||
 *
 | 
			
		||||
 * @NL80211_ATTR_MAX: highest attribute number currently defined
 | 
			
		||||
 * @__NL80211_ATTR_AFTER_LAST: internal use
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -372,6 +396,13 @@ enum nl80211_attrs {
 | 
			
		|||
	NL80211_ATTR_MGMT_SUBTYPE,
 | 
			
		||||
	NL80211_ATTR_IE,
 | 
			
		||||
 | 
			
		||||
	NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
 | 
			
		||||
 | 
			
		||||
	NL80211_ATTR_SCAN_FREQUENCIES,
 | 
			
		||||
	NL80211_ATTR_SCAN_SSIDS,
 | 
			
		||||
	NL80211_ATTR_SCAN_GENERATION,
 | 
			
		||||
	NL80211_ATTR_BSS,
 | 
			
		||||
 | 
			
		||||
	/* add attributes here, update the policy in nl80211.c */
 | 
			
		||||
 | 
			
		||||
	__NL80211_ATTR_AFTER_LAST,
 | 
			
		||||
| 
						 | 
				
			
			@ -841,4 +872,38 @@ enum nl80211_channel_type {
 | 
			
		|||
	NL80211_CHAN_HT40MINUS,
 | 
			
		||||
	NL80211_CHAN_HT40PLUS
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * enum nl80211_bss - netlink attributes for a BSS
 | 
			
		||||
 *
 | 
			
		||||
 * @__NL80211_BSS_INVALID: invalid
 | 
			
		||||
 * @NL80211_BSS_FREQUENCY: frequency in MHz (u32)
 | 
			
		||||
 * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64)
 | 
			
		||||
 * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16)
 | 
			
		||||
 * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16)
 | 
			
		||||
 * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the
 | 
			
		||||
 *	raw information elements from the probe response/beacon (bin)
 | 
			
		||||
 * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon
 | 
			
		||||
 *	in mBm (100 * dBm) (s32)
 | 
			
		||||
 * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon
 | 
			
		||||
 *	in unspecified units, scaled to 0..100 (u8)
 | 
			
		||||
 * @__NL80211_BSS_AFTER_LAST: internal
 | 
			
		||||
 * @NL80211_BSS_MAX: highest BSS attribute
 | 
			
		||||
 */
 | 
			
		||||
enum nl80211_bss {
 | 
			
		||||
	__NL80211_BSS_INVALID,
 | 
			
		||||
	NL80211_BSS_BSSID,
 | 
			
		||||
	NL80211_BSS_FREQUENCY,
 | 
			
		||||
	NL80211_BSS_TSF,
 | 
			
		||||
	NL80211_BSS_BEACON_INTERVAL,
 | 
			
		||||
	NL80211_BSS_CAPABILITY,
 | 
			
		||||
	NL80211_BSS_INFORMATION_ELEMENTS,
 | 
			
		||||
	NL80211_BSS_SIGNAL_MBM,
 | 
			
		||||
	NL80211_BSS_SIGNAL_UNSPEC,
 | 
			
		||||
 | 
			
		||||
	/* keep last */
 | 
			
		||||
	__NL80211_BSS_AFTER_LAST,
 | 
			
		||||
	NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* __LINUX_NL80211_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,10 @@
 | 
			
		|||
#include <linux/netlink.h>
 | 
			
		||||
#include <linux/skbuff.h>
 | 
			
		||||
#include <linux/nl80211.h>
 | 
			
		||||
#include <linux/if_ether.h>
 | 
			
		||||
#include <linux/ieee80211.h>
 | 
			
		||||
#include <linux/wireless.h>
 | 
			
		||||
#include <net/iw_handler.h>
 | 
			
		||||
#include <net/genetlink.h>
 | 
			
		||||
/* remove once we remove the wext stuff */
 | 
			
		||||
#include <net/iw_handler.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -504,6 +508,83 @@ struct wiphy;
 | 
			
		|||
/* from net/ieee80211.h */
 | 
			
		||||
struct ieee80211_channel;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * struct cfg80211_ssid - SSID description
 | 
			
		||||
 * @ssid: the SSID
 | 
			
		||||
 * @ssid_len: length of the ssid
 | 
			
		||||
 */
 | 
			
		||||
struct cfg80211_ssid {
 | 
			
		||||
	u8 ssid[IEEE80211_MAX_SSID_LEN];
 | 
			
		||||
	u8 ssid_len;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * struct cfg80211_scan_request - scan request description
 | 
			
		||||
 *
 | 
			
		||||
 * @ssids: SSIDs to scan for (active scan only)
 | 
			
		||||
 * @n_ssids: number of SSIDs
 | 
			
		||||
 * @channels: channels to scan on.
 | 
			
		||||
 * @n_channels: number of channels for each band
 | 
			
		||||
 * @wiphy: the wiphy this was for
 | 
			
		||||
 * @ifidx: the interface index
 | 
			
		||||
 */
 | 
			
		||||
struct cfg80211_scan_request {
 | 
			
		||||
	struct cfg80211_ssid *ssids;
 | 
			
		||||
	int n_ssids;
 | 
			
		||||
	struct ieee80211_channel **channels;
 | 
			
		||||
	u32 n_channels;
 | 
			
		||||
 | 
			
		||||
	/* internal */
 | 
			
		||||
	struct wiphy *wiphy;
 | 
			
		||||
	int ifidx;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * enum cfg80211_signal_type - signal type
 | 
			
		||||
 *
 | 
			
		||||
 * @CFG80211_SIGNAL_TYPE_NONE: no signal strength information available
 | 
			
		||||
 * @CFG80211_SIGNAL_TYPE_MBM: signal strength in mBm (100*dBm)
 | 
			
		||||
 * @CFG80211_SIGNAL_TYPE_UNSPEC: signal strength, increasing from 0 through 100
 | 
			
		||||
 */
 | 
			
		||||
enum cfg80211_signal_type {
 | 
			
		||||
	CFG80211_SIGNAL_TYPE_NONE,
 | 
			
		||||
	CFG80211_SIGNAL_TYPE_MBM,
 | 
			
		||||
	CFG80211_SIGNAL_TYPE_UNSPEC,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * struct cfg80211_bss - BSS description
 | 
			
		||||
 *
 | 
			
		||||
 * This structure describes a BSS (which may also be a mesh network)
 | 
			
		||||
 * for use in scan results and similar.
 | 
			
		||||
 *
 | 
			
		||||
 * @bssid: BSSID of the BSS
 | 
			
		||||
 * @tsf: timestamp of last received update
 | 
			
		||||
 * @beacon_interval: the beacon interval as from the frame
 | 
			
		||||
 * @capability: the capability field in host byte order
 | 
			
		||||
 * @information_elements: the information elements (Note that there
 | 
			
		||||
 *	is no guarantee that these are well-formed!)
 | 
			
		||||
 * @len_information_elements: total length of the information elements
 | 
			
		||||
 * @signal: signal strength value
 | 
			
		||||
 * @signal_type: signal type
 | 
			
		||||
 * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes
 | 
			
		||||
 */
 | 
			
		||||
struct cfg80211_bss {
 | 
			
		||||
	struct ieee80211_channel *channel;
 | 
			
		||||
 | 
			
		||||
	u8 bssid[ETH_ALEN];
 | 
			
		||||
	u64 tsf;
 | 
			
		||||
	u16 beacon_interval;
 | 
			
		||||
	u16 capability;
 | 
			
		||||
	u8 *information_elements;
 | 
			
		||||
	size_t len_information_elements;
 | 
			
		||||
 | 
			
		||||
	s32 signal;
 | 
			
		||||
	enum cfg80211_signal_type signal_type;
 | 
			
		||||
 | 
			
		||||
	u8 priv[0] __attribute__((__aligned__(sizeof(void *))));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * struct cfg80211_ops - backend description for wireless configuration
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -571,6 +652,11 @@ struct ieee80211_channel;
 | 
			
		|||
 * @set_channel: Set channel
 | 
			
		||||
 *
 | 
			
		||||
 * @set_mgmt_extra_ie: Set extra IE data for management frames
 | 
			
		||||
 *
 | 
			
		||||
 * @scan: Request to do a scan. If returning zero, the scan request is given
 | 
			
		||||
 *	the driver, and will be valid until passed to cfg80211_scan_done().
 | 
			
		||||
 *	For scan results, call cfg80211_inform_bss(); you can call this outside
 | 
			
		||||
 *	the scan/scan_done bracket too.
 | 
			
		||||
 */
 | 
			
		||||
struct cfg80211_ops {
 | 
			
		||||
	int	(*suspend)(struct wiphy *wiphy);
 | 
			
		||||
| 
						 | 
				
			
			@ -648,6 +734,9 @@ struct cfg80211_ops {
 | 
			
		|||
	int	(*set_mgmt_extra_ie)(struct wiphy *wiphy,
 | 
			
		||||
				     struct net_device *dev,
 | 
			
		||||
				     struct mgmt_extra_ie_params *params);
 | 
			
		||||
 | 
			
		||||
	int	(*scan)(struct wiphy *wiphy, struct net_device *dev,
 | 
			
		||||
			struct cfg80211_scan_request *request);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* temporary wext handlers */
 | 
			
		||||
| 
						 | 
				
			
			@ -658,5 +747,47 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
 | 
			
		|||
			  u32 *mode, char *extra);
 | 
			
		||||
int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
 | 
			
		||||
			  u32 *mode, char *extra);
 | 
			
		||||
int cfg80211_wext_siwscan(struct net_device *dev,
 | 
			
		||||
			  struct iw_request_info *info,
 | 
			
		||||
			  union iwreq_data *wrqu, char *extra);
 | 
			
		||||
int cfg80211_wext_giwscan(struct net_device *dev,
 | 
			
		||||
			  struct iw_request_info *info,
 | 
			
		||||
			  struct iw_point *data, char *extra);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * cfg80211_scan_done - notify that scan finished
 | 
			
		||||
 *
 | 
			
		||||
 * @request: the corresponding scan request
 | 
			
		||||
 * @aborted: set to true if the scan was aborted for any reason,
 | 
			
		||||
 *	userspace will be notified of that
 | 
			
		||||
 */
 | 
			
		||||
void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * cfg80211_inform_bss - inform cfg80211 of a new BSS
 | 
			
		||||
 *
 | 
			
		||||
 * @wiphy: the wiphy reporting the BSS
 | 
			
		||||
 * @bss: the found BSS
 | 
			
		||||
 * @gfp: context flags
 | 
			
		||||
 *
 | 
			
		||||
 * This informs cfg80211 that BSS information was found and
 | 
			
		||||
 * the BSS should be updated/added.
 | 
			
		||||
 */
 | 
			
		||||
struct cfg80211_bss*
 | 
			
		||||
cfg80211_inform_bss_frame(struct wiphy *wiphy,
 | 
			
		||||
			  struct ieee80211_channel *channel,
 | 
			
		||||
			  struct ieee80211_mgmt *mgmt, size_t len,
 | 
			
		||||
			  s32 signal, enum cfg80211_signal_type sigtype,
 | 
			
		||||
			  gfp_t gfp);
 | 
			
		||||
 | 
			
		||||
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
 | 
			
		||||
				      struct ieee80211_channel *channel,
 | 
			
		||||
				      const u8 *bssid,
 | 
			
		||||
				      const u8 *ssid, size_t ssid_len);
 | 
			
		||||
struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy,
 | 
			
		||||
				       struct ieee80211_channel *channel,
 | 
			
		||||
				       const u8 *meshid, size_t meshidlen,
 | 
			
		||||
				       const u8 *meshcfg);
 | 
			
		||||
void cfg80211_put_bss(struct cfg80211_bss *bss);
 | 
			
		||||
 | 
			
		||||
#endif /* __NET_CFG80211_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1406,7 +1406,8 @@ struct ieee80211_ops {
 | 
			
		|||
	void (*update_tkip_key)(struct ieee80211_hw *hw,
 | 
			
		||||
			struct ieee80211_key_conf *conf, const u8 *address,
 | 
			
		||||
			u32 iv32, u16 *phase1key);
 | 
			
		||||
	int (*hw_scan)(struct ieee80211_hw *hw, u8 *ssid, size_t len);
 | 
			
		||||
	int (*hw_scan)(struct ieee80211_hw *hw,
 | 
			
		||||
		       struct cfg80211_scan_request *req);
 | 
			
		||||
	int (*get_stats)(struct ieee80211_hw *hw,
 | 
			
		||||
			 struct ieee80211_low_level_stats *stats);
 | 
			
		||||
	void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx,
 | 
			
		||||
| 
						 | 
				
			
			@ -1844,8 +1845,9 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw);
 | 
			
		|||
 * mac80211 that the scan finished.
 | 
			
		||||
 *
 | 
			
		||||
 * @hw: the hardware that finished the scan
 | 
			
		||||
 * @aborted: set to true if scan was aborted
 | 
			
		||||
 */
 | 
			
		||||
void ieee80211_scan_completed(struct ieee80211_hw *hw);
 | 
			
		||||
void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ieee80211_iterate_active_interfaces - iterate active interfaces
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -213,6 +213,9 @@ struct wiphy {
 | 
			
		|||
	bool custom_regulatory;
 | 
			
		||||
	bool strict_regulatory;
 | 
			
		||||
 | 
			
		||||
	int bss_priv_size;
 | 
			
		||||
	u8 max_scan_ssids;
 | 
			
		||||
 | 
			
		||||
	/* If multiple wiphys are registered and you're handed e.g.
 | 
			
		||||
	 * a regular netdev with assigned ieee80211_ptr, you won't
 | 
			
		||||
	 * know whether it points to a wiphy your driver has registered
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1277,6 +1277,25 @@ static int ieee80211_resume(struct wiphy *wiphy)
 | 
			
		|||
#define ieee80211_resume NULL
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int ieee80211_scan(struct wiphy *wiphy,
 | 
			
		||||
			  struct net_device *dev,
 | 
			
		||||
			  struct cfg80211_scan_request *req)
 | 
			
		||||
{
 | 
			
		||||
	struct ieee80211_sub_if_data *sdata;
 | 
			
		||||
 | 
			
		||||
	if (!netif_running(dev))
 | 
			
		||||
		return -ENETDOWN;
 | 
			
		||||
 | 
			
		||||
	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 | 
			
		||||
 | 
			
		||||
	if (sdata->vif.type != NL80211_IFTYPE_STATION &&
 | 
			
		||||
	    sdata->vif.type != NL80211_IFTYPE_ADHOC &&
 | 
			
		||||
	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	return ieee80211_request_scan(sdata, req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct cfg80211_ops mac80211_config_ops = {
 | 
			
		||||
	.add_virtual_intf = ieee80211_add_iface,
 | 
			
		||||
	.del_virtual_intf = ieee80211_del_iface,
 | 
			
		||||
| 
						 | 
				
			
			@ -1309,4 +1328,5 @@ struct cfg80211_ops mac80211_config_ops = {
 | 
			
		|||
	.set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie,
 | 
			
		||||
	.suspend = ieee80211_suspend,
 | 
			
		||||
	.resume = ieee80211_resume,
 | 
			
		||||
	.scan = ieee80211_scan,
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -294,8 +294,6 @@ struct ieee80211_if_sta {
 | 
			
		|||
	u8 ssid[IEEE80211_MAX_SSID_LEN];
 | 
			
		||||
	enum ieee80211_sta_mlme_state state;
 | 
			
		||||
	size_t ssid_len;
 | 
			
		||||
	u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
 | 
			
		||||
	size_t scan_ssid_len;
 | 
			
		||||
	u16 aid;
 | 
			
		||||
	u16 ap_capab, capab;
 | 
			
		||||
	u8 *extra_ie; /* to be added to the end of AssocReq */
 | 
			
		||||
| 
						 | 
				
			
			@ -658,17 +656,18 @@ struct ieee80211_local {
 | 
			
		|||
 | 
			
		||||
	/* Scanning and BSS list */
 | 
			
		||||
	bool sw_scanning, hw_scanning;
 | 
			
		||||
	struct cfg80211_ssid scan_ssid;
 | 
			
		||||
	struct cfg80211_scan_request int_scan_req;
 | 
			
		||||
	struct cfg80211_scan_request *scan_req;
 | 
			
		||||
	struct ieee80211_channel *scan_channel;
 | 
			
		||||
	int scan_channel_idx;
 | 
			
		||||
	enum ieee80211_band scan_band;
 | 
			
		||||
 | 
			
		||||
	enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
 | 
			
		||||
	unsigned long last_scan_completed;
 | 
			
		||||
	struct delayed_work scan_work;
 | 
			
		||||
	struct ieee80211_sub_if_data *scan_sdata;
 | 
			
		||||
	struct ieee80211_channel *oper_channel, *scan_channel, *csa_channel;
 | 
			
		||||
	enum nl80211_channel_type oper_channel_type;
 | 
			
		||||
	u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
 | 
			
		||||
	size_t scan_ssid_len;
 | 
			
		||||
	struct ieee80211_channel *oper_channel, *csa_channel;
 | 
			
		||||
	struct list_head bss_list;
 | 
			
		||||
	struct ieee80211_bss *bss_hash[STA_HASH_SIZE];
 | 
			
		||||
	spinlock_t bss_lock;
 | 
			
		||||
| 
						 | 
				
			
			@ -929,7 +928,7 @@ void ieee80211_send_pspoll(struct ieee80211_local *local,
 | 
			
		|||
 | 
			
		||||
/* scan/BSS handling */
 | 
			
		||||
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
 | 
			
		||||
			   u8 *ssid, size_t ssid_len);
 | 
			
		||||
			   struct cfg80211_scan_request *req);
 | 
			
		||||
int ieee80211_scan_results(struct ieee80211_local *local,
 | 
			
		||||
			   struct iw_request_info *info,
 | 
			
		||||
			   char *buf, size_t len);
 | 
			
		||||
| 
						 | 
				
			
			@ -944,14 +943,15 @@ int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata,
 | 
			
		|||
 | 
			
		||||
void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local);
 | 
			
		||||
int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
 | 
			
		||||
			 u8 *ssid, size_t ssid_len);
 | 
			
		||||
			 struct cfg80211_scan_request *req);
 | 
			
		||||
struct ieee80211_bss *
 | 
			
		||||
ieee80211_bss_info_update(struct ieee80211_local *local,
 | 
			
		||||
			  struct ieee80211_rx_status *rx_status,
 | 
			
		||||
			  struct ieee80211_mgmt *mgmt,
 | 
			
		||||
			  size_t len,
 | 
			
		||||
			  struct ieee802_11_elems *elems,
 | 
			
		||||
			  int freq, bool beacon);
 | 
			
		||||
			  struct ieee80211_channel *channel,
 | 
			
		||||
			  bool beacon);
 | 
			
		||||
struct ieee80211_bss *
 | 
			
		||||
ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq,
 | 
			
		||||
		     u8 *ssid, u8 ssid_len);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -522,7 +522,7 @@ static int ieee80211_stop(struct net_device *dev)
 | 
			
		|||
			 * scan event to userspace -- the scan is incomplete.
 | 
			
		||||
			 */
 | 
			
		||||
			if (local->sw_scanning)
 | 
			
		||||
				ieee80211_scan_completed(&local->hw);
 | 
			
		||||
				ieee80211_scan_completed(&local->hw, true);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		conf.vif = &sdata->vif;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -733,6 +733,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
 | 
			
		|||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	wiphy->privid = mac80211_wiphy_privid;
 | 
			
		||||
	wiphy->max_scan_ssids = 4;
 | 
			
		||||
 | 
			
		||||
	local = wiphy_priv(wiphy);
 | 
			
		||||
	local->hw.wiphy = wiphy;
 | 
			
		||||
| 
						 | 
				
			
			@ -817,25 +818,33 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 | 
			
		|||
	enum ieee80211_band band;
 | 
			
		||||
	struct net_device *mdev;
 | 
			
		||||
	struct ieee80211_master_priv *mpriv;
 | 
			
		||||
	int channels, i, j;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * generic code guarantees at least one band,
 | 
			
		||||
	 * set this very early because much code assumes
 | 
			
		||||
	 * that hw.conf.channel is assigned
 | 
			
		||||
	 */
 | 
			
		||||
	channels = 0;
 | 
			
		||||
	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
 | 
			
		||||
		struct ieee80211_supported_band *sband;
 | 
			
		||||
 | 
			
		||||
		sband = local->hw.wiphy->bands[band];
 | 
			
		||||
		if (sband) {
 | 
			
		||||
		if (sband && !local->oper_channel) {
 | 
			
		||||
			/* init channel we're on */
 | 
			
		||||
			local->hw.conf.channel =
 | 
			
		||||
			local->oper_channel =
 | 
			
		||||
			local->scan_channel = &sband->channels[0];
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		if (sband)
 | 
			
		||||
			channels += sband->n_channels;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	local->int_scan_req.n_channels = channels;
 | 
			
		||||
	local->int_scan_req.channels = kzalloc(sizeof(void *) * channels, GFP_KERNEL);
 | 
			
		||||
	if (!local->int_scan_req.channels)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	/* if low-level driver supports AP, we also support VLAN */
 | 
			
		||||
	if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP))
 | 
			
		||||
		local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN);
 | 
			
		||||
| 
						 | 
				
			
			@ -845,7 +854,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 | 
			
		|||
 | 
			
		||||
	result = wiphy_register(local->hw.wiphy);
 | 
			
		||||
	if (result < 0)
 | 
			
		||||
		return result;
 | 
			
		||||
		goto fail_wiphy_register;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We use the number of queues for feature tests (QoS, HT) internally
 | 
			
		||||
| 
						 | 
				
			
			@ -948,6 +957,20 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 | 
			
		|||
 | 
			
		||||
	ieee80211_led_init(local);
 | 
			
		||||
 | 
			
		||||
	/* alloc internal scan request */
 | 
			
		||||
	i = 0;
 | 
			
		||||
	local->int_scan_req.ssids = &local->scan_ssid;
 | 
			
		||||
	local->int_scan_req.n_ssids = 1;
 | 
			
		||||
	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
 | 
			
		||||
		if (!hw->wiphy->bands[band])
 | 
			
		||||
			continue;
 | 
			
		||||
		for (j = 0; j < hw->wiphy->bands[band]->n_channels; j++) {
 | 
			
		||||
			local->int_scan_req.channels[i] =
 | 
			
		||||
				&hw->wiphy->bands[band]->channels[j];
 | 
			
		||||
			i++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
fail_wep:
 | 
			
		||||
| 
						 | 
				
			
			@ -966,6 +989,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 | 
			
		|||
		free_netdev(local->mdev);
 | 
			
		||||
fail_mdev_alloc:
 | 
			
		||||
	wiphy_unregister(local->hw.wiphy);
 | 
			
		||||
fail_wiphy_register:
 | 
			
		||||
	kfree(local->int_scan_req.channels);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(ieee80211_register_hw);
 | 
			
		||||
| 
						 | 
				
			
			@ -1011,6 +1036,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
 | 
			
		|||
	ieee80211_wep_free(local);
 | 
			
		||||
	ieee80211_led_exit(local);
 | 
			
		||||
	free_netdev(local->mdev);
 | 
			
		||||
	kfree(local->int_scan_req.channels);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(ieee80211_unregister_hw);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1743,7 +1743,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
 | 
			
		||||
					freq, beacon);
 | 
			
		||||
					channel, beacon);
 | 
			
		||||
	if (!bss)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2162,7 +2162,15 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata,
 | 
			
		|||
 | 
			
		||||
	printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other "
 | 
			
		||||
	       "IBSS networks with same SSID (merge)\n", sdata->dev->name);
 | 
			
		||||
	ieee80211_request_scan(sdata, ifsta->ssid, ifsta->ssid_len);
 | 
			
		||||
 | 
			
		||||
	/* XXX maybe racy? */
 | 
			
		||||
	if (sdata->local->scan_req)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	memcpy(sdata->local->int_scan_req.ssids[0].ssid,
 | 
			
		||||
	       ifsta->ssid, IEEE80211_MAX_SSID_LEN);
 | 
			
		||||
	sdata->local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len;
 | 
			
		||||
	ieee80211_request_scan(sdata, &sdata->local->int_scan_req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2378,8 +2386,15 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
 | 
			
		|||
			      IEEE80211_SCAN_INTERVAL)) {
 | 
			
		||||
		printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to "
 | 
			
		||||
		       "join\n", sdata->dev->name);
 | 
			
		||||
		return ieee80211_request_scan(sdata, ifsta->ssid,
 | 
			
		||||
					      ifsta->ssid_len);
 | 
			
		||||
 | 
			
		||||
		/* XXX maybe racy? */
 | 
			
		||||
		if (local->scan_req)
 | 
			
		||||
			return -EBUSY;
 | 
			
		||||
 | 
			
		||||
		memcpy(local->int_scan_req.ssids[0].ssid,
 | 
			
		||||
		       ifsta->ssid, IEEE80211_MAX_SSID_LEN);
 | 
			
		||||
		local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len;
 | 
			
		||||
		return ieee80211_request_scan(sdata, &local->int_scan_req);
 | 
			
		||||
	} else if (ifsta->state != IEEE80211_STA_MLME_IBSS_JOINED) {
 | 
			
		||||
		int interval = IEEE80211_SCAN_INTERVAL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2478,11 +2493,16 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
 | 
			
		|||
	} else {
 | 
			
		||||
		if (ifsta->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) {
 | 
			
		||||
			ifsta->assoc_scan_tries++;
 | 
			
		||||
			/* XXX maybe racy? */
 | 
			
		||||
			if (local->scan_req)
 | 
			
		||||
				return -1;
 | 
			
		||||
			memcpy(local->int_scan_req.ssids[0].ssid,
 | 
			
		||||
			       ifsta->ssid, IEEE80211_MAX_SSID_LEN);
 | 
			
		||||
			if (ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL)
 | 
			
		||||
				ieee80211_start_scan(sdata, NULL, 0);
 | 
			
		||||
				local->int_scan_req.ssids[0].ssid_len = 0;
 | 
			
		||||
			else
 | 
			
		||||
				ieee80211_start_scan(sdata, ifsta->ssid,
 | 
			
		||||
							 ifsta->ssid_len);
 | 
			
		||||
				local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len;
 | 
			
		||||
			ieee80211_start_scan(sdata, &local->int_scan_req);
 | 
			
		||||
			ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
 | 
			
		||||
			set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
 | 
			
		||||
		} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -2520,8 +2540,7 @@ static void ieee80211_sta_work(struct work_struct *work)
 | 
			
		|||
	    ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE &&
 | 
			
		||||
	    ifsta->state != IEEE80211_STA_MLME_ASSOCIATE &&
 | 
			
		||||
	    test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) {
 | 
			
		||||
		ieee80211_start_scan(sdata, ifsta->scan_ssid,
 | 
			
		||||
				     ifsta->scan_ssid_len);
 | 
			
		||||
		ieee80211_start_scan(sdata, local->scan_req);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,9 @@
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
/* TODO:
 | 
			
		||||
 * figure out how to avoid that the "current BSS" expires
 | 
			
		||||
 * clean up IBSS code (in MLME), see why it adds a BSS to the list
 | 
			
		||||
 * use cfg80211's BSS handling (depends on IBSS TODO above)
 | 
			
		||||
 * order BSS list by RSSI(?) ("quality of AP")
 | 
			
		||||
 * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE,
 | 
			
		||||
 *    SSID)
 | 
			
		||||
| 
						 | 
				
			
			@ -225,10 +228,26 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
 | 
			
		|||
			  struct ieee80211_mgmt *mgmt,
 | 
			
		||||
			  size_t len,
 | 
			
		||||
			  struct ieee802_11_elems *elems,
 | 
			
		||||
			  int freq, bool beacon)
 | 
			
		||||
			  struct ieee80211_channel *channel,
 | 
			
		||||
			  bool beacon)
 | 
			
		||||
{
 | 
			
		||||
	struct ieee80211_bss *bss;
 | 
			
		||||
	int clen;
 | 
			
		||||
	int clen, freq = channel->center_freq;
 | 
			
		||||
	enum cfg80211_signal_type sigtype = CFG80211_SIGNAL_TYPE_NONE;
 | 
			
		||||
	s32 signal = 0;
 | 
			
		||||
 | 
			
		||||
	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
 | 
			
		||||
		sigtype = CFG80211_SIGNAL_TYPE_MBM;
 | 
			
		||||
		signal = rx_status->signal * 100;
 | 
			
		||||
	} else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) {
 | 
			
		||||
		sigtype = CFG80211_SIGNAL_TYPE_UNSPEC;
 | 
			
		||||
		signal = (rx_status->signal * 100) / local->hw.max_signal;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cfg80211_put_bss(
 | 
			
		||||
		cfg80211_inform_bss_frame(local->hw.wiphy, channel,
 | 
			
		||||
					  mgmt, len, signal, sigtype,
 | 
			
		||||
					  GFP_ATOMIC));
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MAC80211_MESH
 | 
			
		||||
	if (elems->mesh_config)
 | 
			
		||||
| 
						 | 
				
			
			@ -401,7 +420,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
 | 
			
		|||
 | 
			
		||||
	bss = ieee80211_bss_info_update(sdata->local, rx_status,
 | 
			
		||||
					mgmt, skb->len, &elems,
 | 
			
		||||
					freq, beacon);
 | 
			
		||||
					channel, beacon);
 | 
			
		||||
	if (bss)
 | 
			
		||||
		ieee80211_rx_bss_put(sdata->local, bss);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -439,26 +458,22 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
 | 
			
		|||
	ieee80211_tx_skb(sdata, skb, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ieee80211_scan_completed(struct ieee80211_hw *hw)
 | 
			
		||||
void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
 | 
			
		||||
{
 | 
			
		||||
	struct ieee80211_local *local = hw_to_local(hw);
 | 
			
		||||
	struct ieee80211_sub_if_data *sdata;
 | 
			
		||||
	union iwreq_data wrqu;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(!local->hw_scanning && !local->sw_scanning))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	local->last_scan_completed = jiffies;
 | 
			
		||||
	memset(&wrqu, 0, sizeof(wrqu));
 | 
			
		||||
	if (WARN_ON(!local->scan_req))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * local->scan_sdata could have been NULLed by the interface
 | 
			
		||||
	 * down code in case we were scanning on an interface that is
 | 
			
		||||
	 * being taken down.
 | 
			
		||||
	 */
 | 
			
		||||
	sdata = local->scan_sdata;
 | 
			
		||||
	if (sdata)
 | 
			
		||||
		wireless_send_event(sdata->dev, SIOCGIWSCAN, &wrqu, NULL);
 | 
			
		||||
	if (local->scan_req != &local->int_scan_req)
 | 
			
		||||
		cfg80211_scan_done(local->scan_req, aborted);
 | 
			
		||||
	local->scan_req = NULL;
 | 
			
		||||
 | 
			
		||||
	local->last_scan_completed = jiffies;
 | 
			
		||||
 | 
			
		||||
	if (local->hw_scanning) {
 | 
			
		||||
		local->hw_scanning = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -520,9 +535,8 @@ void ieee80211_scan_work(struct work_struct *work)
 | 
			
		|||
	struct ieee80211_local *local =
 | 
			
		||||
		container_of(work, struct ieee80211_local, scan_work.work);
 | 
			
		||||
	struct ieee80211_sub_if_data *sdata = local->scan_sdata;
 | 
			
		||||
	struct ieee80211_supported_band *sband;
 | 
			
		||||
	struct ieee80211_channel *chan;
 | 
			
		||||
	int skip;
 | 
			
		||||
	int skip, i;
 | 
			
		||||
	unsigned long next_delay = 0;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
| 
						 | 
				
			
			@ -533,33 +547,13 @@ void ieee80211_scan_work(struct work_struct *work)
 | 
			
		|||
 | 
			
		||||
	switch (local->scan_state) {
 | 
			
		||||
	case SCAN_SET_CHANNEL:
 | 
			
		||||
		/*
 | 
			
		||||
		 * Get current scan band. scan_band may be IEEE80211_NUM_BANDS
 | 
			
		||||
		 * after we successfully scanned the last channel of the last
 | 
			
		||||
		 * band (and the last band is supported by the hw)
 | 
			
		||||
		 */
 | 
			
		||||
		if (local->scan_band < IEEE80211_NUM_BANDS)
 | 
			
		||||
			sband = local->hw.wiphy->bands[local->scan_band];
 | 
			
		||||
		else
 | 
			
		||||
			sband = NULL;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * If we are at an unsupported band and have more bands
 | 
			
		||||
		 * left to scan, advance to the next supported one.
 | 
			
		||||
		 */
 | 
			
		||||
		while (!sband && local->scan_band < IEEE80211_NUM_BANDS - 1) {
 | 
			
		||||
			local->scan_band++;
 | 
			
		||||
			sband = local->hw.wiphy->bands[local->scan_band];
 | 
			
		||||
			local->scan_channel_idx = 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* if no more bands/channels left, complete scan */
 | 
			
		||||
		if (!sband || local->scan_channel_idx >= sband->n_channels) {
 | 
			
		||||
			ieee80211_scan_completed(local_to_hw(local));
 | 
			
		||||
		if (local->scan_channel_idx >= local->scan_req->n_channels) {
 | 
			
		||||
			ieee80211_scan_completed(local_to_hw(local), false);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		skip = 0;
 | 
			
		||||
		chan = &sband->channels[local->scan_channel_idx];
 | 
			
		||||
		chan = local->scan_req->channels[local->scan_channel_idx];
 | 
			
		||||
 | 
			
		||||
		if (chan->flags & IEEE80211_CHAN_DISABLED ||
 | 
			
		||||
		    (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
 | 
			
		||||
| 
						 | 
				
			
			@ -575,15 +569,6 @@ void ieee80211_scan_work(struct work_struct *work)
 | 
			
		|||
 | 
			
		||||
		/* advance state machine to next channel/band */
 | 
			
		||||
		local->scan_channel_idx++;
 | 
			
		||||
		if (local->scan_channel_idx >= sband->n_channels) {
 | 
			
		||||
			/*
 | 
			
		||||
			 * scan_band may end up == IEEE80211_NUM_BANDS, but
 | 
			
		||||
			 * we'll catch that case above and complete the scan
 | 
			
		||||
			 * if that is the case.
 | 
			
		||||
			 */
 | 
			
		||||
			local->scan_band++;
 | 
			
		||||
			local->scan_channel_idx = 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (skip)
 | 
			
		||||
			break;
 | 
			
		||||
| 
						 | 
				
			
			@ -596,10 +581,14 @@ void ieee80211_scan_work(struct work_struct *work)
 | 
			
		|||
		next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
 | 
			
		||||
		local->scan_state = SCAN_SET_CHANNEL;
 | 
			
		||||
 | 
			
		||||
		if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN)
 | 
			
		||||
		if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN ||
 | 
			
		||||
		    !local->scan_req->n_ssids)
 | 
			
		||||
			break;
 | 
			
		||||
		ieee80211_send_probe_req(sdata, NULL, local->scan_ssid,
 | 
			
		||||
					 local->scan_ssid_len);
 | 
			
		||||
		for (i = 0; i < local->scan_req->n_ssids; i++)
 | 
			
		||||
			ieee80211_send_probe_req(
 | 
			
		||||
				sdata, NULL,
 | 
			
		||||
				local->scan_req->ssids[i].ssid,
 | 
			
		||||
				local->scan_req->ssids[i].ssid_len);
 | 
			
		||||
		next_delay = IEEE80211_CHANNEL_TIME;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -610,14 +599,19 @@ void ieee80211_scan_work(struct work_struct *work)
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
 | 
			
		||||
			 u8 *ssid, size_t ssid_len)
 | 
			
		||||
			 struct cfg80211_scan_request *req)
 | 
			
		||||
{
 | 
			
		||||
	struct ieee80211_local *local = scan_sdata->local;
 | 
			
		||||
	struct ieee80211_sub_if_data *sdata;
 | 
			
		||||
 | 
			
		||||
	if (ssid_len > IEEE80211_MAX_SSID_LEN)
 | 
			
		||||
	if (!req)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (local->scan_req && local->scan_req != req)
 | 
			
		||||
		return -EBUSY;
 | 
			
		||||
 | 
			
		||||
	local->scan_req = req;
 | 
			
		||||
 | 
			
		||||
	/* MLME-SCAN.request (page 118)  page 144 (11.1.3.1)
 | 
			
		||||
	 * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS
 | 
			
		||||
	 * BSSID: MACAddress
 | 
			
		||||
| 
						 | 
				
			
			@ -645,7 +639,7 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
 | 
			
		|||
		int rc;
 | 
			
		||||
 | 
			
		||||
		local->hw_scanning = true;
 | 
			
		||||
		rc = local->ops->hw_scan(local_to_hw(local), ssid, ssid_len);
 | 
			
		||||
		rc = local->ops->hw_scan(local_to_hw(local), req);
 | 
			
		||||
		if (rc) {
 | 
			
		||||
			local->hw_scanning = false;
 | 
			
		||||
			return rc;
 | 
			
		||||
| 
						 | 
				
			
			@ -678,15 +672,10 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
 | 
			
		|||
	}
 | 
			
		||||
	mutex_unlock(&local->iflist_mtx);
 | 
			
		||||
 | 
			
		||||
	if (ssid) {
 | 
			
		||||
		local->scan_ssid_len = ssid_len;
 | 
			
		||||
		memcpy(local->scan_ssid, ssid, ssid_len);
 | 
			
		||||
	} else
 | 
			
		||||
		local->scan_ssid_len = 0;
 | 
			
		||||
	local->scan_state = SCAN_SET_CHANNEL;
 | 
			
		||||
	local->scan_channel_idx = 0;
 | 
			
		||||
	local->scan_band = IEEE80211_BAND_2GHZ;
 | 
			
		||||
	local->scan_sdata = scan_sdata;
 | 
			
		||||
	local->scan_req = req;
 | 
			
		||||
 | 
			
		||||
	netif_addr_lock_bh(local->mdev);
 | 
			
		||||
	local->filter_flags |= FIF_BCN_PRBRESP_PROMISC;
 | 
			
		||||
| 
						 | 
				
			
			@ -706,13 +695,21 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
 | 
			
		||||
			   u8 *ssid, size_t ssid_len)
 | 
			
		||||
			   struct cfg80211_scan_request *req)
 | 
			
		||||
{
 | 
			
		||||
	struct ieee80211_local *local = sdata->local;
 | 
			
		||||
	struct ieee80211_if_sta *ifsta;
 | 
			
		||||
 | 
			
		||||
	if (!req)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (local->scan_req && local->scan_req != req)
 | 
			
		||||
		return -EBUSY;
 | 
			
		||||
 | 
			
		||||
	local->scan_req = req;
 | 
			
		||||
 | 
			
		||||
	if (sdata->vif.type != NL80211_IFTYPE_STATION)
 | 
			
		||||
		return ieee80211_start_scan(sdata, ssid, ssid_len);
 | 
			
		||||
		return ieee80211_start_scan(sdata, req);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * STA has a state machine that might need to defer scanning
 | 
			
		||||
| 
						 | 
				
			
			@ -727,241 +724,8 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	ifsta = &sdata->u.sta;
 | 
			
		||||
 | 
			
		||||
	ifsta->scan_ssid_len = ssid_len;
 | 
			
		||||
	if (ssid_len)
 | 
			
		||||
		memcpy(ifsta->scan_ssid, ssid, ssid_len);
 | 
			
		||||
	set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request);
 | 
			
		||||
	queue_work(local->hw.workqueue, &ifsta->work);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void ieee80211_scan_add_ies(struct iw_request_info *info,
 | 
			
		||||
				   struct ieee80211_bss *bss,
 | 
			
		||||
				   char **current_ev, char *end_buf)
 | 
			
		||||
{
 | 
			
		||||
	u8 *pos, *end, *next;
 | 
			
		||||
	struct iw_event iwe;
 | 
			
		||||
 | 
			
		||||
	if (bss == NULL || bss->ies == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If needed, fragment the IEs buffer (at IE boundaries) into short
 | 
			
		||||
	 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
 | 
			
		||||
	 */
 | 
			
		||||
	pos = bss->ies;
 | 
			
		||||
	end = pos + bss->ies_len;
 | 
			
		||||
 | 
			
		||||
	while (end - pos > IW_GENERIC_IE_MAX) {
 | 
			
		||||
		next = pos + 2 + pos[1];
 | 
			
		||||
		while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
 | 
			
		||||
			next = next + 2 + next[1];
 | 
			
		||||
 | 
			
		||||
		memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
		iwe.cmd = IWEVGENIE;
 | 
			
		||||
		iwe.u.data.length = next - pos;
 | 
			
		||||
		*current_ev = iwe_stream_add_point(info, *current_ev,
 | 
			
		||||
						   end_buf, &iwe, pos);
 | 
			
		||||
 | 
			
		||||
		pos = next;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (end > pos) {
 | 
			
		||||
		memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
		iwe.cmd = IWEVGENIE;
 | 
			
		||||
		iwe.u.data.length = end - pos;
 | 
			
		||||
		*current_ev = iwe_stream_add_point(info, *current_ev,
 | 
			
		||||
						   end_buf, &iwe, pos);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
ieee80211_scan_result(struct ieee80211_local *local,
 | 
			
		||||
		      struct iw_request_info *info,
 | 
			
		||||
		      struct ieee80211_bss *bss,
 | 
			
		||||
		      char *current_ev, char *end_buf)
 | 
			
		||||
{
 | 
			
		||||
	struct iw_event iwe;
 | 
			
		||||
	char *buf;
 | 
			
		||||
 | 
			
		||||
	if (time_after(jiffies,
 | 
			
		||||
		       bss->last_update + IEEE80211_SCAN_RESULT_EXPIRE))
 | 
			
		||||
		return current_ev;
 | 
			
		||||
 | 
			
		||||
	memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
	iwe.cmd = SIOCGIWAP;
 | 
			
		||||
	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
 | 
			
		||||
	memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
 | 
			
		||||
	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
 | 
			
		||||
					  IW_EV_ADDR_LEN);
 | 
			
		||||
 | 
			
		||||
	memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
	iwe.cmd = SIOCGIWESSID;
 | 
			
		||||
	if (bss_mesh_cfg(bss)) {
 | 
			
		||||
		iwe.u.data.length = bss_mesh_id_len(bss);
 | 
			
		||||
		iwe.u.data.flags = 1;
 | 
			
		||||
		current_ev = iwe_stream_add_point(info, current_ev, end_buf,
 | 
			
		||||
						  &iwe, bss_mesh_id(bss));
 | 
			
		||||
	} else {
 | 
			
		||||
		iwe.u.data.length = bss->ssid_len;
 | 
			
		||||
		iwe.u.data.flags = 1;
 | 
			
		||||
		current_ev = iwe_stream_add_point(info, current_ev, end_buf,
 | 
			
		||||
						  &iwe, bss->ssid);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)
 | 
			
		||||
	    || bss_mesh_cfg(bss)) {
 | 
			
		||||
		memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
		iwe.cmd = SIOCGIWMODE;
 | 
			
		||||
		if (bss_mesh_cfg(bss))
 | 
			
		||||
			iwe.u.mode = IW_MODE_MESH;
 | 
			
		||||
		else if (bss->capability & WLAN_CAPABILITY_ESS)
 | 
			
		||||
			iwe.u.mode = IW_MODE_MASTER;
 | 
			
		||||
		else
 | 
			
		||||
			iwe.u.mode = IW_MODE_ADHOC;
 | 
			
		||||
		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
 | 
			
		||||
						  &iwe, IW_EV_UINT_LEN);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
	iwe.cmd = SIOCGIWFREQ;
 | 
			
		||||
	iwe.u.freq.m = ieee80211_frequency_to_channel(bss->freq);
 | 
			
		||||
	iwe.u.freq.e = 0;
 | 
			
		||||
	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
 | 
			
		||||
					  IW_EV_FREQ_LEN);
 | 
			
		||||
 | 
			
		||||
	memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
	iwe.cmd = SIOCGIWFREQ;
 | 
			
		||||
	iwe.u.freq.m = bss->freq;
 | 
			
		||||
	iwe.u.freq.e = 6;
 | 
			
		||||
	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
 | 
			
		||||
					  IW_EV_FREQ_LEN);
 | 
			
		||||
	memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
	iwe.cmd = IWEVQUAL;
 | 
			
		||||
	iwe.u.qual.qual = bss->qual;
 | 
			
		||||
	iwe.u.qual.level = bss->signal;
 | 
			
		||||
	iwe.u.qual.noise = bss->noise;
 | 
			
		||||
	iwe.u.qual.updated = local->wstats_flags;
 | 
			
		||||
	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
 | 
			
		||||
					  IW_EV_QUAL_LEN);
 | 
			
		||||
 | 
			
		||||
	memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
	iwe.cmd = SIOCGIWENCODE;
 | 
			
		||||
	if (bss->capability & WLAN_CAPABILITY_PRIVACY)
 | 
			
		||||
		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
 | 
			
		||||
	else
 | 
			
		||||
		iwe.u.data.flags = IW_ENCODE_DISABLED;
 | 
			
		||||
	iwe.u.data.length = 0;
 | 
			
		||||
	current_ev = iwe_stream_add_point(info, current_ev, end_buf,
 | 
			
		||||
					  &iwe, "");
 | 
			
		||||
 | 
			
		||||
	ieee80211_scan_add_ies(info, bss, ¤t_ev, end_buf);
 | 
			
		||||
 | 
			
		||||
	if (bss->supp_rates_len > 0) {
 | 
			
		||||
		/* display all supported rates in readable format */
 | 
			
		||||
		char *p = current_ev + iwe_stream_lcp_len(info);
 | 
			
		||||
		int i;
 | 
			
		||||
 | 
			
		||||
		memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
		iwe.cmd = SIOCGIWRATE;
 | 
			
		||||
		/* Those two flags are ignored... */
 | 
			
		||||
		iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
 | 
			
		||||
 | 
			
		||||
		for (i = 0; i < bss->supp_rates_len; i++) {
 | 
			
		||||
			iwe.u.bitrate.value = ((bss->supp_rates[i] &
 | 
			
		||||
							0x7f) * 500000);
 | 
			
		||||
			p = iwe_stream_add_value(info, current_ev, p,
 | 
			
		||||
					end_buf, &iwe, IW_EV_PARAM_LEN);
 | 
			
		||||
		}
 | 
			
		||||
		current_ev = p;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf = kmalloc(30, GFP_ATOMIC);
 | 
			
		||||
	if (buf) {
 | 
			
		||||
		memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
		iwe.cmd = IWEVCUSTOM;
 | 
			
		||||
		sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp));
 | 
			
		||||
		iwe.u.data.length = strlen(buf);
 | 
			
		||||
		current_ev = iwe_stream_add_point(info, current_ev, end_buf,
 | 
			
		||||
						  &iwe, buf);
 | 
			
		||||
		memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
		iwe.cmd = IWEVCUSTOM;
 | 
			
		||||
		sprintf(buf, " Last beacon: %dms ago",
 | 
			
		||||
			jiffies_to_msecs(jiffies - bss->last_update));
 | 
			
		||||
		iwe.u.data.length = strlen(buf);
 | 
			
		||||
		current_ev = iwe_stream_add_point(info, current_ev,
 | 
			
		||||
						  end_buf, &iwe, buf);
 | 
			
		||||
		kfree(buf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bss_mesh_cfg(bss)) {
 | 
			
		||||
		u8 *cfg = bss_mesh_cfg(bss);
 | 
			
		||||
		buf = kmalloc(50, GFP_ATOMIC);
 | 
			
		||||
		if (buf) {
 | 
			
		||||
			memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
			iwe.cmd = IWEVCUSTOM;
 | 
			
		||||
			sprintf(buf, "Mesh network (version %d)", cfg[0]);
 | 
			
		||||
			iwe.u.data.length = strlen(buf);
 | 
			
		||||
			current_ev = iwe_stream_add_point(info, current_ev,
 | 
			
		||||
							  end_buf,
 | 
			
		||||
							  &iwe, buf);
 | 
			
		||||
			sprintf(buf, "Path Selection Protocol ID: "
 | 
			
		||||
				"0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3],
 | 
			
		||||
							cfg[4]);
 | 
			
		||||
			iwe.u.data.length = strlen(buf);
 | 
			
		||||
			current_ev = iwe_stream_add_point(info, current_ev,
 | 
			
		||||
							  end_buf,
 | 
			
		||||
							  &iwe, buf);
 | 
			
		||||
			sprintf(buf, "Path Selection Metric ID: "
 | 
			
		||||
				"0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7],
 | 
			
		||||
							cfg[8]);
 | 
			
		||||
			iwe.u.data.length = strlen(buf);
 | 
			
		||||
			current_ev = iwe_stream_add_point(info, current_ev,
 | 
			
		||||
							  end_buf,
 | 
			
		||||
							  &iwe, buf);
 | 
			
		||||
			sprintf(buf, "Congestion Control Mode ID: "
 | 
			
		||||
				"0x%02X%02X%02X%02X", cfg[9], cfg[10],
 | 
			
		||||
							cfg[11], cfg[12]);
 | 
			
		||||
			iwe.u.data.length = strlen(buf);
 | 
			
		||||
			current_ev = iwe_stream_add_point(info, current_ev,
 | 
			
		||||
							  end_buf,
 | 
			
		||||
							  &iwe, buf);
 | 
			
		||||
			sprintf(buf, "Channel Precedence: "
 | 
			
		||||
				"0x%02X%02X%02X%02X", cfg[13], cfg[14],
 | 
			
		||||
							cfg[15], cfg[16]);
 | 
			
		||||
			iwe.u.data.length = strlen(buf);
 | 
			
		||||
			current_ev = iwe_stream_add_point(info, current_ev,
 | 
			
		||||
							  end_buf,
 | 
			
		||||
							  &iwe, buf);
 | 
			
		||||
			kfree(buf);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return current_ev;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int ieee80211_scan_results(struct ieee80211_local *local,
 | 
			
		||||
			   struct iw_request_info *info,
 | 
			
		||||
			   char *buf, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	char *current_ev = buf;
 | 
			
		||||
	char *end_buf = buf + len;
 | 
			
		||||
	struct ieee80211_bss *bss;
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&local->bss_lock);
 | 
			
		||||
	list_for_each_entry(bss, &local->bss_list, list) {
 | 
			
		||||
		if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
 | 
			
		||||
			spin_unlock_bh(&local->bss_lock);
 | 
			
		||||
			return -E2BIG;
 | 
			
		||||
		}
 | 
			
		||||
		current_ev = ieee80211_scan_result(local, info, bss,
 | 
			
		||||
						       current_ev, end_buf);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock_bh(&local->bss_lock);
 | 
			
		||||
	return current_ev - buf;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -173,8 +173,9 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev,
 | 
			
		|||
	range->num_encoding_sizes = 2;
 | 
			
		||||
	range->max_encoding_tokens = NUM_DEFAULT_KEYS;
 | 
			
		||||
 | 
			
		||||
	/* cfg80211 requires this, and enforces 0..100 */
 | 
			
		||||
	if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
 | 
			
		||||
		range->max_qual.level = local->hw.max_signal;
 | 
			
		||||
		range->max_qual.level = 100;
 | 
			
		||||
	else if  (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
 | 
			
		||||
		range->max_qual.level = -110;
 | 
			
		||||
	else
 | 
			
		||||
| 
						 | 
				
			
			@ -415,58 +416,6 @@ static int ieee80211_ioctl_giwap(struct net_device *dev,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int ieee80211_ioctl_siwscan(struct net_device *dev,
 | 
			
		||||
				   struct iw_request_info *info,
 | 
			
		||||
				   union iwreq_data *wrqu, char *extra)
 | 
			
		||||
{
 | 
			
		||||
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 | 
			
		||||
	struct iw_scan_req *req = NULL;
 | 
			
		||||
	u8 *ssid = NULL;
 | 
			
		||||
	size_t ssid_len = 0;
 | 
			
		||||
 | 
			
		||||
	if (!netif_running(dev))
 | 
			
		||||
		return -ENETDOWN;
 | 
			
		||||
 | 
			
		||||
	if (sdata->vif.type != NL80211_IFTYPE_STATION &&
 | 
			
		||||
	    sdata->vif.type != NL80211_IFTYPE_ADHOC &&
 | 
			
		||||
	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	/* if SSID was specified explicitly then use that */
 | 
			
		||||
	if (wrqu->data.length == sizeof(struct iw_scan_req) &&
 | 
			
		||||
	    wrqu->data.flags & IW_SCAN_THIS_ESSID) {
 | 
			
		||||
		req = (struct iw_scan_req *)extra;
 | 
			
		||||
		ssid = req->essid;
 | 
			
		||||
		ssid_len = req->essid_len;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ieee80211_request_scan(sdata, ssid, ssid_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int ieee80211_ioctl_giwscan(struct net_device *dev,
 | 
			
		||||
				   struct iw_request_info *info,
 | 
			
		||||
				   struct iw_point *data, char *extra)
 | 
			
		||||
{
 | 
			
		||||
	int res;
 | 
			
		||||
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 | 
			
		||||
	struct ieee80211_sub_if_data *sdata;
 | 
			
		||||
 | 
			
		||||
	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 | 
			
		||||
 | 
			
		||||
	if (local->sw_scanning || local->hw_scanning)
 | 
			
		||||
		return -EAGAIN;
 | 
			
		||||
 | 
			
		||||
	res = ieee80211_scan_results(local, info, extra, data->length);
 | 
			
		||||
	if (res >= 0) {
 | 
			
		||||
		data->length = res;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	data->length = 0;
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int ieee80211_ioctl_siwrate(struct net_device *dev,
 | 
			
		||||
				  struct iw_request_info *info,
 | 
			
		||||
				  struct iw_param *rate, char *extra)
 | 
			
		||||
| 
						 | 
				
			
			@ -1165,8 +1114,8 @@ static const iw_handler ieee80211_handler[] =
 | 
			
		|||
	(iw_handler) ieee80211_ioctl_giwap,		/* SIOCGIWAP */
 | 
			
		||||
	(iw_handler) ieee80211_ioctl_siwmlme,		/* SIOCSIWMLME */
 | 
			
		||||
	(iw_handler) NULL,				/* SIOCGIWAPLIST */
 | 
			
		||||
	(iw_handler) ieee80211_ioctl_siwscan,		/* SIOCSIWSCAN */
 | 
			
		||||
	(iw_handler) ieee80211_ioctl_giwscan,		/* SIOCGIWSCAN */
 | 
			
		||||
	(iw_handler) cfg80211_wext_siwscan,		/* SIOCSIWSCAN */
 | 
			
		||||
	(iw_handler) cfg80211_wext_giwscan,		/* SIOCGIWSCAN */
 | 
			
		||||
	(iw_handler) ieee80211_ioctl_siwessid,		/* SIOCSIWESSID */
 | 
			
		||||
	(iw_handler) ieee80211_ioctl_giwessid,		/* SIOCGIWESSID */
 | 
			
		||||
	(iw_handler) NULL,				/* SIOCSIWNICKN */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o
 | 
			
		|||
obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
 | 
			
		||||
obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
 | 
			
		||||
 | 
			
		||||
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o
 | 
			
		||||
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o
 | 
			
		||||
cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o
 | 
			
		||||
cfg80211-$(CONFIG_NL80211) += nl80211.o
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -240,6 +240,8 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
 | 
			
		|||
	mutex_init(&drv->mtx);
 | 
			
		||||
	mutex_init(&drv->devlist_mtx);
 | 
			
		||||
	INIT_LIST_HEAD(&drv->netdev_list);
 | 
			
		||||
	spin_lock_init(&drv->bss_lock);
 | 
			
		||||
	INIT_LIST_HEAD(&drv->bss_list);
 | 
			
		||||
 | 
			
		||||
	device_initialize(&drv->wiphy.dev);
 | 
			
		||||
	drv->wiphy.dev.class = &ieee80211_class;
 | 
			
		||||
| 
						 | 
				
			
			@ -259,6 +261,9 @@ int wiphy_register(struct wiphy *wiphy)
 | 
			
		|||
	int i;
 | 
			
		||||
	u16 ifmodes = wiphy->interface_modes;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(wiphy->max_scan_ssids < 1))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* sanity check ifmodes */
 | 
			
		||||
	WARN_ON(!ifmodes);
 | 
			
		||||
	ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1;
 | 
			
		||||
| 
						 | 
				
			
			@ -367,8 +372,11 @@ EXPORT_SYMBOL(wiphy_unregister);
 | 
			
		|||
 | 
			
		||||
void cfg80211_dev_free(struct cfg80211_registered_device *drv)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_internal_bss *scan, *tmp;
 | 
			
		||||
	mutex_destroy(&drv->mtx);
 | 
			
		||||
	mutex_destroy(&drv->devlist_mtx);
 | 
			
		||||
	list_for_each_entry_safe(scan, tmp, &drv->bss_list, list)
 | 
			
		||||
		kfree(scan);
 | 
			
		||||
	kfree(drv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,8 @@
 | 
			
		|||
#include <linux/mutex.h>
 | 
			
		||||
#include <linux/list.h>
 | 
			
		||||
#include <linux/netdevice.h>
 | 
			
		||||
#include <linux/kref.h>
 | 
			
		||||
#include <linux/rbtree.h>
 | 
			
		||||
#include <net/genetlink.h>
 | 
			
		||||
#include <net/wireless.h>
 | 
			
		||||
#include <net/cfg80211.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +43,13 @@ struct cfg80211_registered_device {
 | 
			
		|||
	struct mutex devlist_mtx;
 | 
			
		||||
	struct list_head netdev_list;
 | 
			
		||||
 | 
			
		||||
	/* BSSes/scanning */
 | 
			
		||||
	spinlock_t bss_lock;
 | 
			
		||||
	struct list_head bss_list;
 | 
			
		||||
	struct rb_root bss_tree;
 | 
			
		||||
	u32 bss_generation;
 | 
			
		||||
	struct cfg80211_scan_request *scan_req; /* protected by RTNL */
 | 
			
		||||
 | 
			
		||||
	/* must be last because of the way we do wiphy_priv(),
 | 
			
		||||
	 * and it should at least be aligned to NETDEV_ALIGN */
 | 
			
		||||
	struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));
 | 
			
		||||
| 
						 | 
				
			
			@ -56,6 +65,15 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)
 | 
			
		|||
extern struct mutex cfg80211_drv_mutex;
 | 
			
		||||
extern struct list_head cfg80211_drv_list;
 | 
			
		||||
 | 
			
		||||
struct cfg80211_internal_bss {
 | 
			
		||||
	struct list_head list;
 | 
			
		||||
	struct rb_node rbn;
 | 
			
		||||
	unsigned long ts;
 | 
			
		||||
	struct kref ref;
 | 
			
		||||
	/* must be last because of priv member */
 | 
			
		||||
	struct cfg80211_bss pub;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This function returns a pointer to the driver
 | 
			
		||||
 * that the genl_info item that is passed refers to.
 | 
			
		||||
| 
						 | 
				
			
			@ -94,4 +112,6 @@ extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv,
 | 
			
		|||
void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
 | 
			
		||||
void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby);
 | 
			
		||||
 | 
			
		||||
void cfg80211_bss_expire(struct cfg80211_registered_device *dev);
 | 
			
		||||
 | 
			
		||||
#endif /* __NET_WIRELESS_CORE_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,7 @@
 | 
			
		|||
#include <linux/nl80211.h>
 | 
			
		||||
#include <linux/rtnetlink.h>
 | 
			
		||||
#include <linux/netlink.h>
 | 
			
		||||
#include <linux/etherdevice.h>
 | 
			
		||||
#include <net/genetlink.h>
 | 
			
		||||
#include <net/cfg80211.h>
 | 
			
		||||
#include "core.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +110,8 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
 | 
			
		|||
	[NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
 | 
			
		||||
	[NL80211_ATTR_IE] = { .type = NLA_BINARY,
 | 
			
		||||
			      .len = IEEE80211_MAX_DATA_LEN },
 | 
			
		||||
	[NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
 | 
			
		||||
	[NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* message building helper */
 | 
			
		||||
| 
						 | 
				
			
			@ -141,6 +144,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
 | 
			
		|||
 | 
			
		||||
	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
 | 
			
		||||
	NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
 | 
			
		||||
	NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
 | 
			
		||||
		   dev->wiphy.max_scan_ssids);
 | 
			
		||||
 | 
			
		||||
	nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
 | 
			
		||||
	if (!nl_modes)
 | 
			
		||||
| 
						 | 
				
			
			@ -2270,6 +2275,246 @@ static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb,
 | 
			
		|||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_registered_device *drv;
 | 
			
		||||
	struct net_device *dev;
 | 
			
		||||
	struct cfg80211_scan_request *request;
 | 
			
		||||
	struct cfg80211_ssid *ssid;
 | 
			
		||||
	struct ieee80211_channel *channel;
 | 
			
		||||
	struct nlattr *attr;
 | 
			
		||||
	struct wiphy *wiphy;
 | 
			
		||||
	int err, tmp, n_ssids = 0, n_channels = 0, i;
 | 
			
		||||
	enum ieee80211_band band;
 | 
			
		||||
 | 
			
		||||
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	wiphy = &drv->wiphy;
 | 
			
		||||
 | 
			
		||||
	if (!drv->ops->scan) {
 | 
			
		||||
		err = -EOPNOTSUPP;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rtnl_lock();
 | 
			
		||||
 | 
			
		||||
	if (drv->scan_req) {
 | 
			
		||||
		err = -EBUSY;
 | 
			
		||||
		goto out_unlock;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
 | 
			
		||||
		nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp)
 | 
			
		||||
			n_channels++;
 | 
			
		||||
		if (!n_channels) {
 | 
			
		||||
			err = -EINVAL;
 | 
			
		||||
			goto out_unlock;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		for (band = 0; band < IEEE80211_NUM_BANDS; band++)
 | 
			
		||||
			if (wiphy->bands[band])
 | 
			
		||||
				n_channels += wiphy->bands[band]->n_channels;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
 | 
			
		||||
		nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
 | 
			
		||||
			n_ssids++;
 | 
			
		||||
 | 
			
		||||
	if (n_ssids > wiphy->max_scan_ssids) {
 | 
			
		||||
		err = -EINVAL;
 | 
			
		||||
		goto out_unlock;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	request = kzalloc(sizeof(*request)
 | 
			
		||||
			+ sizeof(*ssid) * n_ssids
 | 
			
		||||
			+ sizeof(channel) * n_channels, GFP_KERNEL);
 | 
			
		||||
	if (!request) {
 | 
			
		||||
		err = -ENOMEM;
 | 
			
		||||
		goto out_unlock;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	request->channels = (void *)((char *)request + sizeof(*request));
 | 
			
		||||
	request->n_channels = n_channels;
 | 
			
		||||
	if (n_ssids)
 | 
			
		||||
		request->ssids = (void *)(request->channels + n_channels);
 | 
			
		||||
	request->n_ssids = n_ssids;
 | 
			
		||||
 | 
			
		||||
	if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
 | 
			
		||||
		/* user specified, bail out if channel not found */
 | 
			
		||||
		request->n_channels = n_channels;
 | 
			
		||||
		i = 0;
 | 
			
		||||
		nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
 | 
			
		||||
			request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr));
 | 
			
		||||
			if (!request->channels[i]) {
 | 
			
		||||
				err = -EINVAL;
 | 
			
		||||
				goto out_free;
 | 
			
		||||
			}
 | 
			
		||||
			i++;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		/* all channels */
 | 
			
		||||
		i = 0;
 | 
			
		||||
		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
 | 
			
		||||
			int j;
 | 
			
		||||
			if (!wiphy->bands[band])
 | 
			
		||||
				continue;
 | 
			
		||||
			for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
 | 
			
		||||
				request->channels[i] = &wiphy->bands[band]->channels[j];
 | 
			
		||||
				i++;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	i = 0;
 | 
			
		||||
	if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
 | 
			
		||||
		nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
 | 
			
		||||
			if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
 | 
			
		||||
				err = -EINVAL;
 | 
			
		||||
				goto out_free;
 | 
			
		||||
			}
 | 
			
		||||
			memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
 | 
			
		||||
			request->ssids[i].ssid_len = nla_len(attr);
 | 
			
		||||
			i++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	request->ifidx = dev->ifindex;
 | 
			
		||||
	request->wiphy = &drv->wiphy;
 | 
			
		||||
 | 
			
		||||
	drv->scan_req = request;
 | 
			
		||||
	err = drv->ops->scan(&drv->wiphy, dev, request);
 | 
			
		||||
 | 
			
		||||
 out_free:
 | 
			
		||||
	if (err) {
 | 
			
		||||
		drv->scan_req = NULL;
 | 
			
		||||
		kfree(request);
 | 
			
		||||
	}
 | 
			
		||||
 out_unlock:
 | 
			
		||||
	rtnl_unlock();
 | 
			
		||||
 out:
 | 
			
		||||
	cfg80211_put_dev(drv);
 | 
			
		||||
	dev_put(dev);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
 | 
			
		||||
			    struct cfg80211_registered_device *rdev,
 | 
			
		||||
			    struct net_device *dev,
 | 
			
		||||
			    struct cfg80211_bss *res)
 | 
			
		||||
{
 | 
			
		||||
	void *hdr;
 | 
			
		||||
	struct nlattr *bss;
 | 
			
		||||
 | 
			
		||||
	hdr = nl80211hdr_put(msg, pid, seq, flags,
 | 
			
		||||
			     NL80211_CMD_NEW_SCAN_RESULTS);
 | 
			
		||||
	if (!hdr)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION,
 | 
			
		||||
		    rdev->bss_generation);
 | 
			
		||||
	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
 | 
			
		||||
 | 
			
		||||
	bss = nla_nest_start(msg, NL80211_ATTR_BSS);
 | 
			
		||||
	if (!bss)
 | 
			
		||||
		goto nla_put_failure;
 | 
			
		||||
	if (!is_zero_ether_addr(res->bssid))
 | 
			
		||||
		NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
 | 
			
		||||
	if (res->information_elements && res->len_information_elements)
 | 
			
		||||
		NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
 | 
			
		||||
			res->len_information_elements,
 | 
			
		||||
			res->information_elements);
 | 
			
		||||
	if (res->tsf)
 | 
			
		||||
		NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
 | 
			
		||||
	if (res->beacon_interval)
 | 
			
		||||
		NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
 | 
			
		||||
	NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
 | 
			
		||||
	NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
 | 
			
		||||
 | 
			
		||||
	switch (res->signal_type) {
 | 
			
		||||
	case CFG80211_SIGNAL_TYPE_MBM:
 | 
			
		||||
		NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
 | 
			
		||||
		break;
 | 
			
		||||
	case CFG80211_SIGNAL_TYPE_UNSPEC:
 | 
			
		||||
		NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nla_nest_end(msg, bss);
 | 
			
		||||
 | 
			
		||||
	return genlmsg_end(msg, hdr);
 | 
			
		||||
 | 
			
		||||
 nla_put_failure:
 | 
			
		||||
	genlmsg_cancel(msg, hdr);
 | 
			
		||||
	return -EMSGSIZE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nl80211_dump_scan(struct sk_buff *skb,
 | 
			
		||||
			     struct netlink_callback *cb)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_registered_device *dev;
 | 
			
		||||
	struct net_device *netdev;
 | 
			
		||||
	struct cfg80211_internal_bss *scan;
 | 
			
		||||
	int ifidx = cb->args[0];
 | 
			
		||||
	int start = cb->args[1], idx = 0;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	if (!ifidx) {
 | 
			
		||||
		err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
 | 
			
		||||
				  nl80211_fam.attrbuf, nl80211_fam.maxattr,
 | 
			
		||||
				  nl80211_policy);
 | 
			
		||||
		if (err)
 | 
			
		||||
			return err;
 | 
			
		||||
 | 
			
		||||
		if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
 | 
			
		||||
		if (!ifidx)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		cb->args[0] = ifidx;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	netdev = dev_get_by_index(&init_net, ifidx);
 | 
			
		||||
	if (!netdev)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	dev = cfg80211_get_dev_from_ifindex(ifidx);
 | 
			
		||||
	if (IS_ERR(dev)) {
 | 
			
		||||
		err = PTR_ERR(dev);
 | 
			
		||||
		goto out_put_netdev;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&dev->bss_lock);
 | 
			
		||||
	cfg80211_bss_expire(dev);
 | 
			
		||||
 | 
			
		||||
	list_for_each_entry(scan, &dev->bss_list, list) {
 | 
			
		||||
		if (++idx <= start)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (nl80211_send_bss(skb,
 | 
			
		||||
				NETLINK_CB(cb->skb).pid,
 | 
			
		||||
				cb->nlh->nlmsg_seq, NLM_F_MULTI,
 | 
			
		||||
				dev, netdev, &scan->pub) < 0) {
 | 
			
		||||
			idx--;
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 out:
 | 
			
		||||
	spin_unlock_bh(&dev->bss_lock);
 | 
			
		||||
 | 
			
		||||
	cb->args[1] = idx;
 | 
			
		||||
	err = skb->len;
 | 
			
		||||
	cfg80211_put_dev(dev);
 | 
			
		||||
 out_put_netdev:
 | 
			
		||||
	dev_put(netdev);
 | 
			
		||||
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct genl_ops nl80211_ops[] = {
 | 
			
		||||
	{
 | 
			
		||||
		.cmd = NL80211_CMD_GET_WIPHY,
 | 
			
		||||
| 
						 | 
				
			
			@ -2443,12 +2688,26 @@ static struct genl_ops nl80211_ops[] = {
 | 
			
		|||
		.policy = nl80211_policy,
 | 
			
		||||
		.flags = GENL_ADMIN_PERM,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd = NL80211_CMD_TRIGGER_SCAN,
 | 
			
		||||
		.doit = nl80211_trigger_scan,
 | 
			
		||||
		.policy = nl80211_policy,
 | 
			
		||||
		.flags = GENL_ADMIN_PERM,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd = NL80211_CMD_GET_SCAN,
 | 
			
		||||
		.policy = nl80211_policy,
 | 
			
		||||
		.dumpit = nl80211_dump_scan,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* multicast groups */
 | 
			
		||||
static struct genl_multicast_group nl80211_config_mcgrp = {
 | 
			
		||||
	.name = "config",
 | 
			
		||||
};
 | 
			
		||||
static struct genl_multicast_group nl80211_scan_mcgrp = {
 | 
			
		||||
	.name = "scan",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* notification functions */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2468,6 +2727,66 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
 | 
			
		|||
	genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nl80211_send_scan_donemsg(struct sk_buff *msg,
 | 
			
		||||
				    struct cfg80211_registered_device *rdev,
 | 
			
		||||
				    struct net_device *netdev,
 | 
			
		||||
				    u32 pid, u32 seq, int flags,
 | 
			
		||||
				    u32 cmd)
 | 
			
		||||
{
 | 
			
		||||
	void *hdr;
 | 
			
		||||
 | 
			
		||||
	hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
 | 
			
		||||
	if (!hdr)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->idx);
 | 
			
		||||
	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
 | 
			
		||||
 | 
			
		||||
	/* XXX: we should probably bounce back the request? */
 | 
			
		||||
 | 
			
		||||
	return genlmsg_end(msg, hdr);
 | 
			
		||||
 | 
			
		||||
 nla_put_failure:
 | 
			
		||||
	genlmsg_cancel(msg, hdr);
 | 
			
		||||
	return -EMSGSIZE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
 | 
			
		||||
			    struct net_device *netdev)
 | 
			
		||||
{
 | 
			
		||||
	struct sk_buff *msg;
 | 
			
		||||
 | 
			
		||||
	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
 | 
			
		||||
				      NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
 | 
			
		||||
		nlmsg_free(msg);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
 | 
			
		||||
			       struct net_device *netdev)
 | 
			
		||||
{
 | 
			
		||||
	struct sk_buff *msg;
 | 
			
		||||
 | 
			
		||||
	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
 | 
			
		||||
				      NL80211_CMD_SCAN_ABORTED) < 0) {
 | 
			
		||||
		nlmsg_free(msg);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* initialisation/exit functions */
 | 
			
		||||
 | 
			
		||||
int nl80211_init(void)
 | 
			
		||||
| 
						 | 
				
			
			@ -2488,6 +2807,10 @@ int nl80211_init(void)
 | 
			
		|||
	if (err)
 | 
			
		||||
		goto err_out;
 | 
			
		||||
 | 
			
		||||
	err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
 | 
			
		||||
	if (err)
 | 
			
		||||
		goto err_out;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 err_out:
 | 
			
		||||
	genl_unregister_family(&nl80211_fam);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,10 @@
 | 
			
		|||
extern int nl80211_init(void);
 | 
			
		||||
extern void nl80211_exit(void);
 | 
			
		||||
extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev);
 | 
			
		||||
extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
 | 
			
		||||
				   struct net_device *netdev);
 | 
			
		||||
extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
 | 
			
		||||
				      struct net_device *netdev);
 | 
			
		||||
#else
 | 
			
		||||
static inline int nl80211_init(void)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -19,6 +23,10 @@ static inline void nl80211_notify_dev_rename(
 | 
			
		|||
	struct cfg80211_registered_device *rdev)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
static inline void
 | 
			
		||||
nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
 | 
			
		||||
		       struct net_device *netdev)
 | 
			
		||||
{}
 | 
			
		||||
#endif /* CONFIG_NL80211 */
 | 
			
		||||
 | 
			
		||||
#endif /* __NET_WIRELESS_NL80211_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										807
									
								
								net/wireless/scan.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										807
									
								
								net/wireless/scan.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,807 @@
 | 
			
		|||
/*
 | 
			
		||||
 * cfg80211 scan result handling
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
 | 
			
		||||
 */
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/netdevice.h>
 | 
			
		||||
#include <linux/wireless.h>
 | 
			
		||||
#include <linux/nl80211.h>
 | 
			
		||||
#include <linux/etherdevice.h>
 | 
			
		||||
#include <net/arp.h>
 | 
			
		||||
#include <net/cfg80211.h>
 | 
			
		||||
#include <net/iw_handler.h>
 | 
			
		||||
#include "core.h"
 | 
			
		||||
#include "nl80211.h"
 | 
			
		||||
 | 
			
		||||
#define IEEE80211_SCAN_RESULT_EXPIRE	(10 * HZ)
 | 
			
		||||
 | 
			
		||||
void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
 | 
			
		||||
{
 | 
			
		||||
	struct net_device *dev;
 | 
			
		||||
#ifdef CONFIG_WIRELESS_EXT
 | 
			
		||||
	union iwreq_data wrqu;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	dev = dev_get_by_index(&init_net, request->ifidx);
 | 
			
		||||
	if (!dev)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
 | 
			
		||||
	wiphy_to_dev(request->wiphy)->scan_req = NULL;
 | 
			
		||||
 | 
			
		||||
	if (aborted)
 | 
			
		||||
		nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev);
 | 
			
		||||
	else
 | 
			
		||||
		nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_WIRELESS_EXT
 | 
			
		||||
	if (!aborted) {
 | 
			
		||||
		memset(&wrqu, 0, sizeof(wrqu));
 | 
			
		||||
 | 
			
		||||
		wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	dev_put(dev);
 | 
			
		||||
 | 
			
		||||
 out:
 | 
			
		||||
	kfree(request);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(cfg80211_scan_done);
 | 
			
		||||
 | 
			
		||||
static void bss_release(struct kref *ref)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_internal_bss *bss;
 | 
			
		||||
 | 
			
		||||
	bss = container_of(ref, struct cfg80211_internal_bss, ref);
 | 
			
		||||
	kfree(bss);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* must hold dev->bss_lock! */
 | 
			
		||||
void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_internal_bss *bss, *tmp;
 | 
			
		||||
	bool expired = false;
 | 
			
		||||
 | 
			
		||||
	list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
 | 
			
		||||
		if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE))
 | 
			
		||||
			continue;
 | 
			
		||||
		list_del(&bss->list);
 | 
			
		||||
		rb_erase(&bss->rbn, &dev->bss_tree);
 | 
			
		||||
		kref_put(&bss->ref, bss_release);
 | 
			
		||||
		expired = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (expired)
 | 
			
		||||
		dev->bss_generation++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u8 *find_ie(u8 num, u8 *ies, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	while (len > 2 && ies[0] != num) {
 | 
			
		||||
		len -= ies[1] + 2;
 | 
			
		||||
		ies += ies[1] + 2;
 | 
			
		||||
	}
 | 
			
		||||
	if (len < 2)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	if (len < 2 + ies[1])
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return ies;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
 | 
			
		||||
{
 | 
			
		||||
	const u8 *ie1 = find_ie(num, ies1, len1);
 | 
			
		||||
	const u8 *ie2 = find_ie(num, ies2, len2);
 | 
			
		||||
	int r;
 | 
			
		||||
 | 
			
		||||
	if (!ie1 && !ie2)
 | 
			
		||||
		return 0;
 | 
			
		||||
	if (!ie1)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	r = memcmp(ie1 + 2, ie2 + 2, min(ie1[1], ie2[1]));
 | 
			
		||||
	if (r == 0 && ie1[1] != ie2[1])
 | 
			
		||||
		return ie2[1] - ie1[1];
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_bss(struct cfg80211_bss *a,
 | 
			
		||||
		   const u8 *bssid,
 | 
			
		||||
		   const u8 *ssid, size_t ssid_len)
 | 
			
		||||
{
 | 
			
		||||
	const u8 *ssidie;
 | 
			
		||||
 | 
			
		||||
	if (compare_ether_addr(a->bssid, bssid))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	ssidie = find_ie(WLAN_EID_SSID,
 | 
			
		||||
			 a->information_elements,
 | 
			
		||||
			 a->len_information_elements);
 | 
			
		||||
	if (!ssidie)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (ssidie[1] != ssid_len)
 | 
			
		||||
		return false;
 | 
			
		||||
	return memcmp(ssidie + 2, ssid, ssid_len) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_mesh(struct cfg80211_bss *a,
 | 
			
		||||
		    const u8 *meshid, size_t meshidlen,
 | 
			
		||||
		    const u8 *meshcfg)
 | 
			
		||||
{
 | 
			
		||||
	const u8 *ie;
 | 
			
		||||
 | 
			
		||||
	if (!is_zero_ether_addr(a->bssid))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	ie = find_ie(WLAN_EID_MESH_ID,
 | 
			
		||||
		     a->information_elements,
 | 
			
		||||
		     a->len_information_elements);
 | 
			
		||||
	if (!ie)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (ie[1] != meshidlen)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (memcmp(ie + 2, meshid, meshidlen))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	ie = find_ie(WLAN_EID_MESH_CONFIG,
 | 
			
		||||
		     a->information_elements,
 | 
			
		||||
		     a->len_information_elements);
 | 
			
		||||
	if (ie[1] != IEEE80211_MESH_CONFIG_LEN)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Ignore mesh capability (last two bytes of the IE) when
 | 
			
		||||
	 * comparing since that may differ between stations taking
 | 
			
		||||
	 * part in the same mesh.
 | 
			
		||||
	 */
 | 
			
		||||
	return memcmp(ie + 2, meshcfg, IEEE80211_MESH_CONFIG_LEN - 2) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cmp_bss(struct cfg80211_bss *a,
 | 
			
		||||
		   struct cfg80211_bss *b)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
 | 
			
		||||
	if (a->channel != b->channel)
 | 
			
		||||
		return b->channel->center_freq - a->channel->center_freq;
 | 
			
		||||
 | 
			
		||||
	r = memcmp(a->bssid, b->bssid, ETH_ALEN);
 | 
			
		||||
	if (r)
 | 
			
		||||
		return r;
 | 
			
		||||
 | 
			
		||||
	if (is_zero_ether_addr(a->bssid)) {
 | 
			
		||||
		r = cmp_ies(WLAN_EID_MESH_ID,
 | 
			
		||||
			    a->information_elements,
 | 
			
		||||
			    a->len_information_elements,
 | 
			
		||||
			    b->information_elements,
 | 
			
		||||
			    b->len_information_elements);
 | 
			
		||||
		if (r)
 | 
			
		||||
			return r;
 | 
			
		||||
		return cmp_ies(WLAN_EID_MESH_CONFIG,
 | 
			
		||||
			       a->information_elements,
 | 
			
		||||
			       a->len_information_elements,
 | 
			
		||||
			       b->information_elements,
 | 
			
		||||
			       b->len_information_elements);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmp_ies(WLAN_EID_SSID,
 | 
			
		||||
		       a->information_elements,
 | 
			
		||||
		       a->len_information_elements,
 | 
			
		||||
		       b->information_elements,
 | 
			
		||||
		       b->len_information_elements);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
 | 
			
		||||
				      struct ieee80211_channel *channel,
 | 
			
		||||
				      const u8 *bssid,
 | 
			
		||||
				      const u8 *ssid, size_t ssid_len)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
 | 
			
		||||
	struct cfg80211_internal_bss *bss, *res = NULL;
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&dev->bss_lock);
 | 
			
		||||
 | 
			
		||||
	list_for_each_entry(bss, &dev->bss_list, list) {
 | 
			
		||||
		if (channel && bss->pub.channel != channel)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
 | 
			
		||||
			res = bss;
 | 
			
		||||
			kref_get(&res->ref);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spin_unlock_bh(&dev->bss_lock);
 | 
			
		||||
	if (!res)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return &res->pub;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(cfg80211_get_bss);
 | 
			
		||||
 | 
			
		||||
struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy,
 | 
			
		||||
				       struct ieee80211_channel *channel,
 | 
			
		||||
				       const u8 *meshid, size_t meshidlen,
 | 
			
		||||
				       const u8 *meshcfg)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
 | 
			
		||||
	struct cfg80211_internal_bss *bss, *res = NULL;
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&dev->bss_lock);
 | 
			
		||||
 | 
			
		||||
	list_for_each_entry(bss, &dev->bss_list, list) {
 | 
			
		||||
		if (channel && bss->pub.channel != channel)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) {
 | 
			
		||||
			res = bss;
 | 
			
		||||
			kref_get(&res->ref);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spin_unlock_bh(&dev->bss_lock);
 | 
			
		||||
	if (!res)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return &res->pub;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(cfg80211_get_mesh);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void rb_insert_bss(struct cfg80211_registered_device *dev,
 | 
			
		||||
			  struct cfg80211_internal_bss *bss)
 | 
			
		||||
{
 | 
			
		||||
	struct rb_node **p = &dev->bss_tree.rb_node;
 | 
			
		||||
	struct rb_node *parent = NULL;
 | 
			
		||||
	struct cfg80211_internal_bss *tbss;
 | 
			
		||||
	int cmp;
 | 
			
		||||
 | 
			
		||||
	while (*p) {
 | 
			
		||||
		parent = *p;
 | 
			
		||||
		tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
 | 
			
		||||
 | 
			
		||||
		cmp = cmp_bss(&bss->pub, &tbss->pub);
 | 
			
		||||
 | 
			
		||||
		if (WARN_ON(!cmp)) {
 | 
			
		||||
			/* will sort of leak this BSS */
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (cmp < 0)
 | 
			
		||||
			p = &(*p)->rb_left;
 | 
			
		||||
		else
 | 
			
		||||
			p = &(*p)->rb_right;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rb_link_node(&bss->rbn, parent, p);
 | 
			
		||||
	rb_insert_color(&bss->rbn, &dev->bss_tree);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct cfg80211_internal_bss *
 | 
			
		||||
rb_find_bss(struct cfg80211_registered_device *dev,
 | 
			
		||||
	    struct cfg80211_internal_bss *res)
 | 
			
		||||
{
 | 
			
		||||
	struct rb_node *n = dev->bss_tree.rb_node;
 | 
			
		||||
	struct cfg80211_internal_bss *bss;
 | 
			
		||||
	int r;
 | 
			
		||||
 | 
			
		||||
	while (n) {
 | 
			
		||||
		bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
 | 
			
		||||
		r = cmp_bss(&res->pub, &bss->pub);
 | 
			
		||||
 | 
			
		||||
		if (r == 0)
 | 
			
		||||
			return bss;
 | 
			
		||||
		else if (r < 0)
 | 
			
		||||
			n = n->rb_left;
 | 
			
		||||
		else
 | 
			
		||||
			n = n->rb_right;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct cfg80211_internal_bss *
 | 
			
		||||
cfg80211_bss_update(struct cfg80211_registered_device *dev,
 | 
			
		||||
		    struct cfg80211_internal_bss *res,
 | 
			
		||||
		    bool overwrite)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_internal_bss *found = NULL;
 | 
			
		||||
	const u8 *meshid, *meshcfg;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The reference to "res" is donated to this function.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(!res->pub.channel)) {
 | 
			
		||||
		kref_put(&res->ref, bss_release);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res->ts = jiffies;
 | 
			
		||||
 | 
			
		||||
	if (is_zero_ether_addr(res->pub.bssid)) {
 | 
			
		||||
		/* must be mesh, verify */
 | 
			
		||||
		meshid = find_ie(WLAN_EID_MESH_ID, res->pub.information_elements,
 | 
			
		||||
				 res->pub.len_information_elements);
 | 
			
		||||
		meshcfg = find_ie(WLAN_EID_MESH_CONFIG,
 | 
			
		||||
				  res->pub.information_elements,
 | 
			
		||||
				  res->pub.len_information_elements);
 | 
			
		||||
		if (!meshid || !meshcfg ||
 | 
			
		||||
		    meshcfg[1] != IEEE80211_MESH_CONFIG_LEN) {
 | 
			
		||||
			/* bogus mesh */
 | 
			
		||||
			kref_put(&res->ref, bss_release);
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&dev->bss_lock);
 | 
			
		||||
 | 
			
		||||
	found = rb_find_bss(dev, res);
 | 
			
		||||
 | 
			
		||||
	if (found && overwrite) {
 | 
			
		||||
		list_replace(&found->list, &res->list);
 | 
			
		||||
		rb_replace_node(&found->rbn, &res->rbn,
 | 
			
		||||
				&dev->bss_tree);
 | 
			
		||||
		kref_put(&found->ref, bss_release);
 | 
			
		||||
		found = res;
 | 
			
		||||
	} else if (found) {
 | 
			
		||||
		kref_get(&found->ref);
 | 
			
		||||
		found->pub.beacon_interval = res->pub.beacon_interval;
 | 
			
		||||
		found->pub.tsf = res->pub.tsf;
 | 
			
		||||
		found->pub.signal = res->pub.signal;
 | 
			
		||||
		found->pub.signal_type = res->pub.signal_type;
 | 
			
		||||
		found->pub.capability = res->pub.capability;
 | 
			
		||||
		found->ts = res->ts;
 | 
			
		||||
		kref_put(&res->ref, bss_release);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* this "consumes" the reference */
 | 
			
		||||
		list_add_tail(&res->list, &dev->bss_list);
 | 
			
		||||
		rb_insert_bss(dev, res);
 | 
			
		||||
		found = res;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dev->bss_generation++;
 | 
			
		||||
	spin_unlock_bh(&dev->bss_lock);
 | 
			
		||||
 | 
			
		||||
	kref_get(&found->ref);
 | 
			
		||||
	return found;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct cfg80211_bss *
 | 
			
		||||
cfg80211_inform_bss_frame(struct wiphy *wiphy,
 | 
			
		||||
			  struct ieee80211_channel *channel,
 | 
			
		||||
			  struct ieee80211_mgmt *mgmt, size_t len,
 | 
			
		||||
			  s32 signal, enum cfg80211_signal_type sigtype,
 | 
			
		||||
			  gfp_t gfp)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_internal_bss *res;
 | 
			
		||||
	size_t ielen = len - offsetof(struct ieee80211_mgmt,
 | 
			
		||||
				      u.probe_resp.variable);
 | 
			
		||||
	bool overwrite;
 | 
			
		||||
	size_t privsz = wiphy->bss_priv_size;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(sigtype == NL80211_BSS_SIGNAL_UNSPEC &&
 | 
			
		||||
	            (signal < 0 || signal > 100)))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(!mgmt || !wiphy ||
 | 
			
		||||
		    len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
 | 
			
		||||
	if (!res)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN);
 | 
			
		||||
	res->pub.channel = channel;
 | 
			
		||||
	res->pub.signal_type = sigtype;
 | 
			
		||||
	res->pub.signal = signal;
 | 
			
		||||
	res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
 | 
			
		||||
	res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
 | 
			
		||||
	res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
 | 
			
		||||
	/* point to after the private area */
 | 
			
		||||
	res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz;
 | 
			
		||||
	memcpy(res->pub.information_elements, mgmt->u.probe_resp.variable, ielen);
 | 
			
		||||
	res->pub.len_information_elements = ielen;
 | 
			
		||||
 | 
			
		||||
	kref_init(&res->ref);
 | 
			
		||||
 | 
			
		||||
	overwrite = ieee80211_is_probe_resp(mgmt->frame_control);
 | 
			
		||||
 | 
			
		||||
	res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite);
 | 
			
		||||
	if (!res)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* cfg80211_bss_update gives us a referenced result */
 | 
			
		||||
	return &res->pub;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(cfg80211_inform_bss_frame);
 | 
			
		||||
 | 
			
		||||
void cfg80211_put_bss(struct cfg80211_bss *pub)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_internal_bss *bss;
 | 
			
		||||
 | 
			
		||||
	if (!pub)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	bss = container_of(pub, struct cfg80211_internal_bss, pub);
 | 
			
		||||
	kref_put(&bss->ref, bss_release);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(cfg80211_put_bss);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_WIRELESS_EXT
 | 
			
		||||
int cfg80211_wext_siwscan(struct net_device *dev,
 | 
			
		||||
			  struct iw_request_info *info,
 | 
			
		||||
			  union iwreq_data *wrqu, char *extra)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_registered_device *rdev;
 | 
			
		||||
	struct wiphy *wiphy;
 | 
			
		||||
	struct iw_scan_req *wreq = NULL;
 | 
			
		||||
	struct cfg80211_scan_request *creq;
 | 
			
		||||
	int i, err, n_channels = 0;
 | 
			
		||||
	enum ieee80211_band band;
 | 
			
		||||
 | 
			
		||||
	if (!netif_running(dev))
 | 
			
		||||
		return -ENETDOWN;
 | 
			
		||||
 | 
			
		||||
	rdev = cfg80211_get_dev_from_ifindex(dev->ifindex);
 | 
			
		||||
 | 
			
		||||
	if (IS_ERR(rdev))
 | 
			
		||||
		return PTR_ERR(rdev);
 | 
			
		||||
 | 
			
		||||
	if (rdev->scan_req) {
 | 
			
		||||
		err = -EBUSY;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wiphy = &rdev->wiphy;
 | 
			
		||||
 | 
			
		||||
	for (band = 0; band < IEEE80211_NUM_BANDS; band++)
 | 
			
		||||
		if (wiphy->bands[band])
 | 
			
		||||
			n_channels += wiphy->bands[band]->n_channels;
 | 
			
		||||
 | 
			
		||||
	creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
 | 
			
		||||
		       n_channels * sizeof(void *),
 | 
			
		||||
		       GFP_ATOMIC);
 | 
			
		||||
	if (!creq) {
 | 
			
		||||
		err = -ENOMEM;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	creq->wiphy = wiphy;
 | 
			
		||||
	creq->ifidx = dev->ifindex;
 | 
			
		||||
	creq->ssids = (void *)(creq + 1);
 | 
			
		||||
	creq->channels = (void *)(creq->ssids + 1);
 | 
			
		||||
	creq->n_channels = n_channels;
 | 
			
		||||
	creq->n_ssids = 1;
 | 
			
		||||
 | 
			
		||||
	/* all channels */
 | 
			
		||||
	i = 0;
 | 
			
		||||
	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
 | 
			
		||||
		int j;
 | 
			
		||||
		if (!wiphy->bands[band])
 | 
			
		||||
			continue;
 | 
			
		||||
		for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
 | 
			
		||||
			creq->channels[i] = &wiphy->bands[band]->channels[j];
 | 
			
		||||
			i++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* translate scan request */
 | 
			
		||||
	if (wrqu->data.length == sizeof(struct iw_scan_req)) {
 | 
			
		||||
		wreq = (struct iw_scan_req *)extra;
 | 
			
		||||
 | 
			
		||||
		if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
 | 
			
		||||
			if (wreq->essid_len > IEEE80211_MAX_SSID_LEN)
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len);
 | 
			
		||||
			creq->ssids[0].ssid_len = wreq->essid_len;
 | 
			
		||||
		}
 | 
			
		||||
		if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE)
 | 
			
		||||
			creq->n_ssids = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rdev->scan_req = creq;
 | 
			
		||||
	err = rdev->ops->scan(wiphy, dev, creq);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		rdev->scan_req = NULL;
 | 
			
		||||
		kfree(creq);
 | 
			
		||||
	}
 | 
			
		||||
 out:
 | 
			
		||||
	cfg80211_put_dev(rdev);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(cfg80211_wext_siwscan);
 | 
			
		||||
 | 
			
		||||
static void ieee80211_scan_add_ies(struct iw_request_info *info,
 | 
			
		||||
				   struct cfg80211_bss *bss,
 | 
			
		||||
				   char **current_ev, char *end_buf)
 | 
			
		||||
{
 | 
			
		||||
	u8 *pos, *end, *next;
 | 
			
		||||
	struct iw_event iwe;
 | 
			
		||||
 | 
			
		||||
	if (!bss->information_elements ||
 | 
			
		||||
	    !bss->len_information_elements)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If needed, fragment the IEs buffer (at IE boundaries) into short
 | 
			
		||||
	 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
 | 
			
		||||
	 */
 | 
			
		||||
	pos = bss->information_elements;
 | 
			
		||||
	end = pos + bss->len_information_elements;
 | 
			
		||||
 | 
			
		||||
	while (end - pos > IW_GENERIC_IE_MAX) {
 | 
			
		||||
		next = pos + 2 + pos[1];
 | 
			
		||||
		while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
 | 
			
		||||
			next = next + 2 + next[1];
 | 
			
		||||
 | 
			
		||||
		memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
		iwe.cmd = IWEVGENIE;
 | 
			
		||||
		iwe.u.data.length = next - pos;
 | 
			
		||||
		*current_ev = iwe_stream_add_point(info, *current_ev,
 | 
			
		||||
						   end_buf, &iwe, pos);
 | 
			
		||||
 | 
			
		||||
		pos = next;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (end > pos) {
 | 
			
		||||
		memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
		iwe.cmd = IWEVGENIE;
 | 
			
		||||
		iwe.u.data.length = end - pos;
 | 
			
		||||
		*current_ev = iwe_stream_add_point(info, *current_ev,
 | 
			
		||||
						   end_buf, &iwe, pos);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
ieee80211_bss(struct iw_request_info *info,
 | 
			
		||||
		      struct cfg80211_internal_bss *bss,
 | 
			
		||||
		      char *current_ev, char *end_buf)
 | 
			
		||||
{
 | 
			
		||||
	struct iw_event iwe;
 | 
			
		||||
	u8 *buf, *cfg, *p;
 | 
			
		||||
	u8 *ie = bss->pub.information_elements;
 | 
			
		||||
	int rem = bss->pub.len_information_elements, i;
 | 
			
		||||
	bool ismesh = false;
 | 
			
		||||
 | 
			
		||||
	memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
	iwe.cmd = SIOCGIWAP;
 | 
			
		||||
	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
 | 
			
		||||
	memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
 | 
			
		||||
	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
 | 
			
		||||
					  IW_EV_ADDR_LEN);
 | 
			
		||||
 | 
			
		||||
	memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
	iwe.cmd = SIOCGIWFREQ;
 | 
			
		||||
	iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
 | 
			
		||||
	iwe.u.freq.e = 0;
 | 
			
		||||
	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
 | 
			
		||||
					  IW_EV_FREQ_LEN);
 | 
			
		||||
 | 
			
		||||
	memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
	iwe.cmd = SIOCGIWFREQ;
 | 
			
		||||
	iwe.u.freq.m = bss->pub.channel->center_freq;
 | 
			
		||||
	iwe.u.freq.e = 6;
 | 
			
		||||
	current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
 | 
			
		||||
					  IW_EV_FREQ_LEN);
 | 
			
		||||
 | 
			
		||||
	if (bss->pub.signal_type != CFG80211_SIGNAL_TYPE_NONE) {
 | 
			
		||||
		memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
		iwe.cmd = IWEVQUAL;
 | 
			
		||||
		iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED |
 | 
			
		||||
				     IW_QUAL_NOISE_INVALID |
 | 
			
		||||
				     IW_QUAL_QUAL_INVALID;
 | 
			
		||||
		switch (bss->pub.signal_type) {
 | 
			
		||||
		case CFG80211_SIGNAL_TYPE_MBM:
 | 
			
		||||
			iwe.u.qual.level = bss->pub.signal / 100;
 | 
			
		||||
			iwe.u.qual.updated |= IW_QUAL_DBM;
 | 
			
		||||
			break;
 | 
			
		||||
		case CFG80211_SIGNAL_TYPE_UNSPEC:
 | 
			
		||||
			iwe.u.qual.level = bss->pub.signal;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* not reached */
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
 | 
			
		||||
						  &iwe, IW_EV_QUAL_LEN);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
	iwe.cmd = SIOCGIWENCODE;
 | 
			
		||||
	if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY)
 | 
			
		||||
		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
 | 
			
		||||
	else
 | 
			
		||||
		iwe.u.data.flags = IW_ENCODE_DISABLED;
 | 
			
		||||
	iwe.u.data.length = 0;
 | 
			
		||||
	current_ev = iwe_stream_add_point(info, current_ev, end_buf,
 | 
			
		||||
					  &iwe, "");
 | 
			
		||||
 | 
			
		||||
	while (rem >= 2) {
 | 
			
		||||
		/* invalid data */
 | 
			
		||||
		if (ie[1] > rem - 2)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		switch (ie[0]) {
 | 
			
		||||
		case WLAN_EID_SSID:
 | 
			
		||||
			memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
			iwe.cmd = SIOCGIWESSID;
 | 
			
		||||
			iwe.u.data.length = ie[1];
 | 
			
		||||
			iwe.u.data.flags = 1;
 | 
			
		||||
			current_ev = iwe_stream_add_point(info, current_ev, end_buf,
 | 
			
		||||
							  &iwe, ie + 2);
 | 
			
		||||
			break;
 | 
			
		||||
		case WLAN_EID_MESH_ID:
 | 
			
		||||
			memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
			iwe.cmd = SIOCGIWESSID;
 | 
			
		||||
			iwe.u.data.length = ie[1];
 | 
			
		||||
			iwe.u.data.flags = 1;
 | 
			
		||||
			current_ev = iwe_stream_add_point(info, current_ev, end_buf,
 | 
			
		||||
							  &iwe, ie + 2);
 | 
			
		||||
			break;
 | 
			
		||||
		case WLAN_EID_MESH_CONFIG:
 | 
			
		||||
			ismesh = true;
 | 
			
		||||
			if (ie[1] != IEEE80211_MESH_CONFIG_LEN)
 | 
			
		||||
				break;
 | 
			
		||||
			buf = kmalloc(50, GFP_ATOMIC);
 | 
			
		||||
			if (!buf)
 | 
			
		||||
				break;
 | 
			
		||||
			cfg = ie + 2;
 | 
			
		||||
			memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
			iwe.cmd = IWEVCUSTOM;
 | 
			
		||||
			sprintf(buf, "Mesh network (version %d)", cfg[0]);
 | 
			
		||||
			iwe.u.data.length = strlen(buf);
 | 
			
		||||
			current_ev = iwe_stream_add_point(info, current_ev,
 | 
			
		||||
							  end_buf,
 | 
			
		||||
							  &iwe, buf);
 | 
			
		||||
			sprintf(buf, "Path Selection Protocol ID: "
 | 
			
		||||
				"0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3],
 | 
			
		||||
							cfg[4]);
 | 
			
		||||
			iwe.u.data.length = strlen(buf);
 | 
			
		||||
			current_ev = iwe_stream_add_point(info, current_ev,
 | 
			
		||||
							  end_buf,
 | 
			
		||||
							  &iwe, buf);
 | 
			
		||||
			sprintf(buf, "Path Selection Metric ID: "
 | 
			
		||||
				"0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7],
 | 
			
		||||
							cfg[8]);
 | 
			
		||||
			iwe.u.data.length = strlen(buf);
 | 
			
		||||
			current_ev = iwe_stream_add_point(info, current_ev,
 | 
			
		||||
							  end_buf,
 | 
			
		||||
							  &iwe, buf);
 | 
			
		||||
			sprintf(buf, "Congestion Control Mode ID: "
 | 
			
		||||
				"0x%02X%02X%02X%02X", cfg[9], cfg[10],
 | 
			
		||||
							cfg[11], cfg[12]);
 | 
			
		||||
			iwe.u.data.length = strlen(buf);
 | 
			
		||||
			current_ev = iwe_stream_add_point(info, current_ev,
 | 
			
		||||
							  end_buf,
 | 
			
		||||
							  &iwe, buf);
 | 
			
		||||
			sprintf(buf, "Channel Precedence: "
 | 
			
		||||
				"0x%02X%02X%02X%02X", cfg[13], cfg[14],
 | 
			
		||||
							cfg[15], cfg[16]);
 | 
			
		||||
			iwe.u.data.length = strlen(buf);
 | 
			
		||||
			current_ev = iwe_stream_add_point(info, current_ev,
 | 
			
		||||
							  end_buf,
 | 
			
		||||
							  &iwe, buf);
 | 
			
		||||
			kfree(buf);
 | 
			
		||||
			break;
 | 
			
		||||
		case WLAN_EID_SUPP_RATES:
 | 
			
		||||
		case WLAN_EID_EXT_SUPP_RATES:
 | 
			
		||||
			/* display all supported rates in readable format */
 | 
			
		||||
			p = current_ev + iwe_stream_lcp_len(info);
 | 
			
		||||
 | 
			
		||||
			memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
			iwe.cmd = SIOCGIWRATE;
 | 
			
		||||
			/* Those two flags are ignored... */
 | 
			
		||||
			iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
 | 
			
		||||
 | 
			
		||||
			for (i = 0; i < ie[1]; i++) {
 | 
			
		||||
				iwe.u.bitrate.value =
 | 
			
		||||
					((ie[i + 2] & 0x7f) * 500000);
 | 
			
		||||
				p = iwe_stream_add_value(info, current_ev, p,
 | 
			
		||||
						end_buf, &iwe, IW_EV_PARAM_LEN);
 | 
			
		||||
			}
 | 
			
		||||
			current_ev = p;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		rem -= ie[1] + 2;
 | 
			
		||||
		ie += ie[1] + 2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)
 | 
			
		||||
	    || ismesh) {
 | 
			
		||||
		memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
		iwe.cmd = SIOCGIWMODE;
 | 
			
		||||
		if (ismesh)
 | 
			
		||||
			iwe.u.mode = IW_MODE_MESH;
 | 
			
		||||
		else if (bss->pub.capability & WLAN_CAPABILITY_ESS)
 | 
			
		||||
			iwe.u.mode = IW_MODE_MASTER;
 | 
			
		||||
		else
 | 
			
		||||
			iwe.u.mode = IW_MODE_ADHOC;
 | 
			
		||||
		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
 | 
			
		||||
						  &iwe, IW_EV_UINT_LEN);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf = kmalloc(30, GFP_ATOMIC);
 | 
			
		||||
	if (buf) {
 | 
			
		||||
		memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
		iwe.cmd = IWEVCUSTOM;
 | 
			
		||||
		sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf));
 | 
			
		||||
		iwe.u.data.length = strlen(buf);
 | 
			
		||||
		current_ev = iwe_stream_add_point(info, current_ev, end_buf,
 | 
			
		||||
						  &iwe, buf);
 | 
			
		||||
		memset(&iwe, 0, sizeof(iwe));
 | 
			
		||||
		iwe.cmd = IWEVCUSTOM;
 | 
			
		||||
		sprintf(buf, " Last beacon: %dms ago",
 | 
			
		||||
			jiffies_to_msecs(jiffies - bss->ts));
 | 
			
		||||
		iwe.u.data.length = strlen(buf);
 | 
			
		||||
		current_ev = iwe_stream_add_point(info, current_ev,
 | 
			
		||||
						  end_buf, &iwe, buf);
 | 
			
		||||
		kfree(buf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ieee80211_scan_add_ies(info, &bss->pub, ¤t_ev, end_buf);
 | 
			
		||||
 | 
			
		||||
	return current_ev;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int ieee80211_scan_results(struct cfg80211_registered_device *dev,
 | 
			
		||||
				  struct iw_request_info *info,
 | 
			
		||||
				  char *buf, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	char *current_ev = buf;
 | 
			
		||||
	char *end_buf = buf + len;
 | 
			
		||||
	struct cfg80211_internal_bss *bss;
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&dev->bss_lock);
 | 
			
		||||
	cfg80211_bss_expire(dev);
 | 
			
		||||
 | 
			
		||||
	list_for_each_entry(bss, &dev->bss_list, list) {
 | 
			
		||||
		if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
 | 
			
		||||
			spin_unlock_bh(&dev->bss_lock);
 | 
			
		||||
			return -E2BIG;
 | 
			
		||||
		}
 | 
			
		||||
		current_ev = ieee80211_bss(info, bss,
 | 
			
		||||
						   current_ev, end_buf);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock_bh(&dev->bss_lock);
 | 
			
		||||
	return current_ev - buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int cfg80211_wext_giwscan(struct net_device *dev,
 | 
			
		||||
			  struct iw_request_info *info,
 | 
			
		||||
			  struct iw_point *data, char *extra)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_registered_device *rdev;
 | 
			
		||||
	int res;
 | 
			
		||||
 | 
			
		||||
	if (!netif_running(dev))
 | 
			
		||||
		return -ENETDOWN;
 | 
			
		||||
 | 
			
		||||
	rdev = cfg80211_get_dev_from_ifindex(dev->ifindex);
 | 
			
		||||
 | 
			
		||||
	if (IS_ERR(rdev))
 | 
			
		||||
		return PTR_ERR(rdev);
 | 
			
		||||
 | 
			
		||||
	if (rdev->scan_req) {
 | 
			
		||||
		res = -EAGAIN;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res = ieee80211_scan_results(rdev, info, extra, data->length);
 | 
			
		||||
	data->length = 0;
 | 
			
		||||
	if (res >= 0) {
 | 
			
		||||
		data->length = res;
 | 
			
		||||
		res = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 out:
 | 
			
		||||
	cfg80211_put_dev(rdev);
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(cfg80211_wext_giwscan);
 | 
			
		||||
#endif
 | 
			
		||||
		Loading…
	
		Reference in a new issue