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
					
				| @ -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 @@ fail_workqueue: | ||||
| 		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 @@ dont_join: | ||||
| 			      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 New Issue
	
	Block a user