From 19468413e8d98d44be8daf0acaf8d576dfc53fa2 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Sat, 28 Jan 2012 17:25:33 +0100 Subject: mac80211: add support for mcs masks * Handle MCS masks set by the user. * Match rates provided by the rate control algorithm to the mask set, also in HT mode, and switch back to legacy mode if necessary. * add debugfs files to observate the rate selection Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: John W. Linville --- net/mac80211/tx.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'net/mac80211/tx.c') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index e05667cd5e76..1be0ca2b5936 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -635,6 +635,9 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) 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], + sizeof(txrc.rate_idx_mcs_mask)); txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP || tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT || tx->sdata->vif.type == NL80211_IFTYPE_ADHOC); @@ -2431,6 +2434,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, txrc.max_rate_idx = -1; else txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; + memcpy(txrc.rate_idx_mcs_mask, sdata->rc_rateidx_mcs_mask[band], + sizeof(txrc.rate_idx_mcs_mask)); txrc.bss = true; rate_control_get_rate(sdata, NULL, &txrc); -- cgit v1.2.3 From 8860020e0be1f03d83dc9e9e93e18a4ddbe01038 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 13 Feb 2012 15:17:18 +0100 Subject: cfg80211: restructure AP/GO mode API The AP/GO mode API isn't very clearly defined, it has "set beacon" and "new beacon" etc. Modify the API to the following: * start AP -- all settings * change beacon -- new beacon data * stop AP -- stop AP mode operation This also reflects in the nl80211 API, rename the commands there correspondingly (but keep the old names for compatibility.) Overall, this makes it much clearer what's going on in the API. Kalle developed the ath6kl changes, I created the rest of the patch. Signed-off-by: Kalle Valo Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 82 ++++++---- include/linux/nl80211.h | 34 ++-- include/net/cfg80211.h | 70 ++++---- net/mac80211/cfg.c | 146 +++++++---------- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/tx.c | 13 +- net/wireless/nl80211.c | 255 ++++++++++++++++------------- 7 files changed, 316 insertions(+), 286 deletions(-) (limited to 'net/mac80211/tx.c') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index d1922d8eb3bb..5370333883e4 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2269,25 +2269,11 @@ static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif, return ret; } -static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info, bool add) +static int ath6kl_set_ies(struct ath6kl_vif *vif, + struct cfg80211_beacon_data *info) { - struct ath6kl *ar = ath6kl_priv(dev); - struct ath6kl_vif *vif = netdev_priv(dev); - struct ieee80211_mgmt *mgmt; - u8 *ies; - int ies_len; - struct wmi_connect_cmd p; + struct ath6kl *ar = vif->ar; int res; - int i, ret; - - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add); - - if (!ath6kl_cfg80211_ready(vif)) - return -EIO; - - if (vif->next_mode != AP_NETWORK) - return -EOPNOTSUPP; if (info->beacon_ies) { res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, @@ -2297,12 +2283,14 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, if (res) return res; } + if (info->proberesp_ies) { res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies, info->proberesp_ies_len); if (res) return res; } + if (info->assocresp_ies) { res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_ASSOC_RESP, @@ -2312,8 +2300,30 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, return res; } - if (!add) - return 0; + return 0; +} + +static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ap_settings *info) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + struct ieee80211_mgmt *mgmt; + u8 *ies; + int ies_len; + struct wmi_connect_cmd p; + int res; + int i, ret; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (vif->next_mode != AP_NETWORK) + return -EOPNOTSUPP; + + res = ath6kl_set_ies(vif, &info->beacon); ar->ap_mode_bkey.valid = false; @@ -2322,13 +2332,13 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, * info->dtim_period */ - if (info->head == NULL) + if (info->beacon.head == NULL) return -EINVAL; - mgmt = (struct ieee80211_mgmt *) info->head; + mgmt = (struct ieee80211_mgmt *) info->beacon.head; ies = mgmt->u.beacon.variable; - if (ies > info->head + info->head_len) + if (ies > info->beacon.head + info->beacon.head_len) return -EINVAL; - ies_len = info->head + info->head_len - ies; + ies_len = info->beacon.head + info->beacon.head_len - ies; if (info->ssid == NULL) return -EINVAL; @@ -2436,19 +2446,21 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, return 0; } -static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info) +static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_beacon_data *beacon) { - return ath6kl_ap_beacon(wiphy, dev, info, true); -} + struct ath6kl_vif *vif = netdev_priv(dev); -static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info) -{ - return ath6kl_ap_beacon(wiphy, dev, info, false); + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (vif->next_mode != AP_NETWORK) + return -EOPNOTSUPP; + + return ath6kl_set_ies(vif, beacon); } -static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev) +static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); @@ -2783,9 +2795,9 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .resume = __ath6kl_cfg80211_resume, #endif .set_channel = ath6kl_set_channel, - .add_beacon = ath6kl_add_beacon, - .set_beacon = ath6kl_set_beacon, - .del_beacon = ath6kl_del_beacon, + .start_ap = ath6kl_start_ap, + .change_beacon = ath6kl_change_beacon, + .stop_ap = ath6kl_stop_ap, .del_station = ath6kl_del_station, .change_station = ath6kl_change_station, .remain_on_channel = ath6kl_remain_on_channel, diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index ad56e21a9f10..be35a68746a7 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -156,21 +156,23 @@ * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX * or %NL80211_ATTR_MAC. * - * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a - * %NL80222_CMD_NEW_BEACON message) - * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface - * using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD, - * %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes. - * Following attributes are provided for drivers that generate full Beacon - * and Probe Response frames internally: %NL80211_ATTR_SSID, + * @NL80211_CMD_GET_BEACON: (not used) + * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface + * using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL + * attributes. For drivers that generate the beacon and probe responses + * internally, the following attributes must be provided: %NL80211_ATTR_IE, + * %NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP. + * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters + * are like for %NL80211_CMD_SET_BEACON, and additionally parameters that + * do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL, + * %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID, * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, - * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, - * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_IE, %NL80211_ATTR_IE_PROBE_RESP, - * %NL80211_ATTR_IE_ASSOC_RESP. - * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface, - * parameters are like for %NL80211_CMD_SET_BEACON. - * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it + * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY and + * %NL80211_ATTR_AUTH_TYPE. + * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP + * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface + * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP * * @NL80211_CMD_GET_STATION: Get station attributes for station identified by * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. @@ -565,8 +567,10 @@ enum nl80211_commands { NL80211_CMD_GET_BEACON, NL80211_CMD_SET_BEACON, - NL80211_CMD_NEW_BEACON, - NL80211_CMD_DEL_BEACON, + NL80211_CMD_START_AP, + NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP, + NL80211_CMD_STOP_AP, + NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP, NL80211_CMD_GET_STATION, NL80211_CMD_SET_STATION, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e0c9ff3a1977..755a7707a7c5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -366,25 +366,13 @@ struct cfg80211_crypto_settings { }; /** - * struct beacon_parameters - beacon parameters - * - * Used to configure the beacon for an interface. - * + * struct cfg80211_beacon_data - beacon data * @head: head portion of beacon (before TIM IE) * or %NULL if not changed * @tail: tail portion of beacon (after TIM IE) * or %NULL if not changed - * @interval: beacon interval or zero if not changed - * @dtim_period: DTIM period or zero if not changed * @head_len: length of @head * @tail_len: length of @tail - * @ssid: SSID to be used in the BSS (note: may be %NULL if not provided from - * user space) - * @ssid_len: length of @ssid - * @hidden_ssid: whether to hide the SSID in Beacon/Probe Response frames - * @crypto: crypto settings - * @privacy: the BSS uses privacy - * @auth_type: Authentication type (algorithm) * @beacon_ies: extra information element(s) to add into Beacon frames or %NULL * @beacon_ies_len: length of beacon_ies in octets * @proberesp_ies: extra information element(s) to add into Probe Response @@ -396,24 +384,46 @@ struct cfg80211_crypto_settings { * @probe_resp_len: length of probe response template (@probe_resp) * @probe_resp: probe response template (AP mode only) */ -struct beacon_parameters { - u8 *head, *tail; - int interval, dtim_period; - int head_len, tail_len; +struct cfg80211_beacon_data { + const u8 *head, *tail; + const u8 *beacon_ies; + const u8 *proberesp_ies; + const u8 *assocresp_ies; + const u8 *probe_resp; + + size_t head_len, tail_len; + size_t beacon_ies_len; + size_t proberesp_ies_len; + size_t assocresp_ies_len; + size_t probe_resp_len; +}; + +/** + * struct cfg80211_ap_settings - AP configuration + * + * Used to configure an AP interface. + * + * @beacon: beacon data + * @beacon_interval: beacon interval + * @dtim_period: DTIM period + * @ssid: SSID to be used in the BSS (note: may be %NULL if not provided from + * user space) + * @ssid_len: length of @ssid + * @hidden_ssid: whether to hide the SSID in Beacon/Probe Response frames + * @crypto: crypto settings + * @privacy: the BSS uses privacy + * @auth_type: Authentication type (algorithm) + */ +struct cfg80211_ap_settings { + struct cfg80211_beacon_data beacon; + + int beacon_interval, dtim_period; const u8 *ssid; size_t ssid_len; enum nl80211_hidden_ssid hidden_ssid; struct cfg80211_crypto_settings crypto; bool privacy; enum nl80211_auth_type auth_type; - const u8 *beacon_ies; - size_t beacon_ies_len; - const u8 *proberesp_ies; - size_t proberesp_ies_len; - const u8 *assocresp_ies; - size_t assocresp_ies_len; - int probe_resp_len; - u8 *probe_resp; }; /** @@ -1518,11 +1528,11 @@ struct cfg80211_ops { struct net_device *netdev, u8 key_index); - int (*add_beacon)(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info); - int (*set_beacon)(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info); - int (*del_beacon)(struct wiphy *wiphy, struct net_device *dev); + int (*start_ap)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ap_settings *settings); + int (*change_beacon)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_beacon_data *info); + int (*stop_ap)(struct wiphy *wiphy, struct net_device *dev); int (*add_station)(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c3de921c8cfd..f7eb25aabf8f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -489,27 +489,13 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, return ret; } -static void ieee80211_config_ap_ssid(struct ieee80211_sub_if_data *sdata, - struct beacon_parameters *params) -{ - struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; - - bss_conf->ssid_len = params->ssid_len; - - if (params->ssid_len) - memcpy(bss_conf->ssid, params->ssid, params->ssid_len); - - bss_conf->hidden_ssid = - (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE); -} - static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, - u8 *resp, size_t resp_len) + const u8 *resp, size_t resp_len) { struct sk_buff *new, *old; if (!resp || !resp_len) - return -EINVAL; + return 1; old = rtnl_dereference(sdata->u.ap.probe_resp); @@ -520,50 +506,28 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, memcpy(skb_put(new, resp_len), resp, resp_len); rcu_assign_pointer(sdata->u.ap.probe_resp, new); - synchronize_rcu(); - - if (old) + if (old) { + /* TODO: use call_rcu() */ + synchronize_rcu(); dev_kfree_skb(old); + } return 0; } -/* - * This handles both adding a beacon and setting new beacon info - */ -static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, - struct beacon_parameters *params) +static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_beacon_data *params) { struct beacon_data *new, *old; int new_head_len, new_tail_len; - int size; - int err = -EINVAL; - u32 changed = 0; + int size, err; + u32 changed = BSS_CHANGED_BEACON; old = rtnl_dereference(sdata->u.ap.beacon); - /* head must not be zero-length */ - if (params->head && !params->head_len) - return -EINVAL; - - /* - * This is a kludge. beacon interval should really be part - * of the beacon information. - */ - if (params->interval && - (sdata->vif.bss_conf.beacon_int != params->interval)) { - sdata->vif.bss_conf.beacon_int = params->interval; - ieee80211_bss_info_change_notify(sdata, - BSS_CHANGED_BEACON_INT); - } - /* Need to have a beacon head if we don't have one yet */ if (!params->head && !old) - return err; - - /* sorry, no way to start beaconing without dtim period */ - if (!params->dtim_period && !old) - return err; + return -EINVAL; /* new or old head? */ if (params->head) @@ -586,12 +550,6 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, /* start filling the new info now */ - /* new or old dtim period? */ - if (params->dtim_period) - new->dtim_period = params->dtim_period; - else - new->dtim_period = old->dtim_period; - /* * pointers go into the block we allocated, * memory is | beacon_data | head | tail | @@ -614,46 +572,37 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, if (old) memcpy(new->tail, old->tail, new_tail_len); - sdata->vif.bss_conf.dtim_period = new->dtim_period; - - rcu_assign_pointer(sdata->u.ap.beacon, new); - - synchronize_rcu(); - - kfree(old); - err = ieee80211_set_probe_resp(sdata, params->probe_resp, params->probe_resp_len); - if (!err) + if (err < 0) + return err; + if (err == 0) changed |= BSS_CHANGED_AP_PROBE_RESP; - ieee80211_config_ap_ssid(sdata, params); - changed |= BSS_CHANGED_BEACON_ENABLED | - BSS_CHANGED_BEACON | - BSS_CHANGED_SSID; + rcu_assign_pointer(sdata->u.ap.beacon, new); + + if (old) + kfree_rcu(old, rcu_head); - ieee80211_bss_info_change_notify(sdata, changed); - return 0; + return changed; } -static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *params) +static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ap_settings *params) { - struct ieee80211_sub_if_data *sdata; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct beacon_data *old; struct ieee80211_sub_if_data *vlan; - int ret; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); + u32 changed = BSS_CHANGED_BEACON_INT | + BSS_CHANGED_BEACON_ENABLED | + BSS_CHANGED_BEACON | + BSS_CHANGED_SSID; + int err; old = rtnl_dereference(sdata->u.ap.beacon); if (old) return -EALREADY; - ret = ieee80211_config_beacon(sdata, params); - if (ret) - return ret; - /* * Apply control port protocol, this allows us to * not encrypt dynamic WEP control frames. @@ -667,14 +616,32 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, params->crypto.control_port_no_encrypt; } + sdata->vif.bss_conf.beacon_int = params->beacon_interval; + sdata->vif.bss_conf.dtim_period = params->dtim_period; + + sdata->vif.bss_conf.ssid_len = params->ssid_len; + if (params->ssid_len) + memcpy(sdata->vif.bss_conf.ssid, params->ssid, + params->ssid_len); + sdata->vif.bss_conf.hidden_ssid = + (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE); + + err = ieee80211_assign_beacon(sdata, ¶ms->beacon); + if (err < 0) + return err; + changed |= err; + + ieee80211_bss_info_change_notify(sdata, changed); + return 0; } -static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *params) +static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_beacon_data *params) { struct ieee80211_sub_if_data *sdata; struct beacon_data *old; + int err; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -682,10 +649,14 @@ static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, if (!old) return -ENOENT; - return ieee80211_config_beacon(sdata, params); + err = ieee80211_assign_beacon(sdata, params); + if (err < 0) + return err; + ieee80211_bss_info_change_notify(sdata, err); + return 0; } -static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) +static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct ieee80211_sub_if_data *sdata; struct beacon_data *old; @@ -697,10 +668,11 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) return -ENOENT; RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); - synchronize_rcu(); - kfree(old); + + kfree_rcu(old, rcu_head); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); + return 0; } @@ -2699,9 +2671,9 @@ struct cfg80211_ops mac80211_config_ops = { .get_key = ieee80211_get_key, .set_default_key = ieee80211_config_default_key, .set_default_mgmt_key = ieee80211_config_default_mgmt_key, - .add_beacon = ieee80211_add_beacon, - .set_beacon = ieee80211_set_beacon, - .del_beacon = ieee80211_del_beacon, + .start_ap = ieee80211_start_ap, + .change_beacon = ieee80211_change_beacon, + .stop_ap = ieee80211_stop_ap, .add_station = ieee80211_add_station, .del_station = ieee80211_del_station, .change_station = ieee80211_change_station, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 74594f012cd3..67aed1eff135 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -228,7 +228,7 @@ struct ieee80211_rx_data { struct beacon_data { u8 *head, *tail; int head_len, tail_len; - int dtim_period; + struct rcu_head rcu_head; }; struct ieee80211_if_ap { diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 1be0ca2b5936..c6eadac9ca4e 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2206,7 +2206,8 @@ void ieee80211_tx_pending(unsigned long data) /* functions for drivers to get certain frames */ -static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss, +static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, + struct ieee80211_if_ap *bss, struct sk_buff *skb, struct beacon_data *beacon) { @@ -2223,7 +2224,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss, IEEE80211_MAX_AID+1); if (bss->dtim_count == 0) - bss->dtim_count = beacon->dtim_period - 1; + bss->dtim_count = sdata->vif.bss_conf.dtim_period - 1; else bss->dtim_count--; @@ -2231,7 +2232,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss, *pos++ = WLAN_EID_TIM; *pos++ = 4; *pos++ = bss->dtim_count; - *pos++ = beacon->dtim_period; + *pos++ = sdata->vif.bss_conf.dtim_period; if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf)) aid0 = 1; @@ -2324,12 +2325,14 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, * of the tim bitmap in mac80211 and the driver. */ if (local->tim_in_locked_section) { - ieee80211_beacon_add_tim(ap, skb, beacon); + ieee80211_beacon_add_tim(sdata, ap, skb, + beacon); } else { unsigned long flags; spin_lock_irqsave(&local->tim_lock, flags); - ieee80211_beacon_add_tim(ap, skb, beacon); + ieee80211_beacon_add_tim(sdata, ap, skb, + beacon); spin_unlock_irqrestore(&local->tim_lock, flags); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index fe2747653564..1998c3682774 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -871,7 +871,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(add_virtual_intf, NEW_INTERFACE); CMD(change_virtual_intf, SET_INTERFACE); CMD(add_key, NEW_KEY); - CMD(add_beacon, NEW_BEACON); + CMD(start_ap, START_AP); CMD(add_station, NEW_STATION); CMD(add_mpath, NEW_MPATH); CMD(update_mesh_config, SET_MESH_CONFIG); @@ -2075,15 +2075,10 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) return err; } -static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) +static int nl80211_parse_beacon(struct genl_info *info, + struct cfg80211_beacon_data *bcn) { - int (*call)(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info); - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *dev = info->user_ptr[1]; - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct beacon_parameters params; - int haveinfo = 0, err; + bool haveinfo = false; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) || !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) || @@ -2091,149 +2086,183 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP])) return -EINVAL; - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - memset(¶ms, 0, sizeof(params)); - - switch (info->genlhdr->cmd) { - case NL80211_CMD_NEW_BEACON: - /* these are required for NEW_BEACON */ - if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] || - !info->attrs[NL80211_ATTR_DTIM_PERIOD] || - !info->attrs[NL80211_ATTR_BEACON_HEAD]) - return -EINVAL; - - params.interval = - nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); - params.dtim_period = - nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); - - err = cfg80211_validate_beacon_int(rdev, params.interval); - if (err) - return err; - - /* - * In theory, some of these attributes could be required for - * NEW_BEACON, but since they were not used when the command was - * originally added, keep them optional for old user space - * programs to work with drivers that do not need the additional - * information. - */ - if (info->attrs[NL80211_ATTR_SSID]) { - params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - params.ssid_len = - nla_len(info->attrs[NL80211_ATTR_SSID]); - if (params.ssid_len == 0 || - params.ssid_len > IEEE80211_MAX_SSID_LEN) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) { - params.hidden_ssid = nla_get_u32( - info->attrs[NL80211_ATTR_HIDDEN_SSID]); - if (params.hidden_ssid != - NL80211_HIDDEN_SSID_NOT_IN_USE && - params.hidden_ssid != - NL80211_HIDDEN_SSID_ZERO_LEN && - params.hidden_ssid != - NL80211_HIDDEN_SSID_ZERO_CONTENTS) - return -EINVAL; - } - - params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; - - if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { - params.auth_type = nla_get_u32( - info->attrs[NL80211_ATTR_AUTH_TYPE]); - if (!nl80211_valid_auth_type(params.auth_type)) - return -EINVAL; - } else - params.auth_type = NL80211_AUTHTYPE_AUTOMATIC; - - err = nl80211_crypto_settings(rdev, info, ¶ms.crypto, - NL80211_MAX_NR_CIPHER_SUITES); - if (err) - return err; - - call = rdev->ops->add_beacon; - break; - case NL80211_CMD_SET_BEACON: - call = rdev->ops->set_beacon; - break; - default: - WARN_ON(1); - return -EOPNOTSUPP; - } - - if (!call) - return -EOPNOTSUPP; + memset(bcn, 0, sizeof(*bcn)); if (info->attrs[NL80211_ATTR_BEACON_HEAD]) { - params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); - params.head_len = - nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]); - haveinfo = 1; + bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); + bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]); + if (!bcn->head_len) + return -EINVAL; + haveinfo = true; } if (info->attrs[NL80211_ATTR_BEACON_TAIL]) { - params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]); - params.tail_len = + bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]); + bcn->tail_len = nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]); - haveinfo = 1; + haveinfo = true; } if (!haveinfo) return -EINVAL; if (info->attrs[NL80211_ATTR_IE]) { - params.beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]); - params.beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); + bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]); + bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); } if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) { - params.proberesp_ies = + bcn->proberesp_ies = nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); - params.proberesp_ies_len = + bcn->proberesp_ies_len = nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); } if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) { - params.assocresp_ies = + bcn->assocresp_ies = nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); - params.assocresp_ies_len = + bcn->assocresp_ies_len = nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); } if (info->attrs[NL80211_ATTR_PROBE_RESP]) { - params.probe_resp = + bcn->probe_resp = nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]); - params.probe_resp_len = + bcn->probe_resp_len = nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]); } - err = call(&rdev->wiphy, dev, ¶ms); - if (!err && params.interval) - wdev->beacon_interval = params.interval; + return 0; +} + +static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_ap_settings params; + int err; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + if (!rdev->ops->start_ap) + return -EOPNOTSUPP; + + if (wdev->beacon_interval) + return -EALREADY; + + memset(¶ms, 0, sizeof(params)); + + /* these are required for START_AP */ + if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] || + !info->attrs[NL80211_ATTR_DTIM_PERIOD] || + !info->attrs[NL80211_ATTR_BEACON_HEAD]) + return -EINVAL; + + err = nl80211_parse_beacon(info, ¶ms.beacon); + if (err) + return err; + + params.beacon_interval = + nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); + params.dtim_period = + nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); + + err = cfg80211_validate_beacon_int(rdev, params.beacon_interval); + if (err) + return err; + + /* + * In theory, some of these attributes should be required here + * but since they were not used when the command was originally + * added, keep them optional for old user space programs to let + * them continue to work with drivers that do not need the + * additional information -- drivers must check! + */ + if (info->attrs[NL80211_ATTR_SSID]) { + params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + params.ssid_len = + nla_len(info->attrs[NL80211_ATTR_SSID]); + if (params.ssid_len == 0 || + params.ssid_len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) { + params.hidden_ssid = nla_get_u32( + info->attrs[NL80211_ATTR_HIDDEN_SSID]); + if (params.hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE && + params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_LEN && + params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_CONTENTS) + return -EINVAL; + } + + params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; + + if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { + params.auth_type = nla_get_u32( + info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(params.auth_type)) + return -EINVAL; + } else + params.auth_type = NL80211_AUTHTYPE_AUTOMATIC; + + err = nl80211_crypto_settings(rdev, info, ¶ms.crypto, + NL80211_MAX_NR_CIPHER_SUITES); + if (err) + return err; + + err = rdev->ops->start_ap(&rdev->wiphy, dev, ¶ms); + if (!err) + wdev->beacon_interval = params.beacon_interval; return err; } -static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) +static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_beacon_data params; + int err; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + if (!rdev->ops->change_beacon) + return -EOPNOTSUPP; + + if (!wdev->beacon_interval) + return -EINVAL; + + err = nl80211_parse_beacon(info, ¶ms); + if (err) + return err; + + return rdev->ops->change_beacon(&rdev->wiphy, dev, ¶ms); +} + +static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; int err; - if (!rdev->ops->del_beacon) + if (!rdev->ops->stop_ap) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; - err = rdev->ops->del_beacon(&rdev->wiphy, dev); + if (!wdev->beacon_interval) + return -ENOENT; + + err = rdev->ops->stop_ap(&rdev->wiphy, dev); if (!err) wdev->beacon_interval = 0; return err; @@ -6357,23 +6386,23 @@ static struct genl_ops nl80211_ops[] = { .cmd = NL80211_CMD_SET_BEACON, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .doit = nl80211_addset_beacon, + .doit = nl80211_set_beacon, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { - .cmd = NL80211_CMD_NEW_BEACON, + .cmd = NL80211_CMD_START_AP, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .doit = nl80211_addset_beacon, + .doit = nl80211_start_ap, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { - .cmd = NL80211_CMD_DEL_BEACON, + .cmd = NL80211_CMD_STOP_AP, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .doit = nl80211_del_beacon, + .doit = nl80211_stop_ap, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, -- cgit v1.2.3 From 005e472b45131250fe09c194f8b872b86fd266c1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 26 Feb 2012 11:24:35 +0100 Subject: mac80211: remove local_to_hw That's a lot longer than open-coding it and doesn't really add value, so just remove it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 6 ------ net/mac80211/main.c | 6 +++--- net/mac80211/tx.c | 2 +- net/mac80211/util.c | 2 +- 4 files changed, 5 insertions(+), 11 deletions(-) (limited to 'net/mac80211/tx.c') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 4a722b10b4dd..4d1682950a60 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1128,12 +1128,6 @@ static inline struct ieee80211_local *hw_to_local( return container_of(hw, struct ieee80211_local, hw); } -static inline struct ieee80211_hw *local_to_hw( - struct ieee80211_local *local) -{ - return &local->hw; -} - static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) { diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 2306d7514fff..36fa8051296c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -286,11 +286,11 @@ static void ieee80211_tasklet_handler(unsigned long data) /* Clear skb->pkt_type in order to not confuse kernel * netstack. */ skb->pkt_type = 0; - ieee80211_rx(local_to_hw(local), skb); + ieee80211_rx(&local->hw, skb); break; case IEEE80211_TX_STATUS_MSG: skb->pkt_type = 0; - ieee80211_tx_status(local_to_hw(local), skb); + ieee80211_tx_status(&local->hw, skb); break; case IEEE80211_EOSP_MSG: eosp_data = (void *)skb->cb; @@ -668,7 +668,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, ieee80211_hw_roc_setup(local); - return local_to_hw(local); + return &local->hw; } EXPORT_SYMBOL(ieee80211_alloc_hw); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index c6eadac9ca4e..7c021f255716 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -625,7 +625,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) tx->local->hw.wiphy->frag_threshold); /* set up the tx rate control struct we give the RC algo */ - txrc.hw = local_to_hw(tx->local); + txrc.hw = &tx->local->hw; txrc.sband = sband; txrc.bss_conf = &tx->sdata->vif.bss_conf; txrc.skb = tx->skb; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 264397aee811..f6e4cef92021 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -753,7 +753,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) use_11b = (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) && !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE); - for (queue = 0; queue < local_to_hw(local)->queues; queue++) { + for (queue = 0; queue < local->hw.queues; queue++) { /* Set defaults according to 802.11-2007 Table 7-37 */ aCWmax = 1023; if (use_11b) -- cgit v1.2.3 From 02f2f1a951f87644166926862ec32fb13511e2f3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 27 Feb 2012 12:18:30 +0100 Subject: mac80211: handle non-bufferable MMPDUs correctly This renames the IEEE80211_TX_CTL_POLL_RESPONSE TX flag to IEEE80211_TX_CTL_NO_PS_BUFFER and also uses it for non-bufferable MMPDUs (all MMPDUs but deauth, disassoc and action frames.) Previously, mac80211 would let the MMPDU through but not set the flag so drivers supporting some hardware aids for avoiding the PS races would then reject the frame. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/4965-mac.c | 2 +- drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 6 +++++- drivers/net/wireless/p54/txrx.c | 2 +- include/net/mac80211.h | 17 ++++++++++------- net/mac80211/sta_info.c | 4 ++-- net/mac80211/tx.c | 17 +++++++++++------ 6 files changed, 30 insertions(+), 18 deletions(-) (limited to 'net/mac80211/tx.c') diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 2d01db0f08e0..2329033e23d7 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -1693,7 +1693,7 @@ il4965_tx_skb(struct il_priv *il, struct sk_buff *skb) sta_priv = (void *)sta->drv_priv; if (sta_priv && sta_priv->asleep && - (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) { + (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { /* * This sends an asynchronous command to the device, * but we can rely on it being processed before the diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index da18a8fc9af7..5f78567f4180 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -322,7 +322,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) sta_priv = (void *)info->control.sta->drv_priv; if (sta_priv && sta_priv->asleep && - (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) { + (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { /* * This sends an asynchronous command to the device, * but we can rely on it being processed before the @@ -331,6 +331,10 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) * counter. * For now set the counter to just 1 since we do not * support uAPSD yet. + * + * FIXME: If we get two non-bufferable frames one + * after the other, we might only send out one of + * them because this is racy. */ iwl_sta_modify_sleep_tx_count(priv, sta_id, 1); } diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 42b97bc38477..a08a6f0e4dd1 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -690,7 +690,7 @@ static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)) *flags |= P54_HDR_FLAG_DATA_OUT_SEQNR; - if (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE) + if (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER) *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index cbff4f94a200..7477f020ee7a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -341,9 +341,9 @@ struct ieee80211_bss_conf { * used to indicate that a frame was already retried due to PS * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211, * used to indicate frame should not be encrypted - * @IEEE80211_TX_CTL_POLL_RESPONSE: This frame is a response to a poll - * frame (PS-Poll or uAPSD) and should be sent although the station - * is in powersave mode. + * @IEEE80211_TX_CTL_NO_PS_BUFFER: This frame is a response to a poll + * frame (PS-Poll or uAPSD) or a non-bufferable MMPDU and must + * be sent although the station is in powersave mode. * @IEEE80211_TX_CTL_MORE_FRAMES: More frames will be passed to the * transmit function after the current frame, this can be used * by drivers to kick the DMA queue only if unset or when the @@ -399,7 +399,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14), IEEE80211_TX_INTFL_RETRIED = BIT(15), IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16), - IEEE80211_TX_CTL_POLL_RESPONSE = BIT(17), + IEEE80211_TX_CTL_NO_PS_BUFFER = BIT(17), IEEE80211_TX_CTL_MORE_FRAMES = BIT(18), IEEE80211_TX_INTFL_RETRANSMISSION = BIT(19), /* hole at 20, use later */ @@ -425,7 +425,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_SEND_AFTER_DTIM | IEEE80211_TX_CTL_AMPDU | \ IEEE80211_TX_STAT_TX_FILTERED | IEEE80211_TX_STAT_ACK | \ IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_STAT_AMPDU_NO_BACK | \ - IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_POLL_RESPONSE | \ + IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_NO_PS_BUFFER | \ IEEE80211_TX_CTL_MORE_FRAMES | IEEE80211_TX_CTL_LDPC | \ IEEE80211_TX_CTL_STBC | IEEE80211_TX_STATUS_EOSP) @@ -1634,7 +1634,7 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * the station sends a PS-Poll or a uAPSD trigger frame, mac80211 * will inform the driver of this with the @allow_buffered_frames * callback; this callback is optional. mac80211 will then transmit - * the frames as usual and set the %IEEE80211_TX_CTL_POLL_RESPONSE + * the frames as usual and set the %IEEE80211_TX_CTL_NO_PS_BUFFER * on each frame. The last frame in the service period (or the only * response to a PS-Poll) also has %IEEE80211_TX_STATUS_EOSP set to * indicate that it ends the service period; as this frame must have @@ -1642,6 +1642,9 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * When TX status is reported for this frame, the service period is * marked has having ended and a new one can be started by the peer. * + * Additionally, non-bufferable MMPDUs can also be transmitted by + * mac80211 with the %IEEE80211_TX_CTL_NO_PS_BUFFER set in them. + * * Another race condition can happen on some devices like iwlwifi * when there are frames queued for the station and it wakes up * or polls; the frames that are already queued could end up being @@ -2140,7 +2143,7 @@ enum ieee80211_frame_release_type { * @allow_buffered_frames: Prepare device to allow the given number of frames * to go out to the given station. The frames will be sent by mac80211 * via the usual TX path after this call. The TX information for frames - * released will also have the %IEEE80211_TX_CTL_POLL_RESPONSE flag set + * released will also have the %IEEE80211_TX_CTL_NO_PS_BUFFER flag set * and the last one will also have %IEEE80211_TX_STATUS_EOSP set. In case * frames from multiple TIDs are released and the driver might reorder * them between the TIDs, it must set the %IEEE80211_TX_STATUS_EOSP flag diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 98613c8f08cf..cd0f265f42e5 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1050,7 +1050,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, * exchange. Also set EOSP to indicate this packet * ends the poll/service period. */ - info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE | + info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER | IEEE80211_TX_STATUS_EOSP | IEEE80211_TX_CTL_REQ_TX_STATUS; @@ -1177,7 +1177,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, * STA may still remain is PS mode after this frame * exchange. */ - info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE; + info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER; /* * Use MoreData flag to indicate whether there are diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7c021f255716..570737df2d22 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -448,18 +448,23 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; struct ieee80211_local *local = tx->local; - if (unlikely(!sta || - ieee80211_is_probe_resp(hdr->frame_control) || - ieee80211_is_auth(hdr->frame_control) || - ieee80211_is_assoc_resp(hdr->frame_control) || - ieee80211_is_reassoc_resp(hdr->frame_control))) + if (unlikely(!sta)) return TX_CONTINUE; if (unlikely((test_sta_flag(sta, WLAN_STA_PS_STA) || test_sta_flag(sta, WLAN_STA_PS_DRIVER)) && - !(info->flags & IEEE80211_TX_CTL_POLL_RESPONSE))) { + !(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) { int ac = skb_get_queue_mapping(tx->skb); + /* only deauth, disassoc and action are bufferable MMPDUs */ + if (ieee80211_is_mgmt(hdr->frame_control) && + !ieee80211_is_deauth(hdr->frame_control) && + !ieee80211_is_disassoc(hdr->frame_control) && + !ieee80211_is_action(hdr->frame_control)) { + info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER; + return TX_CONTINUE; + } + #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "STA %pM aid %d: PS buffer for AC %d\n", sta->sta.addr, sta->sta.aid, ac); -- cgit v1.2.3 From aa4545806025c63ec12cfe17528de16dca36b785 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Wed, 7 Mar 2012 17:20:30 +0100 Subject: mac80211: Limit TID buffering during BA session setup/teardown While setting up or tearing down a BA session mac80211 is buffering pending frames for the according TID. However, there's currently no limit on how many frames are buffered possibly leading to an out-of- memory situation. This can happen on systems with little memory when the CPU is fully loaded since the BA session work is executed in process context while frames can still come via softirq. Apply a limitation to the TIDs pending queue to avoid consuming too much memory in this situation. Signed-off-by: Helmut Schaa Signed-off-by: John W. Linville --- net/mac80211/tx.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net/mac80211/tx.c') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 570737df2d22..d9e791d2b543 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1065,6 +1065,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, { bool queued = false; bool reset_agg_timer = false; + struct sk_buff *purge_skb = NULL; if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { info->flags |= IEEE80211_TX_CTL_AMPDU; @@ -1106,8 +1107,13 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, info->control.vif = &tx->sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; __skb_queue_tail(&tid_tx->pending, skb); + if (skb_queue_len(&tid_tx->pending) > STA_MAX_TX_BUFFER) + purge_skb = __skb_dequeue(&tid_tx->pending); } spin_unlock(&tx->sta->lock); + + if (purge_skb) + dev_kfree_skb(purge_skb); } /* reset session timer */ -- cgit v1.2.3 From dc41e4d474bb18e60bc6678e58adc52ed227f105 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 14 Mar 2012 16:15:03 +0200 Subject: mac80211: make uapsd_* keys per-vif uapsd_queues and uapsd_max_sp_len are relevant only for managed interfaces, and can be configured differently for each vif. Move them from the local struct to sdata->u.mgd, and update the debugfs functions accordingly. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- net/mac80211/debugfs.c | 81 ------------------------------------------- net/mac80211/debugfs_netdev.c | 58 +++++++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 28 +++++++-------- net/mac80211/main.c | 2 -- net/mac80211/mlme.c | 8 +++-- net/mac80211/tx.c | 4 +-- 6 files changed, 79 insertions(+), 102 deletions(-) (limited to 'net/mac80211/tx.c') diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 483e96ed95c1..cc5b7a6e7e0b 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -97,85 +97,6 @@ static const struct file_operations reset_ops = { .llseek = noop_llseek, }; -static ssize_t uapsd_queues_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - return mac80211_format_buffer(user_buf, count, ppos, "0x%x\n", - local->uapsd_queues); -} - -static ssize_t uapsd_queues_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - u8 val; - int ret; - - ret = kstrtou8_from_user(user_buf, count, 0, &val); - if (ret) - return ret; - - if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) - return -ERANGE; - - local->uapsd_queues = val; - - return count; -} - -static const struct file_operations uapsd_queues_ops = { - .read = uapsd_queues_read, - .write = uapsd_queues_write, - .open = mac80211_open_file_generic, - .llseek = default_llseek, -}; - -static ssize_t uapsd_max_sp_len_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - - return mac80211_format_buffer(user_buf, count, ppos, "0x%x\n", - local->uapsd_max_sp_len); -} - -static ssize_t uapsd_max_sp_len_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - unsigned long val; - char buf[10]; - size_t len; - int ret; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - buf[len] = '\0'; - - ret = kstrtoul(buf, 0, &val); - - if (ret) - return -EINVAL; - - if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) - return -ERANGE; - - local->uapsd_max_sp_len = val; - - return count; -} - -static const struct file_operations uapsd_max_sp_len_ops = { - .read = uapsd_max_sp_len_read, - .write = uapsd_max_sp_len_write, - .open = mac80211_open_file_generic, - .llseek = default_llseek, -}; - static ssize_t channel_type_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -362,8 +283,6 @@ void debugfs_hw_add(struct ieee80211_local *local) DEBUGFS_ADD(wep_iv); DEBUGFS_ADD(queues); DEBUGFS_ADD_MODE(reset, 0200); - DEBUGFS_ADD(uapsd_queues); - DEBUGFS_ADD(uapsd_max_sp_len); DEBUGFS_ADD(channel_type); DEBUGFS_ADD(hwflags); DEBUGFS_ADD(user_power); diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index ef5cf2685657..a32eeda04aa3 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -337,6 +337,62 @@ static ssize_t ieee80211_if_parse_tkip_mic_test( __IEEE80211_IF_FILE_W(tkip_mic_test); +static ssize_t ieee80211_if_fmt_uapsd_queues( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + + return snprintf(buf, buflen, "0x%x\n", ifmgd->uapsd_queues); +} + +static ssize_t ieee80211_if_parse_uapsd_queues( + struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + u8 val; + int ret; + + ret = kstrtou8(buf, 0, &val); + if (ret) + return ret; + + if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) + return -ERANGE; + + ifmgd->uapsd_queues = val; + + return buflen; +} +__IEEE80211_IF_FILE_W(uapsd_queues); + +static ssize_t ieee80211_if_fmt_uapsd_max_sp_len( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + + return snprintf(buf, buflen, "0x%x\n", ifmgd->uapsd_max_sp_len); +} + +static ssize_t ieee80211_if_parse_uapsd_max_sp_len( + struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + unsigned long val; + int ret; + + ret = kstrtoul(buf, 0, &val); + if (ret) + return -EINVAL; + + if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) + return -ERANGE; + + ifmgd->uapsd_max_sp_len = val; + + return buflen; +} +__IEEE80211_IF_FILE_W(uapsd_max_sp_len); + /* AP attributes */ IEEE80211_IF_FILE(num_sta_authorized, u.ap.num_sta_authorized, ATOMIC); IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); @@ -469,6 +525,8 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(ave_beacon); DEBUGFS_ADD_MODE(smps, 0600); DEBUGFS_ADD_MODE(tkip_mic_test, 0200); + DEBUGFS_ADD_MODE(uapsd_queues, 0600); + DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600); } static void add_ap_files(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 63fb0eb79d8e..d9798a307f20 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -459,6 +459,20 @@ struct ieee80211_if_managed { IEEE80211_MFP_REQUIRED } mfp; /* management frame protection */ + /* + * Bitmask of enabled u-apsd queues, + * IEEE80211_WMM_IE_STA_QOSINFO_AC_BE & co. Needs a new association + * to take effect. + */ + unsigned int uapsd_queues; + + /* + * Maximum number of buffered frames AP can deliver during a + * service period, IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL or similar. + * Needs a new association to take effect. + */ + unsigned int uapsd_max_sp_len; + int wmm_last_param_set; u8 use_4addr; @@ -1017,20 +1031,6 @@ struct ieee80211_local { */ unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */ - /* - * Bitmask of enabled u-apsd queues, - * IEEE80211_WMM_IE_STA_QOSINFO_AC_BE & co. Needs a new association - * to take effect. - */ - unsigned int uapsd_queues; - - /* - * Maximum number of buffered frames AP can deliver during a - * service period, IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL or similar. - * Needs a new association to take effect. - */ - unsigned int uapsd_max_sp_len; - bool pspolling; bool offchannel_ps_enabled; /* diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 36fa8051296c..b581a24fa15c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -595,8 +595,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.conf.long_frame_max_tx_count = wiphy->retry_long; local->hw.conf.short_frame_max_tx_count = wiphy->retry_short; local->user_power_level = -1; - local->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES; - local->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN; wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; INIT_LIST_HEAD(&local->interfaces); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 0df22372af8d..576fb25456dd 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -572,8 +572,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) if (assoc_data->wmm) { if (assoc_data->uapsd) { - qos_info = local->uapsd_queues; - qos_info |= (local->uapsd_max_sp_len << + qos_info = ifmgd->uapsd_queues; + qos_info |= (ifmgd->uapsd_max_sp_len << IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT); } else { qos_info = 0; @@ -1192,7 +1192,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, return; if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) - uapsd_queues = local->uapsd_queues; + uapsd_queues = ifmgd->uapsd_queues; count = wmm_param[6] & 0x0f; if (count == ifmgd->wmm_last_param_set) @@ -3013,6 +3013,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ifmgd->flags = 0; ifmgd->powersave = sdata->wdev.ps; + ifmgd->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES; + ifmgd->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN; mutex_init(&ifmgd->mtx); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index d9e791d2b543..782a60198df4 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -226,12 +226,12 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx) * have correct qos tag for some reason, due the network or the * peer application. * - * Note: local->uapsd_queues access is racy here. If the value is + * Note: ifmgd->uapsd_queues access is racy here. If the value is * changed via debugfs, user needs to reassociate manually to have * everything in sync. */ if ((ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) - && (local->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) + && (ifmgd->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) && skb_get_queue_mapping(tx->skb) == 0) return TX_CONTINUE; -- cgit v1.2.3