diff options
author | John W. Linville <linville@tuxdriver.com> | 2012-08-21 20:00:21 (GMT) |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-08-21 20:00:21 (GMT) |
commit | 01e17dacd47101ad7d33152bbfbbd4394352d2e6 (patch) | |
tree | d94706b87fcad04d08a46a9b3555396d21ab41ec /net | |
parent | 22c5649eef0fc37532e20c14d2656b28ca708a69 (diff) | |
parent | dcf33963c48e1959c83fda84e336dbb000eefa3f (diff) | |
download | linux-01e17dacd47101ad7d33152bbfbbd4394352d2e6.tar.xz |
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Conflicts:
drivers/net/wireless/mac80211_hwsim.c
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/aes_cmac.c | 6 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 68 | ||||
-rw-r--r-- | net/mac80211/debugfs.c | 32 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 11 | ||||
-rw-r--r-- | net/mac80211/ibss.c | 15 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 30 | ||||
-rw-r--r-- | net/mac80211/iface.c | 289 | ||||
-rw-r--r-- | net/mac80211/main.c | 21 | ||||
-rw-r--r-- | net/mac80211/mesh.c | 28 | ||||
-rw-r--r-- | net/mac80211/mesh.h | 3 | ||||
-rw-r--r-- | net/mac80211/mesh_hwmp.c | 2 | ||||
-rw-r--r-- | net/mac80211/mesh_pathtbl.c | 44 | ||||
-rw-r--r-- | net/mac80211/mesh_plink.c | 38 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 240 | ||||
-rw-r--r-- | net/mac80211/offchannel.c | 6 | ||||
-rw-r--r-- | net/mac80211/rate.h | 2 | ||||
-rw-r--r-- | net/mac80211/rx.c | 58 | ||||
-rw-r--r-- | net/mac80211/scan.c | 12 | ||||
-rw-r--r-- | net/mac80211/status.c | 22 | ||||
-rw-r--r-- | net/mac80211/trace.h | 11 | ||||
-rw-r--r-- | net/mac80211/tx.c | 71 | ||||
-rw-r--r-- | net/mac80211/util.c | 57 | ||||
-rw-r--r-- | net/wireless/chan.c | 7 | ||||
-rw-r--r-- | net/wireless/core.c | 53 | ||||
-rw-r--r-- | net/wireless/mlme.c | 10 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 122 | ||||
-rw-r--r-- | net/wireless/radiotap.c | 2 | ||||
-rw-r--r-- | net/wireless/util.c | 36 |
28 files changed, 858 insertions, 438 deletions
diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c index 8dfd70d..a04752e 100644 --- a/net/mac80211/aes_cmac.c +++ b/net/mac80211/aes_cmac.c @@ -38,14 +38,10 @@ static void gf_mulx(u8 *pad) static void aes_128_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - u8 scratch[2 * AES_BLOCK_SIZE]; - u8 *cbc, *pad; + u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE]; const u8 *pos, *end; size_t i, e, left, total_len; - cbc = scratch; - pad = scratch + AES_BLOCK_SIZE; - memset(cbc, 0, AES_BLOCK_SIZE); total_len = 0; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d41974a..69b322f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -102,6 +102,18 @@ static int ieee80211_change_iface(struct wiphy *wiphy, return 0; } +static int ieee80211_start_p2p_device(struct wiphy *wiphy, + struct wireless_dev *wdev) +{ + return ieee80211_do_open(wdev, true); +} + +static void ieee80211_stop_p2p_device(struct wiphy *wiphy, + struct wireless_dev *wdev) +{ + ieee80211_sdata_stop(IEEE80211_WDEV_TO_SUB_IF(wdev)); +} + static int ieee80211_set_noack_map(struct wiphy *wiphy, struct net_device *dev, u16 noack_map) @@ -330,7 +342,7 @@ static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, in if (!(rate->flags & RATE_INFO_FLAGS_MCS)) { struct ieee80211_supported_band *sband; sband = sta->local->hw.wiphy->bands[ - sta->local->hw.conf.channel->band]; + sta->local->oper_channel->band]; rate->legacy = sband->bitrates[idx].bitrate; } else rate->mcs = idx; @@ -725,25 +737,23 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy, static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, const u8 *resp, size_t resp_len) { - struct sk_buff *new, *old; + struct probe_resp *new, *old; if (!resp || !resp_len) - return 1; + return -EINVAL; old = rtnl_dereference(sdata->u.ap.probe_resp); - new = dev_alloc_skb(resp_len); + new = kzalloc(sizeof(struct probe_resp) + resp_len, GFP_KERNEL); if (!new) return -ENOMEM; - memcpy(skb_put(new, resp_len), resp, resp_len); + new->len = resp_len; + memcpy(new->data, resp, resp_len); rcu_assign_pointer(sdata->u.ap.probe_resp, new); - if (old) { - /* TODO: use call_rcu() */ - synchronize_rcu(); - dev_kfree_skb(old); - } + if (old) + kfree_rcu(old, rcu_head); return 0; } @@ -950,7 +960,7 @@ static void ieee80211_send_layer2_update(struct sta_info *sta) /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID) * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ - memset(msg->da, 0xff, ETH_ALEN); + eth_broadcast_addr(msg->da); memcpy(msg->sa, sta->sta.addr, ETH_ALEN); msg->len = htons(6); msg->dsap = 0; @@ -1285,9 +1295,10 @@ static int ieee80211_change_station(struct wiphy *wiphy, mutex_unlock(&local->sta_mtx); if (sdata->vif.type == NL80211_IFTYPE_STATION && - params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) + params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { ieee80211_recalc_ps(local, -1); - + ieee80211_recalc_ps_vif(sdata); + } return 0; } @@ -1661,7 +1672,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy, } if (!sdata->vif.bss_conf.use_short_slot && - sdata->local->hw.conf.channel->band == IEEE80211_BAND_5GHZ) { + sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ) { sdata->vif.bss_conf.use_short_slot = true; changed |= BSS_CHANGED_ERP_SLOT; } @@ -1775,6 +1786,7 @@ static int ieee80211_scan(struct wiphy *wiphy, case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_DEVICE: break; case NL80211_IFTYPE_P2P_GO: if (sdata->local->ops->hw_scan) @@ -1927,7 +1939,7 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type, int mbm) { struct ieee80211_local *local = wiphy_priv(wiphy); - struct ieee80211_channel *chan = local->hw.conf.channel; + struct ieee80211_channel *chan = local->oper_channel; u32 changes = 0; switch (type) { @@ -2079,6 +2091,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); ieee80211_recalc_ps(local, -1); + ieee80211_recalc_ps_vif(sdata); return 0; } @@ -2461,6 +2474,9 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, if (!sdata->u.mgd.associated) need_offchan = true; break; + case NL80211_IFTYPE_P2P_DEVICE: + need_offchan = true; + break; default: return -EOPNOTSUPP; } @@ -2653,6 +2669,7 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, u16 status_code, struct sk_buff *skb) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; struct ieee80211_tdls_data *tf; tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); @@ -2672,8 +2689,10 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, tf->u.setup_req.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - ieee80211_add_srates_ie(sdata, skb, false); - ieee80211_add_ext_srates_ie(sdata, skb, false); + ieee80211_add_srates_ie(sdata, skb, false, + local->oper_channel->band); + ieee80211_add_ext_srates_ie(sdata, skb, false, + local->oper_channel->band); ieee80211_tdls_add_ext_capab(skb); break; case WLAN_TDLS_SETUP_RESPONSE: @@ -2686,8 +2705,10 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, tf->u.setup_resp.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - ieee80211_add_srates_ie(sdata, skb, false); - ieee80211_add_ext_srates_ie(sdata, skb, false); + ieee80211_add_srates_ie(sdata, skb, false, + local->oper_channel->band); + ieee80211_add_ext_srates_ie(sdata, skb, false, + local->oper_channel->band); ieee80211_tdls_add_ext_capab(skb); break; case WLAN_TDLS_SETUP_CONFIRM: @@ -2725,6 +2746,7 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, u16 status_code, struct sk_buff *skb) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; struct ieee80211_mgmt *mgmt; mgmt = (void *)skb_put(skb, 24); @@ -2747,8 +2769,10 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, mgmt->u.action.u.tdls_discover_resp.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - ieee80211_add_srates_ie(sdata, skb, false); - ieee80211_add_ext_srates_ie(sdata, skb, false); + ieee80211_add_srates_ie(sdata, skb, false, + local->oper_channel->band); + ieee80211_add_ext_srates_ie(sdata, skb, false, + local->oper_channel->band); ieee80211_tdls_add_ext_capab(skb); break; default: @@ -3005,6 +3029,8 @@ struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, .change_virtual_intf = ieee80211_change_iface, + .start_p2p_device = ieee80211_start_p2p_device, + .stop_p2p_device = ieee80211_stop_p2p_device, .add_key = ieee80211_add_key, .del_key = ieee80211_del_key, .get_key = ieee80211_get_key, diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index b8dfb44..97173f8 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -63,8 +63,6 @@ DEBUGFS_READONLY_FILE(user_power, "%d", local->user_power_level); DEBUGFS_READONLY_FILE(power, "%d", local->hw.conf.power_level); -DEBUGFS_READONLY_FILE(frequency, "%d", - local->hw.conf.channel->center_freq); DEBUGFS_READONLY_FILE(total_ps_buffered, "%d", local->total_ps_buffered); DEBUGFS_READONLY_FILE(wep_iv, "%#08x", @@ -91,33 +89,6 @@ static const struct file_operations reset_ops = { .llseek = noop_llseek, }; -static ssize_t channel_type_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - const char *buf; - - switch (local->hw.conf.channel_type) { - case NL80211_CHAN_NO_HT: - buf = "no ht\n"; - break; - case NL80211_CHAN_HT20: - buf = "ht20\n"; - break; - case NL80211_CHAN_HT40MINUS: - buf = "ht40-\n"; - break; - case NL80211_CHAN_HT40PLUS: - buf = "ht40+\n"; - break; - default: - buf = "???"; - break; - } - - return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); -} - static ssize_t hwflags_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -205,7 +176,6 @@ static ssize_t queues_read(struct file *file, char __user *user_buf, } DEBUGFS_READONLY_FILE_OPS(hwflags); -DEBUGFS_READONLY_FILE_OPS(channel_type); DEBUGFS_READONLY_FILE_OPS(queues); /* statistics stuff */ @@ -272,12 +242,10 @@ void debugfs_hw_add(struct ieee80211_local *local) local->debugfs.keys = debugfs_create_dir("keys", phyd); - DEBUGFS_ADD(frequency); DEBUGFS_ADD(total_ps_buffered); DEBUGFS_ADD(wep_iv); DEBUGFS_ADD(queues); DEBUGFS_ADD_MODE(reset, 0200); - DEBUGFS_ADD(channel_type); DEBUGFS_ADD(hwflags); DEBUGFS_ADD(user_power); DEBUGFS_ADD(power); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index df92031..da9003b 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -9,7 +9,7 @@ static inline void check_sdata_in_driver(struct ieee80211_sub_if_data *sdata) { WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER), "%s: Failed check-sdata-in-driver check, flags: 0x%x\n", - sdata->dev->name, sdata->flags); + sdata->dev ? sdata->dev->name : sdata->name, sdata->flags); } static inline struct ieee80211_sub_if_data * @@ -22,9 +22,11 @@ get_bss_sdata(struct ieee80211_sub_if_data *sdata) return sdata; } -static inline void drv_tx(struct ieee80211_local *local, struct sk_buff *skb) +static inline void drv_tx(struct ieee80211_local *local, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { - local->ops->tx(&local->hw, skb); + local->ops->tx(&local->hw, control, skb); } static inline void drv_get_et_strings(struct ieee80211_sub_if_data *sdata, @@ -526,6 +528,9 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local, sdata = get_bss_sdata(sdata); check_sdata_in_driver(sdata); + WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && + sdata->vif.type != NL80211_IFTYPE_ADHOC); + trace_drv_sta_rc_update(local, sdata, sta, changed); if (local->ops->sta_rc_update) local->ops->sta_rc_update(&local->hw, &sdata->vif, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 5746d62..a9d9328 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -109,7 +109,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); - memset(mgmt->da, 0xff, ETH_ALEN); + eth_broadcast_addr(mgmt->da); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_int); @@ -205,7 +205,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, mod_timer(&ifibss->timer, round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); - bss = cfg80211_inform_bss_frame(local->hw.wiphy, local->hw.conf.channel, + bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan, mgmt, skb->len, 0, GFP_KERNEL); cfg80211_put_bss(bss); netif_carrier_on(sdata->dev); @@ -294,7 +294,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; struct sta_info *sta; - int band = local->hw.conf.channel->band; + int band = local->oper_channel->band; /* * XXX: Consider removing the least recently used entry and @@ -459,8 +459,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, } } - if (sta && rates_updated) + if (sta && rates_updated) { + drv_sta_rc_update(local, sdata, &sta->sta, + IEEE80211_RC_SUPP_RATES_CHANGED); rate_control_rate_init(sta); + } rcu_read_unlock(); } @@ -561,7 +564,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; struct sta_info *sta; - int band = local->hw.conf.channel->band; + int band = local->oper_channel->band; /* * XXX: Consider removing the least recently used entry and @@ -759,7 +762,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) return; } sdata_info(sdata, "IBSS not allowed on %d MHz\n", - local->hw.conf.channel->center_freq); + local->oper_channel->center_freq); /* No IBSS found - decrease scan interval and continue * scanning. */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index bb61f77..204bfed 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -193,8 +193,6 @@ struct ieee80211_tx_data { struct sta_info *sta; struct ieee80211_key *key; - struct ieee80211_channel *channel; - unsigned int flags; }; @@ -274,9 +272,15 @@ struct beacon_data { struct rcu_head rcu_head; }; +struct probe_resp { + struct rcu_head rcu_head; + int len; + u8 data[0]; +}; + struct ieee80211_if_ap { struct beacon_data __rcu *beacon; - struct sk_buff __rcu *probe_resp; + struct probe_resp __rcu *probe_resp; struct list_head vlans; @@ -359,6 +363,7 @@ enum ieee80211_sta_flags { IEEE80211_STA_NULLFUNC_ACKED = BIT(8), IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9), IEEE80211_STA_DISABLE_40MHZ = BIT(10), + IEEE80211_STA_DISABLE_VHT = BIT(11), }; struct ieee80211_mgd_auth_data { @@ -1075,6 +1080,8 @@ struct ieee80211_local { struct idr ack_status_frames; spinlock_t ack_status_lock; + struct ieee80211_sub_if_data __rcu *p2p_sdata; + /* dummy netdev for use w/ NAPI */ struct net_device napi_dev; @@ -1131,7 +1138,7 @@ struct ieee802_11_elems { u8 *prep; u8 *perr; struct ieee80211_rann_ie *rann; - u8 *ch_switch_elem; + struct ieee80211_channel_sw_ie *ch_switch_ie; u8 *country_elem; u8 *pwr_constr_elem; u8 *quiet_elem; /* first quite element */ @@ -1157,7 +1164,6 @@ struct ieee802_11_elems { u8 preq_len; u8 prep_len; u8 perr_len; - u8 ch_switch_elem_len; u8 country_elem_len; u8 pwr_constr_elem_len; u8 quiet_elem_len; @@ -1202,6 +1208,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, void ieee80211_send_pspoll(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency); +void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata); int ieee80211_max_network_latency(struct notifier_block *nb, unsigned long data, void *dummy); int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata); @@ -1291,6 +1298,8 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local); void ieee80211_recalc_idle(struct ieee80211_local *local); void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata, const int offset); +int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up); +void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata); static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) { @@ -1425,7 +1434,6 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr); void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr, bool ack); -void ieee80211_beacon_connection_loss_work(struct work_struct *work); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason); @@ -1457,13 +1465,15 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, u8 channel); struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, u32 ratemask, + struct ieee80211_channel *chan, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, bool directed); void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, - u32 ratemask, bool directed, bool no_cck); + u32 ratemask, bool directed, bool no_cck, + struct ieee80211_channel *channel); void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, const size_t supp_rates_len, @@ -1487,9 +1497,11 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u32 cap); int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, bool need_basic); + struct sk_buff *skb, bool need_basic, + enum ieee80211_band band); int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, bool need_basic); + struct sk_buff *skb, bool need_basic, + enum ieee80211_band band); /* channel management */ enum ieee80211_chan_mode { diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index bfb57dc..59f8adc 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -100,6 +100,10 @@ static u32 __ieee80211_recalc_idle(struct ieee80211_local *local) sdata->vif.bss_conf.idle = true; continue; } + + if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) + continue; + /* count everything else */ sdata->vif.bss_conf.idle = false; count++; @@ -121,7 +125,8 @@ static u32 __ieee80211_recalc_idle(struct ieee80211_local *local) list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type == NL80211_IFTYPE_MONITOR || - sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) continue; if (sdata->old_idle == sdata->vif.bss_conf.idle) continue; @@ -204,6 +209,8 @@ static inline int identical_mac_addr_allowed(int type1, int type2) { return type1 == NL80211_IFTYPE_MONITOR || type2 == NL80211_IFTYPE_MONITOR || + type1 == NL80211_IFTYPE_P2P_DEVICE || + type2 == NL80211_IFTYPE_P2P_DEVICE || (type1 == NL80211_IFTYPE_AP && type2 == NL80211_IFTYPE_WDS) || (type1 == NL80211_IFTYPE_WDS && (type2 == NL80211_IFTYPE_WDS || @@ -406,9 +413,10 @@ static void ieee80211_del_virtual_monitor(struct ieee80211_local *local) * an error on interface type changes that have been pre-checked, so most * checks should be in ieee80211_check_concurrent_iface. */ -static int ieee80211_do_open(struct net_device *dev, bool coming_up) +int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + struct net_device *dev = wdev->netdev; struct ieee80211_local *local = sdata->local; struct sta_info *sta; u32 changed = 0; @@ -443,6 +451,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_P2P_DEVICE: /* no special treatment */ break; case NL80211_IFTYPE_UNSPECIFIED: @@ -471,7 +480,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) * Copy the hopefully now-present MAC address to * this interface, if it has the special null one. */ - if (is_zero_ether_addr(dev->dev_addr)) { + if (dev && is_zero_ether_addr(dev->dev_addr)) { memcpy(dev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN); @@ -536,15 +545,23 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) local->fif_probe_req++; } - changed |= ieee80211_reset_erp_info(sdata); + if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) + changed |= ieee80211_reset_erp_info(sdata); ieee80211_bss_info_change_notify(sdata, changed); - if (sdata->vif.type == NL80211_IFTYPE_STATION || - sdata->vif.type == NL80211_IFTYPE_ADHOC || - sdata->vif.type == NL80211_IFTYPE_AP) + switch (sdata->vif.type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: netif_carrier_off(dev); - else + break; + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_P2P_DEVICE: + break; + default: netif_carrier_on(dev); + } /* * set default queue parameters so drivers don't @@ -576,6 +593,9 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) } rate_control_rate_init(sta); + netif_carrier_on(dev); + } else if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) { + rcu_assign_pointer(local->p2p_sdata, sdata); } /* @@ -601,7 +621,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) ieee80211_recalc_ps(local, -1); - netif_tx_start_all_queues(dev); + if (dev) + netif_tx_start_all_queues(dev); return 0; err_del_interface: @@ -631,7 +652,7 @@ static int ieee80211_open(struct net_device *dev) if (err) return err; - return ieee80211_do_open(dev, true); + return ieee80211_do_open(&sdata->wdev, true); } static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, @@ -652,7 +673,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, /* * Stop TX on this interface first. */ - netif_tx_stop_all_queues(sdata->dev); + if (sdata->dev) + netif_tx_stop_all_queues(sdata->dev); ieee80211_roc_purge(sdata); @@ -691,14 +713,16 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, local->fif_probe_req--; } - netif_addr_lock_bh(sdata->dev); - spin_lock_bh(&local->filter_lock); - __hw_addr_unsync(&local->mc_list, &sdata->dev->mc, - sdata->dev->addr_len); - spin_unlock_bh(&local->filter_lock); - netif_addr_unlock_bh(sdata->dev); + if (sdata->dev) { + netif_addr_lock_bh(sdata->dev); + spin_lock_bh(&local->filter_lock); + __hw_addr_unsync(&local->mc_list, &sdata->dev->mc, + sdata->dev->addr_len); + spin_unlock_bh(&local->filter_lock); + netif_addr_unlock_bh(sdata->dev); - ieee80211_configure_filter(local); + ieee80211_configure_filter(local); + } del_timer_sync(&local->dynamic_ps_timer); cancel_work_sync(&local->dynamic_ps_enable_work); @@ -708,7 +732,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, struct ieee80211_sub_if_data *vlan, *tmpsdata; struct beacon_data *old_beacon = rtnl_dereference(sdata->u.ap.beacon); - struct sk_buff *old_probe_resp = + struct probe_resp *old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp); /* sdata_running will return false, so this will disable */ @@ -720,7 +744,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL); synchronize_rcu(); kfree(old_beacon); - kfree_skb(old_probe_resp); + kfree(old_probe_resp); /* down all dependent devices, that is VLANs */ list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans, @@ -759,6 +783,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ieee80211_adjust_monitor_flags(sdata, -1); ieee80211_configure_filter(local); break; + case NL80211_IFTYPE_P2P_DEVICE: + /* relies on synchronize_rcu() below */ + rcu_assign_pointer(local->p2p_sdata, NULL); + /* fall through */ default: flush_work(&sdata->work); /* @@ -771,14 +799,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, skb_queue_purge(&sdata->skb_queue); /* - * Disable beaconing here for mesh only, AP and IBSS - * are already taken care of. - */ - if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) - ieee80211_bss_info_change_notify(sdata, - BSS_CHANGED_BEACON_ENABLED); - - /* * Free all remaining keys, there shouldn't be any, * except maybe group keys in AP more or WDS? */ @@ -877,9 +897,8 @@ static void ieee80211_set_multicast_list(struct net_device *dev) * Called when the netdev is removed or, by the code below, before * the interface type changes. */ -static void ieee80211_teardown_sdata(struct net_device *dev) +static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; int flushed; int i; @@ -900,6 +919,11 @@ static void ieee80211_teardown_sdata(struct net_device *dev) WARN_ON(flushed); } +static void ieee80211_uninit(struct net_device *dev) +{ + ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev)); +} + static u16 ieee80211_netdev_select_queue(struct net_device *dev, struct sk_buff *skb) { @@ -909,7 +933,7 @@ static u16 ieee80211_netdev_select_queue(struct net_device *dev, static const struct net_device_ops ieee80211_dataif_ops = { .ndo_open = ieee80211_open, .ndo_stop = ieee80211_stop, - .ndo_uninit = ieee80211_teardown_sdata, + .ndo_uninit = ieee80211_uninit, .ndo_start_xmit = ieee80211_subif_start_xmit, .ndo_set_rx_mode = ieee80211_set_multicast_list, .ndo_change_mtu = ieee80211_change_mtu, @@ -940,7 +964,7 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev, static const struct net_device_ops ieee80211_monitorif_ops = { .ndo_open = ieee80211_open, .ndo_stop = ieee80211_stop, - .ndo_uninit = ieee80211_teardown_sdata, + .ndo_uninit = ieee80211_uninit, .ndo_start_xmit = ieee80211_monitor_start_xmit, .ndo_set_rx_mode = ieee80211_set_multicast_list, .ndo_change_mtu = ieee80211_change_mtu, @@ -1099,7 +1123,6 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, /* and set some type-dependent values */ sdata->vif.type = type; sdata->vif.p2p = false; - sdata->dev->netdev_ops = &ieee80211_dataif_ops; sdata->wdev.iftype = type; sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE); @@ -1107,8 +1130,11 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->noack_map = 0; - /* only monitor differs */ - sdata->dev->type = ARPHRD_ETHER; + /* only monitor/p2p-device differ */ + if (sdata->dev) { + sdata->dev->netdev_ops = &ieee80211_dataif_ops; + sdata->dev->type = ARPHRD_ETHER; + } skb_queue_head_init(&sdata->skb_queue); INIT_WORK(&sdata->work, ieee80211_iface_work); @@ -1146,6 +1172,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_P2P_DEVICE: break; case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: @@ -1156,18 +1183,6 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, ieee80211_debugfs_add_netdev(sdata); } -static void ieee80211_clean_sdata(struct ieee80211_sub_if_data *sdata) -{ - switch (sdata->vif.type) { - case NL80211_IFTYPE_MESH_POINT: - mesh_path_flush_by_iface(sdata); - break; - - default: - break; - } -} - static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, enum nl80211_iftype type) { @@ -1225,7 +1240,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, ieee80211_do_stop(sdata, false); - ieee80211_teardown_sdata(sdata->dev); + ieee80211_teardown_sdata(sdata); ret = drv_change_interface(local, sdata, internal_type, p2p); if (ret) @@ -1240,7 +1255,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, ieee80211_setup_sdata(sdata, type); - err = ieee80211_do_open(sdata->dev, false); + err = ieee80211_do_open(&sdata->wdev, false); WARN(err, "type change: do_open returned %d", err); return ret; @@ -1267,14 +1282,14 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, return ret; } else { /* Purge and reset type-dependent state. */ - ieee80211_teardown_sdata(sdata->dev); + ieee80211_teardown_sdata(sdata); ieee80211_setup_sdata(sdata, type); } /* reset some values that shouldn't be kept across type changes */ sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(sdata->local, - sdata->local->hw.conf.channel->band); + sdata->local->oper_channel->band); sdata->drop_unencrypted = 0; if (type == NL80211_IFTYPE_STATION) sdata->u.mgd.use_4addr = false; @@ -1283,8 +1298,7 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, } static void ieee80211_assign_perm_addr(struct ieee80211_local *local, - struct net_device *dev, - enum nl80211_iftype type) + u8 *perm_addr, enum nl80211_iftype type) { struct ieee80211_sub_if_data *sdata; u64 mask, start, addr, val, inc; @@ -1293,13 +1307,12 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, int i; /* default ... something at least */ - memcpy(dev->perm_addr, local->hw.wiphy->perm_addr, ETH_ALEN); + memcpy(perm_addr, local->hw.wiphy->perm_addr, ETH_ALEN); if (is_zero_ether_addr(local->hw.wiphy->addr_mask) && local->hw.wiphy->n_addresses <= 1) return; - mutex_lock(&local->iflist_mtx); switch (type) { @@ -1312,11 +1325,24 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type != NL80211_IFTYPE_AP) continue; - memcpy(dev->perm_addr, sdata->vif.addr, ETH_ALEN); + memcpy(perm_addr, sdata->vif.addr, ETH_ALEN); break; } /* keep default if no AP interface present */ break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + if (local->hw.flags & IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF) { + list_for_each_entry(sdata, &local->interfaces, list) { + if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) + continue; + if (!ieee80211_sdata_running(sdata)) + continue; + memcpy(perm_addr, sdata->vif.addr, ETH_ALEN); + goto out_unlock; + } + } + /* otherwise fall through */ default: /* assign a new address if possible -- try n_addresses first */ for (i = 0; i < local->hw.wiphy->n_addresses; i++) { @@ -1331,7 +1357,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, } if (!used) { - memcpy(dev->perm_addr, + memcpy(perm_addr, local->hw.wiphy->addresses[i].addr, ETH_ALEN); break; @@ -1382,7 +1408,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, } if (!used) { - memcpy(dev->perm_addr, tmp_addr, ETH_ALEN); + memcpy(perm_addr, tmp_addr, ETH_ALEN); break; } addr = (start & ~mask) | (val & mask); @@ -1391,6 +1417,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, break; } + out_unlock: mutex_unlock(&local->iflist_mtx); } @@ -1398,49 +1425,68 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, struct wireless_dev **new_wdev, enum nl80211_iftype type, struct vif_params *params) { - struct net_device *ndev; + struct net_device *ndev = NULL; struct ieee80211_sub_if_data *sdata = NULL; int ret, i; int txqs = 1; ASSERT_RTNL(); - if (local->hw.queues >= IEEE80211_NUM_ACS) - txqs = IEEE80211_NUM_ACS; - - ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size, - name, ieee80211_if_setup, txqs, 1); - if (!ndev) - return -ENOMEM; - dev_net_set(ndev, wiphy_net(local->hw.wiphy)); - - ndev->needed_headroom = local->tx_headroom + - 4*6 /* four MAC addresses */ - + 2 + 2 + 2 + 2 /* ctl, dur, seq, qos */ - + 6 /* mesh */ - + 8 /* rfc1042/bridge tunnel */ - - ETH_HLEN /* ethernet hard_header_len */ - + IEEE80211_ENCRYPT_HEADROOM; - ndev->needed_tailroom = IEEE80211_ENCRYPT_TAILROOM; - - ret = dev_alloc_name(ndev, ndev->name); - if (ret < 0) - goto fail; - - ieee80211_assign_perm_addr(local, ndev, type); - memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); - SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); - - /* don't use IEEE80211_DEV_TO_SUB_IF because it checks too much */ - sdata = netdev_priv(ndev); - ndev->ieee80211_ptr = &sdata->wdev; - memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN); - memcpy(sdata->name, ndev->name, IFNAMSIZ); + if (type == NL80211_IFTYPE_P2P_DEVICE) { + struct wireless_dev *wdev; + + sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, + GFP_KERNEL); + if (!sdata) + return -ENOMEM; + wdev = &sdata->wdev; + + sdata->dev = NULL; + strlcpy(sdata->name, name, IFNAMSIZ); + ieee80211_assign_perm_addr(local, wdev->address, type); + memcpy(sdata->vif.addr, wdev->address, ETH_ALEN); + } else { + if (local->hw.queues >= IEEE80211_NUM_ACS) + txqs = IEEE80211_NUM_ACS; + + ndev = alloc_netdev_mqs(sizeof(*sdata) + + local->hw.vif_data_size, + name, ieee80211_if_setup, txqs, 1); + if (!ndev) + return -ENOMEM; + dev_net_set(ndev, wiphy_net(local->hw.wiphy)); + + ndev->needed_headroom = local->tx_headroom + + 4*6 /* four MAC addresses */ + + 2 + 2 + 2 + 2 /* ctl, dur, seq, qos */ + + 6 /* mesh */ + + 8 /* rfc1042/bridge tunnel */ + - ETH_HLEN /* ethernet hard_header_len */ + + IEEE80211_ENCRYPT_HEADROOM; + ndev->needed_tailroom = IEEE80211_ENCRYPT_TAILROOM; + + ret = dev_alloc_name(ndev, ndev->name); + if (ret < 0) { + free_netdev(ndev); + return ret; + } + + ieee80211_assign_perm_addr(local, ndev->perm_addr, type); + memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); + SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); + + /* don't use IEEE80211_DEV_TO_SUB_IF -- it checks too much */ + sdata = netdev_priv(ndev); + ndev->ieee80211_ptr = &sdata->wdev; + memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN); + memcpy(sdata->name, ndev->name, IFNAMSIZ); + + sdata->dev = ndev; + } /* initialise type-independent data */ sdata->wdev.wiphy = local->hw.wiphy; sdata->local = local; - sdata->dev = ndev; #ifdef CONFIG_INET sdata->arp_filter_state = true; #endif @@ -1469,17 +1515,21 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, /* setup type-dependent data */ ieee80211_setup_sdata(sdata, type); - if (params) { - ndev->ieee80211_ptr->use_4addr = params->use_4addr; - if (type == NL80211_IFTYPE_STATION) - sdata->u.mgd.use_4addr = params->use_4addr; - } + if (ndev) { + if (params) { + ndev->ieee80211_ptr->use_4addr = params->use_4addr; + if (type == NL80211_IFTYPE_STATION) + sdata->u.mgd.use_4addr = params->use_4addr; + } - ndev->features |= local->hw.netdev_features; + ndev->features |= local->hw.netdev_features; - ret = register_netdevice(ndev); - if (ret) - goto fail; + ret = register_netdevice(ndev); + if (ret) { + free_netdev(ndev); + return ret; + } + } mutex_lock(&local->iflist_mtx); list_add_tail_rcu(&sdata->list, &local->interfaces); @@ -1489,10 +1539,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, *new_wdev = &sdata->wdev; return 0; - - fail: - free_netdev(ndev); - return ret; } void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata) @@ -1503,11 +1549,22 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata) list_del_rcu(&sdata->list); mutex_unlock(&sdata->local->iflist_mtx); - /* clean up type-dependent data */ - ieee80211_clean_sdata(sdata); - synchronize_rcu(); - unregister_netdevice(sdata->dev); + + if (sdata->dev) { + unregister_netdevice(sdata->dev); + } else { + cfg80211_unregister_wdev(&sdata->wdev); + kfree(sdata); + } +} + +void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata) +{ + if (WARN_ON_ONCE(!test_bit(SDATA_STATE_RUNNING, &sdata->state))) + return; + ieee80211_do_stop(sdata, true); + ieee80211_teardown_sdata(sdata); } /* @@ -1518,6 +1575,7 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata, *tmp; LIST_HEAD(unreg_list); + LIST_HEAD(wdev_list); ASSERT_RTNL(); @@ -1525,13 +1583,20 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local) list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { list_del(&sdata->list); - ieee80211_clean_sdata(sdata); - - unregister_netdevice_queue(sdata->dev, &unreg_list); + if (sdata->dev) + unregister_netdevice_queue(sdata->dev, &unreg_list); + else + list_add(&sdata->list, &wdev_list); } mutex_unlock(&local->iflist_mtx); unregister_netdevice_many(&unreg_list); list_del(&unreg_list); + + list_for_each_entry_safe(sdata, tmp, &wdev_list, list) { + list_del(&sdata->list); + cfg80211_unregister_wdev(&sdata->wdev); + kfree(sdata); + } } static int netdev_notify(struct notifier_block *nb, diff --git a/net/mac80211/main.c b/net/mac80211/main.c index c26e231..bd75293 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -207,6 +207,10 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.bssid = NULL; else if (ieee80211_vif_is_mesh(&sdata->vif)) { sdata->vif.bss_conf.bssid = zero; + } else if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) { + sdata->vif.bss_conf.bssid = sdata->vif.addr; + WARN_ONCE(changed & ~(BSS_CHANGED_IDLE), + "P2P Device BSS changed %#x", changed); } else { WARN_ON(1); return; @@ -514,6 +518,11 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4), }, + [NL80211_IFTYPE_P2P_DEVICE] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, }; static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = { @@ -536,6 +545,11 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, int priv_size, i; struct wiphy *wiphy; + if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config || + !ops->add_interface || !ops->remove_interface || + !ops->configure_filter)) + return NULL; + if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove))) return NULL; @@ -588,13 +602,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); - BUG_ON(!ops->tx); - BUG_ON(!ops->start); - BUG_ON(!ops->stop); - BUG_ON(!ops->config); - BUG_ON(!ops->add_interface); - BUG_ON(!ops->remove_interface); - BUG_ON(!ops->configure_filter); local->ops = ops; /* set up some defaults */ diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 856dcf4..f4a636f 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -109,11 +109,11 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, /* Disallow HT40+/- mismatch */ if (ie->ht_operation && - (local->_oper_channel_type == NL80211_CHAN_HT40MINUS || - local->_oper_channel_type == NL80211_CHAN_HT40PLUS) && + (sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40MINUS || + sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40PLUS) && (sta_channel_type == NL80211_CHAN_HT40MINUS || sta_channel_type == NL80211_CHAN_HT40PLUS) && - local->_oper_channel_type != sta_channel_type) + sdata->vif.bss_conf.channel_type != sta_channel_type) goto mismatch; return true; @@ -355,17 +355,18 @@ int mesh_add_ds_params_ie(struct sk_buff *skb, { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan = local->oper_channel; u8 *pos; if (skb_tailroom(skb) < 3) return -ENOMEM; - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + sband = local->hw.wiphy->bands[chan->band]; if (sband->band == IEEE80211_BAND_2GHZ) { pos = skb_put(skb, 2 + 1); *pos++ = WLAN_EID_DS_PARAMS; *pos++ = 1; - *pos++ = ieee80211_frequency_to_channel(local->hw.conf.channel->center_freq); + *pos++ = ieee80211_frequency_to_channel(chan->center_freq); } return 0; @@ -380,7 +381,7 @@ int mesh_add_ht_cap_ie(struct sk_buff *skb, sband = local->hw.wiphy->bands[local->oper_channel->band]; if (!sband->ht_cap.ht_supported || - local->_oper_channel_type == NL80211_CHAN_NO_HT) + sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap)) @@ -397,7 +398,8 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb, { struct ieee80211_local *local = sdata->local; struct ieee80211_channel *channel = local->oper_channel; - enum nl80211_channel_type channel_type = local->_oper_channel_type; + enum nl80211_channel_type channel_type = + sdata->vif.bss_conf.channel_type; struct ieee80211_supported_band *sband = local->hw.wiphy->bands[channel->band]; struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap; @@ -608,12 +610,14 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(sdata->local, - sdata->local->hw.conf.channel->band); + sdata->local->oper_channel->band); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_HT | BSS_CHANGED_BASIC_RATES | BSS_CHANGED_BEACON_INT); + + netif_carrier_on(sdata->dev); } void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) @@ -621,9 +625,15 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + netif_carrier_off(sdata->dev); + + /* stop the beacon */ ifmsh->mesh_id_len = 0; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); - sta_info_flush(local, NULL); + + /* flush STAs and mpaths on this iface */ + sta_info_flush(sdata->local, sdata); + mesh_path_flush_by_iface(sdata); del_timer_sync(&sdata->u.mesh.housekeeping_timer); del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 13fd5b5..25d0f17 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -215,6 +215,9 @@ struct mesh_rmc { /* Maximum number of paths per interface */ #define MESH_MAX_MPATHS 1024 +/* Number of frames buffered per destination for unresolved destinations */ +#define MESH_FRAME_QUEUE_LEN 10 + /* Public interfaces */ /* Various */ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 494bc39..47aeee2 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -17,8 +17,6 @@ #define MAX_METRIC 0xffffffff #define ARITH_SHIFT 8 -/* Number of frames buffered per destination for unresolved destinations */ -#define MESH_FRAME_QUEUE_LEN 10 #define MAX_PREQ_QUEUE_LEN 64 /* Destination only */ diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 075bc53..aa74981 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -203,23 +203,17 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) { struct sk_buff *skb; struct ieee80211_hdr *hdr; - struct sk_buff_head tmpq; unsigned long flags; rcu_assign_pointer(mpath->next_hop, sta); - __skb_queue_head_init(&tmpq); - spin_lock_irqsave(&mpath->frame_queue.lock, flags); - - while ((skb = __skb_dequeue(&mpath->frame_queue)) != NULL) { + skb_queue_walk(&mpath->frame_queue, skb) { hdr = (struct ieee80211_hdr *) skb->data; memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN); memcpy(hdr->addr2, mpath->sdata->vif.addr, ETH_ALEN); - __skb_queue_tail(&tmpq, skb); } - skb_queue_splice(&tmpq, &mpath->frame_queue); spin_unlock_irqrestore(&mpath->frame_queue.lock, flags); } @@ -285,40 +279,42 @@ static void mesh_path_move_to_queue(struct mesh_path *gate_mpath, struct mesh_path *from_mpath, bool copy) { - struct sk_buff *skb, *cp_skb = NULL; - struct sk_buff_head gateq, failq; + struct sk_buff *skb, *fskb, *tmp; + struct sk_buff_head failq; unsigned long flags; - int num_skbs; BUG_ON(gate_mpath == from_mpath); BUG_ON(!gate_mpath->next_hop); - __skb_queue_head_init(&gateq); __skb_queue_head_init(&failq); spin_lock_irqsave(&from_mpath->frame_queue.lock, flags); skb_queue_splice_init(&from_mpath->frame_queue, &failq); spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags); - num_skbs = skb_queue_len(&failq); - - while (num_skbs--) { - skb = __skb_dequeue(&failq); - if (copy) { - cp_skb = skb_copy(skb, GFP_ATOMIC); - if (cp_skb) - __skb_queue_tail(&failq, cp_skb); + skb_queue_walk_safe(&failq, fskb, tmp) { + if (skb_queue_len(&gate_mpath->frame_queue) >= + MESH_FRAME_QUEUE_LEN) { + mpath_dbg(gate_mpath->sdata, "mpath queue full!\n"); + break; } + skb = skb_copy(fskb, GFP_ATOMIC); + if (WARN_ON(!skb)) + break; + prepare_for_gate(skb, gate_mpath->dst, gate_mpath); - __skb_queue_tail(&gateq, skb); + skb_queue_tail(&gate_mpath->frame_queue, skb); + + if (copy) + continue; + + __skb_unlink(fskb, &failq); + kfree_skb(fskb); } - spin_lock_irqsave(&gate_mpath->frame_queue.lock, flags); - skb_queue_splice(&gateq, &gate_mpath->frame_queue); mpath_dbg(gate_mpath->sdata, "Mpath queue for gate %pM has %d frames\n", gate_mpath->dst, skb_queue_len(&gate_mpath->frame_queue)); - spin_unlock_irqrestore(&gate_mpath->frame_queue.lock, flags); if (!copy) return; @@ -531,7 +527,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) read_lock_bh(&pathtbl_resize_lock); memcpy(new_mpath->dst, dst, ETH_ALEN); - memset(new_mpath->rann_snd_addr, 0xff, ETH_ALEN); + eth_broadcast_addr(new_mpath->rann_snd_addr); new_mpath->is_root = false; new_mpath->sdata = sdata; new_mpath->flags = 0; diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index f20e9f2..9d7ad36 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -117,7 +117,7 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) u16 ht_opmode; bool non_ht_sta = false, ht20_sta = false; - if (local->_oper_channel_type == NL80211_CHAN_NO_HT) + if (sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) return 0; rcu_read_lock(); @@ -147,7 +147,8 @@ out: if (non_ht_sta) ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED; - else if (ht20_sta && local->_oper_channel_type > NL80211_CHAN_HT20) + else if (ht20_sta && + sdata->vif.bss_conf.channel_type > NL80211_CHAN_HT20) ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ; else ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE; @@ -215,12 +216,14 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, u8 *da, __le16 llid, __le16 plid, __le16 reason) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; + struct ieee80211_tx_info *info; struct ieee80211_mgmt *mgmt; bool include_plid = false; u16 peering_proto = 0; u8 *pos, ie_len = 4; int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.self_prot) + sizeof(mgmt->u.action.u.self_prot); + int err = -ENOMEM; skb = dev_alloc_skb(local->tx_headroom + hdr_len + @@ -236,6 +239,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, sdata->u.mesh.ie_len); if (!skb) return -1; + info = IEEE80211_SKB_CB(skb); skb_reserve(skb, local->tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); memset(mgmt, 0, hdr_len); @@ -256,15 +260,18 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, pos = skb_put(skb, 2); memcpy(pos + 2, &plid, 2); } - if (ieee80211_add_srates_ie(sdata, skb, true) || - ieee80211_add_ext_srates_ie(sdata, skb, true) || + if (ieee80211_add_srates_ie(sdata, skb, true, + local->oper_channel->band) || + ieee80211_add_ext_srates_ie(sdata, skb, true, + local->oper_channel->band) || mesh_add_rsn_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata)) - return -1; + goto free; } else { /* WLAN_SP_MESH_PEERING_CLOSE */ + info->flags |= IEEE80211_TX_CTL_NO_ACK; if (mesh_add_meshid_ie(skb, sdata)) - return -1; + goto free; } /* Add Mesh Peering Management element */ @@ -283,11 +290,12 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, ie_len += 2; /* reason code */ break; default: - return -EINVAL; + err = -EINVAL; + goto free; } if (WARN_ON(skb_tailroom(skb) < 2 + ie_len)) - return -ENOMEM; + goto free; pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_PEER_MGMT; @@ -308,14 +316,17 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, if (action != WLAN_SP_MESH_PEERING_CLOSE) { if (mesh_add_ht_cap_ie(skb, sdata) || mesh_add_ht_oper_ie(skb, sdata)) - return -1; + goto free; } if (mesh_add_vendor_ies(skb, sdata)) - return -1; + goto free; ieee80211_tx_skb(sdata, skb); return 0; +free: + kfree_skb(skb); + return err; } /** @@ -360,9 +371,14 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, spin_lock_bh(&sta->lock); sta->last_rx = jiffies; + if (sta->plink_state == NL80211_PLINK_ESTAB) { + spin_unlock_bh(&sta->lock); + return sta; + } + sta->sta.supp_rates[band] = rates; if (elems->ht_cap_elem && - sdata->local->_oper_channel_type != NL80211_CHAN_NO_HT) + sdata->vif.bss_conf.channel_type != NL80211_CHAN_NO_HT) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, elems->ht_cap_elem, &sta->sta.ht_cap); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index cef0c9e..b65b214 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -146,6 +146,9 @@ void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata) if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER) return; + if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) + return; + mod_timer(&sdata->u.mgd.bcn_mon_timer, round_jiffies_up(jiffies + sdata->u.mgd.beacon_timeout)); } @@ -182,15 +185,15 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, u16 ht_opmode; bool disable_40 = false; - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + sband = local->hw.wiphy->bands[local->oper_channel->band]; switch (sdata->vif.bss_conf.channel_type) { case NL80211_CHAN_HT40PLUS: - if (local->hw.conf.channel->flags & IEEE80211_CHAN_NO_HT40PLUS) + if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40PLUS) disable_40 = true; break; case NL80211_CHAN_HT40MINUS: - if (local->hw.conf.channel->flags & IEEE80211_CHAN_NO_HT40MINUS) + if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40MINUS) disable_40 = true; break; default: @@ -326,6 +329,26 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, ieee80211_ie_build_ht_cap(pos, &ht_cap, cap); } +static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, + struct ieee80211_supported_band *sband) +{ + u8 *pos; + u32 cap; + struct ieee80211_sta_vht_cap vht_cap; + + BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap)); + + memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap)); + + /* determine capability flags */ + cap = vht_cap.cap; + + /* reserve and fill IE */ + pos = skb_put(skb, sizeof(struct ieee80211_vht_capabilities) + 2); + ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); +} + static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; @@ -371,6 +394,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) 4 + /* power capability */ 2 + 2 * sband->n_channels + /* supported channels */ 2 + sizeof(struct ieee80211_ht_cap) + /* HT */ + 2 + sizeof(struct ieee80211_vht_capabilities) + /* VHT */ assoc_data->ie_len + /* extra IEs */ 9, /* WMM */ GFP_KERNEL); @@ -503,6 +527,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, sband, local->oper_channel, ifmgd->ap_smps); + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) + ieee80211_add_vht_ie(sdata, skb, sband); + /* if present, add any custom non-vendor IEs that go after HT */ if (assoc_data->ie_len && assoc_data->ie) { noffset = ieee80211_ie_split_vendor(assoc_data->ie, @@ -583,8 +610,6 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; - drv_mgd_prepare_tx(local, sdata); - ieee80211_tx_skb(sdata, skb); } } @@ -687,6 +712,7 @@ static void ieee80211_chswitch_work(struct work_struct *work) /* XXX: shouldn't really modify cfg80211-owned data! */ ifmgd->associated->channel = sdata->local->oper_channel; + /* XXX: wait for a beacon first? */ ieee80211_wake_queues_by_reason(&sdata->local->hw, IEEE80211_QUEUE_STOP_REASON_CSA); out: @@ -763,36 +789,32 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, sdata->local->csa_channel = new_ch; + ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; + + if (sw_elem->mode) + ieee80211_stop_queues_by_reason(&sdata->local->hw, + IEEE80211_QUEUE_STOP_REASON_CSA); + if (sdata->local->ops->channel_switch) { /* use driver's channel switch callback */ - struct ieee80211_channel_switch ch_switch; - memset(&ch_switch, 0, sizeof(ch_switch)); - ch_switch.timestamp = timestamp; - if (sw_elem->mode) { - ch_switch.block_tx = true; - ieee80211_stop_queues_by_reason(&sdata->local->hw, - IEEE80211_QUEUE_STOP_REASON_CSA); - } - ch_switch.channel = new_ch; - ch_switch.count = sw_elem->count; - ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; + struct ieee80211_channel_switch ch_switch = { + .timestamp = timestamp, + .block_tx = sw_elem->mode, + .channel = new_ch, + .count = sw_elem->count, + }; + drv_channel_switch(sdata->local, &ch_switch); return; } /* channel switch handled in software */ - if (sw_elem->count <= 1) { + if (sw_elem->count <= 1) ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); - } else { - if (sw_elem->mode) - ieee80211_stop_queues_by_reason(&sdata->local->hw, - IEEE80211_QUEUE_STOP_REASON_CSA); - ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; + else mod_timer(&ifmgd->chswitch_timer, - jiffies + - msecs_to_jiffies(sw_elem->count * - cbss->beacon_interval)); - } + TU_TO_EXP_TIME(sw_elem->count * + cbss->beacon_interval)); } static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, @@ -1007,6 +1029,16 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) ieee80211_change_ps(local); } +void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata) +{ + bool ps_allowed = ieee80211_powersave_allowed(sdata); + + if (sdata->vif.bss_conf.ps != ps_allowed) { + sdata->vif.bss_conf.ps = ps_allowed; + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_PS); + } +} + void ieee80211_dynamic_ps_disable_work(struct work_struct *work) { struct ieee80211_local *local = @@ -1239,7 +1271,7 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, } use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME); - if (sdata->local->hw.conf.channel->band == IEEE80211_BAND_5GHZ) + if (sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ) use_short_slot = true; if (use_protection != bss_conf->use_cts_prot) { @@ -1310,6 +1342,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_smps(local); mutex_unlock(&local->iflist_mtx); + ieee80211_recalc_ps_vif(sdata); + netif_tx_start_all_queues(sdata->dev); netif_carrier_on(sdata->dev); } @@ -1371,6 +1405,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, } local->ps_sdata = NULL; + /* disable per-vif ps */ + ieee80211_recalc_ps_vif(sdata); + /* flush out any pending frame (e.g. DELBA) before deauth/disassoc */ if (tx) drv_flush(local, false); @@ -1540,7 +1577,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) ssid_len = ssid[1]; ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, - 0, (u32) -1, true, false); + 0, (u32) -1, true, false, + ifmgd->associated->channel); } ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); @@ -1643,7 +1681,9 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, ssid_len = ssid[1]; skb = ieee80211_build_probe_req(sdata, cbss->bssid, - (u32) -1, ssid + 2, ssid_len, + (u32) -1, + sdata->local->oper_channel, + ssid + 2, ssid_len, NULL, 0, true); return skb; @@ -1654,7 +1694,6 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; - u8 bssid[ETH_ALEN]; u8 frame_buf[DEAUTH_DISASSOC_LEN]; mutex_lock(&ifmgd->mtx); @@ -1663,9 +1702,8 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) return; } - memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN); - - sdata_info(sdata, "Connection to AP %pM lost\n", bssid); + sdata_info(sdata, "Connection to AP %pM lost\n", + ifmgd->associated->bssid); ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, @@ -1683,7 +1721,7 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) mutex_unlock(&local->mtx); } -void ieee80211_beacon_connection_loss_work(struct work_struct *work) +static void ieee80211_beacon_connection_loss_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, @@ -2230,14 +2268,10 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, mutex_unlock(&local->iflist_mtx); } - if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) && - (memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid, - ETH_ALEN) == 0)) { - struct ieee80211_channel_sw_ie *sw_elem = - (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; - ieee80211_sta_process_chanswitch(sdata, sw_elem, + if (elems->ch_switch_ie && + memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid, ETH_ALEN) == 0) + ieee80211_sta_process_chanswitch(sdata, elems->ch_switch_ie, bss, rx_status->mactime); - } } @@ -2324,7 +2358,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (baselen > len) return; - if (rx_status->freq != local->hw.conf.channel->center_freq) + if (rx_status->freq != local->oper_channel->center_freq) return; if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon && @@ -2488,7 +2522,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) { struct ieee80211_supported_band *sband; - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + sband = local->hw.wiphy->bands[local->oper_channel->band]; changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, bssid, true); @@ -2671,7 +2705,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) * will not answer to direct packet in unassociated state. */ ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], - NULL, 0, (u32) -1, true, false); + NULL, 0, (u32) -1, true, false, + auth_data->bss->channel); } auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; @@ -2998,41 +3033,17 @@ int ieee80211_max_network_latency(struct notifier_block *nb, return 0; } -static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, - struct cfg80211_bss *cbss, bool assoc) +static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, + struct cfg80211_bss *cbss) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_bss *bss = (void *)cbss->priv; - struct sta_info *sta = NULL; - bool have_sta = false; - int err; int ht_cfreq; enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; const u8 *ht_oper_ie; const struct ieee80211_ht_operation *ht_oper = NULL; struct ieee80211_supported_band *sband; - if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data)) - return -EINVAL; - - if (assoc) { - rcu_read_lock(); - have_sta = sta_info_get(sdata, cbss->bssid); - rcu_read_unlock(); - } - - if (!have_sta) { - sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL); - if (!sta) - return -ENOMEM; - } - - mutex_lock(&local->mtx); - ieee80211_recalc_idle(sdata->local); - mutex_unlock(&local->mtx); - - /* switch to the right channel */ sband = local->hw.wiphy->bands[cbss->channel->band]; ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ; @@ -3095,10 +3106,51 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, local->oper_channel = cbss->channel; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); - if (sta) { + return 0; +} + +static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, + struct cfg80211_bss *cbss, bool assoc) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_bss *bss = (void *)cbss->priv; + struct sta_info *new_sta = NULL; + bool have_sta = false; + int err; + + if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data)) + return -EINVAL; + + if (assoc) { + rcu_read_lock(); + have_sta = sta_info_get(sdata, cbss->bssid); + rcu_read_unlock(); + } + + if (!have_sta) { + new_sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL); + if (!new_sta) + return -ENOMEM; + } + + mutex_lock(&local->mtx); + ieee80211_recalc_idle(sdata->local); + mutex_unlock(&local->mtx); + + if (new_sta) { u32 rates = 0, basic_rates = 0; bool have_higher_than_11mbit; int min_rate = INT_MAX, min_rate_index = -1; + struct ieee80211_supported_band *sband; + + sband = local->hw.wiphy->bands[cbss->channel->band]; + + err = ieee80211_prep_channel(sdata, cbss); + if (err) { + sta_info_free(local, new_sta); + return err; + } ieee80211_get_rates(sband, bss->supp_rates, bss->supp_rates_len, @@ -3120,7 +3172,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, basic_rates = BIT(min_rate_index); } - sta->sta.supp_rates[cbss->channel->band] = rates; + new_sta->sta.supp_rates[cbss->channel->band] = rates; sdata->vif.bss_conf.basic_rates = basic_rates; /* cf. IEEE 802.11 9.2.12 */ @@ -3143,10 +3195,10 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, BSS_CHANGED_BEACON_INT); if (assoc) - sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); + sta_info_pre_move_state(new_sta, IEEE80211_STA_AUTH); - err = sta_info_insert(sta); - sta = NULL; + err = sta_info_insert(new_sta); + new_sta = NULL; if (err) { sdata_info(sdata, "failed to insert STA entry for the AP (error %d)\n", @@ -3298,9 +3350,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, } /* prepare assoc data */ - - ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N; - ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED; + + /* + * keep only the 40 MHz disable bit set as it might have + * been set during authentication already, all other bits + * should be reset for a new connection + */ + ifmgd->flags &= IEEE80211_STA_DISABLE_40MHZ; ifmgd->beacon_crc_valid = false; @@ -3316,21 +3372,34 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) { ifmgd->flags |= IEEE80211_STA_DISABLE_11N; + ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; netdev_info(sdata->dev, - "disabling HT due to WEP/TKIP use\n"); + "disabling HT/VHT due to WEP/TKIP use\n"); } } - if (req->flags & ASSOC_REQ_DISABLE_HT) + if (req->flags & ASSOC_REQ_DISABLE_HT) { ifmgd->flags |= IEEE80211_STA_DISABLE_11N; + ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; + } /* Also disable HT if we don't support it or the AP doesn't use WMM */ sband = local->hw.wiphy->bands[req->bss->channel->band]; if (!sband->ht_cap.ht_supported || local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) { ifmgd->flags |= IEEE80211_STA_DISABLE_11N; - netdev_info(sdata->dev, - "disabling HT as WMM/QoS is not supported\n"); + if (!bss->wmm_used) + netdev_info(sdata->dev, + "disabling HT as WMM/QoS is not supported by the AP\n"); + } + + /* disable VHT if we don't support it or the AP doesn't use WMM */ + if (!sband->vht_cap.vht_supported || + local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) { + ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; + if (!bss->wmm_used) + netdev_info(sdata->dev, + "disabling VHT as WMM/QoS is not supported by the AP\n"); } memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa)); @@ -3465,14 +3534,17 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, req->bssid, req->reason_code); if (ifmgd->associated && - ether_addr_equal(ifmgd->associated->bssid, req->bssid)) + ether_addr_equal(ifmgd->associated->bssid, req->bssid)) { ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, req->reason_code, true, frame_buf); - else + } else { + drv_mgd_prepare_tx(sdata->local, sdata); ieee80211_send_deauth_disassoc(sdata, req->bssid, IEEE80211_STYPE_DEAUTH, req->reason_code, true, frame_buf); + } + mutex_unlock(&ifmgd->mtx); __cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN); diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 635c325..507121d 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -116,6 +116,9 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, if (!ieee80211_sdata_running(sdata)) continue; + if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) + continue; + if (sdata->vif.type != NL80211_IFTYPE_MONITOR) set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); @@ -144,6 +147,9 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { + if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) + continue; + if (sdata->vif.type != NL80211_IFTYPE_MONITOR) clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index 6e4fd32..10de668 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -56,7 +56,7 @@ static inline void rate_control_rate_init(struct sta_info *sta) if (!ref) return; - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + sband = local->hw.wiphy->bands[local->oper_channel->band]; ref->ops->rate_init(ref->priv, sband, ista, priv_sta); set_sta_flag(sta, WLAN_STA_RATE_CONTROL); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 0cb4ede..b382605 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -60,7 +60,9 @@ static inline int should_drop_frame(struct sk_buff *skb, struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) + if (status->flag & (RX_FLAG_FAILED_FCS_CRC | + RX_FLAG_FAILED_PLCP_CRC | + RX_FLAG_AMPDU_IS_ZEROLEN)) return 1; if (unlikely(skb->len < 16 + present_fcs_len)) return 1; @@ -91,6 +93,13 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local, if (status->flag & RX_FLAG_HT) /* HT info */ len += 3; + if (status->flag & RX_FLAG_AMPDU_DETAILS) { + /* padding */ + while (len & 3) + len++; + len += 8; + } + return len; } @@ -215,6 +224,37 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, pos++; *pos++ = status->rate_idx; } + + if (status->flag & RX_FLAG_AMPDU_DETAILS) { + u16 flags = 0; + + /* ensure 4 byte alignment */ + while ((pos - (u8 *)rthdr) & 3) + pos++; + rthdr->it_present |= + cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS); + put_unaligned_le32(status->ampdu_reference, pos); + pos += 4; + if (status->flag & RX_FLAG_AMPDU_REPORT_ZEROLEN) + flags |= IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN; + if (status->flag & RX_FLAG_AMPDU_IS_ZEROLEN) + flags |= IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN; + if (status->flag & RX_FLAG_AMPDU_LAST_KNOWN) + flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN; + if (status->flag & RX_FLAG_AMPDU_IS_LAST) + flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST; + if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_ERROR) + flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR; + if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) + flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN; + put_unaligned_le16(flags, pos); + pos += 2; + if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) + *pos++ = status->ampdu_delimiter_crc; + else + *pos++ = 0; + *pos++ = 0; + } } /* @@ -2268,7 +2308,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) goto queue; case WLAN_CATEGORY_SPECTRUM_MGMT: - if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ) + if (status->band != IEEE80211_BAND_5GHZ) break; if (sdata->vif.type != NL80211_IFTYPE_STATION) @@ -2772,8 +2812,7 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, if (!bssid) { if (!ether_addr_equal(sdata->vif.addr, hdr->addr1)) return 0; - } else if (!ieee80211_bssid_match(bssid, - sdata->vif.addr)) { + } else if (!ieee80211_bssid_match(bssid, sdata->vif.addr)) { /* * Accept public action frames even when the * BSSID doesn't match, this is used for P2P @@ -2793,9 +2832,18 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, if (!ether_addr_equal(sdata->u.wds.remote_addr, hdr->addr2)) return 0; break; + case NL80211_IFTYPE_P2P_DEVICE: + if (!ieee80211_is_public_action(hdr, skb->len) && + !ieee80211_is_probe_req(hdr->frame_control) && + !ieee80211_is_probe_resp(hdr->frame_control) && + !ieee80211_is_beacon(hdr->frame_control)) + return 0; + if (!ether_addr_equal(sdata->vif.addr, hdr->addr1)) + status->rx_flags &= ~IEEE80211_RX_RA_MATCH; + break; default: /* should never get here */ - WARN_ON(1); + WARN_ON_ONCE(1); break; } diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index bcaee5d..ef1d693 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -416,7 +416,8 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, local->scan_req->ssids[i].ssid_len, local->scan_req->ie, local->scan_req->ie_len, local->scan_req->rates[band], false, - local->scan_req->no_cck); + local->scan_req->no_cck, + local->hw.conf.channel); /* * After sending probe requests, wait for probe responses @@ -479,11 +480,10 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, if (local->ops->hw_scan) { __set_bit(SCAN_HW_SCANNING, &local->scanning); } else if ((req->n_channels == 1) && - (req->channels[0]->center_freq == - local->hw.conf.channel->center_freq)) { - - /* If we are scanning only on the current channel, then - * we do not need to stop normal activities + (req->channels[0] == local->oper_channel)) { + /* + * If we are scanning only on the operating channel + * then we do not need to stop normal activities */ unsigned long next_delay; diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 8cd7291..b0801b7 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -519,19 +519,27 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) u64 cookie = (unsigned long)skb; acked = info->flags & IEEE80211_TX_STAT_ACK; - /* - * TODO: When we have non-netdev frame TX, - * we cannot use skb->dev->ieee80211_ptr - */ - if (ieee80211_is_nullfunc(hdr->frame_control) || - ieee80211_is_qos_nullfunc(hdr->frame_control)) + ieee80211_is_qos_nullfunc(hdr->frame_control)) { cfg80211_probe_status(skb->dev, hdr->addr1, cookie, acked, GFP_ATOMIC); - else + } else if (skb->dev) { cfg80211_mgmt_tx_status( skb->dev->ieee80211_ptr, cookie, skb->data, skb->len, acked, GFP_ATOMIC); + } else { + struct ieee80211_sub_if_data *p2p_sdata; + + rcu_read_lock(); + + p2p_sdata = rcu_dereference(local->p2p_sdata); + if (p2p_sdata) { + cfg80211_mgmt_tx_status( + &p2p_sdata->wdev, cookie, skb->data, + skb->len, acked, GFP_ATOMIC); + } + rcu_read_unlock(); + } } if (unlikely(info->ack_frame_id)) { diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index c6d33b5..18d9c8a 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -24,7 +24,7 @@ __string(vif_name, sdata->dev ? sdata->dev->name : "<nodev>") #define VIF_ASSIGN __entry->vif_type = sdata->vif.type; __entry->sdata = sdata; \ __entry->p2p = sdata->vif.p2p; \ - __assign_str(vif_name, sdata->dev ? sdata->dev->name : "<nodev>") + __assign_str(vif_name, sdata->dev ? sdata->dev->name : sdata->name) #define VIF_PR_FMT " vif:%s(%d%s)" #define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" @@ -274,9 +274,12 @@ TRACE_EVENT(drv_config, __entry->dynamic_ps_timeout = local->hw.conf.dynamic_ps_timeout; __entry->max_sleep_period = local->hw.conf.max_sleep_period; __entry->listen_interval = local->hw.conf.listen_interval; - __entry->long_frame_max_tx_count = local->hw.conf.long_frame_max_tx_count; - __entry->short_frame_max_tx_count = local->hw.conf.short_frame_max_tx_count; - __entry->center_freq = local->hw.conf.channel->center_freq; + __entry->long_frame_max_tx_count = + local->hw.conf.long_frame_max_tx_count; + __entry->short_frame_max_tx_count = + local->hw.conf.short_frame_max_tx_count; + __entry->center_freq = local->hw.conf.channel ? + local->hw.conf.channel->center_freq : 0; __entry->channel_type = local->hw.conf.channel_type; __entry->smps = local->hw.conf.smps_mode; ), diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index acf712f..3b807bc 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -55,7 +55,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, if (WARN_ON_ONCE(info->control.rates[0].idx < 0)) return 0; - sband = local->hw.wiphy->bands[tx->channel->band]; + sband = local->hw.wiphy->bands[info->band]; txrate = &sband->bitrates[info->control.rates[0].idx]; erp = txrate->flags & IEEE80211_RATE_ERP_G; @@ -615,7 +615,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) memset(&txrc, 0, sizeof(txrc)); - sband = tx->local->hw.wiphy->bands[tx->channel->band]; + sband = tx->local->hw.wiphy->bands[info->band]; len = min_t(u32, tx->skb->len + FCS_LEN, tx->local->hw.wiphy->frag_threshold); @@ -626,13 +626,13 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) txrc.bss_conf = &tx->sdata->vif.bss_conf; txrc.skb = tx->skb; txrc.reported_rate.idx = -1; - txrc.rate_idx_mask = tx->sdata->rc_rateidx_mask[tx->channel->band]; + txrc.rate_idx_mask = tx->sdata->rc_rateidx_mask[info->band]; if (txrc.rate_idx_mask == (1 << sband->n_bitrates) - 1) txrc.max_rate_idx = -1; else txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; memcpy(txrc.rate_idx_mcs_mask, - tx->sdata->rc_rateidx_mcs_mask[tx->channel->band], + tx->sdata->rc_rateidx_mcs_mask[info->band], sizeof(txrc.rate_idx_mcs_mask)); txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP || tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT || @@ -667,7 +667,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) "scanning and associated. Target station: " "%pM on %d GHz band\n", tx->sdata->name, hdr->addr1, - tx->channel->band ? 5 : 2)) + info->band ? 5 : 2)) return TX_DROP; /* @@ -1131,7 +1131,6 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, tx->skb = skb; tx->local = local; tx->sdata = sdata; - tx->channel = local->hw.conf.channel; __skb_queue_head_init(&tx->skbs); /* @@ -1204,6 +1203,7 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local, struct sk_buff_head *skbs, bool txpending) { + struct ieee80211_tx_control control; struct sk_buff *skb, *tmp; unsigned long flags; @@ -1240,10 +1240,10 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local, spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); info->control.vif = vif; - info->control.sta = sta; + control.sta = sta; __skb_unlink(skb, skbs); - drv_tx(local, skb); + drv_tx(local, &control, skb); } return true; @@ -1399,8 +1399,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, goto out; } - tx.channel = local->hw.conf.channel; - info->band = tx.channel->band; + info->band = local->hw.conf.channel->band; /* set up hw_queue value early */ if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) || @@ -1720,7 +1719,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info; - int ret = NETDEV_TX_BUSY, head_need; + int head_need; u16 ethertype, hdrlen, meshhdrlen = 0; __le16 fc; struct ieee80211_hdr hdr; @@ -1736,10 +1735,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, u32 info_flags = 0; u16 info_id = 0; - if (unlikely(skb->len < ETH_HLEN)) { - ret = NETDEV_TX_OK; + if (unlikely(skb->len < ETH_HLEN)) goto fail; - } /* convert Ethernet header to proper 802.11 header (based on * operation mode) */ @@ -1787,7 +1784,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, if (!sdata->u.mesh.mshcfg.dot11MeshTTL) { /* Do not send frames with mesh_ttl == 0 */ sdata->u.mesh.mshstats.dropped_frames_ttl++; - ret = NETDEV_TX_OK; goto fail; } rcu_read_lock(); @@ -1880,10 +1876,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, if (tdls_direct) { /* link during setup - throw out frames to peer */ - if (!tdls_auth) { - ret = NETDEV_TX_OK; + if (!tdls_auth) goto fail; - } /* DA SA BSSID */ memcpy(hdr.addr1, skb->data, ETH_ALEN); @@ -1917,7 +1911,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, hdrlen = 24; break; default: - ret = NETDEV_TX_OK; goto fail; } @@ -1962,7 +1955,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, I802_DEBUG_INC(local->tx_handlers_drop_unauth_port); - ret = NETDEV_TX_OK; goto fail; } @@ -2017,10 +2009,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, skb = skb_clone(skb, GFP_ATOMIC); kfree_skb(tmp_skb); - if (!skb) { - ret = NETDEV_TX_OK; + if (!skb) goto fail; - } } hdr.frame_control = fc; @@ -2123,10 +2113,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; fail: - if (ret == NETDEV_TX_OK) - dev_kfree_skb(skb); - - return ret; + dev_kfree_skb(skb); + return NETDEV_TX_OK; } @@ -2301,12 +2289,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, struct ieee80211_sub_if_data *sdata = NULL; struct ieee80211_if_ap *ap = NULL; struct beacon_data *beacon; - struct ieee80211_supported_band *sband; - enum ieee80211_band band = local->hw.conf.channel->band; + enum ieee80211_band band = local->oper_channel->band; struct ieee80211_tx_rate_control txrc; - sband = local->hw.wiphy->bands[band]; - rcu_read_lock(); sdata = vif_to_sdata(vif); @@ -2416,7 +2401,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); - memset(mgmt->da, 0xff, ETH_ALEN); + eth_broadcast_addr(mgmt->da); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); mgmt->u.beacon.beacon_int = @@ -2428,9 +2413,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, *pos++ = WLAN_EID_SSID; *pos++ = 0x0; - if (ieee80211_add_srates_ie(sdata, skb, true) || + if (ieee80211_add_srates_ie(sdata, skb, true, band) || mesh_add_ds_params_ie(skb, sdata) || - ieee80211_add_ext_srates_ie(sdata, skb, true) || + ieee80211_add_ext_srates_ie(sdata, skb, true, band) || mesh_add_rsn_ie(skb, sdata) || mesh_add_ht_cap_ie(skb, sdata) || mesh_add_ht_oper_ie(skb, sdata) || @@ -2453,12 +2438,12 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, memset(&txrc, 0, sizeof(txrc)); txrc.hw = hw; - txrc.sband = sband; + txrc.sband = local->hw.wiphy->bands[band]; txrc.bss_conf = &sdata->vif.bss_conf; txrc.skb = skb; txrc.reported_rate.idx = -1; txrc.rate_idx_mask = sdata->rc_rateidx_mask[band]; - if (txrc.rate_idx_mask == (1 << sband->n_bitrates) - 1) + if (txrc.rate_idx_mask == (1 << txrc.sband->n_bitrates) - 1) txrc.max_rate_idx = -1; else txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; @@ -2482,7 +2467,8 @@ struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ieee80211_if_ap *ap = NULL; - struct sk_buff *presp = NULL, *skb = NULL; + struct sk_buff *skb = NULL; + struct probe_resp *presp = NULL; struct ieee80211_hdr *hdr; struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); @@ -2496,10 +2482,12 @@ struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw, if (!presp) goto out; - skb = skb_copy(presp, GFP_ATOMIC); + skb = dev_alloc_skb(presp->len); if (!skb) goto out; + memcpy(skb_put(skb, presp->len), presp->data, presp->len); + hdr = (struct ieee80211_hdr *) skb->data; memset(hdr->addr1, 0, sizeof(hdr->addr1)); @@ -2610,9 +2598,9 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, memset(hdr, 0, sizeof(*hdr)); hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ); - memset(hdr->addr1, 0xff, ETH_ALEN); + eth_broadcast_addr(hdr->addr1); memcpy(hdr->addr2, vif->addr, ETH_ALEN); - memset(hdr->addr3, 0xff, ETH_ALEN); + eth_broadcast_addr(hdr->addr3); pos = skb_put(skb, ie_ssid_len); *pos++ = WLAN_EID_SSID; @@ -2709,8 +2697,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, info = IEEE80211_SKB_CB(skb); tx.flags |= IEEE80211_TX_PS_BUFFERED; - tx.channel = local->hw.conf.channel; - info->band = tx.channel->band; + info->band = local->oper_channel->band; if (invoke_tx_handlers(&tx)) skb = NULL; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 39b82fe..471fb05 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -276,6 +276,9 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) list_for_each_entry_rcu(sdata, &local->interfaces, list) { int ac; + if (!sdata->dev) + continue; + if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) continue; @@ -364,6 +367,9 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, list_for_each_entry_rcu(sdata, &local->interfaces, list) { int ac; + if (!sdata->dev) + continue; + for (ac = 0; ac < n_acs; ac++) { if (sdata->vif.hw_queue[ac] == queue || sdata->vif.cab_queue == queue) @@ -768,8 +774,11 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, elem_parse_failed = true; break; case WLAN_EID_CHANNEL_SWITCH: - elems->ch_switch_elem = pos; - elems->ch_switch_elem_len = elen; + if (elen != sizeof(struct ieee80211_channel_sw_ie)) { + elem_parse_failed = true; + break; + } + elems->ch_switch_ie = (void *)pos; break; case WLAN_EID_QUIET: if (!elems->quiet_elem) { @@ -832,7 +841,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, memset(&qparam, 0, sizeof(qparam)); - use_11b = (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) && + use_11b = (local->oper_channel->band == IEEE80211_BAND_2GHZ) && !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE); /* @@ -899,7 +908,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, drv_conf_tx(local, sdata, ac, &qparam); } - if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { + if (sdata->vif.type != NL80211_IFTYPE_MONITOR && + sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) { sdata->vif.bss_conf.qos = enable_qos; if (bss_notify) ieee80211_bss_info_change_notify(sdata, @@ -919,7 +929,7 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, if ((supp_rates[i] & 0x7f) * 5 > 110) have_higher_than_11mbit = 1; - if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ && + if (local->oper_channel->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit) sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; else @@ -1100,6 +1110,7 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, u32 ratemask, + struct ieee80211_channel *chan, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, bool directed) @@ -1109,7 +1120,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt; size_t buf_len; u8 *buf; - u8 chan; + u8 chan_no; /* FIXME: come up with a proper value */ buf = kmalloc(200 + ie_len, GFP_KERNEL); @@ -1122,14 +1133,12 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, * badly-behaved APs don't respond when this parameter is included. */ if (directed) - chan = 0; + chan_no = 0; else - chan = ieee80211_frequency_to_channel( - local->hw.conf.channel->center_freq); + chan_no = ieee80211_frequency_to_channel(chan->center_freq); - buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, - local->hw.conf.channel->band, - ratemask, chan); + buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, chan->band, + ratemask, chan_no); skb = ieee80211_probereq_get(&local->hw, &sdata->vif, ssid, ssid_len, @@ -1154,11 +1163,13 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, - u32 ratemask, bool directed, bool no_cck) + u32 ratemask, bool directed, bool no_cck, + struct ieee80211_channel *channel) { struct sk_buff *skb; - skb = ieee80211_build_probe_req(sdata, dst, ratemask, ssid, ssid_len, + skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel, + ssid, ssid_len, ie, ie_len, directed); if (skb) { if (no_cck) @@ -1359,7 +1370,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: changed |= BSS_CHANGED_ASSOC | - BSS_CHANGED_ARP_FILTER; + BSS_CHANGED_ARP_FILTER | + BSS_CHANGED_PS; mutex_lock(&sdata->u.mgd.mtx); ieee80211_bss_info_change_notify(sdata, changed); mutex_unlock(&sdata->u.mgd.mtx); @@ -1385,6 +1397,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) case NL80211_IFTYPE_MONITOR: /* ignore virtual */ break; + case NL80211_IFTYPE_P2P_DEVICE: + changed = BSS_CHANGED_IDLE; + break; case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: @@ -1571,6 +1586,8 @@ void ieee80211_recalc_smps(struct ieee80211_local *local) list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; + if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) + continue; if (sdata->vif.type != NL80211_IFTYPE_STATION) goto set; @@ -1809,7 +1826,8 @@ ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper) } int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, bool need_basic) + struct sk_buff *skb, bool need_basic, + enum ieee80211_band band) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; @@ -1817,7 +1835,7 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, u8 i, rates, *pos; u32 basic_rates = sdata->vif.bss_conf.basic_rates; - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + sband = local->hw.wiphy->bands[band]; rates = sband->n_bitrates; if (rates > 8) rates = 8; @@ -1840,7 +1858,8 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, } int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, bool need_basic) + struct sk_buff *skb, bool need_basic, + enum ieee80211_band band) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; @@ -1848,7 +1867,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, u8 i, exrates, *pos; u32 basic_rates = sdata->vif.bss_conf.basic_rates; - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + sband = local->hw.wiphy->bands[band]; exrates = sband->n_bitrates; if (exrates > 8) exrates -= 8; diff --git a/net/wireless/chan.c b/net/wireless/chan.c index d355f67..2f876b9 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -105,7 +105,7 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, ASSERT_WDEV_LOCK(wdev); - if (!netif_running(wdev->netdev)) + if (wdev->netdev && !netif_running(wdev->netdev)) return; switch (wdev->iftype) { @@ -143,6 +143,11 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, case NL80211_IFTYPE_WDS: /* these interface types don't really have a channel */ return; + case NL80211_IFTYPE_P2P_DEVICE: + if (wdev->wiphy->features & + NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL) + *chanmode = CHAN_MODE_EXCLUSIVE; + return; case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: WARN_ON(1); diff --git a/net/wireless/core.c b/net/wireless/core.c index 31b40cc..91b3004 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -230,9 +230,24 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked) rtnl_lock(); mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->wdev_list, list) - if (wdev->netdev) + list_for_each_entry(wdev, &rdev->wdev_list, list) { + if (wdev->netdev) { dev_close(wdev->netdev); + continue; + } + /* otherwise, check iftype */ + switch (wdev->iftype) { + case NL80211_IFTYPE_P2P_DEVICE: + if (!wdev->p2p_started) + break; + rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); + wdev->p2p_started = false; + rdev->opencount--; + break; + default: + break; + } + } mutex_unlock(&rdev->devlist_mtx); rtnl_unlock(); @@ -407,6 +422,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) if (WARN_ON(wiphy->software_iftypes & types)) return -EINVAL; + /* Only a single P2P_DEVICE can be allowed */ + if (WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) && + c->limits[j].max > 1)) + return -EINVAL; + cnt += c->limits[j].max; /* * Don't advertise an unsupported type @@ -734,6 +754,35 @@ static void wdev_cleanup_work(struct work_struct *work) dev_put(wdev->netdev); } +void cfg80211_unregister_wdev(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + + ASSERT_RTNL(); + + if (WARN_ON(wdev->netdev)) + return; + + mutex_lock(&rdev->devlist_mtx); + list_del_rcu(&wdev->list); + rdev->devlist_generation++; + + switch (wdev->iftype) { + case NL80211_IFTYPE_P2P_DEVICE: + if (!wdev->p2p_started) + break; + rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); + wdev->p2p_started = false; + rdev->opencount--; + break; + default: + WARN_ON_ONCE(1); + break; + } + mutex_unlock(&rdev->devlist_mtx); +} +EXPORT_SYMBOL(cfg80211_unregister_wdev); + static struct device_type wiphy_type = { .name = "wlan", }; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 1cdb1d5..8fd0242 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -736,7 +736,6 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie) { - struct net_device *dev = wdev->netdev; const struct ieee80211_mgmt *mgmt; u16 stype; @@ -796,7 +795,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_AP_VLAN: - if (!ether_addr_equal(mgmt->bssid, dev->dev_addr)) + if (!ether_addr_equal(mgmt->bssid, wdev_address(wdev))) err = -EINVAL; break; case NL80211_IFTYPE_MESH_POINT: @@ -809,6 +808,11 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, * cfg80211 doesn't track the stations */ break; + case NL80211_IFTYPE_P2P_DEVICE: + /* + * fall through, P2P device only supports + * public action frames + */ default: err = -EOPNOTSUPP; break; @@ -819,7 +823,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, return err; } - if (!ether_addr_equal(mgmt->sa, dev->dev_addr)) + if (!ether_addr_equal(mgmt->sa, wdev_address(wdev))) return -EINVAL; /* Transmit the Action frame as requested by user space */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 97026f3..787aeaa 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1100,6 +1100,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS)) goto nla_put_failure; } + CMD(start_p2p_device, START_P2P_DEVICE); #ifdef CONFIG_NL80211_TESTMODE CMD(testmode_cmd, TESTMODE); @@ -1748,13 +1749,13 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (dev && (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || - nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dev->dev_addr))) + nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name))) goto nla_put_failure; if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFTYPE, wdev->iftype) || nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, wdev_address(wdev)) || nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->devlist_generation ^ (cfg80211_rdev_list_generation << 2))) @@ -2021,8 +2022,10 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) return PTR_ERR(wdev); } - if (type == NL80211_IFTYPE_MESH_POINT && - info->attrs[NL80211_ATTR_MESH_ID]) { + switch (type) { + case NL80211_IFTYPE_MESH_POINT: + if (!info->attrs[NL80211_ATTR_MESH_ID]) + break; wdev_lock(wdev); BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); @@ -2031,6 +2034,26 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), wdev->mesh_id_up_len); wdev_unlock(wdev); + break; + case NL80211_IFTYPE_P2P_DEVICE: + /* + * P2P Device doesn't have a netdev, so doesn't go + * through the netdev notifier and must be added here + */ + mutex_init(&wdev->mtx); + INIT_LIST_HEAD(&wdev->event_list); + spin_lock_init(&wdev->event_lock); + INIT_LIST_HEAD(&wdev->mgmt_registrations); + spin_lock_init(&wdev->mgmt_registrations_lock); + + mutex_lock(&rdev->devlist_mtx); + wdev->identifier = ++rdev->wdev_id; + list_add_rcu(&wdev->list, &rdev->wdev_list); + rdev->devlist_generation++; + mutex_unlock(&rdev->devlist_mtx); + break; + default: + break; } if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, @@ -6053,6 +6076,7 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: break; default: return -EOPNOTSUPP; @@ -6099,6 +6123,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: break; default: return -EOPNOTSUPP; @@ -6195,6 +6220,7 @@ static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *in case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: break; default: return -EOPNOTSUPP; @@ -6810,6 +6836,68 @@ static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) return 0; } +static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = info->user_ptr[1]; + int err; + + if (!rdev->ops->start_p2p_device) + return -EOPNOTSUPP; + + if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE) + return -EOPNOTSUPP; + + if (wdev->p2p_started) + return 0; + + mutex_lock(&rdev->devlist_mtx); + err = cfg80211_can_add_interface(rdev, wdev->iftype); + mutex_unlock(&rdev->devlist_mtx); + if (err) + return err; + + err = rdev->ops->start_p2p_device(&rdev->wiphy, wdev); + if (err) + return err; + + wdev->p2p_started = true; + mutex_lock(&rdev->devlist_mtx); + rdev->opencount++; + mutex_unlock(&rdev->devlist_mtx); + + return 0; +} + +static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = info->user_ptr[1]; + + if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE) + return -EOPNOTSUPP; + + if (!rdev->ops->stop_p2p_device) + return -EOPNOTSUPP; + + if (!wdev->p2p_started) + return 0; + + rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); + wdev->p2p_started = false; + + mutex_lock(&rdev->devlist_mtx); + rdev->opencount--; + mutex_unlock(&rdev->devlist_mtx); + + if (WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev)) { + rdev->scan_req->aborted = true; + ___cfg80211_scan_done(rdev, true); + } + + return 0; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -6817,7 +6905,7 @@ static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) #define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\ NL80211_FLAG_CHECK_NETDEV_UP) #define NL80211_FLAG_NEED_WDEV 0x10 -/* If a netdev is associated, it must be UP */ +/* If a netdev is associated, it must be UP, P2P must be started */ #define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\ NL80211_FLAG_CHECK_NETDEV_UP) @@ -6878,6 +6966,13 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, } dev_hold(dev); + } else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) { + if (!wdev->p2p_started) { + mutex_unlock(&cfg80211_mutex); + if (rtnl) + rtnl_unlock(); + return -ENETDOWN; + } } cfg80211_lock_rdev(rdev); @@ -7439,7 +7534,22 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, - + { + .cmd = NL80211_CMD_START_P2P_DEVICE, + .doit = nl80211_start_p2p_device, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_STOP_P2P_DEVICE, + .doit = nl80211_stop_p2p_device, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { diff --git a/net/wireless/radiotap.c b/net/wireless/radiotap.c index c4ad795..7d604c0 100644 --- a/net/wireless/radiotap.c +++ b/net/wireless/radiotap.c @@ -41,6 +41,8 @@ static const struct radiotap_align_size rtap_namespace_sizes[] = { [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, }, [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, }, + [IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, }, /* * add more here as they are defined in radiotap.h */ diff --git a/net/wireless/util.c b/net/wireless/util.c index 26f8cd3..d7b6722 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -684,22 +684,10 @@ EXPORT_SYMBOL(cfg80211_classify8021d); const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) { - u8 *end, *pos; - - pos = bss->information_elements; - if (pos == NULL) + if (bss->information_elements == NULL) return NULL; - end = pos + bss->len_information_elements; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; + return cfg80211_find_ie(ie, bss->information_elements, + bss->len_information_elements); } EXPORT_SYMBOL(ieee80211_bss_get_ie); @@ -812,6 +800,10 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, if (otype == NL80211_IFTYPE_AP_VLAN) return -EOPNOTSUPP; + /* cannot change into P2P device type */ + if (ntype == NL80211_IFTYPE_P2P_DEVICE) + return -EOPNOTSUPP; + if (!rdev->ops->change_virtual_intf || !(rdev->wiphy.interface_modes & (1 << ntype))) return -EOPNOTSUPP; @@ -889,6 +881,9 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, case NUM_NL80211_IFTYPES: /* not happening */ break; + case NL80211_IFTYPE_P2P_DEVICE: + WARN_ON(1); + break; } } @@ -1053,8 +1048,15 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, list_for_each_entry(wdev_iter, &rdev->wdev_list, list) { if (wdev_iter == wdev) continue; - if (!netif_running(wdev_iter->netdev)) - continue; + if (wdev_iter->netdev) { + if (!netif_running(wdev_iter->netdev)) + continue; + } else if (wdev_iter->iftype == NL80211_IFTYPE_P2P_DEVICE) { + if (!wdev_iter->p2p_started) + continue; + } else { + WARN_ON(1); + } if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) continue; |