diff options
author | David S. Miller <davem@davemloft.net> | 2010-11-29 11:19:09 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-11-29 11:19:09 -0800 |
commit | 77148625e10d0fda50d05e92d199c0df17b66e9a (patch) | |
tree | fdb5bb1e06933dc8891a3c4d3c93c371801d8b37 /net | |
parent | a41778694806ac1ccd4b1dafed1abef8d5ba98ac (diff) | |
parent | 51cce8a590c4696d62bfacc63378d1036084cef7 (diff) | |
download | blackbird-op-linux-77148625e10d0fda50d05e92d199c0df17b66e9a.tar.gz blackbird-op-linux-77148625e10d0fda50d05e92d199c0df17b66e9a.zip |
Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/cfg.c | 26 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 37 | ||||
-rw-r--r-- | net/mac80211/driver-trace.h | 71 | ||||
-rw-r--r-- | net/mac80211/ibss.c | 2 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 10 | ||||
-rw-r--r-- | net/mac80211/key.c | 9 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 143 | ||||
-rw-r--r-- | net/mac80211/rate.c | 18 | ||||
-rw-r--r-- | net/mac80211/rc80211_minstrel_ht.c | 3 | ||||
-rw-r--r-- | net/mac80211/rx.c | 2 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 17 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 3 | ||||
-rw-r--r-- | net/mac80211/status.c | 26 | ||||
-rw-r--r-- | net/mac80211/tx.c | 16 | ||||
-rw-r--r-- | net/mac80211/util.c | 40 | ||||
-rw-r--r-- | net/mac80211/wme.c | 11 | ||||
-rw-r--r-- | net/wireless/core.c | 8 | ||||
-rw-r--r-- | net/wireless/lib80211.c | 8 | ||||
-rw-r--r-- | net/wireless/lib80211_crypt_tkip.c | 16 | ||||
-rw-r--r-- | net/wireless/mlme.c | 12 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 111 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 4 | ||||
-rw-r--r-- | net/wireless/reg.c | 127 | ||||
-rw-r--r-- | net/wireless/util.c | 11 | ||||
-rw-r--r-- | net/wireless/wext-core.c | 10 |
25 files changed, 606 insertions, 135 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 18bd0e550600..0c544074479e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1299,6 +1299,13 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) struct ieee80211_local *local = wiphy_priv(wiphy); int err; + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + err = drv_set_frag_threshold(local, wiphy->frag_threshold); + + if (err) + return err; + } + if (changed & WIPHY_PARAM_COVERAGE_CLASS) { err = drv_set_coverage_class(local, wiphy->coverage_class); @@ -1621,6 +1628,23 @@ static void ieee80211_mgmt_frame_register(struct wiphy *wiphy, ieee80211_queue_work(&local->hw, &local->reconfig_filter); } +static int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + + if (local->started) + return -EOPNOTSUPP; + + return drv_set_antenna(local, tx_ant, rx_ant); +} + +static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + + return drv_get_antenna(local, tx_ant, rx_ant); +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1673,4 +1697,6 @@ struct cfg80211_ops mac80211_config_ops = { .mgmt_tx = ieee80211_mgmt_tx, .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config, .mgmt_frame_register = ieee80211_mgmt_frame_register, + .set_antenna = ieee80211_set_antenna, + .get_antenna = ieee80211_get_antenna, }; diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 16983825f8e8..4244554d218a 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -233,6 +233,20 @@ static inline void drv_get_tkip_seq(struct ieee80211_local *local, trace_drv_get_tkip_seq(local, hw_key_idx, iv32, iv16); } +static inline int drv_set_frag_threshold(struct ieee80211_local *local, + u32 value) +{ + int ret = 0; + + might_sleep(); + + trace_drv_set_frag_threshold(local, value); + if (local->ops->set_frag_threshold) + ret = local->ops->set_frag_threshold(&local->hw, value); + trace_drv_return_int(local, ret); + return ret; +} + static inline int drv_set_rts_threshold(struct ieee80211_local *local, u32 value) { @@ -428,4 +442,27 @@ static inline void drv_channel_switch(struct ieee80211_local *local, trace_drv_return_void(local); } + +static inline int drv_set_antenna(struct ieee80211_local *local, + u32 tx_ant, u32 rx_ant) +{ + int ret = -EOPNOTSUPP; + might_sleep(); + if (local->ops->set_antenna) + ret = local->ops->set_antenna(&local->hw, tx_ant, rx_ant); + trace_drv_set_antenna(local, tx_ant, rx_ant, ret); + return ret; +} + +static inline int drv_get_antenna(struct ieee80211_local *local, + u32 *tx_ant, u32 *rx_ant) +{ + int ret = -EOPNOTSUPP; + might_sleep(); + if (local->ops->get_antenna) + ret = local->ops->get_antenna(&local->hw, tx_ant, rx_ant); + trace_drv_get_antenna(local, *tx_ant, *rx_ant, ret); + return ret; +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 6831fb1641c8..c2772f23ac9c 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -531,6 +531,27 @@ TRACE_EVENT(drv_get_tkip_seq, ) ); +TRACE_EVENT(drv_set_frag_threshold, + TP_PROTO(struct ieee80211_local *local, u32 value), + + TP_ARGS(local, value), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u32, value) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->value = value; + ), + + TP_printk( + LOCAL_PR_FMT " value:%d", + LOCAL_PR_ARG, __entry->value + ) +); + TRACE_EVENT(drv_set_rts_threshold, TP_PROTO(struct ieee80211_local *local, u32 value), @@ -862,6 +883,56 @@ TRACE_EVENT(drv_channel_switch, ) ); +TRACE_EVENT(drv_set_antenna, + TP_PROTO(struct ieee80211_local *local, u32 tx_ant, u32 rx_ant, int ret), + + TP_ARGS(local, tx_ant, rx_ant, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u32, tx_ant) + __field(u32, rx_ant) + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->tx_ant = tx_ant; + __entry->rx_ant = rx_ant; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT " tx_ant:%d rx_ant:%d ret:%d", + LOCAL_PR_ARG, __entry->tx_ant, __entry->rx_ant, __entry->ret + ) +); + +TRACE_EVENT(drv_get_antenna, + TP_PROTO(struct ieee80211_local *local, u32 tx_ant, u32 rx_ant, int ret), + + TP_ARGS(local, tx_ant, rx_ant, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u32, tx_ant) + __field(u32, rx_ant) + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->tx_ant = tx_ant; + __entry->rx_ant = rx_ant; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT " tx_ant:%d rx_ant:%d ret:%d", + LOCAL_PR_ARG, __entry->tx_ant, __entry->rx_ant, __entry->ret + ) +); + /* * Tracing for API calls that drivers call. */ diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 239c4836a946..410d104b1347 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -915,6 +915,8 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->u.ibss.privacy = params->privacy; sdata->u.ibss.basic_rates = params->basic_rates; + memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate, + sizeof(params->mcast_rate)); sdata->vif.bss_conf.beacon_int = params->beacon_interval; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b80c38689927..5bc0745368fe 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -349,6 +349,7 @@ struct ieee80211_if_managed { struct work_struct chswitch_work; struct work_struct beacon_connection_loss_work; + unsigned long beacon_timeout; unsigned long probe_timeout; int probe_send_count; @@ -1264,6 +1265,8 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, int powersave); 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); void ieee80211_beacon_connection_loss_work(struct work_struct *work); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, @@ -1278,6 +1281,9 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local, struct sk_buff *skb); int ieee80211_add_pending_skbs(struct ieee80211_local *local, struct sk_buff_head *skbs); +int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, + struct sk_buff_head *skbs, + void (*fn)(void *data), void *data); void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, @@ -1287,6 +1293,10 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, const u8 *ie, size_t ie_len, enum ieee80211_band band, u32 rate_mask, u8 channel); +struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, + u8 *dst, + const u8 *ssid, size_t ssid_len, + const u8 *ie, size_t ie_len); 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); diff --git a/net/mac80211/key.c b/net/mac80211/key.c index ccd676b2f599..72df1ca7299b 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -84,10 +84,17 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) goto out_unsupported; sdata = key->sdata; - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { + /* + * The driver doesn't know anything about VLAN interfaces. + * Hence, don't send GTKs for VLAN interfaces to the driver. + */ + if (!(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE)) + goto out_unsupported; sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); + } ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a3a9421555af..794807914940 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -28,13 +28,19 @@ #include "rate.h" #include "led.h" +#define IEEE80211_MAX_NULLFUNC_TRIES 2 #define IEEE80211_MAX_PROBE_TRIES 5 /* - * beacon loss detection timeout - * XXX: should depend on beacon interval + * Beacon loss timeout is calculated as N frames times the + * advertised beacon interval. This may need to be somewhat + * higher than what hardware might detect to account for + * delays in the host processing frames. But since we also + * probe on beacon miss before declaring the connection lost + * default to what we want. */ -#define IEEE80211_BEACON_LOSS_TIME (2 * HZ) +#define IEEE80211_BEACON_LOSS_COUNT 7 + /* * Time the connection can be idle before we probe * it to see if we can still talk to the AP. @@ -121,7 +127,7 @@ void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata) return; mod_timer(&sdata->u.mgd.bcn_mon_timer, - round_jiffies_up(jiffies + IEEE80211_BEACON_LOSS_TIME)); + round_jiffies_up(jiffies + sdata->u.mgd.beacon_timeout)); } void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata) @@ -871,6 +877,9 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_info_changed |= ieee80211_handle_bss_capability(sdata, cbss->capability, bss->has_erp_value, bss->erp_value); + sdata->u.mgd.beacon_timeout = usecs_to_jiffies(ieee80211_tu_to_usec( + IEEE80211_BEACON_LOSS_COUNT * bss_conf->beacon_int)); + sdata->u.mgd.associated = cbss; memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN); @@ -1026,6 +1035,51 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, ieee80211_sta_reset_conn_monitor(sdata); } +static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + + if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL | + IEEE80211_STA_CONNECTION_POLL))) + return; + + ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL | + IEEE80211_STA_BEACON_POLL); + mutex_lock(&sdata->local->iflist_mtx); + ieee80211_recalc_ps(sdata->local, -1); + mutex_unlock(&sdata->local->iflist_mtx); + + if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) + return; + + /* + * We've received a probe response, but are not sure whether + * we have or will be receiving any beacons or data, so let's + * schedule the timers again, just in case. + */ + ieee80211_sta_reset_beacon_monitor(sdata); + + mod_timer(&ifmgd->conn_mon_timer, + round_jiffies_up(jiffies + + IEEE80211_CONNECTION_IDLE_TIME)); +} + +void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, + struct ieee80211_hdr *hdr) +{ + if (!ieee80211_is_data(hdr->frame_control) && + !ieee80211_is_nullfunc(hdr->frame_control)) + return; + + ieee80211_sta_reset_conn_monitor(sdata); + + if (ieee80211_is_nullfunc(hdr->frame_control) && + sdata->u.mgd.probe_send_count > 0) { + sdata->u.mgd.probe_send_count = 0; + ieee80211_queue_work(&sdata->local->hw, &sdata->work); + } +} + static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -1041,8 +1095,19 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) if (ifmgd->probe_send_count >= unicast_limit) dst = NULL; - ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); - ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0); + /* + * When the hardware reports an accurate Tx ACK status, it's + * better to send a nullfunc frame instead of a probe request, + * as it will kick us off the AP quickly if we aren't associated + * anymore. The timeout will be reset if the frame is ACKed by + * the AP. + */ + if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) + ieee80211_send_nullfunc(sdata->local, sdata, 0); + else { + ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); + ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0); + } ifmgd->probe_send_count++; ifmgd->probe_timeout = jiffies + IEEE80211_PROBE_WAIT; @@ -1108,6 +1173,30 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, mutex_unlock(&ifmgd->mtx); } +struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct sk_buff *skb; + const u8 *ssid; + + if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) + return NULL; + + ASSERT_MGD_MTX(ifmgd); + + if (!ifmgd->associated) + return NULL; + + ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); + skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid, + ssid + 2, ssid[1], NULL, 0); + + return skb; +} +EXPORT_SYMBOL(ieee80211_ap_probereq_get); + static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -1485,29 +1574,8 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); if (ifmgd->associated && - memcmp(mgmt->bssid, ifmgd->associated->bssid, ETH_ALEN) == 0 && - ifmgd->flags & (IEEE80211_STA_BEACON_POLL | - IEEE80211_STA_CONNECTION_POLL)) { - ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL | - IEEE80211_STA_BEACON_POLL); - mutex_lock(&sdata->local->iflist_mtx); - ieee80211_recalc_ps(sdata->local, -1); - mutex_unlock(&sdata->local->iflist_mtx); - - if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) - return; - - /* - * We've received a probe response, but are not sure whether - * we have or will be receiving any beacons or data, so let's - * schedule the timers again, just in case. - */ - ieee80211_sta_reset_beacon_monitor(sdata); - - mod_timer(&ifmgd->conn_mon_timer, - round_jiffies_up(jiffies + - IEEE80211_CONNECTION_IDLE_TIME)); - } + memcmp(mgmt->bssid, ifmgd->associated->bssid, ETH_ALEN) == 0) + ieee80211_reset_ap_probe(sdata); } /* @@ -1857,12 +1925,23 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) IEEE80211_STA_CONNECTION_POLL) && ifmgd->associated) { u8 bssid[ETH_ALEN]; + int max_tries; memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN); - if (time_is_after_jiffies(ifmgd->probe_timeout)) + + if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) + max_tries = IEEE80211_MAX_NULLFUNC_TRIES; + else + max_tries = IEEE80211_MAX_PROBE_TRIES; + + /* ACK received for nullfunc probing frame */ + if (!ifmgd->probe_send_count) + ieee80211_reset_ap_probe(sdata); + + else if (time_is_after_jiffies(ifmgd->probe_timeout)) run_again(ifmgd, ifmgd->probe_timeout); - else if (ifmgd->probe_send_count < IEEE80211_MAX_PROBE_TRIES) { + else if (ifmgd->probe_send_count < max_tries) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG wiphy_debug(local->hw.wiphy, "%s: No probe response from AP %pM" @@ -1988,6 +2067,8 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) add_timer(&ifmgd->timer); if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running)) add_timer(&ifmgd->chswitch_timer); + ieee80211_sta_reset_beacon_monitor(sdata); + ieee80211_restart_sta_timer(sdata); } #endif diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 33f76993da08..3d5a2cb835c4 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -211,7 +211,8 @@ static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc) return (info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc); } -static void rc_send_low_broadcast(s8 *idx, u32 basic_rates, u8 max_rate_idx) +static void rc_send_low_broadcast(s8 *idx, u32 basic_rates, + struct ieee80211_supported_band *sband) { u8 i; @@ -222,7 +223,7 @@ static void rc_send_low_broadcast(s8 *idx, u32 basic_rates, u8 max_rate_idx) if (basic_rates & (1 << *idx)) return; /* selected rate is a basic rate */ - for (i = *idx + 1; i <= max_rate_idx; i++) { + for (i = *idx + 1; i <= sband->n_bitrates; i++) { if (basic_rates & (1 << i)) { *idx = i; return; @@ -237,16 +238,25 @@ bool rate_control_send_low(struct ieee80211_sta *sta, struct ieee80211_tx_rate_control *txrc) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb); + struct ieee80211_supported_band *sband = txrc->sband; + int mcast_rate; if (!sta || !priv_sta || rc_no_data_or_no_ack(txrc)) { info->control.rates[0].idx = rate_lowest_index(txrc->sband, sta); info->control.rates[0].count = (info->flags & IEEE80211_TX_CTL_NO_ACK) ? 1 : txrc->hw->max_rate_tries; - if (!sta && txrc->ap) + if (!sta && txrc->bss) { + mcast_rate = txrc->bss_conf->mcast_rate[sband->band]; + if (mcast_rate > 0) { + info->control.rates[0].idx = mcast_rate - 1; + return true; + } + rc_send_low_broadcast(&info->control.rates[0].idx, txrc->bss_conf->basic_rates, - txrc->sband->n_bitrates); + sband); + } return true; } return false; diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 2d6f0259e0c6..4ad7a362fcc1 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -371,6 +371,9 @@ minstrel_aggr_check(struct minstrel_priv *mp, struct ieee80211_sta *pubsta, stru if (likely(sta->ampdu_mlme.tid_tx[tid])) return; + if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO) + return; + ieee80211_start_tx_ba_session(pubsta, tid); } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 902b03ee8f60..d2fcd22ab06d 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1102,8 +1102,6 @@ static void ap_sta_ps_end(struct sta_info *sta) atomic_dec(&sdata->bss->num_sta_ps); - clear_sta_flags(sta, WLAN_STA_PS_STA); - #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "%s: STA %pM aid %d exits power save mode\n", sdata->name, sta->sta.addr, sta->sta.aid); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 6d8f897d8763..eff58571fd7e 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -199,8 +199,11 @@ static void sta_unblock(struct work_struct *wk) if (!test_sta_flags(sta, WLAN_STA_PS_STA)) ieee80211_sta_ps_deliver_wakeup(sta); - else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) + else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) { + clear_sta_flags(sta, WLAN_STA_PS_DRIVER); ieee80211_sta_ps_deliver_poll_response(sta); + } else + clear_sta_flags(sta, WLAN_STA_PS_DRIVER); } static int sta_prepare_rate_control(struct ieee80211_local *local, @@ -880,6 +883,13 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif, } EXPORT_SYMBOL(ieee80211_find_sta); +static void clear_sta_ps_flags(void *_sta) +{ + struct sta_info *sta = _sta; + + clear_sta_flags(sta, WLAN_STA_PS_DRIVER | WLAN_STA_PS_STA); +} + /* powersave support code */ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) { @@ -894,7 +904,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) /* Send all buffered frames to the station */ sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered); - buffered = ieee80211_add_pending_skbs(local, &sta->ps_tx_buf); + buffered = ieee80211_add_pending_skbs_fn(local, &sta->ps_tx_buf, + clear_sta_ps_flags, sta); sent += buffered; local->total_ps_buffered -= buffered; @@ -973,7 +984,7 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, if (block) set_sta_flags(sta, WLAN_STA_PS_DRIVER); - else + else if (test_sta_flags(sta, WLAN_STA_PS_DRIVER)) ieee80211_queue_work(hw, &sta->drv_unblock_wk); } EXPORT_SYMBOL(ieee80211_sta_block_awake); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 9265acadef32..b562d9b6a702 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -248,6 +248,7 @@ enum plink_state { * @sta: station information we share with the driver * @dead: set to true when sta is unlinked * @uploaded: set to true when sta is uploaded to the driver + * @lost_packets: number of consecutive lost packets */ struct sta_info { /* General information, mostly static */ @@ -335,6 +336,8 @@ struct sta_info { } debugfs; #endif + unsigned int lost_packets; + /* keep last! */ struct ieee80211_sta sta; }; diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 3153c19893b8..bed7e32ed908 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -155,8 +155,21 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) ieee80211_queue_work(&local->hw, &local->recalc_smps); } + + if ((sdata->vif.type == NL80211_IFTYPE_STATION) && + (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) + ieee80211_sta_tx_notify(sdata, (void *) skb->data); } +/* + * Use a static threshold for now, best value to be determined + * by testing ... + * Should it depend on: + * - on # of retransmissions + * - current throughput (higher value for higher tpt)? + */ +#define STA_LOST_PKT_THRESHOLD 50 + void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) { struct sk_buff *skb2; @@ -243,6 +256,19 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && (info->flags & IEEE80211_TX_STAT_ACK)) ieee80211_frame_acked(sta, skb); + + if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) { + if (info->flags & IEEE80211_TX_STAT_ACK) { + if (sta->lost_packets) + sta->lost_packets = 0; + } else if (++sta->lost_packets >= STA_LOST_PKT_THRESHOLD) { + cfg80211_cqm_pktloss_notify(sta->sdata->dev, + sta->sta.addr, + sta->lost_packets, + GFP_ATOMIC); + sta->lost_packets = 0; + } + } } rcu_read_unlock(); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 96c594309506..e69483647f33 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -622,7 +622,8 @@ 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; - txrc.ap = tx->sdata->vif.type == NL80211_IFTYPE_AP; + txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP || + tx->sdata->vif.type == NL80211_IFTYPE_ADHOC); /* set up RTS protection if desired */ if (len > tx->local->hw.wiphy->rts_threshold) { @@ -1033,6 +1034,7 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, struct ieee80211_radiotap_header *rthdr = (struct ieee80211_radiotap_header *) skb->data; struct ieee80211_supported_band *sband; + bool hw_frag; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, NULL); @@ -1042,6 +1044,9 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; tx->flags &= ~IEEE80211_TX_FRAGMENTED; + /* packet is fragmented in HW if we have a non-NULL driver callback */ + hw_frag = (tx->local->ops->set_frag_threshold != NULL); + /* * for every radiotap entry that is present * (ieee80211_radiotap_iterator_next returns -ENOENT when no more @@ -1078,7 +1083,8 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, } if (*iterator.this_arg & IEEE80211_RADIOTAP_F_WEP) info->flags &= ~IEEE80211_TX_INTFL_DONT_ENCRYPT; - if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG) + if ((*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG) && + !hw_frag) tx->flags |= IEEE80211_TX_FRAGMENTED; break; @@ -1181,8 +1187,10 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, /* * Set this flag (used below to indicate "automatic fragmentation"), * it will be cleared/left by radiotap as desired. + * Only valid when fragmentation is done by the stack. */ - tx->flags |= IEEE80211_TX_FRAGMENTED; + if (!local->ops->set_frag_threshold) + tx->flags |= IEEE80211_TX_FRAGMENTED; /* process and remove the injection radiotap header */ if (unlikely(info->flags & IEEE80211_TX_INTFL_HAS_RADIOTAP)) { @@ -2301,7 +2309,7 @@ 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; - txrc.ap = true; + txrc.bss = true; rate_control_get_rate(sdata, NULL, &txrc); info->control.vif = vif; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0b6fc92bc0d7..e497476174ce 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -368,8 +368,9 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local, spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } -int ieee80211_add_pending_skbs(struct ieee80211_local *local, - struct sk_buff_head *skbs) +int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, + struct sk_buff_head *skbs, + void (*fn)(void *data), void *data) { struct ieee80211_hw *hw = &local->hw; struct sk_buff *skb; @@ -394,6 +395,9 @@ int ieee80211_add_pending_skbs(struct ieee80211_local *local, __skb_queue_tail(&local->pending[queue], skb); } + if (fn) + fn(data); + for (i = 0; i < hw->queues; i++) __ieee80211_wake_queue(hw, i, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); @@ -402,6 +406,12 @@ int ieee80211_add_pending_skbs(struct ieee80211_local *local, return ret; } +int ieee80211_add_pending_skbs(struct ieee80211_local *local, + struct sk_buff_head *skbs) +{ + return ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL); +} + void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason) { @@ -1011,9 +1021,10 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, return pos - buffer; } -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) +struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, + u8 *dst, + const u8 *ssid, size_t ssid_len, + const u8 *ie, size_t ie_len) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; @@ -1027,7 +1038,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, if (!buf) { printk(KERN_DEBUG "%s: failed to allocate temporary IE " "buffer\n", sdata->name); - return; + return NULL; } chan = ieee80211_frequency_to_channel( @@ -1050,8 +1061,20 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, } IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; - ieee80211_tx_skb(sdata, skb); kfree(buf); + + return skb; +} + +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) +{ + struct sk_buff *skb; + + skb = ieee80211_build_probe_req(sdata, dst, ssid, ssid_len, ie, ie_len); + if (skb) + ieee80211_tx_skb(sdata, skb); } u32 ieee80211_sta_get_rates(struct ieee80211_local *local, @@ -1152,6 +1175,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) } mutex_unlock(&local->sta_mtx); + /* setup fragmentation threshold */ + drv_set_frag_threshold(local, hw->wiphy->frag_threshold); + /* setup RTS threshold */ drv_set_rts_threshold(local, hw->wiphy->rts_threshold); diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 34e6d02da779..58e75bbc1f91 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -21,7 +21,16 @@ /* Default mapping in classifier to work with default * queue setup. */ -const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 }; +const int ieee802_1d_to_ac[8] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO +}; static int wme_downgrade_ac(struct sk_buff *skb) { diff --git a/net/wireless/core.c b/net/wireless/core.c index 9c21ebf9780e..630bcf0a2f04 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -4,6 +4,8 @@ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/if.h> #include <linux/module.h> #include <linux/err.h> @@ -216,8 +218,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, rdev->wiphy.debugfsdir, rdev->wiphy.debugfsdir->d_parent, newname)) - printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n", - newname); + pr_err("failed to rename debugfs dir to %s!\n", newname); nl80211_notify_dev_rename(rdev); @@ -699,8 +700,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, "phy80211")) { - printk(KERN_ERR "wireless: failed to add phy80211 " - "symlink to netdev!\n"); + pr_err("failed to add phy80211 symlink to netdev!\n"); } wdev->netdev = dev; wdev->sme_state = CFG80211_SME_IDLE; diff --git a/net/wireless/lib80211.c b/net/wireless/lib80211.c index 97d411f74507..3268fac5ab22 100644 --- a/net/wireless/lib80211.c +++ b/net/wireless/lib80211.c @@ -13,6 +13,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/ctype.h> #include <linux/ieee80211.h> @@ -224,8 +226,8 @@ int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops) return -EINVAL; found: - printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm " - "'%s'\n", ops->name); + printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm '%s'\n", + ops->name); list_del(&alg->list); spin_unlock_irqrestore(&lib80211_crypto_lock, flags); kfree(alg); @@ -270,7 +272,7 @@ static struct lib80211_crypto_ops lib80211_crypt_null = { static int __init lib80211_init(void) { - printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION "\n"); + pr_info(DRV_DESCRIPTION "\n"); return lib80211_register_crypto_ops(&lib80211_crypt_null); } diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c index 0fe40510e2cb..7ea4f2b0770e 100644 --- a/net/wireless/lib80211_crypt_tkip.c +++ b/net/wireless/lib80211_crypt_tkip.c @@ -10,6 +10,8 @@ * more details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/err.h> #include <linux/module.h> #include <linux/init.h> @@ -99,8 +101,7 @@ static void *lib80211_tkip_init(int key_idx) priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->tx_tfm_arc4)) { - printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate " - "crypto API arc4\n"); + printk(KERN_DEBUG pr_fmt("could not allocate crypto API arc4\n")); priv->tx_tfm_arc4 = NULL; goto fail; } @@ -108,8 +109,7 @@ static void *lib80211_tkip_init(int key_idx) priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->tx_tfm_michael)) { - printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate " - "crypto API michael_mic\n"); + printk(KERN_DEBUG pr_fmt("could not allocate crypto API michael_mic\n")); priv->tx_tfm_michael = NULL; goto fail; } @@ -117,8 +117,7 @@ static void *lib80211_tkip_init(int key_idx) priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->rx_tfm_arc4)) { - printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate " - "crypto API arc4\n"); + printk(KERN_DEBUG pr_fmt("could not allocate crypto API arc4\n")); priv->rx_tfm_arc4 = NULL; goto fail; } @@ -126,8 +125,7 @@ static void *lib80211_tkip_init(int key_idx) priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->rx_tfm_michael)) { - printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate " - "crypto API michael_mic\n"); + printk(KERN_DEBUG pr_fmt("could not allocate crypto API michael_mic\n")); priv->rx_tfm_michael = NULL; goto fail; } @@ -536,7 +534,7 @@ static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr, struct scatterlist sg[2]; if (tfm_michael == NULL) { - printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n"); + pr_warn("%s(): tfm_michael == NULL\n", __func__); return -1; } sg_init_table(sg, 2); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 26838d903b9a..6980a0c315b2 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -1028,3 +1028,15 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp); } EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); + +void cfg80211_cqm_pktloss_notify(struct net_device *dev, + const u8 *peer, u32 num_packets, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + /* Indicate roaming trigger event to user space */ + nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp); +} +EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4e78e3f26798..67ff7e92cb99 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -166,7 +166,13 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 }, + [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 }, + + [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 }, + + [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, }; /* policy for the key attributes */ @@ -526,7 +532,6 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, dev->wiphy.rts_threshold); NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, dev->wiphy.coverage_class); - NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, dev->wiphy.max_scan_ssids); NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, @@ -545,6 +550,16 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE); + if (dev->ops->get_antenna) { + u32 tx_ant = 0, rx_ant = 0; + int res; + res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant); + if (!res) { + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant); + } + } + nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); if (!nl_modes) goto nla_put_failure; @@ -1024,6 +1039,22 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) goto bad_res; } + if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] && + info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) { + u32 tx_ant, rx_ant; + if (!rdev->ops->set_antenna) { + result = -EOPNOTSUPP; + goto bad_res; + } + + tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]); + rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]); + + result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant); + if (result) + goto bad_res; + } + changed = 0; if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) { @@ -3569,6 +3600,34 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) local_state_change); } +static bool +nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev, + int mcast_rate[IEEE80211_NUM_BANDS], + int rateval) +{ + struct wiphy *wiphy = &rdev->wiphy; + bool found = false; + int band, i; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + struct ieee80211_supported_band *sband; + + sband = wiphy->bands[band]; + if (!sband) + continue; + + for (i = 0; i < sband->n_bitrates; i++) { + if (sband->bitrates[i].bitrate == rateval) { + mcast_rate[band] = i + 1; + found = true; + break; + } + } + } + + return found; +} + static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -3653,6 +3712,11 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) } } + if (info->attrs[NL80211_ATTR_MCAST_RATE] && + !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate, + nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) + return -EINVAL; + if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { connkeys = nl80211_parse_connkeys(rdev, info->attrs[NL80211_ATTR_KEYS]); @@ -5651,6 +5715,51 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void +nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *peer, + u32 num_packets, gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *pinfoattr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer); + + pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); + if (!pinfoattr) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets); + + nla_nest_end(msg, pinfoattr); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + static int nl80211_netlink_notify(struct notifier_block * nb, unsigned long state, void *_notify) diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 30d2f939150d..16c2f7190768 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -87,5 +87,9 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, enum nl80211_cqm_rssi_threshold_event rssi_event, gfp_t gfp); +void +nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *peer, + u32 num_packets, gfp_t gfp); #endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 3be18d9a944f..5ed615f94e0c 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -32,6 +32,9 @@ * rely on some SHA1 checksum of the regdomain for example. * */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/slab.h> #include <linux/list.h> @@ -48,7 +51,7 @@ #ifdef CONFIG_CFG80211_REG_DEBUG #define REG_DBG_PRINT(format, args...) \ do { \ - printk(KERN_DEBUG "cfg80211: " format , ## args); \ + printk(KERN_DEBUG pr_fmt(format), ##args); \ } while (0) #else #define REG_DBG_PRINT(args...) @@ -96,6 +99,9 @@ struct reg_beacon { struct ieee80211_channel chan; }; +static void reg_todo(struct work_struct *work); +static DECLARE_WORK(reg_work, reg_todo); + /* We keep a static world regulatory domain in case of the absence of CRDA */ static const struct ieee80211_regdomain world_regdom = { .n_reg_rules = 5, @@ -367,11 +373,10 @@ static int call_crda(const char *alpha2) }; if (!is_world_regdom((char *) alpha2)) - printk(KERN_INFO "cfg80211: Calling CRDA for country: %c%c\n", + pr_info("Calling CRDA for country: %c%c\n", alpha2[0], alpha2[1]); else - printk(KERN_INFO "cfg80211: Calling CRDA to update world " - "regulatory domain\n"); + pr_info("Calling CRDA to update world regulatory domain\n"); /* query internal regulatory database (if it exists) */ reg_regdb_query(alpha2); @@ -1317,6 +1322,21 @@ static int ignore_request(struct wiphy *wiphy, return -EINVAL; } +static void reg_set_request_processed(void) +{ + bool need_more_processing = false; + + last_request->processed = true; + + spin_lock(®_requests_lock); + if (!list_empty(®_requests_list)) + need_more_processing = true; + spin_unlock(®_requests_lock); + + if (need_more_processing) + schedule_work(®_work); +} + /** * __regulatory_hint - hint to the wireless core a regulatory domain * @wiphy: if the hint comes from country information from an AP, this @@ -1392,8 +1412,10 @@ new_request: * have applied the requested regulatory domain before we just * inform userspace we have processed the request */ - if (r == -EALREADY) + if (r == -EALREADY) { nl80211_send_reg_change_event(last_request); + reg_set_request_processed(); + } return r; } @@ -1409,16 +1431,13 @@ static void reg_process_hint(struct regulatory_request *reg_request) BUG_ON(!reg_request->alpha2); - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); - if (wiphy_idx_valid(reg_request->wiphy_idx)) wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) { kfree(reg_request); - goto out; + return; } r = __regulatory_hint(wiphy, reg_request); @@ -1426,28 +1445,46 @@ static void reg_process_hint(struct regulatory_request *reg_request) if (r == -EALREADY && wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) wiphy_update_regulatory(wiphy, initiator); -out: - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); } -/* Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* */ +/* + * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* + * Regulatory hints come on a first come first serve basis and we + * must process each one atomically. + */ static void reg_process_pending_hints(void) - { +{ struct regulatory_request *reg_request; + mutex_lock(&cfg80211_mutex); + mutex_lock(®_mutex); + + /* When last_request->processed becomes true this will be rescheduled */ + if (last_request && !last_request->processed) { + REG_DBG_PRINT("Pending regulatory request, waiting " + "for it to be processed..."); + goto out; + } + spin_lock(®_requests_lock); - while (!list_empty(®_requests_list)) { - reg_request = list_first_entry(®_requests_list, - struct regulatory_request, - list); - list_del_init(®_request->list); + if (list_empty(®_requests_list)) { spin_unlock(®_requests_lock); - reg_process_hint(reg_request); - spin_lock(®_requests_lock); + goto out; } + + reg_request = list_first_entry(®_requests_list, + struct regulatory_request, + list); + list_del_init(®_request->list); + spin_unlock(®_requests_lock); + + reg_process_hint(reg_request); + +out: + mutex_unlock(®_mutex); + mutex_unlock(&cfg80211_mutex); } /* Processes beacon hints -- this has nothing to do with country IEs */ @@ -1494,8 +1531,6 @@ static void reg_todo(struct work_struct *work) reg_process_pending_beacon_hints(); } -static DECLARE_WORK(reg_work, reg_todo); - static void queue_regulatory_request(struct regulatory_request *request) { if (isalpha(request->alpha2[0])) @@ -1530,12 +1565,7 @@ static int regulatory_hint_core(const char *alpha2) request->alpha2[1] = alpha2[1]; request->initiator = NL80211_REGDOM_SET_BY_CORE; - /* - * This ensures last_request is populated once modules - * come swinging in and calling regulatory hints and - * wiphy_apply_custom_regulatory(). - */ - reg_process_hint(request); + queue_regulatory_request(request); return 0; } @@ -1823,8 +1853,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) const struct ieee80211_freq_range *freq_range = NULL; const struct ieee80211_power_rule *power_rule = NULL; - printk(KERN_INFO " (start_freq - end_freq @ bandwidth), " - "(max_antenna_gain, max_eirp)\n"); + pr_info(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n"); for (i = 0; i < rd->n_reg_rules; i++) { reg_rule = &rd->reg_rules[i]; @@ -1836,16 +1865,14 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) * in certain regions */ if (power_rule->max_antenna_gain) - printk(KERN_INFO " (%d KHz - %d KHz @ %d KHz), " - "(%d mBi, %d mBm)\n", + pr_info(" (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm)\n", freq_range->start_freq_khz, freq_range->end_freq_khz, freq_range->max_bandwidth_khz, power_rule->max_antenna_gain, power_rule->max_eirp); else - printk(KERN_INFO " (%d KHz - %d KHz @ %d KHz), " - "(N/A, %d mBm)\n", + pr_info(" (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm)\n", freq_range->start_freq_khz, freq_range->end_freq_khz, freq_range->max_bandwidth_khz, @@ -1864,27 +1891,20 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) rdev = cfg80211_rdev_by_wiphy_idx( last_request->wiphy_idx); if (rdev) { - printk(KERN_INFO "cfg80211: Current regulatory " - "domain updated by AP to: %c%c\n", + pr_info("Current regulatory domain updated by AP to: %c%c\n", rdev->country_ie_alpha2[0], rdev->country_ie_alpha2[1]); } else - printk(KERN_INFO "cfg80211: Current regulatory " - "domain intersected:\n"); + pr_info("Current regulatory domain intersected:\n"); } else - printk(KERN_INFO "cfg80211: Current regulatory " - "domain intersected:\n"); + pr_info("Current regulatory domain intersected:\n"); } else if (is_world_regdom(rd->alpha2)) - printk(KERN_INFO "cfg80211: World regulatory " - "domain updated:\n"); + pr_info("World regulatory domain updated:\n"); else { if (is_unknown_alpha2(rd->alpha2)) - printk(KERN_INFO "cfg80211: Regulatory domain " - "changed to driver built-in settings " - "(unknown country)\n"); + pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); else - printk(KERN_INFO "cfg80211: Regulatory domain " - "changed to country: %c%c\n", + pr_info("Regulatory domain changed to country: %c%c\n", rd->alpha2[0], rd->alpha2[1]); } print_rd_rules(rd); @@ -1892,8 +1912,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) static void print_regdomain_info(const struct ieee80211_regdomain *rd) { - printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", - rd->alpha2[0], rd->alpha2[1]); + pr_info("Regulatory domain: %c%c\n", rd->alpha2[0], rd->alpha2[1]); print_rd_rules(rd); } @@ -1944,8 +1963,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) return -EINVAL; if (!is_valid_rd(rd)) { - printk(KERN_ERR "cfg80211: Invalid " - "regulatory domain detected:\n"); + pr_err("Invalid regulatory domain detected:\n"); print_regdomain_info(rd); return -EINVAL; } @@ -2061,6 +2079,8 @@ int set_regdom(const struct ieee80211_regdomain *rd) nl80211_send_reg_change_event(last_request); + reg_set_request_processed(); + mutex_unlock(®_mutex); return r; @@ -2117,8 +2137,7 @@ int __init regulatory_init(void) * early boot for call_usermodehelper(). For now treat these * errors as non-fatal. */ - printk(KERN_ERR "cfg80211: kobject_uevent_env() was unable " - "to call CRDA during init"); + pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); #ifdef CONFIG_CFG80211_REG_DEBUG /* We want to find out exactly why when debugging */ WARN_ON(err); diff --git a/net/wireless/util.c b/net/wireless/util.c index 76120aeda57d..fee020b15a4e 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -502,7 +502,7 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, skb_orphan(skb); if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) { - printk(KERN_ERR "failed to reallocate Tx buffer\n"); + pr_err("failed to reallocate Tx buffer\n"); return -ENOMEM; } skb->truesize += head_need; @@ -685,20 +685,17 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev) continue; if (rdev->ops->add_key(wdev->wiphy, dev, i, false, NULL, &wdev->connect_keys->params[i])) { - printk(KERN_ERR "%s: failed to set key %d\n", - dev->name, i); + netdev_err(dev, "failed to set key %d\n", i); continue; } if (wdev->connect_keys->def == i) if (rdev->ops->set_default_key(wdev->wiphy, dev, i)) { - printk(KERN_ERR "%s: failed to set defkey %d\n", - dev->name, i); + netdev_err(dev, "failed to set defkey %d\n", i); continue; } if (wdev->connect_keys->defmgmt == i) if (rdev->ops->set_default_mgmt_key(wdev->wiphy, dev, i)) - printk(KERN_ERR "%s: failed to set mgtdef %d\n", - dev->name, i); + netdev_err(dev, "failed to set mgtdef %d\n", i); } kfree(wdev->connect_keys); diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index dc675a3daa3d..fdbc23c10d8c 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -467,8 +467,8 @@ void wireless_send_event(struct net_device * dev, * The best the driver could do is to log an error message. * We will do it ourselves instead... */ - printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n", - dev->name, cmd); + netdev_err(dev, "(WE) : Invalid/Unknown Wireless Event (0x%04X)\n", + cmd); return; } @@ -476,11 +476,13 @@ void wireless_send_event(struct net_device * dev, if (descr->header_type == IW_HEADER_TYPE_POINT) { /* Check if number of token fits within bounds */ if (wrqu->data.length > descr->max_tokens) { - printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length); + netdev_err(dev, "(WE) : Wireless Event too big (%d)\n", + wrqu->data.length); return; } if (wrqu->data.length < descr->min_tokens) { - printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length); + netdev_err(dev, "(WE) : Wireless Event too small (%d)\n", + wrqu->data.length); return; } /* Calculate extra_len - extra is NULL for restricted events */ |