diff options
author | David S. Miller <davem@davemloft.net> | 2010-07-23 14:03:38 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-07-23 14:03:38 -0700 |
commit | 2a88e7e559f2358f4e1422d0b0c0278a74136581 (patch) | |
tree | b4e9e56dbde79fe7185cba511a1ffd85f5d2018e /net | |
parent | 344e0f623cec5eba273db06fe57db080988d6b26 (diff) | |
parent | 7a17a33c0da37f8d24222c967550d19dabf13617 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Conflicts:
drivers/net/wireless/iwlwifi/iwl-commands.h
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/cfg.c | 4 | ||||
-rw-r--r-- | net/mac80211/ht.c | 2 | ||||
-rw-r--r-- | net/mac80211/ibss.c | 92 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 7 | ||||
-rw-r--r-- | net/mac80211/iface.c | 6 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 13 | ||||
-rw-r--r-- | net/mac80211/util.c | 7 | ||||
-rw-r--r-- | net/mac80211/wep.c | 5 | ||||
-rw-r--r-- | net/wireless/core.c | 14 | ||||
-rw-r--r-- | net/wireless/genregdb.awk | 1 | ||||
-rw-r--r-- | net/wireless/lib80211_crypt_tkip.c | 2 | ||||
-rw-r--r-- | net/wireless/reg.c | 654 | ||||
-rw-r--r-- | net/wireless/scan.c | 5 | ||||
-rw-r--r-- | net/wireless/sme.c | 2 | ||||
-rw-r--r-- | net/wireless/wext-compat.c | 1 |
15 files changed, 98 insertions, 717 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 5e56e91c92c4..a3f3325df9f2 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1154,10 +1154,6 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy, return -EINVAL; } - /* enable WMM or activate new settings */ - local->hw.conf.flags |= IEEE80211_CONF_QOS; - drv_config(local, IEEE80211_CONF_CHANGE_QOS); - return 0; } diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index be928ef7ef51..9d101fb33861 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -29,7 +29,7 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, memset(ht_cap, 0, sizeof(*ht_cap)); - if (!ht_cap_ie) + if (!ht_cap_ie || !sband->ht_cap.ht_supported) return; ht_cap->ht_supported = true; diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index d4e84b22a66d..c691780725a7 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -43,6 +43,8 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, { u16 auth_alg, auth_transaction, status_code; + lockdep_assert_held(&sdata->u.ibss.mtx); + if (len < 24 + 6) return; @@ -78,6 +80,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, u32 bss_change; u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; + lockdep_assert_held(&ifibss->mtx); + /* Reset own TSF to allow time synchronization work. */ drv_reset_tsf(local); @@ -205,6 +209,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, int i, j; u16 beacon_int = cbss->beacon_interval; + lockdep_assert_held(&sdata->u.ibss.mtx); + if (beacon_int < 10) beacon_int = 10; @@ -449,6 +455,8 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) int active = 0; struct sta_info *sta; + lockdep_assert_held(&sdata->u.ibss.mtx); + rcu_read_lock(); list_for_each_entry_rcu(sta, &local->sta_list, list) { @@ -473,6 +481,8 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + lockdep_assert_held(&ifibss->mtx); + mod_timer(&ifibss->timer, round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); @@ -505,6 +515,8 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) u16 capability; int i; + lockdep_assert_held(&ifibss->mtx); + if (ifibss->fixed_bssid) { memcpy(bssid, ifibss->bssid, ETH_ALEN); } else { @@ -549,6 +561,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) int active_ibss; u16 capability; + lockdep_assert_held(&ifibss->mtx); + active_ibss = ieee80211_sta_active_ibss(sdata); #ifdef CONFIG_MAC80211_IBSS_DEBUG printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n", @@ -637,6 +651,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *resp; u8 *pos, *end; + lockdep_assert_held(&ifibss->mtx); + if (ifibss->state != IEEE80211_IBSS_MLME_JOINED || len < 24 + 2 || !ifibss->presp) return; @@ -740,6 +756,8 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, mgmt = (struct ieee80211_mgmt *) skb->data; fc = le16_to_cpu(mgmt->frame_control); + mutex_lock(&sdata->u.ibss.mtx); + switch (fc & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_PROBE_REQ: ieee80211_rx_mgmt_probe_req(sdata, mgmt, skb->len); @@ -756,14 +774,23 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len); break; } + + mutex_unlock(&sdata->u.ibss.mtx); } void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; - if (!test_and_clear_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request)) - return; + mutex_lock(&ifibss->mtx); + + /* + * Work could be scheduled after scan or similar + * when we aren't even joined (or trying) with a + * network. + */ + if (!ifibss->ssid_len) + goto out; switch (ifibss->state) { case IEEE80211_IBSS_MLME_SEARCH: @@ -776,15 +803,9 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) WARN_ON(1); break; } -} -static void ieee80211_queue_ibss_work(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; - struct ieee80211_local *local = sdata->local; - - set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request); - ieee80211_queue_work(&local->hw, &sdata->work); + out: + mutex_unlock(&ifibss->mtx); } static void ieee80211_ibss_timer(unsigned long data) @@ -799,7 +820,7 @@ static void ieee80211_ibss_timer(unsigned long data) return; } - ieee80211_queue_ibss_work(sdata); + ieee80211_queue_work(&local->hw, &sdata->work); } #ifdef CONFIG_PM @@ -828,6 +849,7 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata) setup_timer(&ifibss->timer, ieee80211_ibss_timer, (unsigned long) sdata); + mutex_init(&ifibss->mtx); } /* scan finished notification */ @@ -841,10 +863,8 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local) continue; if (sdata->vif.type != NL80211_IFTYPE_ADHOC) continue; - if (!sdata->u.ibss.ssid_len) - continue; sdata->u.ibss.last_scan_completed = jiffies; - ieee80211_queue_ibss_work(sdata); + ieee80211_queue_work(&local->hw, &sdata->work); } mutex_unlock(&local->iflist_mtx); } @@ -854,6 +874,17 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, { struct sk_buff *skb; + skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom + + 36 /* bitrates */ + + 34 /* SSID */ + + 3 /* DS params */ + + 4 /* IBSS params */ + + params->ie_len); + if (!skb) + return -ENOMEM; + + mutex_lock(&sdata->u.ibss.mtx); + if (params->bssid) { memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN); sdata->u.ibss.fixed_bssid = true; @@ -882,35 +913,19 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->u.ibss.ie_len = params->ie_len; } - skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom + - 36 /* bitrates */ + - 34 /* SSID */ + - 3 /* DS params */ + - 4 /* IBSS params */ + - params->ie_len); - if (!skb) - return -ENOMEM; - sdata->u.ibss.skb = skb; sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH; sdata->u.ibss.ibss_join_req = jiffies; memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN); - - /* - * The ssid_len setting below is used to see whether - * we are active, and we need all other settings - * before that may get visible. - */ - mb(); - sdata->u.ibss.ssid_len = params->ssid_len; ieee80211_recalc_idle(sdata->local); - set_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request); ieee80211_queue_work(&sdata->local->hw, &sdata->work); + mutex_unlock(&sdata->u.ibss.mtx); + return 0; } @@ -921,7 +936,9 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; struct cfg80211_bss *cbss; u16 capability; - int active_ibss = 0; + int active_ibss; + + mutex_lock(&sdata->u.ibss.mtx); active_ibss = ieee80211_sta_active_ibss(sdata); @@ -943,11 +960,6 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) } } - del_timer_sync(&sdata->u.ibss.timer); - clear_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request); - cancel_work_sync(&sdata->work); - clear_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request); - sta_info_flush(sdata->local, sdata); /* remove beacon */ @@ -964,6 +976,10 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) memset(sdata->u.ibss.bssid, 0, ETH_ALEN); sdata->u.ibss.ssid_len = 0; + del_timer_sync(&sdata->u.ibss.timer); + + mutex_unlock(&sdata->u.ibss.mtx); + ieee80211_recalc_idle(sdata->local); return 0; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a3649a86a784..ef470064b154 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -377,14 +377,11 @@ struct ieee80211_if_managed { int last_cqm_event_signal; }; -enum ieee80211_ibss_request { - IEEE80211_IBSS_REQ_RUN = 0, -}; - struct ieee80211_if_ibss { struct timer_list timer; - unsigned long request; + struct mutex mtx; + unsigned long last_scan_completed; u32 basic_rates; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 910729fc18cd..ebbe264e2b0b 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -249,6 +249,8 @@ static int ieee80211_open(struct net_device *dev) local->fif_other_bss++; ieee80211_configure_filter(local); + + netif_carrier_on(dev); break; default: res = drv_add_interface(local, &sdata->vif); @@ -741,7 +743,7 @@ static void ieee80211_iface_work(struct work_struct *work) int len = skb->len; mutex_lock(&local->sta_mtx); - sta = sta_info_get(sdata, mgmt->sa); + sta = sta_info_get_bss(sdata, mgmt->sa); if (sta) { switch (mgmt->u.action.u.addba_req.action_code) { case WLAN_ACTION_ADDBA_REQ: @@ -782,7 +784,7 @@ static void ieee80211_iface_work(struct work_struct *work) * right, so terminate the session. */ mutex_lock(&local->sta_mtx); - sta = sta_info_get(sdata, mgmt->sa); + sta = sta_info_get_bss(sdata, mgmt->sa); if (sta) { u16 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d1962650b254..cf8d72196c65 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -698,10 +698,11 @@ void ieee80211_dynamic_ps_timer(unsigned long data) /* MLME */ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, - struct ieee80211_if_managed *ifmgd, + struct ieee80211_sub_if_data *sdata, u8 *wmm_param, size_t wmm_param_len) { struct ieee80211_tx_queue_params params; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; size_t left; int count; u8 *pos, uapsd_queues = 0; @@ -790,8 +791,8 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, } /* enable WMM or activate new settings */ - local->hw.conf.flags |= IEEE80211_CONF_QOS; - drv_config(local, IEEE80211_CONF_CHANGE_QOS); + sdata->vif.bss_conf.qos = true; + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS); } static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, @@ -1325,7 +1326,7 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, } if (elems.wmm_param) - ieee80211_sta_wmm_params(local, ifmgd, elems.wmm_param, + ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, elems.wmm_param_len); else ieee80211_set_wmm_default(sdata); @@ -1597,7 +1598,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true); - ieee80211_sta_wmm_params(local, ifmgd, elems.wmm_param, + ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, elems.wmm_param_len); } @@ -2030,6 +2031,8 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, auth_alg = WLAN_AUTH_OPEN; break; case NL80211_AUTHTYPE_SHARED_KEY: + if (IS_ERR(sdata->local->wep_tx_tfm)) + return -EOPNOTSUPP; auth_alg = WLAN_AUTH_SHARED_KEY; break; case NL80211_AUTHTYPE_FT: diff --git a/net/mac80211/util.c b/net/mac80211/util.c index a54cf146ed50..794792177376 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -803,8 +803,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) /* after reinitialize QoS TX queues setting to default, * disable QoS at all */ - local->hw.conf.flags &= ~IEEE80211_CONF_QOS; - drv_config(local, IEEE80211_CONF_CHANGE_QOS); + sdata->vif.bss_conf.qos = sdata->vif.type != NL80211_IFTYPE_STATION; + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS); } void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, @@ -1161,7 +1161,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) BSS_CHANGED_BASIC_RATES | BSS_CHANGED_BEACON_INT | BSS_CHANGED_BSSID | - BSS_CHANGED_CQM; + BSS_CHANGED_CQM | + BSS_CHANGED_QOS; switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index 6d133b6efce5..9ebc8d8a1f5b 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -32,13 +32,16 @@ int ieee80211_wep_init(struct ieee80211_local *local) local->wep_tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(local->wep_tx_tfm)) + if (IS_ERR(local->wep_tx_tfm)) { + local->wep_rx_tfm = ERR_PTR(-EINVAL); return PTR_ERR(local->wep_tx_tfm); + } local->wep_rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(local->wep_rx_tfm)) { crypto_free_blkcipher(local->wep_tx_tfm); + local->wep_tx_tfm = ERR_PTR(-EINVAL); return PTR_ERR(local->wep_rx_tfm); } diff --git a/net/wireless/core.c b/net/wireless/core.c index 47fcfd0eebc2..f65c6494ede9 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -472,24 +472,22 @@ int wiphy_register(struct wiphy *wiphy) /* check and set up bitrates */ ieee80211_set_bitrate_flags(wiphy); + mutex_lock(&cfg80211_mutex); + res = device_add(&rdev->wiphy.dev); if (res) - return res; + goto out_unlock; res = rfkill_register(rdev->rfkill); if (res) goto out_rm_dev; - mutex_lock(&cfg80211_mutex); - /* set up regulatory info */ wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); list_add_rcu(&rdev->list, &cfg80211_rdev_list); cfg80211_rdev_list_generation++; - mutex_unlock(&cfg80211_mutex); - /* add to debugfs */ rdev->wiphy.debugfsdir = debugfs_create_dir(wiphy_name(&rdev->wiphy), @@ -509,11 +507,15 @@ int wiphy_register(struct wiphy *wiphy) } cfg80211_debugfs_rdev_add(rdev); + mutex_unlock(&cfg80211_mutex); return 0; - out_rm_dev: +out_rm_dev: device_del(&rdev->wiphy.dev); + +out_unlock: + mutex_unlock(&cfg80211_mutex); return res; } EXPORT_SYMBOL(wiphy_register); diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk index 3cc9e69880a8..53c143f5e770 100644 --- a/net/wireless/genregdb.awk +++ b/net/wireless/genregdb.awk @@ -21,6 +21,7 @@ BEGIN { print "" print "#include <linux/nl80211.h>" print "#include <net/cfg80211.h>" + print "#include \"regdb.h\"" print "" regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n" } diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c index 8cbdb32ff316..a7f995613f1f 100644 --- a/net/wireless/lib80211_crypt_tkip.c +++ b/net/wireless/lib80211_crypt_tkip.c @@ -578,7 +578,7 @@ static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr) } if (ieee80211_is_data_qos(hdr11->frame_control)) { - hdr[12] = le16_to_cpu(*ieee80211_get_qos_ctl(hdr11)) + hdr[12] = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(hdr11))) & IEEE80211_QOS_CTL_TID_MASK; } else hdr[12] = 0; /* priority */ diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 1ac2bdd46ecf..48baf28cc4b6 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -67,17 +67,9 @@ static struct platform_device *reg_pdev; const struct ieee80211_regdomain *cfg80211_regdomain; /* - * We use this as a place for the rd structure built from the - * last parsed country IE to rest until CRDA gets back to us with - * what it thinks should apply for the same country - */ -static const struct ieee80211_regdomain *country_ie_regdomain; - -/* * Protects static reg.c components: * - cfg80211_world_regdom * - cfg80211_regdom - * - country_ie_regdomain * - last_request */ static DEFINE_MUTEX(reg_mutex); @@ -275,25 +267,6 @@ static bool is_user_regdom_saved(void) return true; } -/** - * country_ie_integrity_changes - tells us if the country IE has changed - * @checksum: checksum of country IE of fields we are interested in - * - * If the country IE has not changed you can ignore it safely. This is - * useful to determine if two devices are seeing two different country IEs - * even on the same alpha2. Note that this will return false if no IE has - * been set on the wireless core yet. - */ -static bool country_ie_integrity_changes(u32 checksum) -{ - /* If no IE has been set then the checksum doesn't change */ - if (unlikely(!last_request->country_ie_checksum)) - return false; - if (unlikely(last_request->country_ie_checksum != checksum)) - return true; - return false; -} - static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, const struct ieee80211_regdomain *src_regd) { @@ -506,471 +479,6 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, } /* - * This is a work around for sanity checking ieee80211_channel_to_frequency()'s - * work. ieee80211_channel_to_frequency() can for example currently provide a - * 2 GHz channel when in fact a 5 GHz channel was desired. An example would be - * an AP providing channel 8 on a country IE triplet when it sent this on the - * 5 GHz band, that channel is designed to be channel 8 on 5 GHz, not a 2 GHz - * channel. - * - * This can be removed once ieee80211_channel_to_frequency() takes in a band. - */ -static bool chan_in_band(int chan, enum ieee80211_band band) -{ - int center_freq = ieee80211_channel_to_frequency(chan); - - switch (band) { - case IEEE80211_BAND_2GHZ: - if (center_freq <= 2484) - return true; - return false; - case IEEE80211_BAND_5GHZ: - if (center_freq >= 5005) - return true; - return false; - default: - return false; - } -} - -/* - * Some APs may send a country IE triplet for each channel they - * support and while this is completely overkill and silly we still - * need to support it. We avoid making a single rule for each channel - * though and to help us with this we use this helper to find the - * actual subband end channel. These type of country IE triplet - * scenerios are handled then, all yielding two regulaotry rules from - * parsing a country IE: - * - * [1] - * [2] - * [36] - * [40] - * - * [1] - * [2-4] - * [5-12] - * [36] - * [40-44] - * - * [1-4] - * [5-7] - * [36-44] - * [48-64] - * - * [36-36] - * [40-40] - * [44-44] - * [48-48] - * [52-52] - * [56-56] - * [60-60] - * [64-64] - * [100-100] - * [104-104] - * [108-108] - * [112-112] - * [116-116] - * [120-120] - * [124-124] - * [128-128] - * [132-132] - * [136-136] - * [140-140] - * - * Returns 0 if the IE has been found to be invalid in the middle - * somewhere. - */ -static int max_subband_chan(enum ieee80211_band band, - int orig_cur_chan, - int orig_end_channel, - s8 orig_max_power, - u8 **country_ie, - u8 *country_ie_len) -{ - u8 *triplets_start = *country_ie; - u8 len_at_triplet = *country_ie_len; - int end_subband_chan = orig_end_channel; - - /* - * We'll deal with padding for the caller unless - * its not immediate and we don't process any channels - */ - if (*country_ie_len == 1) { - *country_ie += 1; - *country_ie_len -= 1; - return orig_end_channel; - } - - /* Move to the next triplet and then start search */ - *country_ie += 3; - *country_ie_len -= 3; - - if (!chan_in_band(orig_cur_chan, band)) - return 0; - - while (*country_ie_len >= 3) { - int end_channel = 0; - struct ieee80211_country_ie_triplet *triplet = - (struct ieee80211_country_ie_triplet *) *country_ie; - int cur_channel = 0, next_expected_chan; - - /* means last triplet is completely unrelated to this one */ - if (triplet->ext.reg_extension_id >= - IEEE80211_COUNTRY_EXTENSION_ID) { - *country_ie -= 3; - *country_ie_len += 3; - break; - } - - if (triplet->chans.first_channel == 0) { - *country_ie += 1; - *country_ie_len -= 1; - if (*country_ie_len != 0) - return 0; - break; - } - - if (triplet->chans.num_channels == 0) - return 0; - - /* Monitonically increasing channel order */ - if (triplet->chans.first_channel <= end_subband_chan) - return 0; - - if (!chan_in_band(triplet->chans.first_channel, band)) - return 0; - - /* 2 GHz */ - if (triplet->chans.first_channel <= 14) { - end_channel = triplet->chans.first_channel + - triplet->chans.num_channels - 1; - } - else { - end_channel = triplet->chans.first_channel + - (4 * (triplet->chans.num_channels - 1)); - } - - if (!chan_in_band(end_channel, band)) - return 0; - - if (orig_max_power != triplet->chans.max_power) { - *country_ie -= 3; - *country_ie_len += 3; - break; - } - - cur_channel = triplet->chans.first_channel; - - /* The key is finding the right next expected channel */ - if (band == IEEE80211_BAND_2GHZ) - next_expected_chan = end_subband_chan + 1; - else - next_expected_chan = end_subband_chan + 4; - - if (cur_channel != next_expected_chan) { - *country_ie -= 3; - *country_ie_len += 3; - break; - } - - end_subband_chan = end_channel; - - /* Move to the next one */ - *country_ie += 3; - *country_ie_len -= 3; - - /* - * Padding needs to be dealt with if we processed - * some channels. - */ - if (*country_ie_len == 1) { - *country_ie += 1; - *country_ie_len -= 1; - break; - } - - /* If seen, the IE is invalid */ - if (*country_ie_len == 2) - return 0; - } - - if (end_subband_chan == orig_end_channel) { - *country_ie = triplets_start; - *country_ie_len = len_at_triplet; - return orig_end_channel; - } - - return end_subband_chan; -} - -/* - * Converts a country IE to a regulatory domain. A regulatory domain - * structure has a lot of information which the IE doesn't yet have, - * so for the other values we use upper max values as we will intersect - * with our userspace regulatory agent to get lower bounds. - */ -static struct ieee80211_regdomain *country_ie_2_rd( - enum ieee80211_band band, - u8 *country_ie, - u8 country_ie_len, - u32 *checksum) -{ - struct ieee80211_regdomain *rd = NULL; - unsigned int i = 0; - char alpha2[2]; - u32 flags = 0; - u32 num_rules = 0, size_of_regd = 0; - u8 *triplets_start = NULL; - u8 len_at_triplet = 0; - /* the last channel we have registered in a subband (triplet) */ - int last_sub_max_channel = 0; - - *checksum = 0xDEADBEEF; - - /* Country IE requirements */ - BUG_ON(country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN || - country_ie_len & 0x01); - - alpha2[0] = country_ie[0]; - alpha2[1] = country_ie[1]; - - /* - * Third octet can be: - * 'I' - Indoor - * 'O' - Outdoor - * - * anything else we assume is no restrictions - */ - if (country_ie[2] == 'I') - flags = NL80211_RRF_NO_OUTDOOR; - else if (country_ie[2] == 'O') - flags = NL80211_RRF_NO_INDOOR; - - country_ie += 3; - country_ie_len -= 3; - - triplets_start = country_ie; - len_at_triplet = country_ie_len; - - *checksum ^= ((flags ^ alpha2[0] ^ alpha2[1]) << 8); - - /* - * We need to build a reg rule for each triplet, but first we must - * calculate the number of reg rules we will need. We will need one - * for each channel subband - */ - while (country_ie_len >= 3) { - int end_channel = 0; - struct ieee80211_country_ie_triplet *triplet = - (struct ieee80211_country_ie_triplet *) country_ie; - int cur_sub_max_channel = 0, cur_channel = 0; - - if (triplet->ext.reg_extension_id >= - IEEE80211_COUNTRY_EXTENSION_ID) { - country_ie += 3; - country_ie_len -= 3; - continue; - } - - /* - * APs can add padding to make length divisible - * by two, required by the spec. - */ - if (triplet->chans.first_channel == 0) { - country_ie++; - country_ie_len--; - /* This is expected to be at the very end only */ - if (country_ie_len != 0) - return NULL; - break; - } - - if (triplet->chans.num_channels == 0) - return NULL; - - if (!chan_in_band(triplet->chans.first_channel, band)) - return NULL; - - /* 2 GHz */ - if (band == IEEE80211_BAND_2GHZ) - end_channel = triplet->chans.first_channel + - triplet->chans.num_channels - 1; - else - /* - * 5 GHz -- For example in country IEs if the first - * channel given is 36 and the number of channels is 4 - * then the individual channel numbers defined for the - * 5 GHz PHY by these parameters are: 36, 40, 44, and 48 - * and not 36, 37, 38, 39. - * - * See: http://tinyurl.com/11d-clarification - */ - end_channel = triplet->chans.first_channel + - (4 * (triplet->chans.num_channels - 1)); - - cur_channel = triplet->chans.first_channel; - - /* - * Enhancement for APs that send a triplet for every channel - * or for whatever reason sends triplets with multiple channels - * separated when in fact they should be together. - */ - end_channel = max_subband_chan(band, - cur_channel, - end_channel, - triplet->chans.max_power, - &country_ie, - &country_ie_len); - if (!end_channel) - return NULL; - - if (!chan_in_band(end_channel, band)) - return NULL; - - cur_sub_max_channel = end_channel; - - /* Basic sanity check */ - if (cur_sub_max_channel < cur_channel) - return NULL; - - /* - * Do not allow overlapping channels. Also channels - * passed in each subband must be monotonically - * increasing - */ - if (last_sub_max_channel) { - if (cur_channel <= last_sub_max_channel) - return NULL; - if (cur_sub_max_channel <= last_sub_max_channel) - return NULL; - } - - /* - * When dot11RegulatoryClassesRequired is supported - * we can throw ext triplets as part of this soup, - * for now we don't care when those change as we - * don't support them - */ - *checksum ^= ((cur_channel ^ cur_sub_max_channel) << 8) | - ((cur_sub_max_channel ^ cur_sub_max_channel) << 16) | - ((triplet->chans.max_power ^ cur_sub_max_channel) << 24); - - last_sub_max_channel = cur_sub_max_channel; - - num_rules++; - - if (country_ie_len >= 3) { - country_ie += 3; - country_ie_len -= 3; - } - - /* - * Note: this is not a IEEE requirement but - * simply a memory requirement - */ - if (num_rules > NL80211_MAX_SUPP_REG_RULES) - return NULL; - } - - country_ie = triplets_start; - country_ie_len = len_at_triplet; - - size_of_regd = sizeof(struct ieee80211_regdomain) + - (num_rules * sizeof(struct ieee80211_reg_rule)); - - rd = kzalloc(size_of_regd, GFP_KERNEL); - if (!rd) - return NULL; - - rd->n_reg_rules = num_rules; - rd->alpha2[0] = alpha2[0]; - rd->alpha2[1] = alpha2[1]; - - /* This time around we fill in the rd */ - while (country_ie_len >= 3) { - int end_channel = 0; - struct ieee80211_country_ie_triplet *triplet = - (struct ieee80211_country_ie_triplet *) country_ie; - struct ieee80211_reg_rule *reg_rule = NULL; - struct ieee80211_freq_range *freq_range = NULL; - struct ieee80211_power_rule *power_rule = NULL; - - /* - * Must parse if dot11RegulatoryClassesRequired is true, - * we don't support this yet - */ - if (triplet->ext.reg_extension_id >= - IEEE80211_COUNTRY_EXTENSION_ID) { - country_ie += 3; - country_ie_len -= 3; - continue; - } - - if (triplet->chans.first_channel == 0) { - country_ie++; - country_ie_len--; - break; - } - - reg_rule = &rd->reg_rules[i]; - freq_range = ®_rule->freq_range; - power_rule = ®_rule->power_rule; - - reg_rule->flags = flags; - - /* 2 GHz */ - if (band == IEEE80211_BAND_2GHZ) - end_channel = triplet->chans.first_channel + - triplet->chans.num_channels -1; - else - end_channel = triplet->chans.first_channel + - (4 * (triplet->chans.num_channels - 1)); - - end_channel = max_subband_chan(band, - triplet->chans.first_channel, - end_channel, - triplet->chans.max_power, - &country_ie, - &country_ie_len); - - /* - * The +10 is since the regulatory domain expects - * the actual band edge, not the center of freq for - * its start and end freqs, assuming 20 MHz bandwidth on - * the channels passed - */ - freq_range->start_freq_khz = - MHZ_TO_KHZ(ieee80211_channel_to_frequency( - triplet->chans.first_channel) - 10); - freq_range->end_freq_khz = - MHZ_TO_KHZ(ieee80211_channel_to_frequency( - end_channel) + 10); - - /* - * These are large arbitrary values we use to intersect later. - * Increment this if we ever support >= 40 MHz channels - * in IEEE 802.11 - */ - freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40); - power_rule->max_antenna_gain = DBI_TO_MBI(100); - power_rule->max_eirp = DBM_TO_MBM(triplet->chans.max_power); - - i++; - - if (country_ie_len >= 3) { - country_ie += 3; - country_ie_len -= 3; - } - - BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); - } - - return rd; |