summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/hci_core.c4
-rw-r--r--net/mac80211/Kconfig1
-rw-r--r--net/mac80211/aes_ccm.c6
-rw-r--r--net/mac80211/cfg.c128
-rw-r--r--net/mac80211/debugfs.c91
-rw-r--r--net/mac80211/debugfs_sta.c26
-rw-r--r--net/mac80211/driver-ops.h87
-rw-r--r--net/mac80211/driver-trace.h275
-rw-r--r--net/mac80211/ibss.c10
-rw-r--r--net/mac80211/ieee80211_i.h34
-rw-r--r--net/mac80211/key.c21
-rw-r--r--net/mac80211/main.c48
-rw-r--r--net/mac80211/mesh.c19
-rw-r--r--net/mac80211/mesh.h3
-rw-r--r--net/mac80211/mesh_hwmp.c5
-rw-r--r--net/mac80211/mesh_pathtbl.c108
-rw-r--r--net/mac80211/mesh_plink.c42
-rw-r--r--net/mac80211/mlme.c48
-rw-r--r--net/mac80211/pm.c29
-rw-r--r--net/mac80211/rx.c90
-rw-r--r--net/mac80211/scan.c101
-rw-r--r--net/mac80211/sta_info.c33
-rw-r--r--net/mac80211/sta_info.h29
-rw-r--r--net/mac80211/status.c19
-rw-r--r--net/mac80211/tkip.c4
-rw-r--r--net/mac80211/tkip.h4
-rw-r--r--net/mac80211/tx.c25
-rw-r--r--net/mac80211/util.c21
-rw-r--r--net/mac80211/wep.c34
-rw-r--r--net/mac80211/wep.h4
-rw-r--r--net/mac80211/work.c6
-rw-r--r--net/mac80211/wpa.c62
-rw-r--r--net/rfkill/Kconfig11
-rw-r--r--net/rfkill/Makefile1
-rw-r--r--net/rfkill/rfkill-regulator.c164
-rw-r--r--net/wireless/core.c37
-rw-r--r--net/wireless/core.h21
-rw-r--r--net/wireless/lib80211_crypt_wep.c3
-rw-r--r--net/wireless/mesh.c23
-rw-r--r--net/wireless/mlme.c19
-rw-r--r--net/wireless/nl80211.c645
-rw-r--r--net/wireless/nl80211.h11
-rw-r--r--net/wireless/reg.c74
-rw-r--r--net/wireless/scan.c84
-rw-r--r--net/wireless/sysfs.c2
45 files changed, 2023 insertions, 489 deletions
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index b6bda3fac10..4487bbd0d72 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -586,9 +586,6 @@ static int hci_dev_do_close(struct hci_dev *hdev)
hci_req_cancel(hdev, ENODEV);
hci_req_lock(hdev);
- /* Stop timer, it might be running */
- del_timer_sync(&hdev->cmd_timer);
-
if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
del_timer_sync(&hdev->cmd_timer);
hci_req_unlock(hdev);
@@ -629,6 +626,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
/* Drop last sent command */
if (hdev->sent_cmd) {
+ del_timer_sync(&hdev->cmd_timer);
kfree_skb(hdev->sent_cmd);
hdev->sent_cmd = NULL;
}
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 513f85cc2ae..f5fdfcbf552 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -2,7 +2,6 @@ config MAC80211
tristate "Generic IEEE 802.11 Networking Stack (mac80211)"
depends on CFG80211
select CRYPTO
- select CRYPTO_ECB
select CRYPTO_ARC4
select CRYPTO_AES
select CRC32
diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c
index 4bd6ef0be38..b9b595c0811 100644
--- a/net/mac80211/aes_ccm.c
+++ b/net/mac80211/aes_ccm.c
@@ -54,13 +54,12 @@ void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
u8 *cdata, u8 *mic)
{
int i, j, last_len, num_blocks;
- u8 *pos, *cpos, *b, *s_0, *e, *b_0, *aad;
+ u8 *pos, *cpos, *b, *s_0, *e, *b_0;
b = scratch;
s_0 = scratch + AES_BLOCK_LEN;
e = scratch + 2 * AES_BLOCK_LEN;
b_0 = scratch + 3 * AES_BLOCK_LEN;
- aad = scratch + 4 * AES_BLOCK_LEN;
num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
last_len = data_len % AES_BLOCK_LEN;
@@ -94,13 +93,12 @@ int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
u8 *cdata, size_t data_len, u8 *mic, u8 *data)
{
int i, j, last_len, num_blocks;
- u8 *pos, *cpos, *b, *s_0, *a, *b_0, *aad;
+ u8 *pos, *cpos, *b, *s_0, *a, *b_0;
b = scratch;
s_0 = scratch + AES_BLOCK_LEN;
a = scratch + 2 * AES_BLOCK_LEN;
b_0 = scratch + 3 * AES_BLOCK_LEN;
- aad = scratch + 4 * AES_BLOCK_LEN;
num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
last_len = data_len % AES_BLOCK_LEN;
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 44049733c4e..ed3400cd7a5 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -136,7 +136,10 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
mutex_lock(&sdata->local->sta_mtx);
if (mac_addr) {
- sta = sta_info_get_bss(sdata, mac_addr);
+ if (ieee80211_vif_is_mesh(&sdata->vif))
+ sta = sta_info_get(sdata, mac_addr);
+ else
+ sta = sta_info_get_bss(sdata, mac_addr);
if (!sta) {
ieee80211_key_free(sdata->local, key);
err = -ENOENT;
@@ -330,6 +333,7 @@ static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, in
static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct timespec uptime;
sinfo->generation = sdata->local->sta_generation;
@@ -342,7 +346,12 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
STATION_INFO_TX_FAILED |
STATION_INFO_TX_BITRATE |
STATION_INFO_RX_BITRATE |
- STATION_INFO_RX_DROP_MISC;
+ STATION_INFO_RX_DROP_MISC |
+ STATION_INFO_BSS_PARAM |
+ STATION_INFO_CONNECTED_TIME;
+
+ do_posix_clock_monotonic_gettime(&uptime);
+ sinfo->connected_time = uptime.tv_sec - sta->last_connected;
sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
sinfo->rx_bytes = sta->rx_bytes;
@@ -389,6 +398,16 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->plink_state = sta->plink_state;
#endif
}
+
+ sinfo->bss_param.flags = 0;
+ if (sdata->vif.bss_conf.use_cts_prot)
+ sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
+ if (sdata->vif.bss_conf.use_short_preamble)
+ sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
+ if (sdata->vif.bss_conf.use_short_slot)
+ sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
+ sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period;
+ sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int;
}
@@ -675,6 +694,12 @@ static void sta_apply_parameters(struct ieee80211_local *local,
if (set & BIT(NL80211_STA_FLAG_MFP))
sta->flags |= WLAN_STA_MFP;
}
+
+ if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
+ sta->flags &= ~WLAN_STA_AUTH;
+ if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
+ sta->flags |= WLAN_STA_AUTH;
+ }
spin_unlock_irqrestore(&sta->flaglock, flags);
/*
@@ -712,15 +737,29 @@ static void sta_apply_parameters(struct ieee80211_local *local,
params->ht_capa,
&sta->sta.ht_cap);
- if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) {
- switch (params->plink_action) {
- case PLINK_ACTION_OPEN:
- mesh_plink_open(sta);
- break;
- case PLINK_ACTION_BLOCK:
- mesh_plink_block(sta);
- break;
- }
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+#ifdef CONFIG_MAC80211_MESH
+ if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED)
+ switch (params->plink_state) {
+ case PLINK_LISTEN:
+ case PLINK_ESTAB:
+ case PLINK_BLOCKED:
+ sta->plink_state = params->plink_state;
+ break;
+ default:
+ /* nothing */
+ break;
+ }
+ else
+ switch (params->plink_action) {
+ case PLINK_ACTION_OPEN:
+ mesh_plink_open(sta);
+ break;
+ case PLINK_ACTION_BLOCK:
+ mesh_plink_block(sta);
+ break;
+ }
+#endif
}
}
@@ -1023,26 +1062,30 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
u8 *new_ie;
const u8 *old_ie;
- /* first allocate the new vendor information element */
+ /* allocate information elements */
new_ie = NULL;
- old_ie = ifmsh->vendor_ie;
+ old_ie = ifmsh->ie;
- ifmsh->vendor_ie_len = setup->vendor_ie_len;
- if (setup->vendor_ie_len) {
- new_ie = kmemdup(setup->vendor_ie, setup->vendor_ie_len,
+ if (setup->ie_len) {
+ new_ie = kmemdup(setup->ie, setup->ie_len,
GFP_KERNEL);
if (!new_ie)
return -ENOMEM;
}
+ ifmsh->ie_len = setup->ie_len;
+ ifmsh->ie = new_ie;
+ kfree(old_ie);
/* now copy the rest of the setup parameters */
ifmsh->mesh_id_len = setup->mesh_id_len;
memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len);
ifmsh->mesh_pp_id = setup->path_sel_proto;
ifmsh->mesh_pm_id = setup->path_metric;
- ifmsh->vendor_ie = new_ie;
-
- kfree(old_ie);
+ ifmsh->security = IEEE80211_MESH_SEC_NONE;
+ if (setup->is_authenticated)
+ ifmsh->security |= IEEE80211_MESH_SEC_AUTHED;
+ if (setup->is_secure)
+ ifmsh->security |= IEEE80211_MESH_SEC_SECURED;
return 0;
}
@@ -1275,9 +1318,10 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
}
#ifdef CONFIG_PM
-static int ieee80211_suspend(struct wiphy *wiphy)
+static int ieee80211_suspend(struct wiphy *wiphy,
+ struct cfg80211_wowlan *wowlan)
{
- return __ieee80211_suspend(wiphy_priv(wiphy));
+ return __ieee80211_suspend(wiphy_priv(wiphy), wowlan);
}
static int ieee80211_resume(struct wiphy *wiphy)
@@ -1320,6 +1364,31 @@ static int ieee80211_scan(struct wiphy *wiphy,
return ieee80211_request_scan(sdata, req);
}
+static int
+ieee80211_sched_scan_start(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_sched_scan_request *req)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (!sdata->local->ops->sched_scan_start)
+ return -EOPNOTSUPP;
+
+ return ieee80211_request_sched_scan_start(sdata, req);
+}
+
+static int
+ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev,
+ bool driver_initiated)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (!sdata->local->ops->sched_scan_stop)
+ return -EOPNOTSUPP;
+
+ return ieee80211_request_sched_scan_stop(sdata, driver_initiated);
+}
+
static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_auth_request *req)
{
@@ -1611,16 +1680,13 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- int i;
-
- /*
- * This _could_ be supported by providing a hook for
- * drivers for this function, but at this point it
- * doesn't seem worth bothering.
- */
- if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
- return -EOPNOTSUPP;
+ int i, ret;
+ if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) {
+ ret = drv_set_bitrate_mask(local, sdata, mask);
+ if (ret)
+ return ret;
+ }
for (i = 0; i < IEEE80211_NUM_BANDS; i++)
sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
@@ -2064,6 +2130,8 @@ struct cfg80211_ops mac80211_config_ops = {
.suspend = ieee80211_suspend,
.resume = ieee80211_resume,
.scan = ieee80211_scan,
+ .sched_scan_start = ieee80211_sched_scan_start,
+ .sched_scan_stop = ieee80211_sched_scan_stop,
.auth = ieee80211_auth,
.assoc = ieee80211_assoc,
.deauth = ieee80211_deauth,
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 51f0d780daf..186e02f7cc3 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -37,7 +37,7 @@ int mac80211_format_buffer(char __user *userbuf, size_t count,
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
-#define DEBUGFS_READONLY_FILE(name, fmt, value...) \
+#define DEBUGFS_READONLY_FILE_FN(name, fmt, value...) \
static ssize_t name## _read(struct file *file, char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
@@ -45,14 +45,19 @@ static ssize_t name## _read(struct file *file, char __user *userbuf, \
\
return mac80211_format_buffer(userbuf, count, ppos, \
fmt "\n", ##value); \
-} \
- \
+}
+
+#define DEBUGFS_READONLY_FILE_OPS(name) \
static const struct file_operations name## _ops = { \
.read = name## _read, \
.open = mac80211_open_file_generic, \
.llseek = generic_file_llseek, \
};
+#define DEBUGFS_READONLY_FILE(name, fmt, value...) \
+ DEBUGFS_READONLY_FILE_FN(name, fmt, value) \
+ DEBUGFS_READONLY_FILE_OPS(name)
+
#define DEBUGFS_ADD(name) \
debugfs_create_file(#name, 0400, phyd, local, &name## _ops);
@@ -130,7 +135,7 @@ static ssize_t reset_write(struct file *file, const char __user *user_buf,
struct ieee80211_local *local = file->private_data;
rtnl_lock();
- __ieee80211_suspend(&local->hw);
+ __ieee80211_suspend(&local->hw, NULL);
__ieee80211_resume(&local->hw);
rtnl_unlock();
@@ -291,11 +296,70 @@ static ssize_t channel_type_read(struct file *file, char __user *user_buf,
return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
}
-static const struct file_operations channel_type_ops = {
- .read = channel_type_read,
- .open = mac80211_open_file_generic,
- .llseek = default_llseek,
-};
+static ssize_t hwflags_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_local *local = file->private_data;
+ int mxln = 500;
+ ssize_t rv;
+ char *buf = kzalloc(mxln, GFP_KERNEL);
+ int sf = 0; /* how many written so far */
+
+ sf += snprintf(buf, mxln - sf, "0x%x\n", local->hw.flags);
+ if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
+ sf += snprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n");
+ if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
+ sf += snprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n");
+ if (local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)
+ sf += snprintf(buf + sf, mxln - sf,
+ "HOST_BCAST_PS_BUFFERING\n");
+ if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)
+ sf += snprintf(buf + sf, mxln - sf,
+ "2GHZ_SHORT_SLOT_INCAPABLE\n");
+ if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)
+ sf += snprintf(buf + sf, mxln - sf,
+ "2GHZ_SHORT_PREAMBLE_INCAPABLE\n");
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
+ sf += snprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
+ sf += snprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");
+ if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD)
+ sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_PERIOD\n");
+ if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)
+ sf += snprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");
+ if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)
+ sf += snprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n");
+ if (local->hw.flags & IEEE80211_HW_SUPPORTS_PS)
+ sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n");
+ if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+ sf += snprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n");
+ if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
+ sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
+ if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE)
+ sf += snprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
+ if (local->hw.flags & IEEE80211_HW_BEACON_FILTER)
+ sf += snprintf(buf + sf, mxln - sf, "BEACON_FILTER\n");
+ if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS)
+ sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n");
+ if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
+ sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_SMPS\n");
+ if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
+ sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");
+ if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+ sf += snprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n");
+ if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+ sf += snprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n");
+ if (local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)
+ sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_CQM_RSSI\n");
+ if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK)
+ sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n");
+ if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
+ sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n");
+
+ rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+ kfree(buf);
+ return rv;
+}
static ssize_t queues_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
@@ -315,11 +379,9 @@ static ssize_t queues_read(struct file *file, char __user *user_buf,
return simple_read_from_buffer(user_buf, count, ppos, buf, res);
}
-static const struct file_operations queues_ops = {
- .read = queues_read,
- .open = mac80211_open_file_generic,
- .llseek = default_llseek,
-};
+DEBUGFS_READONLY_FILE_OPS(hwflags);
+DEBUGFS_READONLY_FILE_OPS(channel_type);
+DEBUGFS_READONLY_FILE_OPS(queues);
/* statistics stuff */
@@ -395,6 +457,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_ADD(uapsd_queues);
DEBUGFS_ADD(uapsd_max_sp_len);
DEBUGFS_ADD(channel_type);
+ DEBUGFS_ADD(hwflags);
DEBUGFS_ADD(user_power);
DEBUGFS_ADD(power);
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index c04a1396cf8..a01d2137fdd 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -92,6 +92,31 @@ static ssize_t sta_inactive_ms_read(struct file *file, char __user *userbuf,
}
STA_OPS(inactive_ms);
+
+static ssize_t sta_connected_time_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct sta_info *sta = file->private_data;
+ struct timespec uptime;
+ struct tm result;
+ long connected_time_secs;
+ char buf[100];
+ int res;
+ do_posix_clock_monotonic_gettime(&uptime);
+ connected_time_secs = uptime.tv_sec - sta->last_connected;
+ time_to_tm(connected_time_secs, 0, &result);
+ result.tm_year -= 70;
+ result.tm_mday -= 1;
+ res = scnprintf(buf, sizeof(buf),
+ "years - %ld\nmonths - %d\ndays - %d\nclock - %d:%d:%d\n\n",
+ result.tm_year, result.tm_mon, result.tm_mday,
+ result.tm_hour, result.tm_min, result.tm_sec);
+ return simple_read_from_buffer(userbuf, count, ppos, buf, res);
+}
+STA_OPS(connected_time);
+
+
+
static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
@@ -324,6 +349,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD(flags);
DEBUGFS_ADD(num_ps_buf_frames);
DEBUGFS_ADD(inactive_ms);
+ DEBUGFS_ADD(connected_time);
DEBUGFS_ADD(last_seq_ctrl);
DEBUGFS_ADD(agg_status);
DEBUGFS_ADD(dev);
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 9c0d62bb0ea..eebf7a67daf 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -41,6 +41,33 @@ static inline void drv_stop(struct ieee80211_local *local)
local->started = false;
}
+#ifdef CONFIG_PM
+static inline int drv_suspend(struct ieee80211_local *local,
+ struct cfg80211_wowlan *wowlan)
+{
+ int ret;
+
+ might_sleep();
+
+ trace_drv_suspend(local);
+ ret = local->ops->suspend(&local->hw, wowlan);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
+static inline int drv_resume(struct ieee80211_local *local)
+{
+ int ret;
+
+ might_sleep();
+
+ trace_drv_resume(local);
+ ret = local->ops->resume(&local->hw);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+#endif
+
static inline int drv_add_interface(struct ieee80211_local *local,
struct ieee80211_vif *vif)
{
@@ -185,12 +212,39 @@ static inline int drv_hw_scan(struct ieee80211_local *local,
might_sleep();
- trace_drv_hw_scan(local, sdata, req);
+ trace_drv_hw_scan(local, sdata);
ret = local->ops->hw_scan(&local->hw, &sdata->vif, req);
trace_drv_return_int(local, ret);
return ret;
}
+static inline int
+drv_sched_scan_start(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_sched_scan_ies *ies)
+{
+ int ret;
+
+ might_sleep();
+
+ trace_drv_sched_scan_start(local, sdata);
+ ret = local->ops->sched_scan_start(&local->hw, &sdata->vif,
+ req, ies);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
+static inline void drv_sched_scan_stop(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ might_sleep();
+
+ trace_drv_sched_scan_stop(local, sdata);
+ local->ops->sched_scan_stop(&local->hw, &sdata->vif);
+ trace_drv_return_void(local);
+}
+
static inline void drv_sw_scan_start(struct ieee80211_local *local)
{
might_sleep();
@@ -552,4 +606,35 @@ static inline void drv_get_ringparam(struct ieee80211_local *local,
trace_drv_return_void(local);
}
+static inline bool drv_tx_frames_pending(struct ieee80211_local *local)
+{
+ bool ret = false;
+
+ might_sleep();
+
+ trace_drv_tx_frames_pending(local);
+ if (local->ops->tx_frames_pending)
+ ret = local->ops->tx_frames_pending(&local->hw);
+ trace_drv_return_bool(local, ret);
+
+ return ret;
+}
+
+static inline int drv_set_bitrate_mask(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ int ret = -EOPNOTSUPP;
+
+ might_sleep();
+
+ trace_drv_set_bitrate_mask(local, sdata, mask);
+ if (local->ops->set_bitrate_mask)
+ ret = local->ops->set_bitrate_mask(&local->hw,
+ &sdata->vif, mask);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index 45aab80738e..ed9edcbd9aa 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -55,6 +55,70 @@ DECLARE_EVENT_CLASS(local_only_evt,
TP_printk(LOCAL_PR_FMT, LOCAL_PR_ARG)
);
+DECLARE_EVENT_CLASS(local_sdata_addr_evt,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __array(char, addr, 6)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ memcpy(__entry->addr, sdata->vif.addr, 6);
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " addr:%pM",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr
+ )
+);
+
+DECLARE_EVENT_CLASS(local_u32_evt,
+ TP_PROTO(struct ieee80211_local *local, u32 value),
+ TP_ARGS(local, value),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(u32, value)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->value = value;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " value:%d",
+ LOCAL_PR_ARG, __entry->value
+ )
+);
+
+DECLARE_EVENT_CLASS(local_sdata_evt,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG
+ )
+);
+
DEFINE_EVENT(local_only_evt, drv_return_void,
TP_PROTO(struct ieee80211_local *local),
TP_ARGS(local)
@@ -74,6 +138,21 @@ TRACE_EVENT(drv_return_int,
TP_printk(LOCAL_PR_FMT " - %d", LOCAL_PR_ARG, __entry->ret)
);
+TRACE_EVENT(drv_return_bool,
+ TP_PROTO(struct ieee80211_local *local, bool ret),
+ TP_ARGS(local, ret),
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(bool, ret)
+ ),
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->ret = ret;
+ ),
+ TP_printk(LOCAL_PR_FMT " - %s", LOCAL_PR_ARG, (__entry->ret) ?
+ "true" : "false")
+);
+
TRACE_EVENT(drv_return_u64,
TP_PROTO(struct ieee80211_local *local, u64 ret),
TP_ARGS(local, ret),
@@ -93,33 +172,25 @@ DEFINE_EVENT(local_only_evt, drv_start,
TP_ARGS(local)
);
+DEFINE_EVENT(local_only_evt, drv_suspend,
+ TP_PROTO(struct ieee80211_local *local),
+ TP_ARGS(local)
+);
+
+DEFINE_EVENT(local_only_evt, drv_resume,
+ TP_PROTO(struct ieee80211_local *local),
+ TP_ARGS(local)
+);
+
DEFINE_EVENT(local_only_evt, drv_stop,
TP_PROTO(struct ieee80211_local *local),
TP_ARGS(local)
);
-TRACE_EVENT(drv_add_interface,
+DEFINE_EVENT(local_sdata_addr_evt, drv_add_interface,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata),
-
- TP_ARGS(local, sdata),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- VIF_ENTRY
- __array(char, addr, 6)
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- VIF_ASSIGN;
- memcpy(__entry->addr, sdata->vif.addr, 6);
- ),
-
- TP_printk(
- LOCAL_PR_FMT VIF_PR_FMT " addr:%pM",
- LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr
- )
+ TP_ARGS(local, sdata)
);
TRACE_EVENT(drv_change_interface,
@@ -150,27 +221,10 @@ TRACE_EVENT(drv_change_interface,
)
);
-TRACE_EVENT(drv_remove_interface,
- TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata),
-
- TP_ARGS(local, sdata),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- VIF_ENTRY
- __array(char, addr, 6)
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- VIF_ASSIGN;
- memcpy(__entry->addr, sdata->vif.addr, 6);
- ),
-
- TP_printk(
- LOCAL_PR_FMT VIF_PR_FMT " addr:%pM",
- LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr
- )
+DEFINE_EVENT(local_sdata_addr_evt, drv_remove_interface,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
);
TRACE_EVENT(drv_config,
@@ -400,27 +454,22 @@ TRACE_EVENT(drv_update_tkip_key,
)
);
-TRACE_EVENT(drv_hw_scan,
+DEFINE_EVENT(local_sdata_evt, drv_hw_scan,
TP_PROTO(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- struct cfg80211_scan_request *req),
-
- TP_ARGS(local, sdata, req),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- VIF_ENTRY
- ),
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
+);
- TP_fast_assign(
- LOCAL_ASSIGN;
- VIF_ASSIGN;
- ),
+DEFINE_EVENT(local_sdata_evt, drv_sched_scan_start,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
+);
- TP_printk(
- LOCAL_PR_FMT VIF_PR_FMT,
- LOCAL_PR_ARG,VIF_PR_ARG
- )
+DEFINE_EVENT(local_sdata_evt, drv_sched_scan_stop,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
);
DEFINE_EVENT(local_only_evt, drv_sw_scan_start,
@@ -489,46 +538,14 @@ TRACE_EVENT(drv_get_tkip_seq,
)
);
-TRACE_EVENT(drv_set_frag_threshold,
+DEFINE_EVENT(local_u32_evt, drv_set_frag_threshold,
TP_PROTO(struct ieee80211_local *local, u32 value),
-
- TP_ARGS(local, value),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- __field(u32, value)
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- __entry->value = value;
- ),
-
- TP_printk(
- LOCAL_PR_FMT " value:%d",
- LOCAL_PR_ARG, __entry->value
- )
+ TP_ARGS(local, value)
);
-TRACE_EVENT(drv_set_rts_threshold,
+DEFINE_EVENT(local_u32_evt, drv_set_rts_threshold,
TP_PROTO(struct ieee80211_local *local, u32 value),
-
- TP_ARGS(local, value),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- __field(u32, value)
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- __entry->value = value;
- ),
-
- TP_printk(
- LOCAL_PR_FMT " value:%d",
- LOCAL_PR_ARG, __entry->value
- )
+ TP_ARGS(local, value)
);
TRACE_EVENT(drv_set_coverage_class,
@@ -964,11 +981,43 @@ TRACE_EVENT(drv_get_ringparam,
)
);
+DEFINE_EVENT(local_only_evt, drv_tx_frames_pending,
+ TP_PROTO(struct ieee80211_local *local),
+ TP_ARGS(local)
+);
+
DEFINE_EVENT(local_only_evt, drv_offchannel_tx_cancel_wait,
TP_PROTO(struct ieee80211_local *local),
TP_ARGS(local)
);
+TRACE_EVENT(drv_set_bitrate_mask,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_bitrate_mask *mask),
+
+ TP_ARGS(local, sdata, mask),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __field(u32, legacy_2g)
+ __field(u32, legacy_5g)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ __entry->legacy_2g = mask->control[IEEE80211_BAND_2GHZ].legacy;
+ __entry->legacy_5g = mask->control[IEEE80211_BAND_5GHZ].legacy;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " 2G Mask:0x%x 5G Mask:0x%x",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->legacy_2g, __entry->legacy_5g
+ )
+);
+
/*
* Tracing for API calls that drivers call.
*/
@@ -1147,6 +1196,42 @@ TRACE_EVENT(api_scan_completed,
)
);
+TRACE_EVENT(api_sched_scan_results,
+ TP_PROTO(struct ieee80211_local *local),
+
+ TP_ARGS(local),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT, LOCAL_PR_ARG
+ )
+);
+
+TRACE_EVENT(api_sched_scan_stopped,
+ TP_PROTO(struct ieee80211_local *local),
+
+ TP_ARGS(local),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT, LOCAL_PR_ARG
+ )
+);
+
TRACE_EVENT(api_sta_block_awake,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sta *sta, bool block),
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 3e81af1fce5..b81860c9469 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -40,7 +40,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
size_t len)
{
- u16 auth_alg, auth_transaction, status_code;
+ u16 auth_alg, auth_transaction;
lockdep_assert_held(&sdata->u.ibss.mtx);
@@ -49,7 +49,6 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
- status_code = le16_to_cpu(mgmt->u.auth.status_code);
/*
* IEEE 802.11 standard does not require authentication in IBSS
@@ -527,8 +526,6 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_supported_band *sband;
u8 bssid[ETH_ALEN];
u16 capability;
int i;
@@ -551,8 +548,6 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %pM\n",
sdata->name, bssid);
- sband = local->hw.wiphy->bands[ifibss->channel->band];
-
capability = WLAN_CAPABILITY_IBSS;
if (ifibss->privacy)
@@ -661,7 +656,6 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
struct sk_buff *req)
{
- struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(req);
struct ieee80211_mgmt *mgmt = (void *)req->data;
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
@@ -685,7 +679,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
mgmt->bssid, tx_last_beacon);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
- if (!tx_last_beacon && !(rx_status->rx_flags & IEEE80211_RX_RA_MATCH))
+ if (!tx_last_beacon && is_multicast_ether_addr(mgmt->da))
return;
if (memcmp(mgmt->bssid, ifibss->bssid, ETH_ALEN) != 0 &&
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index a4040170142..6f55a789c09 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -488,8 +488,13 @@ struct ieee80211_if_mesh {
struct mesh_config mshcfg;
u32 mesh_seqnum;
bool accepting_plinks;
- const u8 *vendor_ie;
- u8 vendor_ie_len;
+ const u8 *ie;
+ u8 ie_len;
+ enum {
+ IEEE80211_MESH_SEC_NONE = 0x0,
+ IEEE80211_MESH_SEC_AUTHED = 0x1,
+ IEEE80211_MESH_SEC_SECURED = 0x2,
+ } security;
};
#ifdef CONFIG_MAC80211_MESH
@@ -763,8 +768,14 @@ struct ieee80211_local {
/* device is started */
bool started;
+ /* wowlan is enabled -- don't reconfig on resume */
+ bool wowlan;
+
int tx_headroom; /* required headroom for hardware/radiotap */
+ /* count for keys needing tailroom space allocation */
+ int crypto_tx_tailroom_needed_cnt;
+
/* Tasklet and skb queue to process calls from IRQ mode. All frames
* added to skb_queue will be processed, but frames in
* skb_queue_unreliable may be dropped if the total length of these
@@ -809,8 +820,8 @@ struct ieee80211_local {
struct rate_control_ref *rate_ctrl;
- struct crypto_blkcipher *wep_tx_tfm;
- struct crypto_blkcipher *wep_rx_tfm;
+ struct crypto_cipher *wep_tx_tfm;
+ struct crypto_cipher *wep_rx_tfm;
u32 wep_iv;
/* see iface.c */
@@ -836,6 +847,9 @@ struct ieee80211_local {
int scan_channel_idx;
int scan_ies_len;
+ bool sched_scanning;
+ struct ieee80211_sched_scan_ies sched_scan_ies;
+
unsigned long leave_oper_channel_time;
enum mac80211_scan_state next_scan_state;
struct delayed_work scan_work;
@@ -1143,6 +1157,12 @@ ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
void ieee80211_rx_bss_put(struct ieee80211_local *local,
struct ieee80211_bss *bss);
+/* scheduled scan handling */
+int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_sched_scan_request *req);
+int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata,
+ bool driver_initiated);
+
/* off-channel helpers */
bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local);
void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local,
@@ -1246,7 +1266,8 @@ int ieee80211_reconfig(struct ieee80211_local *local);
void ieee80211_stop_device(struct ieee80211_local *local);
#ifdef CONFIG_PM
-int __ieee80211_suspend(struct ieee80211_hw *hw);
+int __ieee80211_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan);
static inline int __ieee80211_resume(struct ieee80211_hw *hw)
{
@@ -1259,7 +1280,8 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
return ieee80211_reconfig(hw_to_local(hw));
}
#else
-static inline int __ieee80211_suspend(struct ieee80211_hw *hw)
+static inline int __ieee80211_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan)
{
return 0;
}
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index af3c56482c8..b510721e3b3 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -101,6 +101,11 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
if (!ret) {
key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
+
+ if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
+ (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)))
+ key->local->crypto_tx_tailroom_needed_cnt--;
+
return 0;
}
@@ -156,6 +161,10 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
key->conf.keyidx, sta ? sta->addr : bcast_addr, ret);
key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
+
+ if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
+ (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)))
+ key->local->crypto_tx_tailroom_needed_cnt++;
}
void ieee80211_key_removed(struct ieee80211_key_conf *key_conf)
@@ -388,8 +397,10 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)
ieee80211_aes_key_free(key->u.ccmp.tfm);
if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)
ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm);
- if (key->local)
+ if (key->local) {
ieee80211_debugfs_key_remove(key);
+ key->local->crypto_tx_tailroom_needed_cnt--;
+ }
kfree(key);
}
@@ -451,6 +462,8 @@ int ieee80211_key_link(struct ieee80211_key *key,
ieee80211_debugfs_key_add(key);
+ key->local->crypto_tx_tailroom_needed_cnt++;
+
ret = ieee80211_key_enable_hw_accel(key);
mutex_unlock(&sdata->local->key_mtx);
@@ -492,8 +505,12 @@ void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)
mutex_lock(&sdata->local->key_mtx);
- list_for_each_entry(key, &sdata->key_list, list)
+ sdata->local->crypto_tx_tailroom_needed_cnt = 0;
+
+ list_for_each_entry(key, &sdata->key_list, list) {
+ sdata->local->crypto_tx_tailroom_needed_cnt++;
ieee80211_key_enable_hw_accel(key);
+ }
mutex_unlock(&sdata->local->key_mtx);
}
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 562d2984c48..30e6a682a04 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -33,12 +33,6 @@
#include "cfg.h"
#include "debugfs.h"
-
-static bool ieee80211_disable_40mhz_24ghz;
-module_param(ieee80211_disable_40mhz_24ghz, bool, 0644);
-MODULE_PARM_DESC(ieee80211_disable_40mhz_24ghz,
- "Disable 40MHz support in the 2.4GHz band");
-
static struct lock_class_key ieee80211_rx_skb_queue_class;
void ieee80211_configure_filter(struct ieee80211_local *local)
@@ -364,7 +358,8 @@ static void ieee80211_restart_work(struct work_struct *work)
flush_workqueue(local->workqueue);
mutex_lock(&local->mtx);
- WARN(test_bit(SCAN_HW_SCANNING, &local->scanning),
+ WARN(test_bit(SCAN_HW_SCANNING, &local->scanning) ||
+ local->sched_scanning,
"%s called with hardware scan in progress\n", __func__);
mutex_unlock(&local->mtx);
@@ -545,7 +540,9 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
},
[NL80211_IFTYPE_MESH_POINT] = {
.tx = 0xffff,
- .rx = BIT(IEEE80211_STYPE_ACTION >> 4),
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4),
},
};
@@ -584,8 +581,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
wiphy->flags |= WIPHY_FLAG_NETNS_OK |
WIPHY_FLAG_4ADDR_AP |
- WIPHY_FLAG_4ADDR_STATION |
- WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS;
+ WIPHY_FLAG_4ADDR_STATION;
if (!ops->set_key)
wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
@@ -701,6 +697,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
WLAN_CIPHER_SUITE_AES_CMAC
};
+ if ((hw->wiphy->wowlan.flags || hw->wiphy->wowlan.n_patterns)
+#ifdef CONFIG_PM
+ && (!local->ops->suspend || !local->ops->resume)
+#endif
+ )
+ return -EINVAL;
+
if (hw->max_report_rates == 0)
hw->max_report_rates = hw->max_rates;
@@ -726,18 +729,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
}
channels += sband->n_channels;
- /*
- * Since ieee80211_disable_40mhz_24ghz is global, we can
- * modify the sband's ht data even if the driver uses a
- * global structure for that.
- */
- if (ieee80211_disable_40mhz_24ghz &&
- band == IEEE80211_BAND_2GHZ &&
- sband->ht_cap.ht_supported) {
- sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40;
- }
-
if (max_bitrates < sband->n_bitrates)
max_bitrates = sband->n_bitrates;
supp_ht = supp_ht || sband->ht_cap.ht_supported;
@@ -760,6 +751,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->hw.wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MESH_POINT);
#endif
+ /* if the underlying driver supports mesh, mac80211 will (at least)
+ * provide routing of mesh authentication frames to userspace */
+ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_MESH_POINT))
+ local->hw.wiphy->flags |= WIPHY_FLAG_MESH_AUTH;
+
/* mac80211 supports control port protocol changing */
local->hw.wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;
@@ -838,6 +834,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (!local->ops->remain_on_channel)
local->hw.wiphy->max_remain_on_channel_duration = 5000;
+ if (local->ops->sched_scan_start)
+ local->hw.wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+
result = wiphy_register(local->hw.wiphy);
if (result < 0)
goto fail_wiphy_register;
@@ -879,10 +878,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->dynamic_ps_forced_timeout = -1;
- result = sta_info_start(local);
- if (result < 0)
- goto fail_sta_info;
-
result = ieee80211_wep_init(local);
if (result < 0)
wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n",
@@ -945,7 +940,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
rtnl_unlock();
ieee80211_wep_free(local);
sta_info_stop(local);
- fail_sta_info:
destroy_workqueue(local->workqueue);
fail_workqueue:
wiphy_unregister(local->hw.wiphy);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 2a57cc02c61..75378e852f0 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -279,9 +279,9 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
*pos++ = 0x00;
- if (sdata->u.mesh.vendor_ie) {
- int len = sdata->u.mesh.vendor_ie_len;
- const u8 *data = sdata->u.mesh.vendor_ie;
+ if (sdata->u.mesh.ie) {
+ int len = sdata->u.mesh.ie_len;
+ const u8 *data = sdata->u.mesh.ie;
if (skb_tailroom(skb) > len)
memcpy(skb_put(skb, len), data, len);
}
@@ -573,6 +573,10 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
&elems);
+ /* ignore beacons from secure mesh peers if our security is off */
+ if (elems.rsn_len && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE)
+ return;
+
if (elems.ds_params && elems.ds_params_len == 1)
freq = ieee80211_channel_to_frequency(elems.ds_params[0], band);
else
@@ -586,9 +590,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
if (elems.mesh_id && elems.mesh_config &&
mesh_matches_local(&elems, sdata)) {
supp_rates = ieee80211_sta_get_rates(local, &elems, band);
-
- mesh_neighbour_update(mgmt->sa, supp_rates, sdata,
- mesh_peer_accepts_plinks(&elems));
+ mesh_neighbour_update(mgmt->sa, supp_rates, sdata, &elems);
}
}
@@ -598,7 +600,7 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
struct ieee80211_rx_status *rx_status)
{
switch (mgmt->u.action.category) {
- case WLAN_CATEGORY_MESH_PLINK:
+ case WLAN_CATEGORY_MESH_ACTION:
mesh_rx_plink_frame(sdata, mgmt, len, rx_status);
break;
case WLAN_CATEGORY_MESH_PATH_SEL:
@@ -611,12 +613,9 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
struct ieee80211_rx_status *rx_status;
- struct ieee80211_if_mesh *ifmsh;
struct ieee80211_mgmt *mgmt;
u16 stype;
- ifmsh = &sdata->u.mesh;
-
rx_status = IEEE80211_SKB_RXCB(skb);
mgmt = (struct ieee80211_mgmt *) skb->data;
stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index b99e230fe31..10acf1cc808 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -226,7 +226,8 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata);
/* Mesh plinks */
void mesh_neighbour_update(u8 *hw_addr, u32 rates,
- struct ieee80211_sub_if_data *sdata, bool add);
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee802_11_elems *ie);
bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie);
void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata);
void mesh_plink_broken(struct sta_info *sta);
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 5bf64d7112b..849fecd0820 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -391,7 +391,6 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
(mpath->flags & MESH_PATH_SN_VALID)) {
if (SN_GT(mpath->sn, orig_sn) ||
(mpath->sn == orig_sn &&
- action == MPATH_PREQ &&
new_metric >= mpath->metric)) {
process = false;
fresh_info = false;
@@ -633,7 +632,6 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
struct mesh_path *mpath;
u8 ttl;
u8 *ta, *target_addr;
- u8 target_flags;
u32 target_sn;
u16 target_rcode;
@@ -644,7 +642,6 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
return;
}
ttl--;
- target_flags = PERR_IE_TARGET_FLAGS(perr_elem);
target_addr = PERR_IE_TARGET_ADDR(perr_elem);
target_sn = PERR_IE_TARGET_SN(perr_elem);
target_rcode = PERR_IE_TARGET_RCODE(perr_elem);
@@ -675,12 +672,10 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_path *mpath;
- u8 *ta;
u8 ttl, flags, hopcount;
u8 *orig_addr;
u32 orig_sn, metric;
- ta = mgmt->sa;
ttl = rann->rann_ttl;
if (ttl <= 1) {
ifmsh->mshstats.dropped_frames_ttl++;
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index 8d65b47d983..c7cb3cc083d 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -55,52 +55,46 @@ void mesh_table_free(struct mesh_table *tbl, bool free_leafs)
mesh_hash = tbl->hash_buckets;
for (i = 0; i <= tbl->hash_mask; i++) {
- spin_lock(&tbl->hashwlock[i]);
+ spin_lock_bh(&tbl->hashwlock[i]);
hlist_for_each_safe(p, q, &mesh_hash[i]) {
tbl->free_node(p, free_leafs);
atomic_dec(&tbl->entries);
}
- spin_unlock(&tbl->hashwlock[i]);
+ spin_unlock_bh(&tbl->hashwlock[i]);
}
__mesh_table_free(tbl);
}
-static struct mesh_table *mesh_table_grow(struct mesh_table *tbl)
+static int mesh_table_grow(struct mesh_table *oldtbl,
+ struct mesh_table *newtbl)
{
- struct mesh_table *newtbl;
struct hlist_head *oldhash;
struct hlist_node *p, *q;
int i;
- if (atomic_read(&tbl->entries)
- < tbl->mean_chain_len * (tbl->hash_mask + 1))
- goto endgrow;
+ if (atomic_read(&oldtbl->entries)
+ < oldtbl->mean_chain_len * (oldtbl->hash_mask + 1))
+ return -EAGAIN;
- newtbl = mesh_table_alloc(tbl->size_order + 1);
- if (!newtbl)
- goto endgrow;
-
- newtbl->free_node = tbl->free_node;
- newtbl->mean_chain_len = tbl->mean_chain_len;
- newtbl->copy_node = tbl->copy_node;
- atomic_set(&newtbl->entries, atomic_read(&tbl->entries));
+ newtbl->free_node = oldtbl->free_node;
+ newtbl->mean_chain_len = oldtbl->mean_chain_len;
+ newtbl->copy_node = oldtbl->copy_node;
+ atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries));
- oldhash = tbl->hash_buckets;
- for (i = 0; i <= tbl->hash_mask; i++)
+ oldhash = oldtbl->hash_buckets;
+ for (i = 0; i <= oldtbl->hash_mask; i++)
hlist_for_each(p, &oldhash[i])
- if (tbl->copy_node(p, newtbl) < 0)
+ if (oldtbl->copy_node(p, newtbl) < 0)
goto errcopy;
- return newtbl;
+ return 0;
errcopy:
for (i = 0; i <= newtbl->hash_mask; i++) {
hlist_for_each_safe(p, q, &newtbl->hash_buckets[i])
- tbl->free_node(p, 0);
+ oldtbl->free_node(p, 0);
}
- __mesh_table_free(newtbl);
-endgrow:
- return NULL;
+ return -ENOMEM;
}
@@ -280,7 +274,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
if (!new_node)
goto err_node_alloc;
- read_lock(&pathtbl_resize_lock);
+ read_lock_bh(&pathtbl_resize_lock);
memcpy(new_mpath->dst, dst, ETH_ALEN);
new_mpath->sdata = sdata;
new_mpath->flags = 0;
@@ -295,7 +289,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
hash_idx = mesh_table_hash(dst, sdata, mesh_paths);
bucket = &mesh_paths->hash_buckets[hash_idx];
- spin_lock(&mesh_paths->hashwlock[hash_idx]);
+ spin_lock_bh(&mesh_paths->hashwlock[hash_idx]);
err = -EEXIST;
hlist_for_each_entry(node, n, bucket, list) {
@@ -311,8 +305,8 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
mesh_paths_generation++;
- spin_unlock(&mesh_paths->hashwlock[hash_idx]);
- read_unlock(&pathtbl_resize_lock);
+ spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]);
+ read_unlock_bh(&pathtbl_resize_lock);
if (grow) {
set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags);
ieee80211_queue_work(&local->hw, &sdata->work);
@@ -320,8 +314,8 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
return 0;
err_exists:
- spin_unlock(&mesh_paths->hashwlock[hash_idx]);
- read_unlock(&pathtbl_resize_lock);
+ spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]);
+ read_unlock_bh(&pathtbl_resize_lock);
kfree(new_node);
err_node_alloc:
kfree(new_mpath);
@@ -334,15 +328,21 @@ void mesh_mpath_table_grow(void)
{
struct mesh_table *oldtbl, *newtbl;
- write_lock(&pathtbl_resize_lock);
+ rcu_read_lock();
+ newtbl = mesh_table_alloc(rcu_dereference(mesh_paths)->size_order + 1);
+ if (!newtbl)
+ return;
+ write_lock_bh(&pathtbl_resize_lock);
oldtbl = mesh_paths;
- newtbl = mesh_table_grow(mesh_paths);
- if (!newtbl) {
- write_unlock(&pathtbl_resize_lock);
+ if (mesh_table_grow(mesh_paths, newtbl) < 0) {
+ rcu_read_unlock();
+ __mesh_table_free(newtbl);
+ write_unlock_bh(&pathtbl_resize_lock);
return;
}
+ rcu_read_unlock();
rcu_assign_pointer(mesh_paths, newtbl);
- write_unlock(&pathtbl_resize_lock);
+ write_unlock_bh(&pathtbl_resize_lock);
synchronize_rcu();
mesh_table_free(oldtbl, false);
@@ -352,15 +352,21 @@ void mesh_mpp_table_grow(void)
{
struct mesh_table *oldtbl, *newtbl;
- write_lock(&pathtbl_resize_lock);
+ rcu_read_lock();
+ newtbl = mesh_table_alloc(rcu_dereference(mpp_paths)->size_order + 1);
+ if (!newtbl)
+ return;
+ write_lock_bh(&pathtbl_resize_lock);
oldtbl = mpp_paths;
- newtbl = mesh_table_grow(mpp_paths);
- if (!newtbl) {
- write_unlock(&pathtbl_resize_lock);
+ if (mesh_table_grow(mpp_paths, newtbl) < 0) {
+ rcu_read_unlock();
+ __mesh_table_free(newtbl);
+ write_unlock_bh(&pathtbl_resize_lock);
return;
}
+ rcu_read_unlock();
rcu_assign_pointer(mpp_paths, newtbl);
- write_unlock(&pathtbl_resize_lock);
+ write_unlock_bh(&pathtbl_resize_lock);
synchronize_rcu();
mesh_table_free(oldtbl, false);
@@ -394,7 +400,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
if (!new_node)
goto err_node_alloc;
- read_lock(&pathtbl_resize_lock);
+ read_lock_bh(&pathtbl_resize_lock);
memcpy(new_mpath->dst, dst, ETH_ALEN);
memcpy(new_mpath->mpp, mpp, ETH_ALEN);
new_mpath->sdata = sdata;
@@ -407,7 +413,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
hash_idx = mesh_table_hash(dst, sdata, mpp_paths);
bucket = &mpp_paths->hash_buckets[hash_idx];
- spin_lock(&mpp_paths->hashwlock[hash_idx]);
+ spin_lock_bh(&mpp_paths->hashwlock[hash_idx]);
err = -EEXIST;
hlist_for_each_entry(node, n, bucket, list) {
@@ -421,8 +427,8 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
mpp_paths->mean_chain_len * (mpp_paths->hash_mask + 1))
grow = 1;
- spin_unlock(&mpp_paths->hashwlock[hash_idx]);
- read_unlock(&pathtbl_resize_lock);
+ spin_unlock_bh(&mpp_paths->hashwlock[hash_idx]);
+ read_unlock_bh(&pathtbl_resize_lock);
if (grow) {
set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags);
ieee80211_queue_work(&local->hw, &sdata->work);
@@ -430,8 +436,8 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
return 0;
err_exists:
- spin_unlock(&mpp_paths->hashwlock[hash_idx]);
- read_unlock(&pathtbl_resize_lock);
+ spin_unlock_bh(&mpp_paths->hashwlock[hash_idx]);
+ read_unlock_bh(&pathtbl_resize_lock);
kfree(new_node);
err_node_alloc:
kfree(new_mpath);
@@ -544,11 +550,11 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
int hash_idx;
int err = 0;
- read_lock(&pathtbl_resize_lock);
+ read_lock_bh(&pathtbl_resize_lock);
hash_idx = mesh_table_hash(addr, sdata, mesh_paths);
bucket = &mesh_paths->hash_buckets[hash_idx];
- spin_lock(&mesh_paths->hashwlock[hash_idx]);
+ spin_lock_bh(&mesh_paths->hashwlock[hash_idx]);
hlist_for_each_entry(node, n, bucket, list) {
mpath = node->mpath;
if (mpath->sdata == sdata &&
@@ -566,8 +572,8 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
err = -ENXIO;
enddel:
mesh_paths_generation++;
- spin_unlock(&mesh_paths->hashwlock[hash_idx]);
- read_unlock(&pathtbl_resize_lock);
+ spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]);
+ read_unlock_bh(&pathtbl_resize_lock);
return err;
}
@@ -719,7 +725,7 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata)
struct hlist_node *p;
int i;
- read_lock(&pathtbl_resize_lock);
+ read_lock_bh(&pathtbl_resize_lock);
for_each_mesh_entry(mesh_paths, p, node, i) {
if (node->mpath->sdata != sdata)
continue;
@@ -734,7 +740,7 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata)
} else
spin_unlock_bh(&mpath->state_lock);
}
- read_unlock(&pathtbl_resize_lock);
+ read_unlock_bh(&pathtbl_resize_lock);
}
void mesh_pathtbl_unregister(void)
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 44b53931ba5..2c37bee3095 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -43,7 +43,7 @@
#define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks)
enum plink_frame_type {
- PLINK_OPEN = 0,
+ PLINK_OPEN = 1,
PLINK_CONFIRM,
PLINK_CLOSE
};
@@ -105,7 +105,7 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
if (!sta)
return NULL;
- sta->flags = WLAN_STA_AUTHORIZED;
+ sta->flags = WLAN_STA_AUTHORIZED | WLAN_STA_AUTH;
sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
rate_control_rate_init(sta);
@@ -161,7 +161,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
__le16 reason) {
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 +
- sdata->u.mesh.vendor_ie_len);
+ sdata->u.mesh.ie_len);
struct ieee80211_mgmt *mgmt;
bool include_plid = false;
static const u8 meshpeeringproto[] = { 0x00, 0x0F, 0xAC, 0x2A };
@@ -181,8 +181,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
IEEE80211_STYPE_ACTION);
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
- /* BSSID is left zeroed, wildcard value */
- mgmt->u.action.category = WLAN_CATEGORY_MESH_PLINK;
+ memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION;
mgmt->u.action.u.plink_action.action_code = action;
if (action == PLINK_CLOSE)
@@ -237,8 +237,9 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
return 0;
}
-void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data *sdata,
- bool peer_accepting_plinks)
+void mesh_neighbour_update(u8 *hw_addr, u32 rates,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee802_11_elems *elems)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
@@ -248,8 +249,14 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data
sta = sta_info_get(sdata, hw_addr);
if (!sta) {
rcu_read_unlock();
-
- sta = mesh_plink_alloc(sdata, hw_addr, rates);
+ /* Userspace handles peer allocation when security is enabled
+ * */
+ if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
+ cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr,
+ elems->ie_start, elems->total_len,
+ GFP_KERNEL);
+ else
+ sta = mesh_plink_alloc(sdata, hw_addr, rates);
if (!sta)
return;
if (sta_info_insert_rcu(sta)) {
@@ -260,7 +267,8 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data
sta->last_rx = jiffies;
sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
- if (peer_accepting_plinks && sta->plink_state == PLINK_LISTEN &&
+ if (mesh_peer_accepts_plinks(elems) &&
+ sta->plink_state == PLINK_LISTEN &&
sdata->u.mesh.accepting_plinks &&
sdata->u.mesh.mshcfg.auto_open_plinks)
mesh_plink_open(sta);
@@ -372,6 +380,9 @@ int mesh_plink_open(struct sta_info *sta)
__le16 llid;
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ if (!test_sta_flags(sta, WLAN_STA_AUTH))
+ return -EPERM;
+
spin_lock_bh(&sta->lock);
get_random_bytes(&llid, 2);
sta->llid = llid;
@@ -449,6 +460,11 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
mpl_dbg("Mesh plink: missing necessary peer link ie\n");
return;
}
+ if (elems.rsn_len &&
+ sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
+ mpl_dbg("Mesh plink: can't establish link with secure peer\n");
+ return;
+ }
ftype = mgmt->u.action.u.plink_action.action_code;
ie_len = elems.peer_link_len;
@@ -480,6 +496,12 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
return;
}
+ if (sta && !test_sta_flags(sta, WLAN_STA_AUTH)) {
+ mpl_dbg("Mesh plink: Action frame from non-authed peer\n");
+ rcu_read_unlock();
+ return;
+ }
+
if (sta && sta->plink_state == PLINK_BLOCKED) {
rcu_read_unlock();
return;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 64d92d5a7f4..4f6b2675e41 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -90,20 +90,11 @@ enum rx_mgmt_action {
/* no action required */
RX_MGMT_NONE,
- /* caller must call cfg80211_send_rx_auth() */
- RX_MGMT_CFG80211_AUTH,
-
- /* caller must call cfg80211_send_rx_assoc() */
- RX_MGMT_CFG80211_ASSOC,
-
/* caller must call cfg80211_send_deauth() */
RX_MGMT_CFG80211_DEAUTH,
/* caller must call cfg80211_send_disassoc() */
RX_MGMT_CFG80211_DISASSOC,
-
- /* caller must tell cfg80211 about internal error */
- RX_MGMT_CFG80211_ASSOC_ERROR,
};
/* utils */
@@ -759,6 +750,8 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
dynamic_ps_enable_work);
struct ieee80211_sub_if_data *sdata = local->ps_sdata;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ unsigned long flags;
+ int q;
/* can only happen when PS was just disabled anyway */
if (!sdata)
@@ -767,18 +760,37 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
if (local->hw.conf.flags & IEEE80211_CONF_PS)
return;
+ /*
+ * transmission can be stopped by others which leads to
+ * dynamic_ps_timer expiry. Postpond the ps timer if it
+ * is not the actual idle state.
+ */
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ for (q = 0; q < local->hw.queues; q++) {
+ if (local->queue_stop_reasons[q]) {
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock,
+ flags);
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(
+ local->hw.conf.dynamic_ps_timeout));
+ return;
+ }
+ }
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
(!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED))) {
netif_tx_stop_all_queues(sdata->dev);
- /*
- * Flush all the frames queued in the driver before
- * going to power save
- */
- drv_flush(local, false);
- ieee80211_send_nullfunc(local, sdata, 1);
- /* Flush once again to get the tx status of nullfunc frame */
- drv_flush(local, false);
+ if (drv_tx_frames_pending(local))
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(
+ local->hw.conf.dynamic_ps_timeout));
+ else {
+ ieee80211_send_nullfunc(local, sdata, 1);
+ /* Flush to get the tx status of nullfunc frame */
+ drv_flush(local, false);
+ }
}
if (!((local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
@@ -789,7 +801,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
- netif_tx_start_all_queues(sdata->dev);
+ netif_tx_wake_all_queues(sdata->dev);
}
void ieee80211_dynamic_ps_timer(unsigned long data)
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index e37355193ed..730778a2c90 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -6,7 +6,7 @@
#include "driver-ops.h"
#include "led.h"
-int __ieee80211_suspend(struct ieee80211_hw *hw)
+int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
@@ -14,12 +14,23 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
ieee80211_scan_cancel(local);
+ if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
+ mutex_lock(&local->sta_mtx);
+ list_for_each_entry(sta, &local->sta_list, list) {
+ set_sta_flags(sta, WLAN_STA_BLOCK_BA);
+ ieee80211_sta_tear_down_BA_sessions(sta, true);
+ }
+ mutex_unlock(&local->sta_mtx);
+ }
+
ieee80211_stop_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
/* flush out all packets */
synchronize_net();
+ drv_flush(local, false);
+
local->quiescing = true;
/* make quiescing visible to timers everywhere */
mb();
@@ -36,6 +47,16 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
cancel_work_sync(&local->dynamic_ps_enable_work);
del_timer_sync(&local->dynamic_ps_timer);
+ local->wowlan = wowlan && local->open_count;
+ if (local->wowlan) {
+ int err = drv_suspend(local, wowlan);
+ if (err) {
+ local->quiescing = false;
+ return err;
+ }
+ goto suspend;
+ }
+
/* disable keys */
list_for_each_entry(sdata, &local->interfaces, list)
ieee80211_disable_keys(sdata);
@@ -43,11 +64,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
/* tear down aggregation sessions and remove STAs */
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
- if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
- set_sta_flags(sta, WLAN_STA_BLOCK_BA);
- ieee80211_sta_tear_down_BA_sessions(sta, true);
- }
-
if (sta->uploaded) {
sdata = sta->sdata;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -98,6 +114,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
if (local->open_count)
ieee80211_stop_device(local);
+ suspend:
local->suspended = true;
/* need suspended to be visible before quiescing is false */
barrier();
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 2afeac9c645..1b9413fb383 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -143,7 +143,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
if (status->flag & RX_FLAG_HT) {
/*
* MCS information is a separate field in radiotap,
- * added below.
+ * added below. The byte here is needed as padding
+ * for the channel though, so initialise it to 0.
*/
*pos = 0;
} else {
@@ -403,11 +404,13 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
struct sk_buff *skb = rx->skb;
- if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN)))
+ if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN) &&
+ !local->sched_scanning))
return RX_CONTINUE;
if (test_bit(SCAN_HW_SCANNING, &local->scanning) ||
- test_bit(SCAN_SW_SCANNING, &local->scanning))
+ test_bit(SCAN_SW_SCANNING, &local->scanning) ||
+ local->sched_scanning)
return ieee80211_scan_rx(rx->sdata, skb);
/* scanning finished during invoking of handlers */
@@ -494,15 +497,19 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
return RX_DROP_MONITOR;
if (ieee80211_is_action(hdr->frame_control)) {
+ u8 category;
mgmt = (struct ieee80211_mgmt *)hdr;
- if (mgmt->u.action.category != WLAN_CATEGORY_MESH_PLINK)
+ category = mgmt->u.action.category;
+ if (category != WLAN_CATEGORY_MESH_ACTION &&
+ category != WLAN_CATEGORY_SELF_PROTECTED)
return RX_DROP_MONITOR;
return RX_CONTINUE;
}
if (ieee80211_is_probe_req(hdr->frame_control) ||
ieee80211_is_probe_resp(hdr->frame_control) ||
- ieee80211_is_beacon(hdr->frame_control))
+ ieee80211_is_beacon(hdr->frame_control) ||
+ ieee80211_is_auth(hdr->frame_control))
return RX_CONTINUE;
return RX_DROP_MONITOR;
@@ -650,7 +657,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw,
set_release_timer:
mod_timer(&tid_agg_rx->reorder_timer,
- tid_agg_rx->reorder_time[j] +
+ tid_agg_rx->reorder_time[j] + 1 +
HT_RX_REORDER_BUF_TIMEOUT);
} else {
del_timer(&tid_agg_rx->reorder_timer);
@@ -707,6 +714,8 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
/*
* If the current MPDU is in the right order and nothing else
* is stored we can process it directly, no need to buffer it.
+ * If it is first but there's something stored, we may be able
+ * to release frames after this one.
*/
if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
tid_agg_rx->stored_mpdu_num == 0) {
@@ -1583,7 +1592,7 @@ ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
}
static int
-__ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
+__ieee80211_data_to_8023(struct ieee80211_rx_data *rx, bool *port_control)
{
struct ieee80211_sub_if_data *sdata = rx->sdata;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
@@ -1591,6 +1600,7 @@ __ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
struct ethhdr *ehdr;
int ret;
+ *port_control = false;
if (ieee80211_has_a4(hdr->frame_control) &&
sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sdata->u.vlan.sta)
return -1;
@@ -1609,11 +1619,13 @@ __ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
return -1;
ret = ieee80211_data_to_8023(rx->skb, sdata->vif.addr, sdata->vif.type);
- if (ret < 0 || !check_port_control)
+ if (ret < 0)
return ret;
ehdr = (struct ethhdr *) rx->skb->data;
- if (ehdr->h_proto != rx->sdata->control_port_protocol)
+ if (ehdr->h_proto == rx->sdata->control_port_protocol)
+ *port_control = true;
+ else if (check_port_control)
return -1;
return 0;
@@ -1914,6 +1926,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
struct net_device *dev = sdata->dev;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
__le16 fc = hdr->frame_control;
+ bool port_control;
int err;
if (unlikely(!ieee80211_is_data(hdr->frame_control)))
@@ -1930,13 +1943,21 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
sdata->vif.type == NL80211_IFTYPE_AP)
return RX_DROP_MONITOR;
- err = __ieee80211_data_to_8023(rx);
+ err = __ieee80211_data_to_8023(rx, &port_control);
if (unlikely(err))
return RX_DROP_UNUSABLE;
if (!ieee80211_frame_allowed(rx, fc))
return RX_DROP_MONITOR;
+ if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+ unlikely(port_control) && sdata->bss) {
+ sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
+ u.ap);
+ dev = sdata->dev;
+ rx->sdata = sdata;
+ }
+
rx->skb->dev = dev;
dev->stats.rx_packets++;
@@ -2189,7 +2210,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
goto handled;
}
break;
- case WLAN_CATEGORY_MESH_PLINK:
+ case WLAN_CATEGORY_MESH_ACTION:
if (!ieee80211_vif_is_mesh(&sdata->vif))
break;
goto queue;
@@ -2352,47 +2373,6 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
return RX_QUEUED;
}
-static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr,
- struct ieee80211_rx_data *rx)
-{
- int keyidx;
- unsigned int hdrlen;
-
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
- if (rx->skb->len >= hdrlen + 4)
- keyidx = rx->skb->data[hdrlen + 3] >> 6;
- else
- keyidx = -1;
-
- if (!rx->sta) {
- /*
- * Some hardware seem to generate incorrect Michael MIC
- * reports; ignore them to avoid triggering countermeasures.
- */
- return;
- }
-
- if (!ieee80211_has_protected(hdr->frame_control))
- return;
-
- if (rx->sdata->vif.type == NL80211_IFTYPE_AP && keyidx) {
- /*
- * APs with pairwise keys should never receive Michael MIC
- * errors for non-zero keyidx because these are reserved for
- * group keys and only the AP is sending real multicast
- * frames in the BSS.
- */
- return;
- }
-
- if (!ieee80211_is_data(hdr->frame_control) &&
- !ieee80211_is_auth(hdr->frame_control))
- return;
-
- mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL,
- GFP_ATOMIC);
-}
-
/* TODO: use IEEE80211_RX_FRAGMENTED */
static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
struct ieee80211_rate *rate)
@@ -2736,12 +2716,6 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
if (!prepares)
return false;
- if (status->flag & RX_FLAG_MMIC_ERROR) {
- if (status->rx_flags & IEEE80211_RX_RA_MATCH)
- ieee80211_rx_michael_mic_report(hdr, rx);
- return false;
- }
-
if (!consume) {
skb = skb_copy(skb, GFP_ATOMIC);
if (!skb) {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 489b6ad200d..ea44a8e941e 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -15,6 +15,7 @@
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/pm_qos_params.h>
+#include <linux/slab.h>
#include <net/sch_generic.h>
#include <linux/slab.h>
#include <net/mac80211.h>
@@ -170,7 +171,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
return RX_CONTINUE;
if (skb->len < 24)
- return RX_DROP_MONITOR;
+ return RX_CONTINUE;
presp = ieee80211_is_probe_resp(fc);
if (presp) {
@@ -850,3 +851,101 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
}
mutex_unlock(&local->mtx);
}
+
+int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_sched_scan_request *req)
+{
+ struct ieee80211_local *local = sdata->local;
+ int ret, i;
+
+ mutex_lock(&sdata->local->mtx);
+
+ if (local->sched_scanning) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (!local->ops->sched_scan_start) {
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
+ local->sched_scan_ies.ie[i] = kzalloc(2 +
+ IEEE80211_MAX_SSID_LEN +
+ local->scan_ies_len,
+ GFP_KERNEL);
+ if (!local->sched_scan_ies.ie[i]) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ local->sched_scan_ies.len[i] =
+ ieee80211_build_preq_ies(local,
+ local->sched_scan_ies.ie[i],
+ req->ie, req->ie_len, i,
+ (u32) -1, 0);
+ }
+
+ ret = drv_sched_scan_start(local, sdata, req,
+ &local->sched_scan_ies);
+ if (ret == 0) {
+ local->sched_scanning = true;
+ goto out;
+ }
+
+out_free:
+ while (i > 0)
+ kfree(local->sched_scan_ies.ie[--i]);
+out:
+ mutex_unlock(&sdata->local->mtx);
+ return ret;
+}
+
+int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata,
+ bool driver_initiated)
+{
+ struct ieee80211_local *local = sdata->local;
+ int ret = 0, i;
+
+ mutex_lock(&sdata->local->mtx);
+
+ if (!local->ops->sched_scan_stop) {
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ if (local->sched_scanning) {
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++)
+ kfree(local->sched_scan_ies.ie[i]);
+
+ if (!driver_initiated)
+ drv_sched_scan_stop(local, sdata);
+ local->sched_scanning = false;
+ }
+
+out:
+ mutex_unlock(&sdata->local->mtx);
+
+ return ret;
+}
+
+void ieee80211_sched_scan_results(struct ieee80211_hw *hw)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ trace_api_sched_scan_results(local);
+
+ cfg80211_sched_scan_results(hw->wiphy);
+}
+EXPORT_SYMBOL(ieee80211_sched_scan_results);
+
+void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ trace_api_sched_scan_stopped(local);
+
+ cfg80211_sched_scan_stopped(hw->wiphy);
+}
+EXPORT_SYMBOL(ieee80211_sched_scan_stopped);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index d0311a322dd..f05244dc773 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -228,6 +228,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
+ struct timespec uptime;
int i;
sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
@@ -245,6 +246,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
sta->sdata = sdata;
sta->last_rx = jiffies;
+ do_posix_clock_monotonic_gettime(&uptime);
+ sta->last_connected = uptime.tv_sec;
ewma_init(&sta->avg_signal, 1024, 8);
if (sta_prepare_rate_control(local, sta, gfp)) {
@@ -584,7 +587,6 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
{
unsigned long flags;
struct sk_buff *skb;
- struct ieee80211_sub_if_data *sdata;
if (skb_queue_empty(&sta->ps_tx_buf))
return false;
@@ -601,7 +603,6 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
if (!skb)
break;
- sdata = sta->sdata;
local->total_ps_buffered--;
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "Buffered frame expired (STA %pM)\n",
@@ -609,7 +610,8 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
#endif
dev_kfree_skb(skb);
- if (skb_queue_empty(&sta->ps_tx_buf))
+ if (skb_queue_empty(&sta->ps_tx_buf) &&
+ !test_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF))
sta_info_clear_tim_bit(sta);
}
@@ -698,6 +700,8 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
cancel_work_sync(&sta->drv_unblock_wk);
+ cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL);
+
rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
@@ -766,9 +770,8 @@ static void sta_info_cleanup(unsigned long data)
if (!timer_needed)
return;
- local->sta_cleanup.expires =
- round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
- add_timer(&local->sta_cleanup);
+ mod_timer(&local->sta_cleanup,
+ round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL));
}
void sta_info_init(struct ieee80211_local *local)
@@ -781,14 +784,6 @@ void sta_info_init(struct ieee80211_local *local)
setup_timer(&local->sta_cleanup, sta_info_cleanup,
(unsigned long)local);
- local->sta_cleanup.expires =
- round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
-}
-
-int sta_info_start(struct ieee80211_local *local)
-{
- add_timer(&local->sta_cleanup);
- return 0;
}
void sta_info_stop(struct ieee80211_local *local)
@@ -900,6 +895,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
struct ieee80211_local *local = sdata->local;
int sent, buffered;
+ clear_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF);
if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta);
@@ -992,3 +988,12 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
ieee80211_queue_work(hw, &sta->drv_unblock_wk);
}
EXPORT_SYMBOL(ieee80211_sta_block_awake);
+
+void ieee80211_sta_set_tim(struct ieee80211_sta *pubsta)
+{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+
+ set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF);
+ sta_info_set_tim_bit(sta);
+}
+EXPORT_SYMBOL(ieee80211_sta_set_tim);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 57681149e37..f00b4dcb49d 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -43,6 +43,8 @@
* be in the queues
* @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping
* station in power-save mode, reply when the driver unblocks.
+ * @WLAN_STA_PS_DRIVER_BUF: Station has frames pending in driver internal
+ * buffers. Automatically cleared on station wake-up.
*/
enum ieee80211_sta_info_flags {
WLAN_STA_AUTH = 1<<0,
@@ -58,6 +60,7 @@ enum ieee80211_sta_info_flags {
WLAN_STA_BLOCK_BA = 1<<11,
WLAN_STA_PS_DRIVER = 1<<12,
WLAN_STA_PSPOLL = 1<<13,
+ WLAN_STA_PS_DRIVER_BUF = 1<<14,
};
#define STA_TID_NUM 16
@@ -171,29 +174,6 @@ struct sta_ampdu_mlme {
/**
- * enum plink_state - state of a mesh peer link finite state machine
- *
- * @PLINK_LISTEN: initial state, considered the implicit state of non existant
- * mesh peer links
- * @PLINK_OPN_SNT: mesh plink open frame has been sent to this mesh peer
- * @PLINK_OPN_RCVD: mesh plink open frame has been received from this mesh peer
- * @PLINK_CNF_RCVD: mesh plink confirm frame has been received from this mesh
- * peer
- * @PLINK_ESTAB: mesh peer link is established
- * @PLINK_HOLDING: mesh peer link is being closed or cancelled
- * @PLINK_BLOCKED: all frames transmitted from this mesh plink are discarded
- */
-enum plink_state {
- PLINK_LISTEN,
- PLINK_OPN_SNT,
- PLINK_OPN_RCVD,
- PLINK_CNF_RCVD,
- PLINK_ESTAB,
- PLINK_HOLDING,
- PLINK_BLOCKED
-};
-
-/**
* struct sta_info - STA information
*
* This structure collects information about a station that
@@ -226,6 +206,7 @@ enum plink_state {
* @rx_bytes: Number of bytes received from this STA
* @wep_weak_iv_count: number of weak WEP IVs received from this station
* @last_rx: time (in jiffies) when last frame was received from this STA
+ * @last_connected: time (in seconds) when a station got connected
* @num_duplicates: number of duplicate frames received from this STA
* @rx_fragments: number of received MPDUs
* @rx_dropped: number of dropped MPDUs from this STA
@@ -295,6 +276,7 @@ struct sta_info {
unsigned long rx_packets, rx_bytes;
unsigned long wep_weak_iv_count;
unsigned long last_rx;
+ long last_connected;
unsigned long num_duplicates;
unsigned long rx_fragments;
unsigned long rx_dropped;
@@ -497,7 +479,6 @@ void sta_info_set_tim_bit(struct sta_info *sta);
void sta_info_clear_tim_bit(struct sta_info *sta);
void sta_info_init(struct ieee80211_local *local);
-int sta_info_start(struct ieee80211_local *local);
void sta_info_stop(struct ieee80211_local *local);
int sta_info_flush(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata);
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index b936dd29e92..1658efaa2e8 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -189,16 +189,19 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
bool acked;
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
- /* the HW cannot have attempted that rate */
- if (i >= hw->max_report_rates) {
+ if (info->status.rates[i].idx < 0) {
+ break;
+ } else if (i >= hw->max_report_rates) {
+ /* the HW cannot have attempted that rate */
info->status.rates[i].idx = -1;
info->status.rates[i].count = 0;
- } else if (info->status.rates[i].idx >= 0) {
- rates_idx = i;
+ break;
}
retry_count += info->status.rates[i].count;
}
+ rates_idx = i - 1;
+
if (retry_count < 0)
retry_count = 0;
@@ -443,3 +446,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
dev_kfree_skb(skb);
}
EXPORT_SYMBOL(ieee80211_tx_status);
+
+void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets)
+{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+ cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr,
+ num_packets, GFP_ATOMIC);
+}
+EXPORT_SYMBOL(ieee80211_report_low_ack);
diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c
index e840c9cd46d..757e4eb2baf 100644
--- a/net/mac80211/tkip.c
+++ b/net/mac80211/tkip.c
@@ -202,7 +202,7 @@ EXPORT_SYMBOL(ieee80211_get_tkip_key);
* @payload_len is the length of payload (_not_ including IV/ICV length).
* @ta is the transmitter addresses.
*/
-int ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm,
+int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm,
struct ieee80211_key *key,
u8 *pos, size_t payload_len, u8 *ta)
{
@@ -223,7 +223,7 @@ int ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm,
* beginning of the buffer containing IEEE 802.11 header payload, i.e.,
* including IV, Ext. IV, real data, Michael MIC, ICV. @payload_len is the
* length of payload, including IV, Ext. IV, MIC, ICV. */
-int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
+int ieee80211_tkip_decrypt_data(struct crypto_cipher *tfm,
struct ieee80211_key *key,
u8 *payload, size_t payload_len, u8 *ta,
u8 *ra, int only_iv, int queue,
diff --git a/net/mac80211/tkip.h b/net/mac80211/tkip.h
index 7e83dee976f..1cab9c86978 100644
--- a/net/mac80211/tkip.h
+++ b/net/mac80211/tkip.h
@@ -15,7 +15,7 @@
u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, u16 iv16);
-int ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm,
+int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm,
struct ieee80211_key *key,
u8 *pos, size_t payload_len, u8 *ta);
enum {
@@ -24,7 +24,7 @@ enum {
TKIP_DECRYPT_INVALID_KEYIDX = -2,
TKIP_DECRYPT_REPLAY = -3,
};
-int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
+int ieee80211_tkip_decrypt_data(struct crypto_cipher *tfm,
struct ieee80211_key *key,
u8 *payload, size_t payload_len, u8 *ta,
u8 *ra, int only_iv, int queue,
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index ce4596ed126..c9f12113ca6 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1036,14 +1036,11 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
struct ieee80211_radiotap_iterator iterator;
struct ieee80211_radiotap_header *rthdr =
(struct ieee80211_radiotap_header *) skb->data;
- struct ieee80211_supported_band *sband;
bool hw_frag;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len,
NULL);
- sband = tx->local->hw.wiphy->bands[tx->channel->band];
-
info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
tx->flags &= ~IEEE80211_TX_FRAGMENTED;
@@ -1442,11 +1439,8 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
struct ieee80211_tx_data tx;
ieee80211_tx_result res_prepare;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- u16 queue;
bool result = true;
- queue = skb_get_queue_mapping(skb);
-
if (unlikely(skb->len < 10)) {
dev_kfree_skb(skb);
return true;
@@ -1482,12 +1476,7 @@ static int ieee80211_skb_resize(struct ieee80211_local *local,
{
int tail_need = 0;
- /*
- * This could be optimised, devices that do full hardware
- * crypto (including TKIP MMIC) need no tailroom... But we
- * have no drivers for such devices currently.
- */
- if (may_encrypt) {
+ if (may_encrypt && local->crypto_tx_tailroom_needed_cnt) {
tail_need = IEEE80211_ENCRYPT_TAILROOM;
tail_need -= skb_tailroom(skb);
tail_need = max_t(int, tail_need, 0);
@@ -1762,6 +1751,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
ret = NETDEV_TX_OK;
goto fail;
}
+ rcu_read_lock();
if (!is_multicast_ether_addr(skb->data))
mppath = mpp_path_lookup(skb->data, sdata);
@@ -1776,13 +1766,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
!(mppath && compare_ether_addr(mppath->mpp, skb->data))) {
hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
skb->data, skb->data + ETH_ALEN);
+ rcu_read_unlock();
meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
sdata, NULL, NULL);
} else {
int is_mesh_mcast = 1;
const u8 *mesh_da;
- rcu_read_lock();
if (is_multicast_ether_addr(skb->data))
/* DA TA mSA AE:SA */
mesh_da = skb->data;
@@ -2262,7 +2252,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
/* headroom, head length, tail length and maximum TIM length */
skb = dev_alloc_skb(local->tx_headroom + 400 +
- sdata->u.mesh.vendor_ie_len);
+ sdata->u.mesh.ie_len);
if (!skb)
goto out;
@@ -2485,7 +2475,6 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
{
struct ieee80211_local *local = hw_to_local(hw);
struct sk_buff *skb = NULL;
- struct sta_info *sta;
struct ieee80211_tx_data tx;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_ap *bss = NULL;
@@ -2527,7 +2516,6 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
info = IEEE80211_SKB_CB(skb);
- sta = tx.sta;
tx.flags |= IEEE80211_TX_PS_BUFFERED;
tx.channel = local->hw.conf.channel;
info->band = tx.channel->band;
@@ -2547,8 +2535,9 @@ void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
skb_set_network_header(skb, 0);
skb_set_transport_header(skb, 0);
- /* send all internal mgmt frames on VO */
- skb_set_queue_mapping(skb, 0);
+ /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
+ skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+ skb->priority = 7;
/*
* The other path calling ieee80211_xmit is from the tasklet,
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 556647a910a..d3fe2d23748 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1125,9 +1125,27 @@ int ieee80211_reconfig(struct ieee80211_local *local)
struct sta_info *sta;
int res;
+#ifdef CONFIG_PM
if (local->suspended)
local->resuming = true;
+ if (local->wowlan) {
+ local->wowlan = false;
+ res = drv_resume(local);
+ if (res < 0) {
+ local->resuming = false;
+ return res;
+ }
+ if (res == 0)
+ goto wake_up;
+ WARN_ON(res > 1);
+ /*
+ * res is 1, which means the driver requested
+ * to go through a regular reset on wakeup.
+ */
+ }
+#endif
+
/* restart hardware */
if (local->open_count) {
/*
@@ -1258,6 +1276,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (ieee80211_sdata_running(sdata))
ieee80211_enable_keys(sdata);
+ wake_up:
ieee80211_wake_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
@@ -1290,7 +1309,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
}
- add_timer(&local->sta_cleanup);
+ mod_timer(&local->sta_cleanup, jiffies + 1);
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list)
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index 2ff6d1e3ed2..a1c6bfd55f0 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -30,17 +30,15 @@ int ieee80211_wep_init(struct ieee80211_local *local)
/* start WEP IV from a random value */
get_random_bytes(&local->wep_iv, WEP_IV_LEN);
- local->wep_tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0,
- CRYPTO_ALG_ASYNC);
+ local->wep_tx_tfm = crypto_alloc_cipher("arc4", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(local->wep_tx_tfm)) {
local->wep_rx_tfm = ERR_PTR(-EINVAL);
return PTR_ERR(local->wep_tx_tfm);
}
- local->wep_rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0,
- CRYPTO_ALG_ASYNC);
+ local->wep_rx_tfm = crypto_alloc_cipher("arc4", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(local->wep_rx_tfm)) {
- crypto_free_blkcipher(local->wep_tx_tfm);
+ crypto_free_cipher(local->wep_tx_tfm);
local->wep_tx_tfm = ERR_PTR(-EINVAL);
return PTR_ERR(local->wep_rx_tfm);
}
@@ -51,9 +49,9 @@ int ieee80211_wep_init(struct ieee80211_local *local)
void ieee80211_wep_free(struct ieee80211_local *local)
{
if (!IS_ERR(local->wep_tx_tfm))
- crypto_free_blkcipher(local->wep_tx_tfm);
+ crypto_free_cipher(local->wep_tx_tfm);
if (!IS_ERR(local->wep_rx_tfm))
- crypto_free_blkcipher(local->wep_rx_tfm);
+ crypto_free_cipher(local->wep_rx_tfm);
}
static inline bool ieee80211_wep_weak_iv(u32 iv, int keylen)
@@ -127,12 +125,11 @@ static void ieee80211_wep_remove_iv(struct ieee80211_local *local,
/* Perform WEP encryption using given key. data buffer must have tailroom
* for 4-byte ICV. data_len must not include this ICV. Note: this function
* does _not_ add IV. data = RC4(data | CRC32(data)) */
-int ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key,
+int ieee80211_wep_encrypt_data(struct crypto_cipher *tfm, u8 *rc4key,
size_t klen, u8 *data, size_t data_len)
{
- struct blkcipher_desc desc = { .tfm = tfm };
- struct scatterlist sg;
__le32 icv;
+ int i;
if (IS_ERR(tfm))
return -1;
@@ -140,9 +137,9 @@ int ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key,
icv = cpu_to_le32(~crc32_le(~0, data, data_len));
put_unaligned(icv, (__le32 *)(data + data_len));
- crypto_blkcipher_setkey(tfm, rc4key, klen);
- sg_init_one(&sg, data, data_len + WEP_ICV_LEN);
- crypto_blkcipher_encrypt(&desc, &sg, &sg, sg.length);
+ crypto_cipher_setkey(tfm, rc4key, klen);
+ for (i = 0; i < data_len + WEP_ICV_LEN; i++)
+ crypto_cipher_encrypt_one(tfm, data + i, data + i);
return 0;
}
@@ -186,19 +183,18 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local,
/* Perform WEP decryption using given key. data buffer includes encrypted
* payload, including 4-byte ICV, but _not_ IV. data_len must not include ICV.
* Return 0 on success and -1 on ICV mismatch. */
-int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key,
+int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key,
size_t klen, u8 *data, size_t data_len)
{
- struct blkcipher_desc desc = { .tfm = tfm };
- struct scatterlist sg;
__le32 crc;
+ int i;
if (IS_ERR(tfm))
return -1;
- crypto_blkcipher_setkey(tfm, rc4key, klen);
- sg_init_one(&sg, data, data_len + WEP_ICV_LEN);
- crypto_blkcipher_decrypt(&desc, &sg, &sg, sg.length);
+ crypto_cipher_setkey(tfm, rc4key, klen);
+ for (i = 0; i < data_len + WEP_ICV_LEN; i++)
+ crypto_cipher_decrypt_one(tfm, data + i, data + i);
crc = cpu_to_le32(~crc32_le(~0, data, data_len));
if (memcmp(&crc, data + data_len, WEP_ICV_LEN) != 0)
diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h
index 58654ee3351..01e54840a62 100644
--- a/net/mac80211/wep.h
+++ b/net/mac80211/wep.h
@@ -18,12 +18,12 @@
int ieee80211_wep_init(struct ieee80211_local *local);
void ieee80211_wep_free(struct ieee80211_local *local);
-int ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key,
+int ieee80211_wep_encrypt_data(struct crypto_cipher *tfm, u8 *rc4key,
size_t klen, u8 *data, size_t data_len);
int ieee80211_wep_encrypt(struct ieee80211_local *local,
struct sk_buff *skb,
const u8 *key, int keylen, int keyidx);
-int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key,
+int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key,
size_t klen, u8 *data, size_t data_len);
bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index e73c8cae036..a94b312dbfa 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -198,9 +198,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
u8 *pos, qos_info;
- const u8 *ies;
size_t offset = 0, noffset;
- int i, len, count, rates_len, supp_rates_len;
+ int i, count, rates_len, supp_rates_len;
u16 capab;
struct ieee80211_supported_band *sband;
u32 rates = 0;
@@ -285,7 +284,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
}
/* SSID */
- ies = pos = skb_put(skb, 2 + wk->assoc.ssid_len);
+ pos = skb_put(skb, 2 + wk->assoc.ssid_len);
*pos++ = WLAN_EID_SSID;
*pos++ = wk->assoc.ssid_len;
memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len);
@@ -295,7 +294,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
if (supp_rates_len > 8)
supp_rates_len = 8;
- len = sband->n_bitrates;
pos = skb_put(skb, supp_rates_len + 2);
*pos++ = WLAN_EID_SUPP_RATES;
*pos++ = supp_rates_len;
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index f1765de2f4b..9dc3b5f26e8 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -87,42 +87,76 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- /* No way to verify the MIC if the hardware stripped it */
- if (status->flag & RX_FLAG_MMIC_STRIPPED)
+ /*
+ * it makes no sense to check for MIC errors on anything other
+ * than data frames.
+ */
+ if (!ieee80211_is_data_present(hdr->frame_control))
+ return RX_CONTINUE;
+
+ /*
+ * No way to verify the MIC if the hardware stripped it or
+ * the IV with the key index. In this case we have solely rely
+ * on the driver to set RX_FLAG_MMIC_ERROR in the event of a
+ * MIC failure report.
+ */
+ if (status->flag & (RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED)) {
+ if (status->flag & RX_FLAG_MMIC_ERROR)
+ goto mic_fail;
+
+ if (!(status->flag & RX_FLAG_IV_STRIPPED))
+ goto update_iv;
+
return RX_CONTINUE;
+ }
+ /*
+ * Some hardware seems to generate Michael MIC failure reports; even
+ * though, the frame was not encrypted with TKIP and therefore has no
+ * MIC. Ignore the flag them to avoid triggering countermeasures.
+ */
if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP ||
- !ieee80211_has_protected(hdr->frame_control) ||
- !ieee80211_is_data_present(hdr->frame_control))
+ !(status->flag & RX_FLAG_DECRYPTED))
return RX_CONTINUE;
+ if (rx->sdata->vif.type == NL80211_IFTYPE_AP && rx->key->conf.keyidx) {
+ /*
+ * APs with pairwise keys should never receive Michael MIC
+ * errors for non-zero keyidx because these are reserved for
+ * group keys and only the AP is sending real multicast
+ * frames in the BSS. (
+ */
+ return RX_DROP_UNUSABLE;
+ }
+
+ if (status->flag & RX_FLAG_MMIC_ERROR)
+ goto mic_fail;
+
hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (skb->len < hdrlen + MICHAEL_MIC_LEN)
return RX_DROP_UNUSABLE;
data = skb->data + hdrlen;
data_len = skb->len - hdrlen - MICHAEL_MIC_LEN;
-
key = &rx->key->conf.key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY];
michael_mic(key, hdr, data, data_len, mic);
- if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0) {
- if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
- return RX_DROP_UNUSABLE;
-
- mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx,
- (void *) skb->data, NULL,
- GFP_ATOMIC);
- return RX_DROP_UNUSABLE;
- }
+ if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0)
+ goto mic_fail;
/* remove Michael MIC from payload */
skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
+update_iv:
/* update IV in key information to be able to detect replays */
rx->key->u.tkip.rx[rx->queue].iv32 = rx->tkip_iv32;
rx->key->u.tkip.rx[rx->queue].iv16 = rx->tkip_iv16;
return RX_CONTINUE;
+
+mic_fail:
+ mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx,
+ (void *) skb->data, NULL, GFP_ATOMIC);
+ return RX_DROP_UNUSABLE;
}
diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig
index 7fce6dfd218..48464ca13b2 100644
--- a/net/rfkill/Kconfig
+++ b/net/rfkill/Kconfig
@@ -22,3 +22,14 @@ config RFKILL_INPUT
depends on RFKILL
depends on INPUT = y || RFKILL = INPUT
default y if !EXPERT
+
+config RFKILL_REGULATOR
+ tristate "Generic rfkill regulator driver"
+ depends on RFKILL || !RFKILL
+ depends on REGULATOR
+ help
+ This options enable controlling radio transmitters connected to
+ voltage regulator using the regulator framework.
+
+ To compile this driver as a module, choose M here: the module will
+ be called rfkill-regulator.
diff --git a/net/rfkill/Makefile b/net/rfkill/Makefile
index 66210535269..d9a5a58ffd8 100644
--- a/net/rfkill/Makefile
+++ b/net/rfkill/Makefile
@@ -5,3 +5,4 @@
rfkill-y += core.o
rfkill-$(CONFIG_RFKILL_INPUT) += input.o
obj-$(CONFIG_RFKILL) += rfkill.o
+obj-$(CONFIG_RFKILL_REGULATOR) += rfkill-regulator.o
diff --git a/net/rfkill/rfkill-regulator.c b/net/rfkill/rfkill-regulator.c
new file mode 100644
index 00000000000..18dc512a10f
--- /dev/null
+++ b/net/rfkill/rfkill-regulator.c
@@ -0,0 +1,164 @@
+/*
+ * rfkill-regulator.c - Regulator consumer driver for rfkill
+ *
+ * Copyright (C) 2009 Guiming Zhuo <gmzhuo@gmail.com>
+ * Copyright (C) 2011 Antonio Ospite <ospite@studenti.unina.it>
+ *
+ * Implementation inspired by leds-regulator driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/rfkill.h>
+#include <linux/rfkill-regulator.h>
+
+struct rfkill_regulator_data {
+ struct rfkill *rf_kill;
+ bool reg_enabled;
+
+ struct regulator *vcc;
+};
+
+static int rfkill_regulator_set_block(void *data, bool blocked)
+{
+ struct rfkill_regulator_data *rfkill_data = data;
+
+ pr_debug("%s: blocked: %d\n", __func__, blocked);
+
+ if (blocked) {
+ if (rfkill_data->reg_enabled) {
+ regulator_disable(rfkill_data->vcc);
+ rfkill_data->reg_enabled = 0;
+ }
+ } else {
+ if (!rfkill_data->reg_enabled) {
+ regulator_enable(rfkill_data->vcc);
+ rfkill_data->reg_enabled = 1;
+ }
+ }
+
+ pr_debug("%s: regulator_is_enabled after set_block: %d\n", __func__,
+ regulator_is_enabled(rfkill_data->vcc));
+
+ return 0;
+}
+
+struct rfkill_ops rfkill_regulator_ops = {
+ .set_block = rfkill_regulator_set_block,
+};
+
+static int __devinit rfkill_regulator_probe(struct platform_device *pdev)
+{
+ struct rfkill_regulator_platform_data *pdata = pdev->dev.platform_data;
+ struct rfkill_regulator_data *rfkill_data;
+ struct regulator *vcc;
+ struct rfkill *rf_kill;
+ int ret = 0;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -ENODEV;
+ }
+
+ if (pdata->name == NULL || pdata->type == 0) {
+ dev_err(&pdev->dev, "invalid name or type in platform data\n");
+ return -EINVAL;
+ }
+
+ vcc = regulator_get_exclusive(&pdev->dev, "vrfkill");
+ if (IS_ERR(vcc)) {
+ dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name);
+ ret = PTR_ERR(vcc);
+ goto out;
+ }
+
+ rfkill_data = kzalloc(sizeof(*rfkill_data), GFP_KERNEL);
+ if (rfkill_data == NULL) {
+ ret = -ENOMEM;
+ goto err_data_alloc;
+ }
+
+ rf_kill = rfkill_alloc(pdata->name, &pdev->dev,
+ pdata->type,
+ &rfkill_regulator_ops, rfkill_data);
+ if (rf_kill == NULL) {
+ dev_err(&pdev->dev, "Cannot alloc rfkill device\n");
+ ret = -ENOMEM;
+ goto err_rfkill_alloc;
+ }
+
+ if (regulator_is_enabled(vcc)) {
+ dev_dbg(&pdev->dev, "Regulator already enabled\n");
+ rfkill_data->reg_enabled = 1;
+ }
+ rfkill_data->vcc = vcc;
+ rfkill_data->rf_kill = rf_kill;
+
+ ret = rfkill_register(rf_kill);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot register rfkill device\n");
+ goto err_rfkill_register;
+ }
+
+ platform_set_drvdata(pdev, rfkill_data);
+ dev_info(&pdev->dev, "%s initialized\n", pdata->name);
+
+ return 0;
+
+err_rfkill_register:
+ rfkill_destroy(rf_kill);
+err_rfkill_alloc:
+ kfree(rfkill_data);
+err_data_alloc:
+ regulator_put(vcc);
+out:
+ return ret;
+}
+
+static int __devexit rfkill_regulator_remove(struct platform_device *pdev)
+{
+ struct rfkill_regulator_data *rfkill_data = platform_get_drvdata(pdev);
+ struct rfkill *rf_kill = rfkill_data->rf_kill;
+
+ rfkill_unregister(rf_kill);
+ rfkill_destroy(rf_kill);
+ regulator_put(rfkill_data->vcc);
+ kfree(rfkill_data);
+
+ return 0;
+}
+
+static struct platform_driver rfkill_regulator_driver = {
+ .probe = rfkill_regulator_probe,
+ .remove = __devexit_p(rfkill_regulator_remove),
+ .driver = {
+ .name = "rfkill-regulator",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init rfkill_regulator_init(void)
+{
+ return platform_driver_register(&rfkill_regulator_driver);
+}
+module_init(rfkill_regulator_init);
+
+static void __exit rfkill_regulator_exit(void)
+{
+ platform_driver_unregister(&rfkill_regulator_driver);
+}
+module_exit(rfkill_regulator_exit);
+
+MODULE_AUTHOR("Guiming Zhuo <gmzhuo@gmail.com>");
+MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
+MODULE_DESCRIPTION("Regulator consumer driver for rfkill");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rfkill-regulator");
diff --git a/net/wireless/core.c b/net/wireless/core.c
index fe01de29bfe..f924a49b242 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -46,6 +46,11 @@ static struct dentry *ieee80211_debugfs_dir;
/* for the cleanup, scan and event works */
struct workqueue_struct *cfg80211_wq;
+static bool cfg80211_disable_40mhz_24ghz;
+module_param(cfg80211_disable_40mhz_24ghz, bool, 0644);
+MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz,
+ "Disable 40MHz support in the 2.4GHz band");
+
/* requires cfg80211_mutex to be held! */
struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
{
@@ -365,7 +370,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
spin_lock_init(&rdev->bss_lock);
INIT_LIST_HEAD(&rdev->bss_list);
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
-
+ INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
+ INIT_WORK(&rdev->sched_scan_stopped_wk, __cfg80211_sched_scan_stopped);
#ifdef CONFIG_CFG80211_WEXT
rdev->wiphy.wext = &cfg80211_wext_handler;
#endif
@@ -451,6 +457,18 @@ int wiphy_register(struct wiphy *wiphy)
return -EINVAL;
/*
+ * Since cfg80211_disable_40mhz_24ghz is global, we can
+ * modify the sband's ht data even if the driver uses a
+ * global structure for that.
+ */
+ if (cfg80211_disable_40mhz_24ghz &&
+ band == IEEE80211_BAND_2GHZ &&
+ sband->ht_cap.ht_supported) {
+ sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40;
+ }
+
+ /*
* Since we use a u32 for rate bitmaps in
* ieee80211_get_response_rate, we cannot
* have more than 32 legacy rates.
@@ -476,6 +494,13 @@ int wiphy_register(struct wiphy *wiphy)
return -EINVAL;
}
+ if (rdev->wiphy.wowlan.n_patterns) {
+ if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len ||
+ rdev->wiphy.wowlan.pattern_min_len >
+ rdev->wiphy.wowlan.pattern_max_len))
+ return -EINVAL;
+ }
+
/* check and set up bitrates */
ieee80211_set_bitrate_flags(wiphy);
@@ -614,6 +639,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
mutex_destroy(&rdev->devlist_mtx);
list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
cfg80211_put_bss(&scan->pub);
+ cfg80211_rdev_free_wowlan(rdev);
kfree(rdev);
}
@@ -647,6 +673,11 @@ static void wdev_cleanup_work(struct work_struct *work)
___cfg80211_scan_done(rdev, true);
}
+ if (WARN_ON(rdev->sched_scan_req &&
+ rdev->sched_scan_req->dev == wdev->netdev)) {
+ __cfg80211_stop_sched_scan(rdev, false);
+ }
+
cfg80211_unlock_rdev(rdev);
mutex_lock(&rdev->devlist_mtx);
@@ -734,6 +765,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
break;
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_STATION:
+ cfg80211_lock_rdev(rdev);
+ __cfg80211_stop_sched_scan(rdev, false);
+ cfg80211_unlock_rdev(rdev);
+
wdev_lock(wdev);
#ifdef CONFIG_CFG80211_WEXT
kfree(wdev->wext.ie);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 26a0a084e16..e3f7b1d995c 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -60,8 +60,11 @@ struct cfg80211_registered_device {
struct rb_root bss_tree;
u32 bss_generation;
struct cfg80211_scan_request *scan_req; /* protected by RTNL */
+ struct cfg80211_sched_scan_request *sched_scan_req;
unsigned long suspend_at;
struct work_struct scan_done_wk;
+ struct work_struct sched_scan_results_wk;
+ struct work_struct sched_scan_stopped_wk;
#ifdef CONFIG_NL80211_TESTMODE
struct genl_info *testmode_info;
@@ -70,6 +73,8 @@ struct cfg80211_registered_device {
struct work_struct conn_work;
struct work_struct event_work;
+ struct cfg80211_wowlan *wowlan;
+
/* 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)));
@@ -89,6 +94,18 @@ bool wiphy_idx_valid(int wiphy_idx)
return wiphy_idx >= 0;
}
+static inline void
+cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
+{
+ int i;
+
+ if (!rdev->wowlan)
+ return;
+ for (i = 0; i < rdev->wowlan->n_patterns; i++)
+ kfree(rdev->wowlan->patterns[i].mask);
+ kfree(rdev->wowlan->patterns);
+ kfree(rdev->wowlan);
+}
extern struct workqueue_struct *cfg80211_wq;
extern struct mutex cfg80211_mutex;
@@ -397,6 +414,10 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
void cfg80211_sme_disassoc(struct net_device *dev, int idx);
void __cfg80211_scan_done(struct work_struct *wk);
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak);
+void __cfg80211_sched_scan_results(struct work_struct *wk);
+int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
+ bool driver_initiated);
+void __cfg80211_sched_scan_stopped(struct work_struct *wk);
void cfg80211_upload_connect_keys(struct wireless_dev *wdev);
int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
struct net_device *dev, enum nl80211_iftype ntype,
diff --git a/net/wireless/lib80211_crypt_wep.c b/net/wireless/lib80211_crypt_wep.c
index e2e88878ba3..2f265e033ae 100644
--- a/net/wireless/lib80211_crypt_wep.c
+++ b/net/wireless/lib80211_crypt_wep.c
@@ -96,13 +96,12 @@ static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len,
u8 *key, int keylen, void *priv)
{
struct lib80211_wep_data *wep = priv;
- u32 klen, len;
+ u32 klen;
u8 *pos;
if (skb_headroom(skb) < 4 || skb->len < hdr_len)
return -1;
- len = skb->len - hdr_len;
pos = skb_push(skb, 4);
memmove(pos, pos + 4, hdr_len);
pos += hdr_len;
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 73e39c171ff..5c116083eec 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -1,5 +1,6 @@
#include <linux/ieee80211.h>
#include <net/cfg80211.h>
+#include "nl80211.h"
#include "core.h"
/* Default values, timeouts in ms */
@@ -53,8 +54,9 @@ const struct mesh_config default_mesh_config = {
const struct mesh_setup default_mesh_setup = {
.path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP,
.path_metric = IEEE80211_PATH_METRIC_AIRTIME,
- .vendor_ie = NULL,
- .vendor_ie_len = 0,
+ .ie = NULL,
+ .ie_len = 0,
+ .is_secure = false,
};
int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
@@ -72,6 +74,10 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
return -EOPNOTSUPP;
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
+ setup->is_secure)
+ return -EOPNOTSUPP;
+
if (wdev->mesh_id_len)
return -EALREADY;
@@ -105,6 +111,19 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
return err;
}
+void cfg80211_notify_new_peer_candidate(struct net_device *dev,
+ const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))
+ return;
+
+ nl80211_send_new_peer_candidate(wiphy_to_dev(wdev->wiphy), dev,
+ macaddr, ie, ie_len, gfp);
+}
+EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate);
+
static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev)
{
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index aa5df8865ff..493b939970c 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -770,6 +770,15 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
}
EXPORT_SYMBOL(cfg80211_new_sta);
+void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp);
+}
+EXPORT_SYMBOL(cfg80211_del_sta);
+
struct cfg80211_mgmt_registration {
struct list_head list;
@@ -954,6 +963,16 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
if (memcmp(mgmt->bssid, dev->dev_addr, ETH_ALEN))
err = -EINVAL;
break;
+ case NL80211_IFTYPE_MESH_POINT:
+ if (memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN)) {
+ err = -EINVAL;
+ break;
+ }
+ /*
+ * check for mesh DA must be done by driver as
+ * cfg80211 doesn't track the stations
+ */
+ break;
default:
err = -EOPNOTSUPP;
break;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 4ebce4284e9..b5b050b62f2 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -124,6 +124,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 },
[NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
+ [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
[NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
.len = NL80211_HT_CAPABILITY_LEN },
@@ -172,6 +173,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
[NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
[NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
+ [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
+ [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
+ [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
};
/* policy for the key attributes */
@@ -193,6 +197,15 @@ nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
[NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
};
+/* policy for WoWLAN attributes */
+static const struct nla_policy
+nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
+ [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
+};
+
/* ifidx get helper */
static int nl80211_get_ifidx(struct netlink_callback *cb)
{
@@ -533,6 +546,7 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_MESH_POINT:
break;
case NL80211_IFTYPE_ADHOC:
if (!wdev->current_bss)
@@ -594,6 +608,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)
NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN);
+ if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH);
NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
sizeof(u32) * dev->wiphy.n_cipher_suites,
@@ -746,6 +762,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
}
CMD(set_channel, SET_CHANNEL);
CMD(set_wds_peer, SET_WDS_PEER);
+ if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
+ CMD(sched_scan_start, START_SCHED_SCAN);
#undef CMD
@@ -818,6 +836,35 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
nla_nest_end(msg, nl_ifs);
}
+ if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
+ struct nlattr *nl_wowlan;
+
+ nl_wowlan = nla_nest_start(msg,
+ NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
+ if (!nl_wowlan)
+ goto nla_put_failure;
+
+ if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
+ if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
+ if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
+ if (dev->wiphy.wowlan.n_patterns) {
+ struct nl80211_wowlan_pattern_support pat = {
+ .max_patterns = dev->wiphy.wowlan.n_patterns,
+ .min_pattern_len =
+ dev->wiphy.wowlan.pattern_min_len,
+ .max_pattern_len =
+ dev->wiphy.wowlan.pattern_max_len,
+ };
+ NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
+ sizeof(pat), &pat);
+ }
+
+ nla_nest_end(msg, nl_wowlan);
+ }
+
return genlmsg_end(msg, hdr);
nla_put_failure:
@@ -1679,14 +1726,6 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
if (err)
goto out;
- if (!(rdev->wiphy.flags &
- WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS)) {
- if (!key.def_uni || !key.def_multi) {
- err = -EOPNOTSUPP;
- goto out;
- }
- }
-
err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx,
key.def_uni, key.def_multi);
@@ -1922,6 +1961,7 @@ static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
[NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
[NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
[NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
+ [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
};
static int parse_station_flags(struct genl_info *info,
@@ -2002,7 +2042,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
const u8 *mac_addr, struct station_info *sinfo)
{
void *hdr;
- struct nlattr *sinfoattr;
+ struct nlattr *sinfoattr, *bss_param;
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
if (!hdr)
@@ -2016,6 +2056,9 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
if (!sinfoattr)
goto nla_put_failure;
+ if (sinfo->filled & STATION_INFO_CONNECTED_TIME)
+ NLA_PUT_U32(msg, NL80211_STA_INFO_CONNECTED_TIME,
+ sinfo->connected_time);
if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
sinfo->inactive_time);
@@ -2062,6 +2105,25 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
if (sinfo->filled & STATION_INFO_TX_FAILED)
NLA_PUT_U32(msg, NL80211_STA_INFO_TX_FAILED,
sinfo->tx_failed);
+ if (sinfo->filled & STATION_INFO_BSS_PARAM) {
+ bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
+ if (!bss_param)
+ goto nla_put_failure;
+
+ if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT)
+ NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_CTS_PROT);
+ if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE)
+ NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE);
+ if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME)
+ NLA_PUT_FLAG(msg,
+ NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME);
+ NLA_PUT_U8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
+ sinfo->bss_param.dtim_period);
+ NLA_PUT_U16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
+ sinfo->bss_param.beacon_interval);
+
+ nla_nest_end(msg, bss_param);
+ }
nla_nest_end(msg, sinfoattr);
return genlmsg_end(msg, hdr);
@@ -2190,6 +2252,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
memset(&params, 0, sizeof(params));
params.listen_interval = -1;
+ params.plink_state = PLINK_INVALID;
if (info->attrs[NL80211_ATTR_STA_AID])
return -EINVAL;
@@ -2221,6 +2284,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
params.plink_action =
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+ if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
+ params.plink_state =
+ nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
+
err = get_vlan(info, rdev, &params.vlan);
if (err)
goto out;
@@ -2260,9 +2327,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
err = -EINVAL;
if (params.listen_interval >= 0)
err = -EINVAL;
- if (params.supported_rates)
- err = -EINVAL;
- if (params.sta_flags_mask)
+ if (params.sta_flags_mask &
+ ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_MFP) |
+ BIT(NL80211_STA_FLAG_AUTHORIZED)))
err = -EINVAL;
break;
default:
@@ -2324,11 +2392,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
params.ht_capa =
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+ if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
+ params.plink_action =
+ nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+
if (parse_station_flags(info, &params))
return -EINVAL;
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EINVAL;
@@ -2804,8 +2877,10 @@ static const struct nla_policy
nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
- [NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE] = { .type = NLA_BINARY,
+ [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
+ [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
+ [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
};
static int nl80211_parse_mesh_config(struct genl_info *info,
@@ -2906,14 +2981,17 @@ static int nl80211_parse_mesh_setup(struct genl_info *info,
IEEE80211_PATH_METRIC_VENDOR :
IEEE80211_PATH_METRIC_AIRTIME;
- if (tb[NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE]) {
+
+ if (tb[NL80211_MESH_SETUP_IE]) {
struct nlattr *ieattr =
- tb[NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE];
+ tb[NL80211_MESH_SETUP_IE];
if (!is_valid_ie_attr(ieattr))
return -EINVAL;
- setup->vendor_ie = nla_data(ieattr);
- setup->vendor_ie_len = nla_len(ieattr);
+ setup->ie = nla_data(ieattr);
+ setup->ie_len = nla_len(ieattr);
}
+ setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
+ setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
return 0;
}
@@ -3282,6 +3360,188 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static int nl80211_start_sched_scan(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_sched_scan_request *request;
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct cfg80211_ssid *ssid;
+ struct ieee80211_channel *channel;
+ struct nlattr *attr;
+ struct wiphy *wiphy;
+ int err, tmp, n_ssids = 0, n_channels, i;
+ u32 interval;
+ enum ieee80211_band band;
+ size_t ie_len;
+
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
+ !rdev->ops->sched_scan_start)
+ return -EOPNOTSUPP;
+
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (rdev->sched_scan_req)
+ return -EINPROGRESS;
+
+ if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+ return -EINVAL;
+
+ interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
+ if (interval == 0)
+ return -EINVAL;
+
+ wiphy = &rdev->wiphy;
+
+ if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ n_channels = validate_scan_freqs(
+ info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
+ if (!n_channels)
+ return -EINVAL;
+ } else {
+ n_channels = 0;
+
+ 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)
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_IE])
+ ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ else
+ ie_len = 0;
+
+ if (ie_len > wiphy->max_scan_ie_len)
+ return -EINVAL;
+
+ request = kzalloc(sizeof(*request)
+ + sizeof(*ssid) * n_ssids
+ + sizeof(channel) * n_channels
+ + ie_len, GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ if (n_ssids)
+ request->ssids = (void *)&request->channels[n_channels];
+ request->n_ssids = n_ssids;
+ if (ie_len) {
+ if (request->ssids)
+ request->ie = (void *)(request->ssids + n_ssids);
+ else
+ request->ie = (void *)(request->channels + n_channels);
+ }
+
+ i = 0;
+ if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ /* user specified, bail out if channel not found */
+ nla_for_each_nested(attr,
+ info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
+ tmp) {
+ struct ieee80211_channel *chan;
+
+ chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
+
+ if (!chan) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ /* ignore disabled channels */
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ request->channels[i] = chan;
+ i++;
+ }
+ } else {
+ /* all channels */
+ 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++) {
+ struct ieee80211_channel *chan;
+
+ chan = &wiphy->bands[band]->channels[j];
+
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ request->channels[i] = chan;
+ i++;
+ }
+ }
+ }
+
+ if (!i) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ request->n_channels = 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++;
+ }
+ }
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ memcpy((void *)request->ie,
+ nla_data(info->attrs[NL80211_ATTR_IE]),
+ request->ie_len);
+ }
+
+ request->dev = dev;
+ request->wiphy = &rdev->wiphy;
+ request->interval = interval;
+
+ err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
+ if (!err) {
+ rdev->sched_scan_req = request;
+ nl80211_send_sched_scan(rdev, dev,
+ NL80211_CMD_START_SCHED_SCAN);
+ goto out;
+ }
+
+out_free:
+ kfree(request);
+out:
+ return err;
+}
+
+static int nl80211_stop_sched_scan(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
+ !rdev->ops->sched_scan_stop)
+ return -EOPNOTSUPP;
+
+ return __cfg80211_stop_sched_scan(rdev, false);
+}
+
static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
@@ -4780,6 +5040,194 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
return cfg80211_leave_mesh(rdev, dev);
}
+static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct sk_buff *msg;
+ void *hdr;
+
+ if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
+ return -EOPNOTSUPP;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_GET_WOWLAN);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (rdev->wowlan) {
+ struct nlattr *nl_wowlan;
+
+ nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
+ if (!nl_wowlan)
+ goto nla_put_failure;
+
+ if (rdev->wowlan->any)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
+ if (rdev->wowlan->disconnect)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
+ if (rdev->wowlan->magic_pkt)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
+ if (rdev->wowlan->n_patterns) {
+ struct nlattr *nl_pats, *nl_pat;
+ int i, pat_len;
+
+ nl_pats = nla_nest_start(msg,
+ NL80211_WOWLAN_TRIG_PKT_PATTERN);
+ if (!nl_pats)
+ goto nla_put_failure;
+
+ for (i = 0; i < rdev->wowlan->n_patterns; i++) {
+ nl_pat = nla_nest_start(msg, i + 1);
+ if (!nl_pat)
+ goto nla_put_failure;
+ pat_len = rdev->wowlan->patterns[i].pattern_len;
+ NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK,
+ DIV_ROUND_UP(pat_len, 8),
+ rdev->wowlan->patterns[i].mask);
+ NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
+ pat_len,
+ rdev->wowlan->patterns[i].pattern);
+ nla_nest_end(msg, nl_pat);
+ }
+ nla_nest_end(msg, nl_pats);
+ }
+
+ nla_nest_end(msg, nl_wowlan);
+ }
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
+ struct cfg80211_wowlan no_triggers = {};
+ struct cfg80211_wowlan new_triggers = {};
+ struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
+ int err, i;
+
+ if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
+ goto no_triggers;
+
+ err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
+ nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
+ nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
+ nl80211_wowlan_policy);
+ if (err)
+ return err;
+
+ if (tb[NL80211_WOWLAN_TRIG_ANY]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
+ return -EINVAL;
+ new_triggers.any = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
+ return -EINVAL;
+ new_triggers.disconnect = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
+ return -EINVAL;
+ new_triggers.magic_pkt = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
+ struct nlattr *pat;
+ int n_patterns = 0;
+ int rem, pat_len, mask_len;
+ struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
+
+ nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
+ rem)
+ n_patterns++;
+ if (n_patterns > wowlan->n_patterns)
+ return -EINVAL;
+
+ new_triggers.patterns = kcalloc(n_patterns,
+ sizeof(new_triggers.patterns[0]),
+ GFP_KERNEL);
+ if (!new_triggers.patterns)
+ return -ENOMEM;
+
+ new_triggers.n_patterns = n_patterns;
+ i = 0;
+
+ nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
+ rem) {
+ nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
+ nla_data(pat), nla_len(pat), NULL);
+ err = -EINVAL;
+ if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
+ !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
+ goto error;
+ pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
+ mask_len = DIV_ROUND_UP(pat_len, 8);
+ if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
+ mask_len)
+ goto error;
+ if (pat_len > wowlan->pattern_max_len ||
+ pat_len < wowlan->pattern_min_len)
+ goto error;
+
+ new_triggers.patterns[i].mask =
+ kmalloc(mask_len + pat_len, GFP_KERNEL);
+ if (!new_triggers.patterns[i].mask) {
+ err = -ENOMEM;
+ goto error;
+ }
+ new_triggers.patterns[i].pattern =
+ new_triggers.patterns[i].mask + mask_len;
+ memcpy(new_triggers.patterns[i].mask,
+ nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
+ mask_len);
+ new_triggers.patterns[i].pattern_len = pat_len;
+ memcpy(new_triggers.patterns[i].pattern,
+ nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
+ pat_len);
+ i++;
+ }
+ }
+
+ if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) {
+ struct cfg80211_wowlan *ntrig;
+ ntrig = kmemdup(&new_triggers, sizeof(new_triggers),
+ GFP_KERNEL);
+ if (!ntrig) {
+ err = -ENOMEM;
+ goto error;
+ }
+ cfg80211_rdev_free_wowlan(rdev);
+ rdev->wowlan = ntrig;
+ } else {
+ no_triggers:
+ cfg80211_rdev_free_wowlan(rdev);
+ rdev->wowlan = NULL;
+ }
+
+ return 0;
+ error:
+ for (i = 0; i < new_triggers.n_patterns; i++)
+ kfree(new_triggers.patterns[i].mask);
+ kfree(new_triggers.patterns);
+ return err;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -5064,6 +5512,22 @@ static struct genl_ops nl80211_ops[] = {
.dumpit = nl80211_dump_scan,
},
{
+ .cmd = NL80211_CMD_START_SCHED_SCAN,
+ .doit = nl80211_start_sched_scan,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_STOP_SCHED_SCAN,
+ .doit = nl80211_stop_sched_scan,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
.cmd = NL80211_CMD_AUTHENTICATE,
.doit = nl80211_authenticate,
.policy = nl80211_policy,
@@ -5278,6 +5742,22 @@ static struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_GET_WOWLAN,
+ .doit = nl80211_get_wowlan,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_WOWLAN,
+ .doit = nl80211_set_wowlan,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -5373,6 +5853,28 @@ static int nl80211_send_scan_msg(struct sk_buff *msg,
return -EMSGSIZE;
}
+static int
+nl80211_send_sched_scan_msg(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->wiphy_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+
+ return genlmsg_end(msg, hdr);
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
struct net_device *netdev)
{
@@ -5430,6 +5932,43 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
nl80211_scan_mcgrp.id, GFP_KERNEL);
}
+void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev)
+{
+ struct sk_buff *msg;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
+ NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_scan_mcgrp.id, GFP_KERNEL);
+}
+
+void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, u32 cmd)
+{
+ struct sk_buff *msg;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_scan_mcgrp.id, GFP_KERNEL);
+}
+
/*
* This can happen on global regulatory changes or device specific settings
* based on custom world regulatory domains.
@@ -5785,6 +6324,44 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
+void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *macaddr, const u8* ie, u8 ie_len,
+ gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr);
+ if (ie_len && ie)
+ NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_mlme_mcgrp.id, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *addr,
enum nl80211_key_type key_type, int key_id,
@@ -5966,6 +6543,40 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
nl80211_mlme_mcgrp.id, gfp);
}
+void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *mac_addr,
+ gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_mlme_mcgrp.id, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u32 nlpid,
int freq, const u8 *buf, size_t len, gfp_t gfp)
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index e3f7fa88696..2f1bfb87a65 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -12,6 +12,10 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
struct net_device *netdev);
void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
struct net_device *netdev);
+void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, u32 cmd);
+void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev);
void nl80211_send_reg_change_event(struct regulatory_request *request);
void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
@@ -50,6 +54,10 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u16 reason,
const u8 *ie, size_t ie_len, bool from_ap);
+void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *macaddr, const u8* ie, u8 ie_len,
+ gfp_t gfp);
void
nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *addr,
@@ -79,6 +87,9 @@ void nl80211_send_remain_on_channel_cancel(
void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *mac_addr,
struct station_info *sinfo, gfp_t gfp);
+void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *mac_addr,
+ gfp_t gfp);
int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u32 nlpid, int freq,
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 3332d5bce31..798cb4cd070 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -106,6 +106,9 @@ struct reg_beacon {
static void reg_todo(struct work_struct *work);
static DECLARE_WORK(reg_work, reg_todo);
+static void reg_timeout_work(struct work_struct *work);
+static DECLARE_DELAYED_WORK(reg_timeout, reg_timeout_work);
+
/* We keep a static world regulatory domain in case of the absence of CRDA */
static const struct ieee80211_regdomain world_regdom = {
.n_reg_rules = 5,
@@ -669,11 +672,9 @@ static int freq_reg_info_regd(struct wiphy *wiphy,
for (i = 0; i < regd->n_reg_rules; i++) {
const struct ieee80211_reg_rule *rr;
const struct ieee80211_freq_range *fr = NULL;
- const struct ieee80211_power_rule *pr = NULL;
rr = &regd->reg_rules[i];
fr = &rr->freq_range;
- pr = &rr->power_rule;
/*
* We only need to know if one frequency rule was
@@ -1330,6 +1331,9 @@ static void reg_set_request_processed(void)
need_more_processing = true;
spin_unlock(&reg_requests_lock);
+ if (last_request->initiator == NL80211_REGDOM_SET_BY_USER)
+ cancel_delayed_work_sync(&reg_timeout);
+
if (need_more_processing)
schedule_work(&reg_work);
}
@@ -1440,8 +1444,18 @@ static void reg_process_hint(struct regulatory_request *reg_request)
r = __regulatory_hint(wiphy, reg_request);
/* This is required so that the orig_* parameters are saved */
if (r == -EALREADY && wiphy &&
- wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
+ wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
wiphy_update_regulatory(wiphy, initiator);
+ return;
+ }
+
+ /*
+ * We only time out user hints, given that they should be the only
+ * source of bogus requests.
+ */
+ if (r != -EALREADY &&
+ reg_request->initiator == NL80211_REGDOM_SET_BY_USER)
+ schedule_delayed_work(&reg_timeout, msecs_to_jiffies(3142));
}
/*
@@ -1744,6 +1758,8 @@ static void restore_regulatory_settings(bool reset_user)
{
char alpha2[2];
struct reg_beacon *reg_beacon, *btmp;
+ struct regulatory_request *reg_request, *tmp;
+ LIST_HEAD(tmp_reg_req_list);
mutex_lock(&cfg80211_mutex);
mutex_lock(&reg_mutex);
@@ -1751,6 +1767,25 @@ static void restore_regulatory_settings(bool reset_user)
reset_regdomains();
restore_alpha2(alpha2, reset_user);
+ /*
+ * If there's any pending requests we simply
+ * stash them to a temporary pending queue and
+ * add then after we've restored regulatory
+ * settings.
+ */
+ spin_lock(&reg_requests_lock);
+ if (!list_empty(&reg_requests_list)) {
+ list_for_each_entry_safe(reg_request, tmp,
+ &reg_requests_list, list) {
+ if (reg_request->initiator !=
+ NL80211_REGDOM_SET_BY_USER)
+ continue;
+ list_del(&reg_request->list);
+ list_add_tail(&reg_request->list, &tmp_reg_req_list);
+ }
+ }
+ spin_unlock(&reg_requests_lock);
+
/* Clear beacon hints */
spin_lock_bh(&reg_pending_beacons_lock);
if (!list_empty(&reg_pending_beacons)) {
@@ -1785,8 +1820,31 @@ static void restore_regulatory_settings(bool reset_user)
*/
if (is_an_alpha2(alpha2))
regulatory_hint_user(user_alpha2);
-}
+ if (list_empty(&tmp_reg_req_list))
+ return;
+
+ mutex_lock(&cfg80211_mutex);
+ mutex_lock(&reg_mutex);
+
+ spin_lock(&reg_requests_lock);
+ list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) {
+ REG_DBG_PRINT("Adding request for country %c%c back "
+ "into the queue\n",
+ reg_request->alpha2[0],
+ reg_request->alpha2[1]);
+ list_del(&reg_request->list);
+ list_add_tail(&reg_request->list, &reg_requests_list);
+ }
+ spin_unlock(&reg_requests_lock);
+
+ mutex_unlock(&reg_mutex);
+ mutex_unlock(&cfg80211_mutex);
+
+ REG_DBG_PRINT("Kicking the queue\n");
+
+ schedule_work(&reg_work);
+}
void regulatory_hint_disconnect(void)
{
@@ -2125,6 +2183,13 @@ out:
mutex_unlock(&reg_mutex);
}
+static void reg_timeout_work(struct work_struct *work)
+{
+ REG_DBG_PRINT("Timeout while waiting for CRDA to reply, "
+ "restoring regulatory settings");
+ restore_regulatory_settings(true);
+}
+
int __init regulatory_init(void)
{
int err = 0;
@@ -2178,6 +2243,7 @@ void /* __init_or_exit */ regulatory_exit(void)
struct reg_beacon *reg_beacon, *btmp;
cancel_work_sync(&reg_work);
+ cancel_delayed_work_sync(&reg_timeout);
mutex_lock(&cfg80211_mutex);
mutex_lock(&reg_mutex);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index fbf6f33ae4d..65dfae3b9d4 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -93,6 +93,76 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
}
EXPORT_SYMBOL(cfg80211_scan_done);
+void __cfg80211_sched_scan_results(struct work_struct *wk)
+{
+ struct cfg80211_registered_device *rdev;
+
+ rdev = container_of(wk, struct cfg80211_registered_device,
+ sched_scan_results_wk);
+
+ cfg80211_lock_rdev(rdev);
+
+ /* we don't have sched_scan_req anymore if the scan is stopping */
+ if (rdev->sched_scan_req)
+ nl80211_send_sched_scan_results(rdev,
+ rdev->sched_scan_req->dev);
+
+ cfg80211_unlock_rdev(rdev);
+}
+
+void cfg80211_sched_scan_results(struct wiphy *wiphy)
+{
+ /* ignore if we're not scanning */
+ if (wiphy_to_dev(wiphy)->sched_scan_req)
+ queue_work(cfg80211_wq,
+ &wiphy_to_dev(wiphy)->sched_scan_results_wk);
+}
+EXPORT_SYMBOL(cfg80211_sched_scan_results);
+
+void __cfg80211_sched_scan_stopped(struct work_struct *wk)
+{
+ struct cfg80211_registered_device *rdev;
+
+ rdev = container_of(wk, struct cfg80211_registered_device,
+ sched_scan_stopped_wk);
+
+ cfg80211_lock_rdev(rdev);
+ __cfg80211_stop_sched_scan(rdev, true);
+ cfg80211_unlock_rdev(rdev);
+}
+
+void cfg80211_sched_scan_stopped(struct wiphy *wiphy)
+{
+ queue_work(cfg80211_wq, &wiphy_to_dev(wiphy)->sched_scan_stopped_wk);
+}
+EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
+
+int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
+ bool driver_initiated)
+{
+ int err;
+ struct net_device *dev;
+
+ ASSERT_RDEV_LOCK(rdev);
+
+ if (!rdev->sched_scan_req)
+ return 0;
+
+ dev = rdev->sched_scan_req->dev;
+
+ err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev,
+ driver_initiated);
+ if (err)
+ return err;
+
+ nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED);
+
+ kfree(rdev->sched_scan_req);
+ rdev->sched_scan_req = NULL;
+
+ return err;
+}
+
static void bss_release(struct kref *ref)
{
struct cfg80211_internal_bss *bss;
@@ -210,7 +280,7 @@ static bool is_mesh(struct cfg80211_bss *a,
{
const u8 *ie;
- if (!is_zero_ether_addr(a->bssid))
+ if (!WLAN_CAPABILITY_IS_MBSS(a->capability))
return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
@@ -248,11 +318,7 @@ static int cmp_bss(struct cfg80211_bss *a,
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)) {
+ if (WLAN_CAPABILITY_IS_MBSS(a->capability | b->capability)) {
r = cmp_ies(WLAN_EID_MESH_ID,
a->information_elements,
a->len_information_elements,
@@ -267,6 +333,10 @@ static int cmp_bss(struct cfg80211_bss *a,
b->len_information_elements);
}
+ r = memcmp(a->bssid, b->bssid, ETH_ALEN);
+ if (r)
+ return r;
+
return cmp_ies(WLAN_EID_SSID,
a->information_elements,
a->len_information_elements,
@@ -407,7 +477,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
res->ts = jiffies;
- if (is_zero_ether_addr(res->pub.bssid)) {
+ if (WLAN_CAPABILITY_IS_MBSS(res->pub.capability)) {
/* must be mesh, verify */
meshid = cfg80211_find_ie(WLAN_EID_MESH_ID,
res->pub.information_elements,
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index 4294fa22bb2..c6e4ca6a7d2 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -93,7 +93,7 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)
if (rdev->ops->suspend) {
rtnl_lock();
- ret = rdev->ops->suspend(&rdev->wiphy);
+ ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan);
rtnl_unlock();
}