diff options
author | David S. Miller <davem@davemloft.net> | 2008-11-26 18:28:40 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-11-26 18:28:40 -0500 |
commit | b5ddedc9cc01b1d86015af08c5f1694191804530 (patch) | |
tree | db08f24da9ef4dcec0976ee4de4d77e5e596057e /net | |
parent | 244e6c2d0724bc4908a1995804704bdee3b31528 (diff) | |
parent | b235507cc5e552b9e75678d596727249e8fba01b (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/Kconfig | 8 | ||||
-rw-r--r-- | net/mac80211/ht.c | 2 | ||||
-rw-r--r-- | net/mac80211/main.c | 5 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 15 | ||||
-rw-r--r-- | net/mac80211/rc80211_pid_algo.c | 2 | ||||
-rw-r--r-- | net/mac80211/wep.c | 7 | ||||
-rw-r--r-- | net/rfkill/rfkill.c | 17 | ||||
-rw-r--r-- | net/wireless/Kconfig | 11 | ||||
-rw-r--r-- | net/wireless/core.c | 5 | ||||
-rw-r--r-- | net/wireless/core.h | 13 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 5 | ||||
-rw-r--r-- | net/wireless/reg.c | 586 | ||||
-rw-r--r-- | net/wireless/reg.h | 21 |
13 files changed, 606 insertions, 91 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 7f710a27e91c..60c16162474c 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig | |||
@@ -16,20 +16,20 @@ menu "Rate control algorithm selection" | |||
16 | 16 | ||
17 | config MAC80211_RC_PID | 17 | config MAC80211_RC_PID |
18 | bool "PID controller based rate control algorithm" if EMBEDDED | 18 | bool "PID controller based rate control algorithm" if EMBEDDED |
19 | default y | ||
20 | ---help--- | 19 | ---help--- |
21 | This option enables a TX rate control algorithm for | 20 | This option enables a TX rate control algorithm for |
22 | mac80211 that uses a PID controller to select the TX | 21 | mac80211 that uses a PID controller to select the TX |
23 | rate. | 22 | rate. |
24 | 23 | ||
25 | config MAC80211_RC_MINSTREL | 24 | config MAC80211_RC_MINSTREL |
26 | bool "Minstrel" | 25 | bool "Minstrel" if EMBEDDED |
26 | default y | ||
27 | ---help--- | 27 | ---help--- |
28 | This option enables the 'minstrel' TX rate control algorithm | 28 | This option enables the 'minstrel' TX rate control algorithm |
29 | 29 | ||
30 | choice | 30 | choice |
31 | prompt "Default rate control algorithm" | 31 | prompt "Default rate control algorithm" |
32 | default MAC80211_RC_DEFAULT_PID | 32 | default MAC80211_RC_DEFAULT_MINSTREL |
33 | ---help--- | 33 | ---help--- |
34 | This option selects the default rate control algorithm | 34 | This option selects the default rate control algorithm |
35 | mac80211 will use. Note that this default can still be | 35 | mac80211 will use. Note that this default can still be |
@@ -55,8 +55,8 @@ endchoice | |||
55 | 55 | ||
56 | config MAC80211_RC_DEFAULT | 56 | config MAC80211_RC_DEFAULT |
57 | string | 57 | string |
58 | default "pid" if MAC80211_RC_DEFAULT_PID | ||
59 | default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL | 58 | default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL |
59 | default "pid" if MAC80211_RC_DEFAULT_PID | ||
60 | default "" | 60 | default "" |
61 | 61 | ||
62 | endmenu | 62 | endmenu |
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 3e231d756776..a1eed7032c9b 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c | |||
@@ -36,7 +36,7 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, | |||
36 | 36 | ||
37 | ht_cap->ht_supported = true; | 37 | ht_cap->ht_supported = true; |
38 | 38 | ||
39 | ht_cap->cap = ht_cap->cap & sband->ht_cap.cap; | 39 | ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) & sband->ht_cap.cap; |
40 | ht_cap->cap &= ~IEEE80211_HT_CAP_SM_PS; | 40 | ht_cap->cap &= ~IEEE80211_HT_CAP_SM_PS; |
41 | ht_cap->cap |= sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS; | 41 | ht_cap->cap |= sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS; |
42 | 42 | ||
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index d631dc96c323..cec9b6d3e1ce 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -722,7 +722,6 @@ EXPORT_SYMBOL(ieee80211_alloc_hw); | |||
722 | int ieee80211_register_hw(struct ieee80211_hw *hw) | 722 | int ieee80211_register_hw(struct ieee80211_hw *hw) |
723 | { | 723 | { |
724 | struct ieee80211_local *local = hw_to_local(hw); | 724 | struct ieee80211_local *local = hw_to_local(hw); |
725 | const char *name; | ||
726 | int result; | 725 | int result; |
727 | enum ieee80211_band band; | 726 | enum ieee80211_band band; |
728 | struct net_device *mdev; | 727 | struct net_device *mdev; |
@@ -787,8 +786,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
787 | mdev->header_ops = &ieee80211_header_ops; | 786 | mdev->header_ops = &ieee80211_header_ops; |
788 | mdev->set_multicast_list = ieee80211_master_set_multicast_list; | 787 | mdev->set_multicast_list = ieee80211_master_set_multicast_list; |
789 | 788 | ||
790 | name = wiphy_dev(local->hw.wiphy)->driver->name; | 789 | local->hw.workqueue = |
791 | local->hw.workqueue = create_freezeable_workqueue(name); | 790 | create_freezeable_workqueue(wiphy_name(local->hw.wiphy)); |
792 | if (!local->hw.workqueue) { | 791 | if (!local->hw.workqueue) { |
793 | result = -ENOMEM; | 792 | result = -ENOMEM; |
794 | goto fail_workqueue; | 793 | goto fail_workqueue; |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d81a4d2cd3aa..7600ac9b87fe 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -802,6 +802,10 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata, | |||
802 | mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT); | 802 | mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT); |
803 | } | 803 | } |
804 | 804 | ||
805 | /* | ||
806 | * The disassoc 'reason' argument can be either our own reason | ||
807 | * if self disconnected or a reason code from the AP. | ||
808 | */ | ||
805 | static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, | 809 | static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, |
806 | struct ieee80211_if_sta *ifsta, bool deauth, | 810 | struct ieee80211_if_sta *ifsta, bool deauth, |
807 | bool self_disconnected, u16 reason) | 811 | bool self_disconnected, u16 reason) |
@@ -848,7 +852,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, | |||
848 | 852 | ||
849 | ieee80211_sta_send_apinfo(sdata, ifsta); | 853 | ieee80211_sta_send_apinfo(sdata, ifsta); |
850 | 854 | ||
851 | if (self_disconnected) | 855 | if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT) |
852 | ifsta->state = IEEE80211_STA_MLME_DISABLED; | 856 | ifsta->state = IEEE80211_STA_MLME_DISABLED; |
853 | 857 | ||
854 | sta_info_unlink(&sta); | 858 | sta_info_unlink(&sta); |
@@ -1163,7 +1167,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, | |||
1163 | IEEE80211_RETRY_AUTH_INTERVAL); | 1167 | IEEE80211_RETRY_AUTH_INTERVAL); |
1164 | } | 1168 | } |
1165 | 1169 | ||
1166 | ieee80211_set_disassoc(sdata, ifsta, false, false, 0); | 1170 | ieee80211_set_disassoc(sdata, ifsta, false, false, reason_code); |
1167 | } | 1171 | } |
1168 | 1172 | ||
1169 | 1173 | ||
@@ -1736,6 +1740,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
1736 | ap_ht_cap_flags); | 1740 | ap_ht_cap_flags); |
1737 | } | 1741 | } |
1738 | 1742 | ||
1743 | if (elems.country_elem) { | ||
1744 | /* Note we are only reviewing this on beacons | ||
1745 | * for the BSSID we are associated to */ | ||
1746 | regulatory_hint_11d(local->hw.wiphy, | ||
1747 | elems.country_elem, elems.country_elem_len); | ||
1748 | } | ||
1749 | |||
1739 | ieee80211_bss_info_change_notify(sdata, changed); | 1750 | ieee80211_bss_info_change_notify(sdata, changed); |
1740 | } | 1751 | } |
1741 | 1752 | ||
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index 96ceb7e86c5c..b16801cde06f 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c | |||
@@ -256,7 +256,7 @@ static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_ba | |||
256 | if (!(info->flags & IEEE80211_TX_STAT_ACK)) { | 256 | if (!(info->flags & IEEE80211_TX_STAT_ACK)) { |
257 | spinfo->tx_num_failed += 2; | 257 | spinfo->tx_num_failed += 2; |
258 | spinfo->tx_num_xmit++; | 258 | spinfo->tx_num_xmit++; |
259 | } else if (info->status.rates[0].count) { | 259 | } else if (info->status.rates[0].count > 1) { |
260 | spinfo->tx_num_failed++; | 260 | spinfo->tx_num_failed++; |
261 | spinfo->tx_num_xmit++; | 261 | spinfo->tx_num_xmit++; |
262 | } | 262 | } |
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index 7bbb98e846a3..7043ddc75498 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/err.h> | 17 | #include <linux/err.h> |
18 | #include <linux/mm.h> | 18 | #include <linux/mm.h> |
19 | #include <linux/scatterlist.h> | 19 | #include <linux/scatterlist.h> |
20 | #include <asm/unaligned.h> | ||
20 | 21 | ||
21 | #include <net/mac80211.h> | 22 | #include <net/mac80211.h> |
22 | #include "ieee80211_i.h" | 23 | #include "ieee80211_i.h" |
@@ -125,10 +126,10 @@ void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, | |||
125 | { | 126 | { |
126 | struct blkcipher_desc desc = { .tfm = tfm }; | 127 | struct blkcipher_desc desc = { .tfm = tfm }; |
127 | struct scatterlist sg; | 128 | struct scatterlist sg; |
128 | __le32 *icv; | 129 | __le32 icv; |
129 | 130 | ||
130 | icv = (__le32 *)(data + data_len); | 131 | icv = cpu_to_le32(~crc32_le(~0, data, data_len)); |
131 | *icv = cpu_to_le32(~crc32_le(~0, data, data_len)); | 132 | put_unaligned(icv, (__le32 *)(data + data_len)); |
132 | 133 | ||
133 | crypto_blkcipher_setkey(tfm, rc4key, klen); | 134 | crypto_blkcipher_setkey(tfm, rc4key, klen); |
134 | sg_init_one(&sg, data, data_len + WEP_ICV_LEN); | 135 | sg_init_one(&sg, data, data_len + WEP_ICV_LEN); |
diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index ec26eae8004d..051d2c9ea66b 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c | |||
@@ -565,16 +565,22 @@ static void rfkill_release(struct device *dev) | |||
565 | #ifdef CONFIG_PM | 565 | #ifdef CONFIG_PM |
566 | static int rfkill_suspend(struct device *dev, pm_message_t state) | 566 | static int rfkill_suspend(struct device *dev, pm_message_t state) |
567 | { | 567 | { |
568 | struct rfkill *rfkill = to_rfkill(dev); | ||
569 | |||
568 | /* mark class device as suspended */ | 570 | /* mark class device as suspended */ |
569 | if (dev->power.power_state.event != state.event) | 571 | if (dev->power.power_state.event != state.event) |
570 | dev->power.power_state = state; | 572 | dev->power.power_state = state; |
571 | 573 | ||
574 | /* store state for the resume handler */ | ||
575 | rfkill->state_for_resume = rfkill->state; | ||
576 | |||
572 | return 0; | 577 | return 0; |
573 | } | 578 | } |
574 | 579 | ||
575 | static int rfkill_resume(struct device *dev) | 580 | static int rfkill_resume(struct device *dev) |
576 | { | 581 | { |
577 | struct rfkill *rfkill = to_rfkill(dev); | 582 | struct rfkill *rfkill = to_rfkill(dev); |
583 | enum rfkill_state newstate; | ||
578 | 584 | ||
579 | if (dev->power.power_state.event != PM_EVENT_ON) { | 585 | if (dev->power.power_state.event != PM_EVENT_ON) { |
580 | mutex_lock(&rfkill->mutex); | 586 | mutex_lock(&rfkill->mutex); |
@@ -582,6 +588,15 @@ static int rfkill_resume(struct device *dev) | |||
582 | dev->power.power_state.event = PM_EVENT_ON; | 588 | dev->power.power_state.event = PM_EVENT_ON; |
583 | 589 | ||
584 | /* | 590 | /* |
591 | * rfkill->state could have been modified before we got | ||
592 | * called, and won't be updated by rfkill_toggle_radio() | ||
593 | * in force mode. Sync it FIRST. | ||
594 | */ | ||
595 | if (rfkill->get_state && | ||
596 | !rfkill->get_state(rfkill->data, &newstate)) | ||
597 | rfkill->state = newstate; | ||
598 | |||
599 | /* | ||
585 | * If we are under EPO, kick transmitter offline, | 600 | * If we are under EPO, kick transmitter offline, |
586 | * otherwise restore to pre-suspend state. | 601 | * otherwise restore to pre-suspend state. |
587 | * | 602 | * |
@@ -590,7 +605,7 @@ static int rfkill_resume(struct device *dev) | |||
590 | rfkill_toggle_radio(rfkill, | 605 | rfkill_toggle_radio(rfkill, |
591 | rfkill_epo_lock_active ? | 606 | rfkill_epo_lock_active ? |
592 | RFKILL_STATE_SOFT_BLOCKED : | 607 | RFKILL_STATE_SOFT_BLOCKED : |
593 | rfkill->state, | 608 | rfkill->state_for_resume, |
594 | 1); | 609 | 1); |
595 | 610 | ||
596 | mutex_unlock(&rfkill->mutex); | 611 | mutex_unlock(&rfkill->mutex); |
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index f7c64dbe86cc..e28e2b8fa436 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig | |||
@@ -1,6 +1,15 @@ | |||
1 | config CFG80211 | 1 | config CFG80211 |
2 | tristate "Improved wireless configuration API" | 2 | tristate "Improved wireless configuration API" |
3 | 3 | ||
4 | config CFG80211_REG_DEBUG | ||
5 | bool "cfg80211 regulatory debugging" | ||
6 | depends on CFG80211 | ||
7 | default n | ||
8 | ---help--- | ||
9 | You can enable this if you want to debug regulatory changes. | ||
10 | |||
11 | If unsure, say N. | ||
12 | |||
4 | config NL80211 | 13 | config NL80211 |
5 | bool "nl80211 new netlink interface support" | 14 | bool "nl80211 new netlink interface support" |
6 | depends on CFG80211 | 15 | depends on CFG80211 |
@@ -40,6 +49,8 @@ config WIRELESS_OLD_REGULATORY | |||
40 | ieee80211_regdom module parameter. This is being phased out and you | 49 | ieee80211_regdom module parameter. This is being phased out and you |
41 | should stop using them ASAP. | 50 | should stop using them ASAP. |
42 | 51 | ||
52 | Note: You will need CRDA if you want 802.11d support | ||
53 | |||
43 | Say Y unless you have installed a new userspace application. | 54 | Say Y unless you have installed a new userspace application. |
44 | Also say Y if have one currently depending on the ieee80211_regdom | 55 | Also say Y if have one currently depending on the ieee80211_regdom |
45 | module parameter and cannot port it to use the new userspace | 56 | module parameter and cannot port it to use the new userspace |
diff --git a/net/wireless/core.c b/net/wireless/core.c index 39e3d10fccde..b96fc0c3f1c4 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -19,7 +19,6 @@ | |||
19 | #include "nl80211.h" | 19 | #include "nl80211.h" |
20 | #include "core.h" | 20 | #include "core.h" |
21 | #include "sysfs.h" | 21 | #include "sysfs.h" |
22 | #include "reg.h" | ||
23 | 22 | ||
24 | /* name for sysfs, %d is appended */ | 23 | /* name for sysfs, %d is appended */ |
25 | #define PHY_NAME "phy" | 24 | #define PHY_NAME "phy" |
@@ -348,6 +347,10 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
348 | /* unlock again before freeing */ | 347 | /* unlock again before freeing */ |
349 | mutex_unlock(&drv->mtx); | 348 | mutex_unlock(&drv->mtx); |
350 | 349 | ||
350 | /* If this device got a regulatory hint tell core its | ||
351 | * free to listen now to a new shiny device regulatory hint */ | ||
352 | reg_device_remove(wiphy); | ||
353 | |||
351 | list_del(&drv->list); | 354 | list_del(&drv->list); |
352 | device_del(&drv->wiphy.dev); | 355 | device_del(&drv->wiphy.dev); |
353 | debugfs_remove(drv->wiphy.debugfsdir); | 356 | debugfs_remove(drv->wiphy.debugfsdir); |
diff --git a/net/wireless/core.h b/net/wireless/core.h index 771cc5cc7658..f7fb9f413028 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <net/genetlink.h> | 11 | #include <net/genetlink.h> |
12 | #include <net/wireless.h> | 12 | #include <net/wireless.h> |
13 | #include <net/cfg80211.h> | 13 | #include <net/cfg80211.h> |
14 | #include "reg.h" | ||
14 | 15 | ||
15 | struct cfg80211_registered_device { | 16 | struct cfg80211_registered_device { |
16 | struct cfg80211_ops *ops; | 17 | struct cfg80211_ops *ops; |
@@ -21,6 +22,18 @@ struct cfg80211_registered_device { | |||
21 | * any call is in progress */ | 22 | * any call is in progress */ |
22 | struct mutex mtx; | 23 | struct mutex mtx; |
23 | 24 | ||
25 | /* ISO / IEC 3166 alpha2 for which this device is receiving | ||
26 | * country IEs on, this can help disregard country IEs from APs | ||
27 | * on the same alpha2 quickly. The alpha2 may differ from | ||
28 | * cfg80211_regdomain's alpha2 when an intersection has occurred. | ||
29 | * If the AP is reconfigured this can also be used to tell us if | ||
30 | * the country on the country IE changed. */ | ||
31 | char country_ie_alpha2[2]; | ||
32 | |||
33 | /* If a Country IE has been received this tells us the environment | ||
34 | * which its telling us its in. This defaults to ENVIRON_ANY */ | ||
35 | enum environment_cap env; | ||
36 | |||
24 | /* wiphy index, internal only */ | 37 | /* wiphy index, internal only */ |
25 | int idx; | 38 | int idx; |
26 | 39 | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e3e1494e769a..c9141e3df9ba 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -198,6 +198,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
198 | if (chan->flags & IEEE80211_CHAN_RADAR) | 198 | if (chan->flags & IEEE80211_CHAN_RADAR) |
199 | NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR); | 199 | NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR); |
200 | 200 | ||
201 | NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, | ||
202 | DBM_TO_MBM(chan->max_power)); | ||
203 | |||
201 | nla_nest_end(msg, nl_freq); | 204 | nla_nest_end(msg, nl_freq); |
202 | } | 205 | } |
203 | 206 | ||
@@ -1760,7 +1763,7 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
1760 | return -EINVAL; | 1763 | return -EINVAL; |
1761 | #endif | 1764 | #endif |
1762 | mutex_lock(&cfg80211_drv_mutex); | 1765 | mutex_lock(&cfg80211_drv_mutex); |
1763 | r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data); | 1766 | r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY); |
1764 | mutex_unlock(&cfg80211_drv_mutex); | 1767 | mutex_unlock(&cfg80211_drv_mutex); |
1765 | return r; | 1768 | return r; |
1766 | } | 1769 | } |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 4c7e39d466c4..0990059f7e48 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -60,12 +60,18 @@ | |||
60 | * @intersect: indicates whether the wireless core should intersect | 60 | * @intersect: indicates whether the wireless core should intersect |
61 | * the requested regulatory domain with the presently set regulatory | 61 | * the requested regulatory domain with the presently set regulatory |
62 | * domain. | 62 | * domain. |
63 | * @country_ie_checksum: checksum of the last processed and accepted | ||
64 | * country IE | ||
65 | * @country_ie_env: lets us know if the AP is telling us we are outdoor, | ||
66 | * indoor, or if it doesn't matter | ||
63 | */ | 67 | */ |
64 | struct regulatory_request { | 68 | struct regulatory_request { |
65 | struct wiphy *wiphy; | 69 | struct wiphy *wiphy; |
66 | enum reg_set_by initiator; | 70 | enum reg_set_by initiator; |
67 | char alpha2[2]; | 71 | char alpha2[2]; |
68 | bool intersect; | 72 | bool intersect; |
73 | u32 country_ie_checksum; | ||
74 | enum environment_cap country_ie_env; | ||
69 | }; | 75 | }; |
70 | 76 | ||
71 | /* Receipt of information from last regulatory request */ | 77 | /* Receipt of information from last regulatory request */ |
@@ -85,6 +91,11 @@ static u32 supported_bandwidths[] = { | |||
85 | * information to give us an alpha2 */ | 91 | * information to give us an alpha2 */ |
86 | static const struct ieee80211_regdomain *cfg80211_regdomain; | 92 | static const struct ieee80211_regdomain *cfg80211_regdomain; |
87 | 93 | ||
94 | /* We use this as a place for the rd structure built from the | ||
95 | * last parsed country IE to rest until CRDA gets back to us with | ||
96 | * what it thinks should apply for the same country */ | ||
97 | static const struct ieee80211_regdomain *country_ie_regdomain; | ||
98 | |||
88 | /* We keep a static world regulatory domain in case of the absence of CRDA */ | 99 | /* We keep a static world regulatory domain in case of the absence of CRDA */ |
89 | static const struct ieee80211_regdomain world_regdom = { | 100 | static const struct ieee80211_regdomain world_regdom = { |
90 | .n_reg_rules = 1, | 101 | .n_reg_rules = 1, |
@@ -264,6 +275,18 @@ static bool is_unknown_alpha2(const char *alpha2) | |||
264 | return false; | 275 | return false; |
265 | } | 276 | } |
266 | 277 | ||
278 | static bool is_intersected_alpha2(const char *alpha2) | ||
279 | { | ||
280 | if (!alpha2) | ||
281 | return false; | ||
282 | /* Special case where regulatory domain is the | ||
283 | * result of an intersection between two regulatory domain | ||
284 | * structures */ | ||
285 | if (alpha2[0] == '9' && alpha2[1] == '8') | ||
286 | return true; | ||
287 | return false; | ||
288 | } | ||
289 | |||
267 | static bool is_an_alpha2(const char *alpha2) | 290 | static bool is_an_alpha2(const char *alpha2) |
268 | { | 291 | { |
269 | if (!alpha2) | 292 | if (!alpha2) |
@@ -292,6 +315,25 @@ static bool regdom_changed(const char *alpha2) | |||
292 | return true; | 315 | return true; |
293 | } | 316 | } |
294 | 317 | ||
318 | /** | ||
319 | * country_ie_integrity_changes - tells us if the country IE has changed | ||
320 | * @checksum: checksum of country IE of fields we are interested in | ||
321 | * | ||
322 | * If the country IE has not changed you can ignore it safely. This is | ||
323 | * useful to determine if two devices are seeing two different country IEs | ||
324 | * even on the same alpha2. Note that this will return false if no IE has | ||
325 | * been set on the wireless core yet. | ||
326 | */ | ||
327 | static bool country_ie_integrity_changes(u32 checksum) | ||
328 | { | ||
329 | /* If no IE has been set then the checksum doesn't change */ | ||
330 | if (unlikely(!last_request->country_ie_checksum)) | ||
331 | return false; | ||
332 | if (unlikely(last_request->country_ie_checksum != checksum)) | ||
333 | return true; | ||
334 | return false; | ||
335 | } | ||
336 | |||
295 | /* This lets us keep regulatory code which is updated on a regulatory | 337 | /* This lets us keep regulatory code which is updated on a regulatory |
296 | * basis in userspace. */ | 338 | * basis in userspace. */ |
297 | static int call_crda(const char *alpha2) | 339 | static int call_crda(const char *alpha2) |
@@ -330,7 +372,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) | |||
330 | const struct ieee80211_freq_range *freq_range = &rule->freq_range; | 372 | const struct ieee80211_freq_range *freq_range = &rule->freq_range; |
331 | u32 freq_diff; | 373 | u32 freq_diff; |
332 | 374 | ||
333 | if (freq_range->start_freq_khz == 0 || freq_range->end_freq_khz == 0) | 375 | if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0) |
334 | return false; | 376 | return false; |
335 | 377 | ||
336 | if (freq_range->start_freq_khz > freq_range->end_freq_khz) | 378 | if (freq_range->start_freq_khz > freq_range->end_freq_khz) |
@@ -352,6 +394,9 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd) | |||
352 | if (!rd->n_reg_rules) | 394 | if (!rd->n_reg_rules) |
353 | return false; | 395 | return false; |
354 | 396 | ||
397 | if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) | ||
398 | return false; | ||
399 | |||
355 | for (i = 0; i < rd->n_reg_rules; i++) { | 400 | for (i = 0; i < rd->n_reg_rules; i++) { |
356 | reg_rule = &rd->reg_rules[i]; | 401 | reg_rule = &rd->reg_rules[i]; |
357 | if (!is_valid_reg_rule(reg_rule)) | 402 | if (!is_valid_reg_rule(reg_rule)) |
@@ -376,6 +421,174 @@ static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range, | |||
376 | return 0; | 421 | return 0; |
377 | } | 422 | } |
378 | 423 | ||
424 | /* Converts a country IE to a regulatory domain. A regulatory domain | ||
425 | * structure has a lot of information which the IE doesn't yet have, | ||
426 | * so for the other values we use upper max values as we will intersect | ||
427 | * with our userspace regulatory agent to get lower bounds. */ | ||
428 | static struct ieee80211_regdomain *country_ie_2_rd( | ||
429 | u8 *country_ie, | ||
430 | u8 country_ie_len, | ||
431 | u32 *checksum) | ||
432 | { | ||
433 | struct ieee80211_regdomain *rd = NULL; | ||
434 | unsigned int i = 0; | ||
435 | char alpha2[2]; | ||
436 | u32 flags = 0; | ||
437 | u32 num_rules = 0, size_of_regd = 0; | ||
438 | u8 *triplets_start = NULL; | ||
439 | u8 len_at_triplet = 0; | ||
440 | /* the last channel we have registered in a subband (triplet) */ | ||
441 | int last_sub_max_channel = 0; | ||
442 | |||
443 | *checksum = 0xDEADBEEF; | ||
444 | |||
445 | /* Country IE requirements */ | ||
446 | BUG_ON(country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN || | ||
447 | country_ie_len & 0x01); | ||
448 | |||
449 | alpha2[0] = country_ie[0]; | ||
450 | alpha2[1] = country_ie[1]; | ||
451 | |||
452 | /* | ||
453 | * Third octet can be: | ||
454 | * 'I' - Indoor | ||
455 | * 'O' - Outdoor | ||
456 | * | ||
457 | * anything else we assume is no restrictions | ||
458 | */ | ||
459 | if (country_ie[2] == 'I') | ||
460 | flags = NL80211_RRF_NO_OUTDOOR; | ||
461 | else if (country_ie[2] == 'O') | ||
462 | flags = NL80211_RRF_NO_INDOOR; | ||
463 | |||
464 | country_ie += 3; | ||
465 | country_ie_len -= 3; | ||
466 | |||
467 | triplets_start = country_ie; | ||
468 | len_at_triplet = country_ie_len; | ||
469 | |||
470 | *checksum ^= ((flags ^ alpha2[0] ^ alpha2[1]) << 8); | ||
471 | |||
472 | /* We need to build a reg rule for each triplet, but first we must | ||
473 | * calculate the number of reg rules we will need. We will need one | ||
474 | * for each channel subband */ | ||
475 | while (country_ie_len >= 3) { | ||
476 | struct ieee80211_country_ie_triplet *triplet = | ||
477 | (struct ieee80211_country_ie_triplet *) country_ie; | ||
478 | int cur_sub_max_channel = 0, cur_channel = 0; | ||
479 | |||
480 | if (triplet->ext.reg_extension_id >= | ||
481 | IEEE80211_COUNTRY_EXTENSION_ID) { | ||
482 | country_ie += 3; | ||
483 | country_ie_len -= 3; | ||
484 | continue; | ||
485 | } | ||
486 | |||
487 | cur_channel = triplet->chans.first_channel; | ||
488 | cur_sub_max_channel = ieee80211_channel_to_frequency( | ||
489 | cur_channel + triplet->chans.num_channels); | ||
490 | |||
491 | /* Basic sanity check */ | ||
492 | if (cur_sub_max_channel < cur_channel) | ||
493 | return NULL; | ||
494 | |||
495 | /* Do not allow overlapping channels. Also channels | ||
496 | * passed in each subband must be monotonically | ||
497 | * increasing */ | ||
498 | if (last_sub_max_channel) { | ||
499 | if (cur_channel <= last_sub_max_channel) | ||
500 | return NULL; | ||
501 | if (cur_sub_max_channel <= last_sub_max_channel) | ||
502 | return NULL; | ||
503 | } | ||
504 | |||
505 | /* When dot11RegulatoryClassesRequired is supported | ||
506 | * we can throw ext triplets as part of this soup, | ||
507 | * for now we don't care when those change as we | ||
508 | * don't support them */ | ||
509 | *checksum ^= ((cur_channel ^ cur_sub_max_channel) << 8) | | ||
510 | ((cur_sub_max_channel ^ cur_sub_max_channel) << 16) | | ||
511 | ((triplet->chans.max_power ^ cur_sub_max_channel) << 24); | ||
512 | |||
513 | last_sub_max_channel = cur_sub_max_channel; | ||
514 | |||
515 | country_ie += 3; | ||
516 | country_ie_len -= 3; | ||
517 | num_rules++; | ||
518 | |||
519 | /* Note: this is not a IEEE requirement but | ||
520 | * simply a memory requirement */ | ||
521 | if (num_rules > NL80211_MAX_SUPP_REG_RULES) | ||
522 | return NULL; | ||
523 | } | ||
524 | |||
525 | country_ie = triplets_start; | ||
526 | country_ie_len = len_at_triplet; | ||
527 | |||
528 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
529 | (num_rules * sizeof(struct ieee80211_reg_rule)); | ||
530 | |||
531 | rd = kzalloc(size_of_regd, GFP_KERNEL); | ||
532 | if (!rd) | ||
533 | return NULL; | ||
534 | |||
535 | rd->n_reg_rules = num_rules; | ||
536 | rd->alpha2[0] = alpha2[0]; | ||
537 | rd->alpha2[1] = alpha2[1]; | ||
538 | |||
539 | /* This time around we fill in the rd */ | ||
540 | while (country_ie_len >= 3) { | ||
541 | struct ieee80211_country_ie_triplet *triplet = | ||
542 | (struct ieee80211_country_ie_triplet *) country_ie; | ||
543 | struct ieee80211_reg_rule *reg_rule = NULL; | ||
544 | struct ieee80211_freq_range *freq_range = NULL; | ||
545 | struct ieee80211_power_rule *power_rule = NULL; | ||
546 | |||
547 | /* Must parse if dot11RegulatoryClassesRequired is true, | ||
548 | * we don't support this yet */ | ||
549 | if (triplet->ext.reg_extension_id >= | ||
550 | IEEE80211_COUNTRY_EXTENSION_ID) { | ||
551 | country_ie += 3; | ||
552 | country_ie_len -= 3; | ||
553 | continue; | ||
554 | } | ||
555 | |||
556 | reg_rule = &rd->reg_rules[i]; | ||
557 | freq_range = ®_rule->freq_range; | ||
558 | power_rule = ®_rule->power_rule; | ||
559 | |||
560 | reg_rule->flags = flags; | ||
561 | |||
562 | /* The +10 is since the regulatory domain expects | ||
563 | * the actual band edge, not the center of freq for | ||
564 | * its start and end freqs, assuming 20 MHz bandwidth on | ||
565 | * the channels passed */ | ||
566 | freq_range->start_freq_khz = | ||
567 | MHZ_TO_KHZ(ieee80211_channel_to_frequency( | ||
568 | triplet->chans.first_channel) - 10); | ||
569 | freq_range->end_freq_khz = | ||
570 | MHZ_TO_KHZ(ieee80211_channel_to_frequency( | ||
571 | triplet->chans.first_channel + | ||
572 | triplet->chans.num_channels) + 10); | ||
573 | |||
574 | /* Large arbitrary values, we intersect later */ | ||
575 | /* Increment this if we ever support >= 40 MHz channels | ||
576 | * in IEEE 802.11 */ | ||
577 | freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40); | ||
578 | power_rule->max_antenna_gain = DBI_TO_MBI(100); | ||
579 | power_rule->max_eirp = DBM_TO_MBM(100); | ||
580 | |||
581 | country_ie += 3; | ||
582 | country_ie_len -= 3; | ||
583 | i++; | ||
584 | |||
585 | BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); | ||
586 | } | ||
587 | |||
588 | return rd; | ||
589 | } | ||
590 | |||
591 | |||
379 | /* Helper for regdom_intersect(), this does the real | 592 | /* Helper for regdom_intersect(), this does the real |
380 | * mathematical intersection fun */ | 593 | * mathematical intersection fun */ |
381 | static int reg_rules_intersect( | 594 | static int reg_rules_intersect( |
@@ -603,12 +816,23 @@ static void handle_band(struct ieee80211_supported_band *sband) | |||
603 | handle_channel(&sband->channels[i]); | 816 | handle_channel(&sband->channels[i]); |
604 | } | 817 | } |
605 | 818 | ||
819 | static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby) | ||
820 | { | ||
821 | if (!last_request) | ||
822 | return true; | ||
823 | if (setby == REGDOM_SET_BY_CORE && | ||
824 | wiphy->fw_handles_regulatory) | ||
825 | return true; | ||
826 | return false; | ||
827 | } | ||
828 | |||
606 | static void update_all_wiphy_regulatory(enum reg_set_by setby) | 829 | static void update_all_wiphy_regulatory(enum reg_set_by setby) |
607 | { | 830 | { |
608 | struct cfg80211_registered_device *drv; | 831 | struct cfg80211_registered_device *drv; |
609 | 832 | ||
610 | list_for_each_entry(drv, &cfg80211_drv_list, list) | 833 | list_for_each_entry(drv, &cfg80211_drv_list, list) |
611 | wiphy_update_regulatory(&drv->wiphy, setby); | 834 | if (!ignore_reg_update(&drv->wiphy, setby)) |
835 | wiphy_update_regulatory(&drv->wiphy, setby); | ||
612 | } | 836 | } |
613 | 837 | ||
614 | void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) | 838 | void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) |
@@ -660,16 +884,14 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | |||
660 | return -EOPNOTSUPP; | 884 | return -EOPNOTSUPP; |
661 | return -EALREADY; | 885 | return -EALREADY; |
662 | } | 886 | } |
663 | /* Two consecutive Country IE hints on the same wiphy */ | 887 | /* Two consecutive Country IE hints on the same wiphy. |
664 | if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) | 888 | * This should be picked up early by the driver/stack */ |
889 | if (WARN_ON(!alpha2_equal(cfg80211_regdomain->alpha2, | ||
890 | alpha2))) | ||
665 | return 0; | 891 | return 0; |
666 | return -EALREADY; | 892 | return -EALREADY; |
667 | } | 893 | } |
668 | /* | 894 | return REG_INTERSECT; |
669 | * Ignore Country IE hints for now, need to think about | ||
670 | * what we need to do to support multi-domain operation. | ||
671 | */ | ||
672 | return -EOPNOTSUPP; | ||
673 | case REGDOM_SET_BY_DRIVER: | 895 | case REGDOM_SET_BY_DRIVER: |
674 | if (last_request->initiator == REGDOM_SET_BY_DRIVER) | 896 | if (last_request->initiator == REGDOM_SET_BY_DRIVER) |
675 | return -EALREADY; | 897 | return -EALREADY; |
@@ -677,6 +899,11 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | |||
677 | case REGDOM_SET_BY_USER: | 899 | case REGDOM_SET_BY_USER: |
678 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) | 900 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) |
679 | return REG_INTERSECT; | 901 | return REG_INTERSECT; |
902 | /* If the user knows better the user should set the regdom | ||
903 | * to their country before the IE is picked up */ | ||
904 | if (last_request->initiator == REGDOM_SET_BY_USER && | ||
905 | last_request->intersect) | ||
906 | return -EOPNOTSUPP; | ||
680 | return 0; | 907 | return 0; |
681 | } | 908 | } |
682 | 909 | ||
@@ -685,7 +912,9 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | |||
685 | 912 | ||
686 | /* Caller must hold &cfg80211_drv_mutex */ | 913 | /* Caller must hold &cfg80211_drv_mutex */ |
687 | int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | 914 | int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, |
688 | const char *alpha2) | 915 | const char *alpha2, |
916 | u32 country_ie_checksum, | ||
917 | enum environment_cap env) | ||
689 | { | 918 | { |
690 | struct regulatory_request *request; | 919 | struct regulatory_request *request; |
691 | bool intersect = false; | 920 | bool intersect = false; |
@@ -698,36 +927,32 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
698 | else if (r) | 927 | else if (r) |
699 | return r; | 928 | return r; |
700 | 929 | ||
701 | switch (set_by) { | 930 | request = kzalloc(sizeof(struct regulatory_request), |
702 | case REGDOM_SET_BY_CORE: | 931 | GFP_KERNEL); |
703 | case REGDOM_SET_BY_COUNTRY_IE: | 932 | if (!request) |
704 | case REGDOM_SET_BY_DRIVER: | 933 | return -ENOMEM; |
705 | case REGDOM_SET_BY_USER: | ||
706 | request = kzalloc(sizeof(struct regulatory_request), | ||
707 | GFP_KERNEL); | ||
708 | if (!request) | ||
709 | return -ENOMEM; | ||
710 | |||
711 | request->alpha2[0] = alpha2[0]; | ||
712 | request->alpha2[1] = alpha2[1]; | ||
713 | request->initiator = set_by; | ||
714 | request->wiphy = wiphy; | ||
715 | request->intersect = intersect; | ||
716 | |||
717 | kfree(last_request); | ||
718 | last_request = request; | ||
719 | r = call_crda(alpha2); | ||
720 | #ifndef CONFIG_WIRELESS_OLD_REGULATORY | ||
721 | if (r) | ||
722 | printk(KERN_ERR "cfg80211: Failed calling CRDA\n"); | ||
723 | #endif | ||
724 | break; | ||
725 | default: | ||
726 | r = -ENOTSUPP; | ||
727 | break; | ||
728 | } | ||
729 | 934 | ||
730 | return r; | 935 | request->alpha2[0] = alpha2[0]; |
936 | request->alpha2[1] = alpha2[1]; | ||
937 | request->initiator = set_by; | ||
938 | request->wiphy = wiphy; | ||
939 | request->intersect = intersect; | ||
940 | request->country_ie_checksum = country_ie_checksum; | ||
941 | request->country_ie_env = env; | ||
942 | |||
943 | kfree(last_request); | ||
944 | last_request = request; | ||
945 | /* | ||
946 | * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled | ||
947 | * AND if CRDA is NOT present nothing will happen, if someone | ||
948 | * wants to bother with 11d with OLD_REG you can add a timer. | ||
949 | * If after x amount of time nothing happens you can call: | ||
950 | * | ||
951 | * return set_regdom(country_ie_regdomain); | ||
952 | * | ||
953 | * to intersect with the static rd | ||
954 | */ | ||
955 | return call_crda(alpha2); | ||
731 | } | 956 | } |
732 | 957 | ||
733 | void regulatory_hint(struct wiphy *wiphy, const char *alpha2) | 958 | void regulatory_hint(struct wiphy *wiphy, const char *alpha2) |
@@ -735,11 +960,120 @@ void regulatory_hint(struct wiphy *wiphy, const char *alpha2) | |||
735 | BUG_ON(!alpha2); | 960 | BUG_ON(!alpha2); |
736 | 961 | ||
737 | mutex_lock(&cfg80211_drv_mutex); | 962 | mutex_lock(&cfg80211_drv_mutex); |
738 | __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2); | 963 | __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, 0, ENVIRON_ANY); |
739 | mutex_unlock(&cfg80211_drv_mutex); | 964 | mutex_unlock(&cfg80211_drv_mutex); |
740 | } | 965 | } |
741 | EXPORT_SYMBOL(regulatory_hint); | 966 | EXPORT_SYMBOL(regulatory_hint); |
742 | 967 | ||
968 | static bool reg_same_country_ie_hint(struct wiphy *wiphy, | ||
969 | u32 country_ie_checksum) | ||
970 | { | ||
971 | if (!last_request->wiphy) | ||
972 | return false; | ||
973 | if (likely(last_request->wiphy != wiphy)) | ||
974 | return !country_ie_integrity_changes(country_ie_checksum); | ||
975 | /* We should not have let these through at this point, they | ||
976 | * should have been picked up earlier by the first alpha2 check | ||
977 | * on the device */ | ||
978 | if (WARN_ON(!country_ie_integrity_changes(country_ie_checksum))) | ||
979 | return true; | ||
980 | return false; | ||
981 | } | ||
982 | |||
983 | void regulatory_hint_11d(struct wiphy *wiphy, | ||
984 | u8 *country_ie, | ||
985 | u8 country_ie_len) | ||
986 | { | ||
987 | struct ieee80211_regdomain *rd = NULL; | ||
988 | char alpha2[2]; | ||
989 | u32 checksum = 0; | ||
990 | enum environment_cap env = ENVIRON_ANY; | ||
991 | |||
992 | mutex_lock(&cfg80211_drv_mutex); | ||
993 | |||
994 | /* IE len must be evenly divisible by 2 */ | ||
995 | if (country_ie_len & 0x01) | ||
996 | goto out; | ||
997 | |||
998 | if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) | ||
999 | goto out; | ||
1000 | |||
1001 | /* Pending country IE processing, this can happen after we | ||
1002 | * call CRDA and wait for a response if a beacon was received before | ||
1003 | * we were able to process the last regulatory_hint_11d() call */ | ||
1004 | if (country_ie_regdomain) | ||
1005 | goto out; | ||
1006 | |||
1007 | alpha2[0] = country_ie[0]; | ||
1008 | alpha2[1] = country_ie[1]; | ||
1009 | |||
1010 | if (country_ie[2] == 'I') | ||
1011 | env = ENVIRON_INDOOR; | ||
1012 | else if (country_ie[2] == 'O') | ||
1013 | env = ENVIRON_OUTDOOR; | ||
1014 | |||
1015 | /* We will run this for *every* beacon processed for the BSSID, so | ||
1016 | * we optimize an early check to exit out early if we don't have to | ||
1017 | * do anything */ | ||
1018 | if (likely(last_request->wiphy)) { | ||
1019 | struct cfg80211_registered_device *drv_last_ie; | ||
1020 | |||
1021 | drv_last_ie = wiphy_to_dev(last_request->wiphy); | ||
1022 | |||
1023 | /* Lets keep this simple -- we trust the first AP | ||
1024 | * after we intersect with CRDA */ | ||
1025 | if (likely(last_request->wiphy == wiphy)) { | ||
1026 | /* Ignore IEs coming in on this wiphy with | ||
1027 | * the same alpha2 and environment cap */ | ||
1028 | if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2, | ||
1029 | alpha2) && | ||
1030 | env == drv_last_ie->env)) { | ||
1031 | goto out; | ||
1032 | } | ||
1033 | /* the wiphy moved on to another BSSID or the AP | ||
1034 | * was reconfigured. XXX: We need to deal with the | ||
1035 | * case where the user suspends and goes to goes | ||
1036 | * to another country, and then gets IEs from an | ||
1037 | * AP with different settings */ | ||
1038 | goto out; | ||
1039 | } else { | ||
1040 | /* Ignore IEs coming in on two separate wiphys with | ||
1041 | * the same alpha2 and environment cap */ | ||
1042 | if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2, | ||
1043 | alpha2) && | ||
1044 | env == drv_last_ie->env)) { | ||
1045 | goto out; | ||
1046 | } | ||
1047 | /* We could potentially intersect though */ | ||
1048 | goto out; | ||
1049 | } | ||
1050 | } | ||
1051 | |||
1052 | rd = country_ie_2_rd(country_ie, country_ie_len, &checksum); | ||
1053 | if (!rd) | ||
1054 | goto out; | ||
1055 | |||
1056 | /* This will not happen right now but we leave it here for the | ||
1057 | * the future when we want to add suspend/resume support and having | ||
1058 | * the user move to another country after doing so, or having the user | ||
1059 | * move to another AP. Right now we just trust the first AP. This is why | ||
1060 | * this is marked as likley(). If we hit this before we add this support | ||
1061 | * we want to be informed of it as it would indicate a mistake in the | ||
1062 | * current design */ | ||
1063 | if (likely(WARN_ON(reg_same_country_ie_hint(wiphy, checksum)))) | ||
1064 | goto out; | ||
1065 | |||
1066 | /* We keep this around for when CRDA comes back with a response so | ||
1067 | * we can intersect with that */ | ||
1068 | country_ie_regdomain = rd; | ||
1069 | |||
1070 | __regulatory_hint(wiphy, REGDOM_SET_BY_COUNTRY_IE, | ||
1071 | country_ie_regdomain->alpha2, checksum, env); | ||
1072 | |||
1073 | out: | ||
1074 | mutex_unlock(&cfg80211_drv_mutex); | ||
1075 | } | ||
1076 | EXPORT_SYMBOL(regulatory_hint_11d); | ||
743 | 1077 | ||
744 | static void print_rd_rules(const struct ieee80211_regdomain *rd) | 1078 | static void print_rd_rules(const struct ieee80211_regdomain *rd) |
745 | { | 1079 | { |
@@ -779,7 +1113,25 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) | |||
779 | static void print_regdomain(const struct ieee80211_regdomain *rd) | 1113 | static void print_regdomain(const struct ieee80211_regdomain *rd) |
780 | { | 1114 | { |
781 | 1115 | ||
782 | if (is_world_regdom(rd->alpha2)) | 1116 | if (is_intersected_alpha2(rd->alpha2)) { |
1117 | struct wiphy *wiphy = NULL; | ||
1118 | struct cfg80211_registered_device *drv; | ||
1119 | |||
1120 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { | ||
1121 | if (last_request->wiphy) { | ||
1122 | wiphy = last_request->wiphy; | ||
1123 | drv = wiphy_to_dev(wiphy); | ||
1124 | printk(KERN_INFO "cfg80211: Current regulatory " | ||
1125 | "domain updated by AP to: %c%c\n", | ||
1126 | drv->country_ie_alpha2[0], | ||
1127 | drv->country_ie_alpha2[1]); | ||
1128 | } else | ||
1129 | printk(KERN_INFO "cfg80211: Current regulatory " | ||
1130 | "domain intersected: \n"); | ||
1131 | } else | ||
1132 | printk(KERN_INFO "cfg80211: Current regulatory " | ||
1133 | "intersected: \n"); | ||
1134 | } else if (is_world_regdom(rd->alpha2)) | ||
783 | printk(KERN_INFO "cfg80211: World regulatory " | 1135 | printk(KERN_INFO "cfg80211: World regulatory " |
784 | "domain updated:\n"); | 1136 | "domain updated:\n"); |
785 | else { | 1137 | else { |
@@ -802,10 +1154,39 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd) | |||
802 | print_rd_rules(rd); | 1154 | print_rd_rules(rd); |
803 | } | 1155 | } |
804 | 1156 | ||
1157 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
1158 | static void reg_country_ie_process_debug( | ||
1159 | const struct ieee80211_regdomain *rd, | ||
1160 | const struct ieee80211_regdomain *country_ie_regdomain, | ||
1161 | const struct ieee80211_regdomain *intersected_rd) | ||
1162 | { | ||
1163 | printk(KERN_DEBUG "cfg80211: Received country IE:\n"); | ||
1164 | print_regdomain_info(country_ie_regdomain); | ||
1165 | printk(KERN_DEBUG "cfg80211: CRDA thinks this should applied:\n"); | ||
1166 | print_regdomain_info(rd); | ||
1167 | if (intersected_rd) { | ||
1168 | printk(KERN_DEBUG "cfg80211: We intersect both of these " | ||
1169 | "and get:\n"); | ||
1170 | print_regdomain_info(rd); | ||
1171 | return; | ||
1172 | } | ||
1173 | printk(KERN_DEBUG "cfg80211: Intersection between both failed\n"); | ||
1174 | } | ||
1175 | #else | ||
1176 | static inline void reg_country_ie_process_debug( | ||
1177 | const struct ieee80211_regdomain *rd, | ||
1178 | const struct ieee80211_regdomain *country_ie_regdomain, | ||
1179 | const struct ieee80211_regdomain *intersected_rd) | ||
1180 | { | ||
1181 | } | ||
1182 | #endif | ||
1183 | |||
805 | /* Takes ownership of rd only if it doesn't fail */ | 1184 | /* Takes ownership of rd only if it doesn't fail */ |
806 | static int __set_regdom(const struct ieee80211_regdomain *rd) | 1185 | static int __set_regdom(const struct ieee80211_regdomain *rd) |
807 | { | 1186 | { |
808 | const struct ieee80211_regdomain *intersected_rd = NULL; | 1187 | const struct ieee80211_regdomain *intersected_rd = NULL; |
1188 | struct cfg80211_registered_device *drv = NULL; | ||
1189 | struct wiphy *wiphy = NULL; | ||
809 | /* Some basic sanity checks first */ | 1190 | /* Some basic sanity checks first */ |
810 | 1191 | ||
811 | if (is_world_regdom(rd->alpha2)) { | 1192 | if (is_world_regdom(rd->alpha2)) { |
@@ -822,10 +1203,18 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
822 | if (!last_request) | 1203 | if (!last_request) |
823 | return -EINVAL; | 1204 | return -EINVAL; |
824 | 1205 | ||
825 | /* allow overriding the static definitions if CRDA is present */ | 1206 | /* Lets only bother proceeding on the same alpha2 if the current |
826 | if (!is_old_static_regdom(cfg80211_regdomain) && | 1207 | * rd is non static (it means CRDA was present and was used last) |
827 | !regdom_changed(rd->alpha2)) | 1208 | * and the pending request came in from a country IE */ |
828 | return -EINVAL; | 1209 | if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) { |
1210 | /* If someone else asked us to change the rd lets only bother | ||
1211 | * checking if the alpha2 changes if CRDA was already called */ | ||
1212 | if (!is_old_static_regdom(cfg80211_regdomain) && | ||
1213 | !regdom_changed(rd->alpha2)) | ||
1214 | return -EINVAL; | ||
1215 | } | ||
1216 | |||
1217 | wiphy = last_request->wiphy; | ||
829 | 1218 | ||
830 | /* Now lets set the regulatory domain, update all driver channels | 1219 | /* Now lets set the regulatory domain, update all driver channels |
831 | * and finally inform them of what we have done, in case they want | 1220 | * and finally inform them of what we have done, in case they want |
@@ -835,36 +1224,78 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
835 | if (WARN_ON(!reg_is_valid_request(rd->alpha2))) | 1224 | if (WARN_ON(!reg_is_valid_request(rd->alpha2))) |
836 | return -EINVAL; | 1225 | return -EINVAL; |
837 | 1226 | ||
838 | reset_regdomains(); | 1227 | if (!is_valid_rd(rd)) { |
1228 | printk(KERN_ERR "cfg80211: Invalid " | ||
1229 | "regulatory domain detected:\n"); | ||
1230 | print_regdomain_info(rd); | ||
1231 | return -EINVAL; | ||
1232 | } | ||
839 | 1233 | ||
840 | /* Country IE parsing coming soon */ | 1234 | if (!last_request->intersect) { |
841 | switch (last_request->initiator) { | 1235 | reset_regdomains(); |
842 | case REGDOM_SET_BY_CORE: | 1236 | cfg80211_regdomain = rd; |
843 | case REGDOM_SET_BY_DRIVER: | 1237 | return 0; |
844 | case REGDOM_SET_BY_USER: | ||
845 | if (!is_valid_rd(rd)) { | ||
846 | printk(KERN_ERR "cfg80211: Invalid " | ||
847 | "regulatory domain detected:\n"); | ||
848 | print_regdomain_info(rd); | ||
849 | return -EINVAL; | ||
850 | } | ||
851 | break; | ||
852 | case REGDOM_SET_BY_COUNTRY_IE: /* Not yet */ | ||
853 | WARN_ON(1); | ||
854 | default: | ||
855 | return -EOPNOTSUPP; | ||
856 | } | 1238 | } |
857 | 1239 | ||
858 | if (unlikely(last_request->intersect)) { | 1240 | /* Intersection requires a bit more work */ |
1241 | |||
1242 | if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) { | ||
1243 | |||
859 | intersected_rd = regdom_intersect(rd, cfg80211_regdomain); | 1244 | intersected_rd = regdom_intersect(rd, cfg80211_regdomain); |
860 | if (!intersected_rd) | 1245 | if (!intersected_rd) |
861 | return -EINVAL; | 1246 | return -EINVAL; |
1247 | |||
1248 | /* We can trash what CRDA provided now */ | ||
862 | kfree(rd); | 1249 | kfree(rd); |
863 | rd = intersected_rd; | 1250 | rd = NULL; |
1251 | |||
1252 | reset_regdomains(); | ||
1253 | cfg80211_regdomain = intersected_rd; | ||
1254 | |||
1255 | return 0; | ||
864 | } | 1256 | } |
865 | 1257 | ||
866 | /* Tada! */ | 1258 | /* |
867 | cfg80211_regdomain = rd; | 1259 | * Country IE requests are handled a bit differently, we intersect |
1260 | * the country IE rd with what CRDA believes that country should have | ||
1261 | */ | ||
1262 | |||
1263 | BUG_ON(!country_ie_regdomain); | ||
1264 | |||
1265 | if (rd != country_ie_regdomain) { | ||
1266 | /* Intersect what CRDA returned and our what we | ||
1267 | * had built from the Country IE received */ | ||
1268 | |||
1269 | intersected_rd = regdom_intersect(rd, country_ie_regdomain); | ||
1270 | |||
1271 | reg_country_ie_process_debug(rd, country_ie_regdomain, | ||
1272 | intersected_rd); | ||
1273 | |||
1274 | kfree(country_ie_regdomain); | ||
1275 | country_ie_regdomain = NULL; | ||
1276 | } else { | ||
1277 | /* This would happen when CRDA was not present and | ||
1278 | * OLD_REGULATORY was enabled. We intersect our Country | ||
1279 | * IE rd and what was set on cfg80211 originally */ | ||
1280 | intersected_rd = regdom_intersect(rd, cfg80211_regdomain); | ||
1281 | } | ||
1282 | |||
1283 | if (!intersected_rd) | ||
1284 | return -EINVAL; | ||
1285 | |||
1286 | drv = wiphy_to_dev(wiphy); | ||
1287 | |||
1288 | drv->country_ie_alpha2[0] = rd->alpha2[0]; | ||
1289 | drv->country_ie_alpha2[1] = rd->alpha2[1]; | ||
1290 | drv->env = last_request->country_ie_env; | ||
1291 | |||
1292 | BUG_ON(intersected_rd == rd); | ||
1293 | |||
1294 | kfree(rd); | ||
1295 | rd = NULL; | ||
1296 | |||
1297 | reset_regdomains(); | ||
1298 | cfg80211_regdomain = intersected_rd; | ||
868 | 1299 | ||
869 | return 0; | 1300 | return 0; |
870 | } | 1301 | } |
@@ -885,16 +1316,28 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
885 | } | 1316 | } |
886 | 1317 | ||
887 | /* This would make this whole thing pointless */ | 1318 | /* This would make this whole thing pointless */ |
888 | BUG_ON(rd != cfg80211_regdomain); | 1319 | if (!last_request->intersect) |
1320 | BUG_ON(rd != cfg80211_regdomain); | ||
889 | 1321 | ||
890 | /* update all wiphys now with the new established regulatory domain */ | 1322 | /* update all wiphys now with the new established regulatory domain */ |
891 | update_all_wiphy_regulatory(last_request->initiator); | 1323 | update_all_wiphy_regulatory(last_request->initiator); |
892 | 1324 | ||
893 | print_regdomain(rd); | 1325 | print_regdomain(cfg80211_regdomain); |
894 | 1326 | ||
895 | return r; | 1327 | return r; |
896 | } | 1328 | } |
897 | 1329 | ||
1330 | /* Caller must hold cfg80211_drv_mutex */ | ||
1331 | void reg_device_remove(struct wiphy *wiphy) | ||
1332 | { | ||
1333 | if (!last_request->wiphy) | ||
1334 | return; | ||
1335 | if (last_request->wiphy != wiphy) | ||
1336 | return; | ||
1337 | last_request->wiphy = NULL; | ||
1338 | last_request->country_ie_env = ENVIRON_ANY; | ||
1339 | } | ||
1340 | |||
898 | int regulatory_init(void) | 1341 | int regulatory_init(void) |
899 | { | 1342 | { |
900 | int err; | 1343 | int err; |
@@ -914,11 +1357,11 @@ int regulatory_init(void) | |||
914 | * that is not a valid ISO / IEC 3166 alpha2 */ | 1357 | * that is not a valid ISO / IEC 3166 alpha2 */ |
915 | if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U') | 1358 | if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U') |
916 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, | 1359 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, |
917 | ieee80211_regdom); | 1360 | ieee80211_regdom, 0, ENVIRON_ANY); |
918 | #else | 1361 | #else |
919 | cfg80211_regdomain = cfg80211_world_regdom; | 1362 | cfg80211_regdomain = cfg80211_world_regdom; |
920 | 1363 | ||
921 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00"); | 1364 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", 0, ENVIRON_ANY); |
922 | if (err) | 1365 | if (err) |
923 | printk(KERN_ERR "cfg80211: calling CRDA failed - " | 1366 | printk(KERN_ERR "cfg80211: calling CRDA failed - " |
924 | "unable to update world regulatory domain, " | 1367 | "unable to update world regulatory domain, " |
@@ -934,6 +1377,9 @@ void regulatory_exit(void) | |||
934 | 1377 | ||
935 | reset_regdomains(); | 1378 | reset_regdomains(); |
936 | 1379 | ||
1380 | kfree(country_ie_regdomain); | ||
1381 | country_ie_regdomain = NULL; | ||
1382 | |||
937 | kfree(last_request); | 1383 | kfree(last_request); |
938 | 1384 | ||
939 | platform_device_unregister(reg_pdev); | 1385 | platform_device_unregister(reg_pdev); |
diff --git a/net/wireless/reg.h b/net/wireless/reg.h index c9b6b6358bbe..a76ea3ff7cd6 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h | |||
@@ -4,28 +4,41 @@ | |||
4 | bool is_world_regdom(const char *alpha2); | 4 | bool is_world_regdom(const char *alpha2); |
5 | bool reg_is_valid_request(const char *alpha2); | 5 | bool reg_is_valid_request(const char *alpha2); |
6 | 6 | ||
7 | void reg_device_remove(struct wiphy *wiphy); | ||
8 | |||
7 | int regulatory_init(void); | 9 | int regulatory_init(void); |
8 | void regulatory_exit(void); | 10 | void regulatory_exit(void); |
9 | 11 | ||
10 | int set_regdom(const struct ieee80211_regdomain *rd); | 12 | int set_regdom(const struct ieee80211_regdomain *rd); |
11 | 13 | ||
14 | enum environment_cap { | ||
15 | ENVIRON_ANY, | ||
16 | ENVIRON_INDOOR, | ||
17 | ENVIRON_OUTDOOR, | ||
18 | }; | ||
19 | |||
20 | |||
12 | /** | 21 | /** |
13 | * __regulatory_hint - hint to the wireless core a regulatory domain | 22 | * __regulatory_hint - hint to the wireless core a regulatory domain |
14 | * @wiphy: if the hint comes from country information from an AP, this | 23 | * @wiphy: if the hint comes from country information from an AP, this |
15 | * is required to be set to the wiphy that received the information | 24 | * is required to be set to the wiphy that received the information |
16 | * @alpha2: the ISO/IEC 3166 alpha2 being claimed the regulatory domain | 25 | * @alpha2: the ISO/IEC 3166 alpha2 being claimed the regulatory domain |
17 | * should be in. | 26 | * should be in. |
27 | * @country_ie_checksum: checksum of processed country IE, set this to 0 | ||
28 | * if the hint did not come from a country IE | ||
29 | * @country_ie_env: the environment the IE told us we are in, %ENVIRON_* | ||
18 | * | 30 | * |
19 | * The Wireless subsystem can use this function to hint to the wireless core | 31 | * The Wireless subsystem can use this function to hint to the wireless core |
20 | * what it believes should be the current regulatory domain by | 32 | * what it believes should be the current regulatory domain by giving it an |
21 | * giving it an ISO/IEC 3166 alpha2 country code it knows its regulatory | 33 | * ISO/IEC 3166 alpha2 country code it knows its regulatory domain should be |
22 | * domain should be in. | 34 | * in. |
23 | * | 35 | * |
24 | * Returns zero if all went fine, %-EALREADY if a regulatory domain had | 36 | * Returns zero if all went fine, %-EALREADY if a regulatory domain had |
25 | * already been set or other standard error codes. | 37 | * already been set or other standard error codes. |
26 | * | 38 | * |
27 | */ | 39 | */ |
28 | extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | 40 | extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, |
29 | const char *alpha2); | 41 | const char *alpha2, u32 country_ie_checksum, |
42 | enum environment_cap country_ie_env); | ||
30 | 43 | ||
31 | #endif /* __NET_WIRELESS_REG_H */ | 44 | #endif /* __NET_WIRELESS_REG_H */ |