Commit 2a519311 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

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: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 849b7967
...@@ -2678,11 +2678,19 @@ static void iwl_bss_info_changed(struct ieee80211_hw *hw, ...@@ -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; unsigned long flags;
struct iwl_priv *priv = hw->priv; struct iwl_priv *priv = hw->priv;
int ret; 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"); 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) ...@@ -2718,7 +2726,7 @@ static int iwl_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t ssid_len)
if (ssid_len) { if (ssid_len) {
priv->one_direct_scan = 1; 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); memcpy(priv->direct_ssid, ssid, priv->direct_ssid_len);
} else { } else {
priv->one_direct_scan = 0; priv->one_direct_scan = 0;
......
...@@ -1271,6 +1271,7 @@ int iwl_setup_mac(struct iwl_priv *priv) ...@@ -1271,6 +1271,7 @@ int iwl_setup_mac(struct iwl_priv *priv)
BIT(NL80211_IFTYPE_ADHOC); BIT(NL80211_IFTYPE_ADHOC);
hw->wiphy->custom_regulatory = true; hw->wiphy->custom_regulatory = true;
hw->wiphy->max_scan_ssids = 1;
/* Default value; 4 EDCA QOS priorities */ /* Default value; 4 EDCA QOS priorities */
hw->queues = 4; hw->queues = 4;
......
...@@ -860,7 +860,7 @@ void iwl_bg_scan_completed(struct work_struct *work) ...@@ -860,7 +860,7 @@ void iwl_bg_scan_completed(struct work_struct *work)
if (test_bit(STATUS_EXIT_PENDING, &priv->status)) if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return; return;
ieee80211_scan_completed(priv->hw); ieee80211_scan_completed(priv->hw, false);
/* Since setting the TXPOWER may have been deferred while /* Since setting the TXPOWER may have been deferred while
* performing the scan, fire one off */ * performing the scan, fire one off */
......
...@@ -4442,15 +4442,23 @@ static void iwl3945_bss_info_changed(struct ieee80211_hw *hw, ...@@ -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; int rc = 0;
unsigned long flags; unsigned long flags;
struct iwl_priv *priv = hw->priv; struct iwl_priv *priv = hw->priv;
size_t len = 0;
u8 *ssid = NULL;
DECLARE_SSID_BUF(ssid_buf); DECLARE_SSID_BUF(ssid_buf);
IWL_DEBUG_MAC80211(priv, "enter\n"); 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); mutex_lock(&priv->mutex);
spin_lock_irqsave(&priv->lock, flags); 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) ...@@ -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); print_ssid(ssid_buf, ssid, len), len);
priv->one_direct_scan = 1; priv->one_direct_scan = 1;
priv->direct_ssid_len = (u8) priv->direct_ssid_len = len;
min((u8) len, (u8) IW_ESSID_MAX_SIZE); memcpy(priv->direct_ssid, ssid, len);
memcpy(priv->direct_ssid, ssid, priv->direct_ssid_len);
} else } else
priv->one_direct_scan = 0; priv->one_direct_scan = 0;
...@@ -5412,6 +5419,8 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e ...@@ -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->custom_regulatory = true;
hw->wiphy->max_scan_ssids = 1;
/* 4 EDCA QOS priorities */ /* 4 EDCA QOS priorities */
hw->queues = 4; hw->queues = 4;
......
...@@ -143,6 +143,13 @@ ...@@ -143,6 +143,13 @@
* added to all specified management frames generated by * added to all specified management frames generated by
* kernel/firmware/driver. * 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_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use * @__NL80211_CMD_AFTER_LAST: internal use
*/ */
...@@ -192,6 +199,11 @@ enum nl80211_commands { ...@@ -192,6 +199,11 @@ enum nl80211_commands {
NL80211_CMD_GET_REG, 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 */ /* add new commands above here */
/* used to define NL80211_CMD_MAX below */ /* used to define NL80211_CMD_MAX below */
...@@ -305,6 +317,18 @@ enum nl80211_commands { ...@@ -305,6 +317,18 @@ enum nl80211_commands {
* @NL80211_ATTR_IE: Information element(s) data (used, e.g., with * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with
* %NL80211_CMD_SET_MGMT_EXTRA_IE). * %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_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
*/ */
...@@ -372,6 +396,13 @@ enum nl80211_attrs { ...@@ -372,6 +396,13 @@ enum nl80211_attrs {
NL80211_ATTR_MGMT_SUBTYPE, NL80211_ATTR_MGMT_SUBTYPE,
NL80211_ATTR_IE, 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 */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
...@@ -841,4 +872,38 @@ enum nl80211_channel_type { ...@@ -841,4 +872,38 @@ enum nl80211_channel_type {
NL80211_CHAN_HT40MINUS, NL80211_CHAN_HT40MINUS,
NL80211_CHAN_HT40PLUS 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 */ #endif /* __LINUX_NL80211_H */
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/nl80211.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> #include <net/genetlink.h>
/* remove once we remove the wext stuff */ /* remove once we remove the wext stuff */
#include <net/iw_handler.h> #include <net/iw_handler.h>
...@@ -504,6 +508,83 @@ struct wiphy; ...@@ -504,6 +508,83 @@ struct wiphy;
/* from net/ieee80211.h */ /* from net/ieee80211.h */
struct ieee80211_channel; 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 * struct cfg80211_ops - backend description for wireless configuration
* *
...@@ -571,6 +652,11 @@ struct ieee80211_channel; ...@@ -571,6 +652,11 @@ struct ieee80211_channel;
* @set_channel: Set channel * @set_channel: Set channel
* *
* @set_mgmt_extra_ie: Set extra IE data for management frames * @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 { struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy); int (*suspend)(struct wiphy *wiphy);
...@@ -648,6 +734,9 @@ struct cfg80211_ops { ...@@ -648,6 +734,9 @@ struct cfg80211_ops {
int (*set_mgmt_extra_ie)(struct wiphy *wiphy, int (*set_mgmt_extra_ie)(struct wiphy *wiphy,
struct net_device *dev, struct net_device *dev,
struct mgmt_extra_ie_params *params); struct mgmt_extra_ie_params *params);
int (*scan)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_scan_request *request);
}; };
/* temporary wext handlers */ /* temporary wext handlers */
...@@ -658,5 +747,47 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, ...@@ -658,5 +747,47 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
u32 *mode, char *extra); u32 *mode, char *extra);
int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
u32 *mode, char *extra); 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 */ #endif /* __NET_CFG80211_H */
...@@ -1406,7 +1406,8 @@ struct ieee80211_ops { ...@@ -1406,7 +1406,8 @@ struct ieee80211_ops {
void (*update_tkip_key)(struct ieee80211_hw *hw, void (*update_tkip_key)(struct ieee80211_hw *hw,
struct ieee80211_key_conf *conf, const u8 *address, struct ieee80211_key_conf *conf, const u8 *address,
u32 iv32, u16 *phase1key); 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, int (*get_stats)(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats); struct ieee80211_low_level_stats *stats);
void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx, void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx,
...@@ -1844,8 +1845,9 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw); ...@@ -1844,8 +1845,9 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw);
* mac80211 that the scan finished. * mac80211 that the scan finished.
* *
* @hw: the hardware that finished the scan * @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 * ieee80211_iterate_active_interfaces - iterate active interfaces
......
...@@ -213,6 +213,9 @@ struct wiphy { ...@@ -213,6 +213,9 @@ struct wiphy {
bool custom_regulatory; bool custom_regulatory;
bool strict_regulatory; bool strict_regulatory;
int bss_priv_size;
u8 max_scan_ssids;
/* If multiple wiphys are registered and you're handed e.g. /* If multiple wiphys are registered and you're handed e.g.
* a regular netdev with assigned ieee80211_ptr, you won't * a regular netdev with assigned ieee80211_ptr, you won't
* know whether it points to a wiphy your driver has registered * know whether it points to a wiphy your driver has registered
......
...@@ -1277,6 +1277,25 @@ static int ieee80211_resume(struct wiphy *wiphy) ...@@ -1277,6 +1277,25 @@ static int ieee80211_resume(struct wiphy *wiphy)
#define ieee80211_resume NULL #define ieee80211_resume NULL
#endif #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 = { struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface, .add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface, .del_virtual_intf = ieee80211_del_iface,
...@@ -1309,4 +1328,5 @@ struct cfg80211_ops mac80211_config_ops = { ...@@ -1309,4 +1328,5 @@ struct cfg80211_ops mac80211_config_ops = {
.set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie, .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie,
.suspend = ieee80211_suspend, .suspend = ieee80211_suspend,
.resume = ieee80211_resume, .resume = ieee80211_resume,
.scan = ieee80211_scan,
}; };
...@@ -294,8 +294,6 @@ struct ieee80211_if_sta { ...@@ -294,8 +294,6 @@ struct ieee80211_if_sta {
u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid[IEEE80211_MAX_SSID_LEN];
enum ieee80211_sta_mlme_state state; enum ieee80211_sta_mlme_state state;
size_t ssid_len; size_t ssid_len;
u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
size_t scan_ssid_len;
u16 aid; u16 aid;
u16 ap_capab, capab; u16 ap_capab, capab;
u8 *extra_ie; /* to be added to the end of AssocReq */ u8 *extra_ie; /* to be added to the end of AssocReq */
...@@ -658,17 +656,18 @@ struct ieee80211_local { ...@@ -658,17 +656,18 @@ struct ieee80211_local {
/* Scanning and BSS list */ /* Scanning and BSS list */
bool sw_scanning, hw_scanning; 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; int scan_channel_idx;
enum ieee80211_band scan_band;
enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
unsigned long last_scan_completed; unsigned long last_scan_completed;
struct delayed_work scan_work; struct delayed_work scan_work;
struct ieee80211_sub_if_data *scan_sdata; struct ieee80211_sub_if_data *scan_sdata;
struct ieee80211_channel *oper_channel, *scan_channel, *csa_channel;
enum nl80211_channel_type oper_channel_type; enum nl80211_channel_type oper_channel_type;
u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; struct ieee80211_channel *oper_channel, *csa_channel;
size_t scan_ssid_len;
struct list_head bss_list; struct list_head bss_list;
struct ieee80211_bss *bss_hash[STA_HASH_SIZE]; struct ieee80211_bss *bss_hash[STA_HASH_SIZE];
spinlock_t bss_lock; spinlock_t bss_lock;
...@@ -929,7 +928,7 @@ void ieee80211_send_pspoll(struct ieee80211_local *local, ...@@ -929,7 +928,7 @@ void ieee80211_send_pspoll(struct ieee80211_local *local,
/* scan/BSS handling */ /* scan/BSS handling */
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, 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, int ieee80211_scan_results(struct ieee80211_local *local,
struct iw_request_info *info, struct iw_request_info *info,
char *buf, size_t len); char *buf, size_t len);
...@@ -944,14 +943,15 @@ int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, ...@@ -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); void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local);
int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, 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 * struct ieee80211_bss *
ieee80211_bss_info_update(struct ieee80211_local *local, ieee80211_bss_info_update(struct ieee80211_local *local,
struct ieee80211_rx_status *rx_status, struct ieee80211_rx_status *rx_status,
struct ieee80211_mgmt *mgmt, struct ieee80211_mgmt *mgmt,
size_t len, size_t len,
struct ieee802_11_elems *elems, struct ieee802_11_elems *elems,
int freq, bool beacon); struct ieee80211_channel *channel,
bool beacon);
struct ieee80211_bss * struct ieee80211_bss *
ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq, ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq,
u8 *ssid, u8 ssid_len); u8 *ssid, u8 ssid_len);
......
...@@ -522,7 +522,7 @@ static int ieee80211_stop(struct net_device *dev) ...@@ -522,7 +522,7 @@ static int ieee80211_stop(struct net_device *dev)
* scan event to userspace -- the scan is incomplete. * scan event to userspace -- the scan is incomplete.
*/ */
if (local->sw_scanning) if (local->sw_scanning)
ieee80211_scan_completed(&local->hw); ieee80211_scan_completed(&local->hw, true);
} }
conf.vif = &sdata->vif; conf.vif = &sdata->vif;
......
...@@ -733,6 +733,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, ...@@ -733,6 +733,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
return NULL; return NULL;
wiphy->privid = mac80211_wiphy_privid; wiphy->privid = mac80211_wiphy_privid;
wiphy->max_scan_ssids = 4;
local = wiphy_priv(wiphy); local = wiphy_priv(wiphy);
local->hw.wiphy = wiphy; local->hw.wiphy = wiphy;
...@@ -817,25 +818,33 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ...@@ -817,25 +818,33 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
enum ieee80211_band band; enum ieee80211_band band;
struct net_device *mdev; struct net_device *mdev;
struct ieee80211_master_priv *mpriv; struct ieee80211_master_priv *mpriv;
int channels, i, j;
/* /*
* generic code guarantees at least one band, * generic code guarantees at least one band,
* set this very early because much code assumes * set this very early because much code assumes
* that hw.conf.channel is assigned * that hw.conf.channel is assigned
*/ */
channels = 0;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) { for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
if (sband) { if (sband && !local->oper_channel) {
/* init channel we're on */ /* init channel we're on */
local->hw.conf.channel = local->hw.conf.channel =
local->oper_channel = local->oper_channel =
local->scan_channel = &sband->channels[0]; 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 low-level driver supports AP, we also support VLAN */
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP))
local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN);
...@@ -845,7 +854,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ...@@ -845,7 +854,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
result = wiphy_register(local->hw.wiphy); result = wiphy_register(local->hw.wiphy);
if (result < 0) if (result < 0)
return result; goto fail_wiphy_register;
/* /*
* We use the number of queues for feature tests (QoS, HT) internally * We use the number of queues for feature tests (QoS, HT) internally
...@@ -948,6 +957,20 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ...@@ -948,6 +957,20 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
ieee80211_led_init(local); 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; return 0;
fail_wep: fail_wep:
...@@ -966,6 +989,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ...@@ -966,6 +989,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
free_netdev(local->mdev); free_netdev(local->mdev);
fail_mdev_alloc: fail_mdev_alloc:
wiphy_unregister(local->hw.wiphy); wiphy_unregister(local->hw.wiphy);
fail_wiphy_register:
kfree(local->int_scan_req.channels);
return result; return result;
} }
EXPORT_SYMBOL(ieee80211_register_hw); EXPORT_SYMBOL(ieee80211_register_hw);
...@@ -1011,6 +1036,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) ...@@ -1011,6 +1036,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
ieee80211_wep_free(local); ieee80211_wep_free(local);
ieee80211_led_exit(local); ieee80211_led_exit(local);
free_netdev(local->mdev); free_netdev(local->mdev);
kfree(local->int_scan_req.channels);
} }
EXPORT_SYMBOL(ieee80211_unregister_hw); EXPORT_SYMBOL(ieee80211_unregister_hw);
......
...@@ -1743,7 +1743,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ...@@ -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, bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
freq, beacon); channel, beacon);
if (!bss) if (!bss)
return; return;
...@@ -2162,7 +2162,15 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata, ...@@ -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 " printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other "
"IBSS networks with same SSID (merge)\n", sdata->dev->name); "IBSS networks with same SSID (merge)\n", sdata->dev->name);
ieee80211_request_scan(sdata, ifsta->ssid, ifsta->ssid_len);
/* XXX maybe racy? */
if (sdata->local->scan_req)
return;
memcpy(sdata->local->int_scan_req.ssids[0].ssid,
ifsta->ssid, IEEE80211_MAX_SSID_LEN);
sdata->local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len;
ieee80211_request_scan(sdata, &sdata->local->int_scan_req);
} }
...@@ -2378,8 +2386,15 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata, ...@@ -2378,8 +2386,15 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
IEEE80211_SCAN_INTERVAL)) { IEEE80211_SCAN_INTERVAL)) {
printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to " printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to "
"join\n", sdata->dev->name); "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) { } else if (ifsta->state != IEEE80211_STA_MLME_IBSS_JOINED) {
int interval = IEEE80211_SCAN_INTERVAL; int interval = IEEE80211_SCAN_INTERVAL;
...@@ -2478,11 +2493,16 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata, ...@@ -2478,11 +2493,16 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
} else { } else {
if (ifsta->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) { if (ifsta->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) {
ifsta->assoc_scan_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) if (ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL)
ieee80211_start_scan(sdata, NULL, 0); local->int_scan_req.ssids[0].ssid_len = 0;
else else
ieee80211_start_scan(sdata, ifsta->ssid, local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len;
ifsta->ssid_len); ieee80211_start_scan(sdata, &local->int_scan_req);
ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE; ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request); set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
} else { } else {
...@@ -2520,8 +2540,7 @@ static void ieee80211_sta_work(struct work_struct *work) ...@@ -2520,8 +2540,7 @@ static void ieee80211_sta_work(struct work_struct *work)
ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE && ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE &&
ifsta->state != IEEE80211_STA_MLME_ASSOCIATE && ifsta->state != IEEE80211_STA_MLME_ASSOCIATE &&
test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) { test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) {
ieee80211_start_scan(sdata, ifsta->scan_ssid, ieee80211_start_scan(sdata, local->scan_req);
ifsta->scan_ssid_len);
return; return;
} }
......
This diff is collapsed.
...@@ -173,8 +173,9 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev, ...@@ -173,8 +173,9 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev,
range->num_encoding_sizes = 2; range->num_encoding_sizes = 2;
range->max_encoding_tokens = NUM_DEFAULT_KEYS; range->max_encoding_tokens = NUM_DEFAULT_KEYS;
/* cfg80211 requires this, and enforces 0..100 */
if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) 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) else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
range->max_qual.level = -110; range->max_qual.level = -110;
else else
...@@ -415,58 +416,6 @@ static int ieee80211_ioctl_giwap(struct net_device *dev, ...@@ -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, static int ieee80211_ioctl_siwrate(struct net_device *dev,
struct iw_request_info *info, struct iw_request_info *info,
struct iw_param *rate, char *extra) struct iw_param *rate, char *extra)
...@@ -1165,8 +1114,8 @@ static const iw_handler ieee80211_handler[] = ...@@ -1165,8 +1114,8 @@ static const iw_handler ieee80211_handler[] =
(iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */ (iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */
(iw_handler) ieee80211_ioctl_siwmlme, /* SIOCSIWMLME */ (iw_handler) ieee80211_ioctl_siwmlme, /* SIOCSIWMLME */
(iw_handler) NULL, /* SIOCGIWAPLIST */ (iw_handler) NULL, /* SIOCGIWAPLIST */
(iw_handler) ieee80211_ioctl_siwscan, /* SIOCSIWSCAN */ (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */
(iw_handler) ieee80211_ioctl_giwscan, /* SIOCGIWSCAN */ (iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */
(iw_handler) ieee80211_ioctl_siwessid, /* SIOCSIWESSID */ (iw_handler) ieee80211_ioctl_siwessid, /* SIOCSIWESSID */
(iw_handler) ieee80211_ioctl_giwessid, /* SIOCGIWESSID */ (iw_handler) ieee80211_ioctl_giwessid, /* SIOCGIWESSID */
(iw_handler) NULL, /* SIOCSIWNICKN */ (iw_handler) NULL, /* SIOCSIWNICKN */
......
...@@ -5,7 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o ...@@ -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_CCMP) += lib80211_crypt_ccmp.o
obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.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_WIRELESS_EXT) += wext-compat.o
cfg80211-$(CONFIG_NL80211) += nl80211.o cfg80211-$(CONFIG_NL80211) += nl80211.o
......
...@@ -240,6 +240,8 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv) ...@@ -240,6 +240,8 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
mutex_init(&drv->mtx); mutex_init(&drv->mtx);
mutex_init(&drv->devlist_mtx); mutex_init(&drv->devlist_mtx);
INIT_LIST_HEAD(&drv->netdev_list); INIT_LIST_HEAD(&drv->netdev_list);
spin_lock_init(&drv->bss_lock);
INIT_LIST_HEAD(&drv->bss_list);
device_initialize(&drv->wiphy.dev); device_initialize(&drv->wiphy.dev);
drv->wiphy.dev.class = &ieee80211_class; drv->wiphy.dev.class = &ieee80211_class;
...@@ -259,6 +261,9 @@ int wiphy_register(struct wiphy *wiphy) ...@@ -259,6 +261,9 @@ int wiphy_register(struct wiphy *wiphy)
int i; int i;
u16 ifmodes = wiphy->interface_modes; u16 ifmodes = wiphy->interface_modes;
if (WARN_ON(wiphy->max_scan_ssids < 1))
return -EINVAL;
/* sanity check ifmodes */ /* sanity check ifmodes */
WARN_ON(!ifmodes); WARN_ON(!ifmodes);
ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1; ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1;
...@@ -367,8 +372,11 @@ EXPORT_SYMBOL(wiphy_unregister); ...@@ -367,8 +372,11 @@ EXPORT_SYMBOL(wiphy_unregister);
void cfg80211_dev_free(struct cfg80211_registered_device *drv) void cfg80211_dev_free(struct cfg80211_registered_device *drv)
{ {
struct cfg80211_internal_bss *scan, *tmp;
mutex_destroy(&drv->mtx); mutex_destroy(&drv->mtx);
mutex_destroy(&drv->devlist_mtx); mutex_destroy(&drv->devlist_mtx);
list_for_each_entry_safe(scan, tmp, &drv->bss_list, list)
kfree(scan);
kfree(drv); kfree(drv);
} }
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/kref.h>
#include <linux/rbtree.h>
#include <net/genetlink.h> #include <net/genetlink.h>
#include <net/wireless.h> #include <net/wireless.h>
#include <net/cfg80211.h> #include <net/cfg80211.h>
...@@ -41,6 +43,13 @@ struct cfg80211_registered_device { ...@@ -41,6 +43,13 @@ struct cfg80211_registered_device {
struct mutex devlist_mtx; struct mutex devlist_mtx;
struct list_head netdev_list; 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(), /* must be last because of the way we do wiphy_priv(),
* and it should at least be aligned to NETDEV_ALIGN */ * and it should at least be aligned to NETDEV_ALIGN */
struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));
...@@ -56,6 +65,15 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) ...@@ -56,6 +65,15 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)
extern struct mutex cfg80211_drv_mutex; extern struct mutex cfg80211_drv_mutex;
extern struct list_head cfg80211_drv_list; 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 * This function returns a pointer to the driver
* that the genl_info item that is passed refers to. * that the genl_info item that is passed refers to.
...@@ -94,4 +112,6 @@ extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv, ...@@ -94,4 +112,6 @@ extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv,
void ieee80211_set_bitrate_flags(struct wiphy *wiphy); void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby); 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 */ #endif /* __NET_WIRELESS_CORE_H */
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/nl80211.h> #include <linux/nl80211.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/etherdevice.h>
#include <net/genetlink.h> #include <net/genetlink.h>
#include <net/cfg80211.h> #include <net/cfg80211.h>
#include "core.h" #include "core.h"
...@@ -109,6 +110,8 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { ...@@ -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_MGMT_SUBTYPE] = { .type = NLA_U8 },
[NL80211_ATTR_IE] = { .type = NLA_BINARY, [NL80211_ATTR_IE] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN }, .len = IEEE80211_MAX_DATA_LEN },
[NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
[NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
}; };
/* message building helper */ /* message building helper */
...@@ -141,6 +144,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, ...@@ -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_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); 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); nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
if (!nl_modes) if (!nl_modes)
...@@ -2270,6 +2275,246 @@ static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb, ...@@ -2270,6 +2275,246 @@ static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb,
return err; 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[] = { static struct genl_ops nl80211_ops[] = {
{ {
.cmd = NL80211_CMD_GET_WIPHY, .cmd = NL80211_CMD_GET_WIPHY,
...@@ -2443,12 +2688,26 @@ static struct genl_ops nl80211_ops[] = { ...@@ -2443,12 +2688,26 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy, .policy = nl80211_policy,
.flags = GENL_ADMIN_PERM, .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 */ /* multicast groups */
static struct genl_multicast_group nl80211_config_mcgrp = { static struct genl_multicast_group nl80211_config_mcgrp = {
.name = "config", .name = "config",
}; };
static struct genl_multicast_group nl80211_scan_mcgrp = {
.name = "scan",
};
/* notification functions */ /* notification functions */
...@@ -2468,6 +2727,66 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) ...@@ -2468,6 +2727,66 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL); 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 */ /* initialisation/exit functions */
int nl80211_init(void) int nl80211_init(void)
...@@ -2488,6 +2807,10 @@ int nl80211_init(void) ...@@ -2488,6 +2807,10 @@ int nl80211_init(void)
if (err) if (err)
goto err_out; goto err_out;
err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
if (err)
goto err_out;
return 0; return 0;
err_out: err_out:
genl_unregister_family(&nl80211_fam); genl_unregister_family(&nl80211_fam);
......
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
extern int nl80211_init(void); extern int nl80211_init(void);
extern void nl80211_exit(void); extern void nl80211_exit(void);
extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); 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 #else
static inline int nl80211_init(void) static inline int nl80211_init(void)
{ {
...@@ -19,6 +23,10 @@ static inline void nl80211_notify_dev_rename( ...@@ -19,6 +23,10 @@ static inline void nl80211_notify_dev_rename(
struct cfg80211_registered_device *rdev) 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 /* CONFIG_NL80211 */
#endif /* __NET_WIRELESS_NL80211_H */ #endif /* __NET_WIRELESS_NL80211_H */
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment