diff options
-rw-r--r-- | include/net/wireless.h | 5 | ||||
-rw-r--r-- | net/wireless/core.c | 5 | ||||
-rw-r--r-- | net/wireless/reg.c | 224 | ||||
-rw-r--r-- | net/wireless/reg.h | 21 | ||||
-rw-r--r-- | net/wireless/scan.c | 3 |
5 files changed, 254 insertions, 4 deletions
diff --git a/include/net/wireless.h b/include/net/wireless.h index 1f4707d18adb..64a76208580c 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h | |||
@@ -69,6 +69,9 @@ enum ieee80211_channel_flags { | |||
69 | * @band: band this channel belongs to. | 69 | * @band: band this channel belongs to. |
70 | * @max_antenna_gain: maximum antenna gain in dBi | 70 | * @max_antenna_gain: maximum antenna gain in dBi |
71 | * @max_power: maximum transmission power (in dBm) | 71 | * @max_power: maximum transmission power (in dBm) |
72 | * @beacon_found: helper to regulatory code to indicate when a beacon | ||
73 | * has been found on this channel. Use regulatory_hint_found_beacon() | ||
74 | * to enable this, this is is useful only on 5 GHz band. | ||
72 | * @orig_mag: internal use | 75 | * @orig_mag: internal use |
73 | * @orig_mpwr: internal use | 76 | * @orig_mpwr: internal use |
74 | */ | 77 | */ |
@@ -80,6 +83,7 @@ struct ieee80211_channel { | |||
80 | u32 flags; | 83 | u32 flags; |
81 | int max_antenna_gain; | 84 | int max_antenna_gain; |
82 | int max_power; | 85 | int max_power; |
86 | bool beacon_found; | ||
83 | u32 orig_flags; | 87 | u32 orig_flags; |
84 | int orig_mag, orig_mpwr; | 88 | int orig_mag, orig_mpwr; |
85 | }; | 89 | }; |
@@ -425,7 +429,6 @@ extern int regulatory_hint(struct wiphy *wiphy, const char *alpha2); | |||
425 | extern void regulatory_hint_11d(struct wiphy *wiphy, | 429 | extern void regulatory_hint_11d(struct wiphy *wiphy, |
426 | u8 *country_ie, | 430 | u8 *country_ie, |
427 | u8 country_ie_len); | 431 | u8 country_ie_len); |
428 | |||
429 | /** | 432 | /** |
430 | * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain | 433 | * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain |
431 | * @wiphy: the wireless device we want to process the regulatory domain on | 434 | * @wiphy: the wireless device we want to process the regulatory domain on |
diff --git a/net/wireless/core.c b/net/wireless/core.c index b1a354b7fc06..dd7f222919fe 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -32,8 +32,9 @@ MODULE_DESCRIPTION("wireless configuration support"); | |||
32 | LIST_HEAD(cfg80211_drv_list); | 32 | LIST_HEAD(cfg80211_drv_list); |
33 | 33 | ||
34 | /* | 34 | /* |
35 | * This is used to protect the cfg80211_drv_list, cfg80211_regdomain, and | 35 | * This is used to protect the cfg80211_drv_list, cfg80211_regdomain, |
36 | * the last reguluatory request receipt in regd.c | 36 | * country_ie_regdomain, the reg_beacon_list and the the last regulatory |
37 | * request receipt (last_request). | ||
37 | */ | 38 | */ |
38 | DEFINE_MUTEX(cfg80211_mutex); | 39 | DEFINE_MUTEX(cfg80211_mutex); |
39 | 40 | ||
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index da2a8aca4280..e5e432d6af34 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -68,9 +68,22 @@ const struct ieee80211_regdomain *cfg80211_regdomain; | |||
68 | */ | 68 | */ |
69 | static const struct ieee80211_regdomain *country_ie_regdomain; | 69 | static const struct ieee80211_regdomain *country_ie_regdomain; |
70 | 70 | ||
71 | /* Used to queue up regulatory hints */ | ||
71 | static LIST_HEAD(reg_requests_list); | 72 | static LIST_HEAD(reg_requests_list); |
72 | static spinlock_t reg_requests_lock; | 73 | static spinlock_t reg_requests_lock; |
73 | 74 | ||
75 | /* Used to queue up beacon hints for review */ | ||
76 | static LIST_HEAD(reg_pending_beacons); | ||
77 | static spinlock_t reg_pending_beacons_lock; | ||
78 | |||
79 | /* Used to keep track of processed beacon hints */ | ||
80 | static LIST_HEAD(reg_beacon_list); | ||
81 | |||
82 | struct reg_beacon { | ||
83 | struct list_head list; | ||
84 | struct ieee80211_channel chan; | ||
85 | }; | ||
86 | |||
74 | /* We keep a static world regulatory domain in case of the absence of CRDA */ | 87 | /* We keep a static world regulatory domain in case of the absence of CRDA */ |
75 | static const struct ieee80211_regdomain world_regdom = { | 88 | static const struct ieee80211_regdomain world_regdom = { |
76 | .n_reg_rules = 3, | 89 | .n_reg_rules = 3, |
@@ -1011,16 +1024,120 @@ static void update_all_wiphy_regulatory(enum reg_set_by setby) | |||
1011 | wiphy_update_regulatory(&drv->wiphy, setby); | 1024 | wiphy_update_regulatory(&drv->wiphy, setby); |
1012 | } | 1025 | } |
1013 | 1026 | ||
1027 | static void handle_reg_beacon(struct wiphy *wiphy, | ||
1028 | unsigned int chan_idx, | ||
1029 | struct reg_beacon *reg_beacon) | ||
1030 | { | ||
1031 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
1032 | #define REG_DEBUG_BEACON_FLAG(desc) \ | ||
1033 | printk(KERN_DEBUG "cfg80211: Enabling " desc " on " \ | ||
1034 | "frequency: %d MHz (Ch %d) on %s\n", \ | ||
1035 | reg_beacon->chan.center_freq, \ | ||
1036 | ieee80211_frequency_to_channel(reg_beacon->chan.center_freq), \ | ||
1037 | wiphy_name(wiphy)); | ||
1038 | #else | ||
1039 | #define REG_DEBUG_BEACON_FLAG(desc) do {} while (0) | ||
1040 | #endif | ||
1041 | struct ieee80211_supported_band *sband; | ||
1042 | struct ieee80211_channel *chan; | ||
1043 | |||
1044 | assert_cfg80211_lock(); | ||
1045 | |||
1046 | sband = wiphy->bands[reg_beacon->chan.band]; | ||
1047 | chan = &sband->channels[chan_idx]; | ||
1048 | |||
1049 | if (likely(chan->center_freq != reg_beacon->chan.center_freq)) | ||
1050 | return; | ||
1051 | |||
1052 | if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) { | ||
1053 | chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; | ||
1054 | REG_DEBUG_BEACON_FLAG("active scanning"); | ||
1055 | } | ||
1056 | |||
1057 | if (chan->flags & IEEE80211_CHAN_NO_IBSS) { | ||
1058 | chan->flags &= ~IEEE80211_CHAN_NO_IBSS; | ||
1059 | REG_DEBUG_BEACON_FLAG("beaconing"); | ||
1060 | } | ||
1061 | |||
1062 | chan->beacon_found = true; | ||
1063 | #undef REG_DEBUG_BEACON_FLAG | ||
1064 | } | ||
1065 | |||
1066 | /* | ||
1067 | * Called when a scan on a wiphy finds a beacon on | ||
1068 | * new channel | ||
1069 | */ | ||
1070 | static void wiphy_update_new_beacon(struct wiphy *wiphy, | ||
1071 | struct reg_beacon *reg_beacon) | ||
1072 | { | ||
1073 | unsigned int i; | ||
1074 | struct ieee80211_supported_band *sband; | ||
1075 | |||
1076 | assert_cfg80211_lock(); | ||
1077 | |||
1078 | if (!wiphy->bands[reg_beacon->chan.band]) | ||
1079 | return; | ||
1080 | |||
1081 | sband = wiphy->bands[reg_beacon->chan.band]; | ||
1082 | |||
1083 | for (i = 0; i < sband->n_channels; i++) | ||
1084 | handle_reg_beacon(wiphy, i, reg_beacon); | ||
1085 | } | ||
1086 | |||
1087 | /* | ||
1088 | * Called upon reg changes or a new wiphy is added | ||
1089 | */ | ||
1090 | static void wiphy_update_beacon_reg(struct wiphy *wiphy) | ||
1091 | { | ||
1092 | unsigned int i; | ||
1093 | struct ieee80211_supported_band *sband; | ||
1094 | struct reg_beacon *reg_beacon; | ||
1095 | |||
1096 | assert_cfg80211_lock(); | ||
1097 | |||
1098 | if (list_empty(®_beacon_list)) | ||
1099 | return; | ||
1100 | |||
1101 | list_for_each_entry(reg_beacon, ®_beacon_list, list) { | ||
1102 | if (!wiphy->bands[reg_beacon->chan.band]) | ||
1103 | continue; | ||
1104 | sband = wiphy->bands[reg_beacon->chan.band]; | ||
1105 | for (i = 0; i < sband->n_channels; i++) | ||
1106 | handle_reg_beacon(wiphy, i, reg_beacon); | ||
1107 | } | ||
1108 | } | ||
1109 | |||
1110 | static bool reg_is_world_roaming(struct wiphy *wiphy) | ||
1111 | { | ||
1112 | if (is_world_regdom(cfg80211_regdomain->alpha2) || | ||
1113 | (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) | ||
1114 | return true; | ||
1115 | if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE && | ||
1116 | wiphy->custom_regulatory) | ||
1117 | return true; | ||
1118 | return false; | ||
1119 | } | ||
1120 | |||
1121 | /* Reap the advantages of previously found beacons */ | ||
1122 | static void reg_process_beacons(struct wiphy *wiphy) | ||
1123 | { | ||
1124 | if (!reg_is_world_roaming(wiphy)) | ||
1125 | return; | ||
1126 | wiphy_update_beacon_reg(wiphy); | ||
1127 | } | ||
1128 | |||
1014 | void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) | 1129 | void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) |
1015 | { | 1130 | { |
1016 | enum ieee80211_band band; | 1131 | enum ieee80211_band band; |
1017 | 1132 | ||
1018 | if (ignore_reg_update(wiphy, setby)) | 1133 | if (ignore_reg_update(wiphy, setby)) |
1019 | return; | 1134 | goto out; |
1020 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 1135 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
1021 | if (wiphy->bands[band]) | 1136 | if (wiphy->bands[band]) |
1022 | handle_band(wiphy, band); | 1137 | handle_band(wiphy, band); |
1023 | } | 1138 | } |
1139 | out: | ||
1140 | reg_process_beacons(wiphy); | ||
1024 | if (wiphy->reg_notifier) | 1141 | if (wiphy->reg_notifier) |
1025 | wiphy->reg_notifier(wiphy, last_request); | 1142 | wiphy->reg_notifier(wiphy, last_request); |
1026 | } | 1143 | } |
@@ -1314,6 +1431,7 @@ out: | |||
1314 | return r; | 1431 | return r; |
1315 | } | 1432 | } |
1316 | 1433 | ||
1434 | /* Processes regulatory hints, this is all the REGDOM_SET_BY_* */ | ||
1317 | static void reg_process_pending_hints(void) | 1435 | static void reg_process_pending_hints(void) |
1318 | { | 1436 | { |
1319 | struct regulatory_request *reg_request; | 1437 | struct regulatory_request *reg_request; |
@@ -1344,9 +1462,44 @@ static void reg_process_pending_hints(void) | |||
1344 | spin_unlock(®_requests_lock); | 1462 | spin_unlock(®_requests_lock); |
1345 | } | 1463 | } |
1346 | 1464 | ||
1465 | /* Processes beacon hints -- this has nothing to do with country IEs */ | ||
1466 | static void reg_process_pending_beacon_hints(void) | ||
1467 | { | ||
1468 | struct cfg80211_registered_device *drv; | ||
1469 | struct reg_beacon *pending_beacon, *tmp; | ||
1470 | |||
1471 | mutex_lock(&cfg80211_mutex); | ||
1472 | |||
1473 | /* This goes through the _pending_ beacon list */ | ||
1474 | spin_lock_bh(®_pending_beacons_lock); | ||
1475 | |||
1476 | if (list_empty(®_pending_beacons)) { | ||
1477 | spin_unlock_bh(®_pending_beacons_lock); | ||
1478 | goto out; | ||
1479 | } | ||
1480 | |||
1481 | list_for_each_entry_safe(pending_beacon, tmp, | ||
1482 | ®_pending_beacons, list) { | ||
1483 | |||
1484 | list_del_init(&pending_beacon->list); | ||
1485 | |||
1486 | /* Applies the beacon hint to current wiphys */ | ||
1487 | list_for_each_entry(drv, &cfg80211_drv_list, list) | ||
1488 | wiphy_update_new_beacon(&drv->wiphy, pending_beacon); | ||
1489 | |||
1490 | /* Remembers the beacon hint for new wiphys or reg changes */ | ||
1491 | list_add_tail(&pending_beacon->list, ®_beacon_list); | ||
1492 | } | ||
1493 | |||
1494 | spin_unlock_bh(®_pending_beacons_lock); | ||
1495 | out: | ||
1496 | mutex_unlock(&cfg80211_mutex); | ||
1497 | } | ||
1498 | |||
1347 | static void reg_todo(struct work_struct *work) | 1499 | static void reg_todo(struct work_struct *work) |
1348 | { | 1500 | { |
1349 | reg_process_pending_hints(); | 1501 | reg_process_pending_hints(); |
1502 | reg_process_pending_beacon_hints(); | ||
1350 | } | 1503 | } |
1351 | 1504 | ||
1352 | static DECLARE_WORK(reg_work, reg_todo); | 1505 | static DECLARE_WORK(reg_work, reg_todo); |
@@ -1587,6 +1740,55 @@ out: | |||
1587 | } | 1740 | } |
1588 | EXPORT_SYMBOL(regulatory_hint_11d); | 1741 | EXPORT_SYMBOL(regulatory_hint_11d); |
1589 | 1742 | ||
1743 | static bool freq_is_chan_12_13_14(u16 freq) | ||
1744 | { | ||
1745 | if (freq == ieee80211_channel_to_frequency(12) || | ||
1746 | freq == ieee80211_channel_to_frequency(13) || | ||
1747 | freq == ieee80211_channel_to_frequency(14)) | ||
1748 | return true; | ||
1749 | return false; | ||
1750 | } | ||
1751 | |||
1752 | int regulatory_hint_found_beacon(struct wiphy *wiphy, | ||
1753 | struct ieee80211_channel *beacon_chan, | ||
1754 | gfp_t gfp) | ||
1755 | { | ||
1756 | struct reg_beacon *reg_beacon; | ||
1757 | |||
1758 | if (likely((beacon_chan->beacon_found || | ||
1759 | (beacon_chan->flags & IEEE80211_CHAN_RADAR) || | ||
1760 | (beacon_chan->band == IEEE80211_BAND_2GHZ && | ||
1761 | !freq_is_chan_12_13_14(beacon_chan->center_freq))))) | ||
1762 | return 0; | ||
1763 | |||
1764 | reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); | ||
1765 | if (!reg_beacon) | ||
1766 | return -ENOMEM; | ||
1767 | |||
1768 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
1769 | printk(KERN_DEBUG "cfg80211: Found new beacon on " | ||
1770 | "frequency: %d MHz (Ch %d) on %s\n", | ||
1771 | beacon_chan->center_freq, | ||
1772 | ieee80211_frequency_to_channel(beacon_chan->center_freq), | ||
1773 | wiphy_name(wiphy)); | ||
1774 | #endif | ||
1775 | memcpy(®_beacon->chan, beacon_chan, | ||
1776 | sizeof(struct ieee80211_channel)); | ||
1777 | |||
1778 | |||
1779 | /* | ||
1780 | * Since we can be called from BH or and non-BH context | ||
1781 | * we must use spin_lock_bh() | ||
1782 | */ | ||
1783 | spin_lock_bh(®_pending_beacons_lock); | ||
1784 | list_add_tail(®_beacon->list, ®_pending_beacons); | ||
1785 | spin_unlock_bh(®_pending_beacons_lock); | ||
1786 | |||
1787 | schedule_work(®_work); | ||
1788 | |||
1789 | return 0; | ||
1790 | } | ||
1791 | |||
1590 | static void print_rd_rules(const struct ieee80211_regdomain *rd) | 1792 | static void print_rd_rules(const struct ieee80211_regdomain *rd) |
1591 | { | 1793 | { |
1592 | unsigned int i; | 1794 | unsigned int i; |
@@ -1908,6 +2110,7 @@ int regulatory_init(void) | |||
1908 | return PTR_ERR(reg_pdev); | 2110 | return PTR_ERR(reg_pdev); |
1909 | 2111 | ||
1910 | spin_lock_init(®_requests_lock); | 2112 | spin_lock_init(®_requests_lock); |
2113 | spin_lock_init(®_pending_beacons_lock); | ||
1911 | 2114 | ||
1912 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | 2115 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY |
1913 | cfg80211_regdomain = static_regdom(ieee80211_regdom); | 2116 | cfg80211_regdomain = static_regdom(ieee80211_regdom); |
@@ -1951,6 +2154,7 @@ int regulatory_init(void) | |||
1951 | void regulatory_exit(void) | 2154 | void regulatory_exit(void) |
1952 | { | 2155 | { |
1953 | struct regulatory_request *reg_request, *tmp; | 2156 | struct regulatory_request *reg_request, *tmp; |
2157 | struct reg_beacon *reg_beacon, *btmp; | ||
1954 | 2158 | ||
1955 | cancel_work_sync(®_work); | 2159 | cancel_work_sync(®_work); |
1956 | 2160 | ||
@@ -1965,6 +2169,24 @@ void regulatory_exit(void) | |||
1965 | 2169 | ||
1966 | platform_device_unregister(reg_pdev); | 2170 | platform_device_unregister(reg_pdev); |
1967 | 2171 | ||
2172 | spin_lock_bh(®_pending_beacons_lock); | ||
2173 | if (!list_empty(®_pending_beacons)) { | ||
2174 | list_for_each_entry_safe(reg_beacon, btmp, | ||
2175 | ®_pending_beacons, list) { | ||
2176 | list_del(®_beacon->list); | ||
2177 | kfree(reg_beacon); | ||
2178 | } | ||
2179 | } | ||
2180 | spin_unlock_bh(®_pending_beacons_lock); | ||
2181 | |||
2182 | if (!list_empty(®_beacon_list)) { | ||
2183 | list_for_each_entry_safe(reg_beacon, btmp, | ||
2184 | ®_beacon_list, list) { | ||
2185 | list_del(®_beacon->list); | ||
2186 | kfree(reg_beacon); | ||
2187 | } | ||
2188 | } | ||
2189 | |||
1968 | spin_lock(®_requests_lock); | 2190 | spin_lock(®_requests_lock); |
1969 | if (!list_empty(®_requests_list)) { | 2191 | if (!list_empty(®_requests_list)) { |
1970 | list_for_each_entry_safe(reg_request, tmp, | 2192 | list_for_each_entry_safe(reg_request, tmp, |
diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 4730def5a69d..65bfd0558ce1 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h | |||
@@ -38,4 +38,25 @@ extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
38 | const char *alpha2, u32 country_ie_checksum, | 38 | const char *alpha2, u32 country_ie_checksum, |
39 | enum environment_cap country_ie_env); | 39 | enum environment_cap country_ie_env); |
40 | 40 | ||
41 | /** | ||
42 | * regulatory_hint_found_beacon - hints a beacon was found on a channel | ||
43 | * @wiphy: the wireless device where the beacon was found on | ||
44 | * @beacon_chan: the channel on which the beacon was found on | ||
45 | * @gfp: context flags | ||
46 | * | ||
47 | * This informs the wireless core that a beacon from an AP was found on | ||
48 | * the channel provided. This allows the wireless core to make educated | ||
49 | * guesses on regulatory to help with world roaming. This is only used for | ||
50 | * world roaming -- when we do not know our current location. This is | ||
51 | * only useful on channels 12, 13 and 14 on the 2 GHz band as channels | ||
52 | * 1-11 are already enabled by the world regulatory domain; and on | ||
53 | * non-radar 5 GHz channels. | ||
54 | * | ||
55 | * Drivers do not need to call this, cfg80211 will do it for after a scan | ||
56 | * on a newly found BSS. | ||
57 | */ | ||
58 | int regulatory_hint_found_beacon(struct wiphy *wiphy, | ||
59 | struct ieee80211_channel *beacon_chan, | ||
60 | gfp_t gfp); | ||
61 | |||
41 | #endif /* __NET_WIRELESS_REG_H */ | 62 | #endif /* __NET_WIRELESS_REG_H */ |
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 60600657b657..280dbcd02c15 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -430,6 +430,9 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
430 | if (!res) | 430 | if (!res) |
431 | return NULL; | 431 | return NULL; |
432 | 432 | ||
433 | if (res->pub.capability & WLAN_CAPABILITY_ESS) | ||
434 | regulatory_hint_found_beacon(wiphy, channel, gfp); | ||
435 | |||
433 | /* cfg80211_bss_update gives us a referenced result */ | 436 | /* cfg80211_bss_update gives us a referenced result */ |
434 | return &res->pub; | 437 | return &res->pub; |
435 | } | 438 | } |