aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorLuis R. Rodriguez <lrodriguez@atheros.com>2009-02-21 00:20:39 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-02-27 14:52:59 -0500
commite38f8a7a8bebbab9d97f204e2cf05ef58b048a1d (patch)
tree6b0e9f6ce18eb6e2e6d1b29db3538399d0735b70 /net/wireless
parent3fc71f775af677f640f0f0780b16f1b0958f6d9d (diff)
cfg80211: Add AP beacon regulatory hints
When devices are world roaming they cannot beacon or do active scan on 5 GHz or on channels 12, 13 and 14 on the 2 GHz band. Although we have a good regulatory API some cards may _always_ world roam, this is also true when a system does not have CRDA present. Devices doing world roaming can still passive scan, if they find a beacon from an AP on one of the world roaming frequencies we make the assumption we can do the same and we also remove the passive scan requirement. This adds support for providing beacon regulatory hints based on scans. This works for devices that do either hardware or software scanning. If a channel has not yet been marked as having had a beacon present on it we queue the beacon hint processing into the workqueue. All wireless devices will benefit from beacon regulatory hints from any wireless device on a system including new devices connected to the system at a later time. Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/core.c5
-rw-r--r--net/wireless/reg.c224
-rw-r--r--net/wireless/reg.h21
-rw-r--r--net/wireless/scan.c3
4 files changed, 250 insertions, 3 deletions
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");
32LIST_HEAD(cfg80211_drv_list); 32LIST_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 */
38DEFINE_MUTEX(cfg80211_mutex); 39DEFINE_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 */
69static const struct ieee80211_regdomain *country_ie_regdomain; 69static const struct ieee80211_regdomain *country_ie_regdomain;
70 70
71/* Used to queue up regulatory hints */
71static LIST_HEAD(reg_requests_list); 72static LIST_HEAD(reg_requests_list);
72static spinlock_t reg_requests_lock; 73static spinlock_t reg_requests_lock;
73 74
75/* Used to queue up beacon hints for review */
76static LIST_HEAD(reg_pending_beacons);
77static spinlock_t reg_pending_beacons_lock;
78
79/* Used to keep track of processed beacon hints */
80static LIST_HEAD(reg_beacon_list);
81
82struct 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 */
75static const struct ieee80211_regdomain world_regdom = { 88static 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
1027static 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 */
1070static 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 */
1090static 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(&reg_beacon_list))
1099 return;
1100
1101 list_for_each_entry(reg_beacon, &reg_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
1110static 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 */
1122static 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
1014void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) 1129void 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 }
1139out:
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_* */
1317static void reg_process_pending_hints(void) 1435static 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(&reg_requests_lock); 1462 spin_unlock(&reg_requests_lock);
1345} 1463}
1346 1464
1465/* Processes beacon hints -- this has nothing to do with country IEs */
1466static 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(&reg_pending_beacons_lock);
1475
1476 if (list_empty(&reg_pending_beacons)) {
1477 spin_unlock_bh(&reg_pending_beacons_lock);
1478 goto out;
1479 }
1480
1481 list_for_each_entry_safe(pending_beacon, tmp,
1482 &reg_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, &reg_beacon_list);
1492 }
1493
1494 spin_unlock_bh(&reg_pending_beacons_lock);
1495out:
1496 mutex_unlock(&cfg80211_mutex);
1497}
1498
1347static void reg_todo(struct work_struct *work) 1499static 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
1352static DECLARE_WORK(reg_work, reg_todo); 1505static DECLARE_WORK(reg_work, reg_todo);
@@ -1587,6 +1740,55 @@ out:
1587} 1740}
1588EXPORT_SYMBOL(regulatory_hint_11d); 1741EXPORT_SYMBOL(regulatory_hint_11d);
1589 1742
1743static 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
1752int 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(&reg_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(&reg_pending_beacons_lock);
1784 list_add_tail(&reg_beacon->list, &reg_pending_beacons);
1785 spin_unlock_bh(&reg_pending_beacons_lock);
1786
1787 schedule_work(&reg_work);
1788
1789 return 0;
1790}
1791
1590static void print_rd_rules(const struct ieee80211_regdomain *rd) 1792static 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(&reg_requests_lock); 2112 spin_lock_init(&reg_requests_lock);
2113 spin_lock_init(&reg_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)
1951void regulatory_exit(void) 2154void 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(&reg_work); 2159 cancel_work_sync(&reg_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(&reg_pending_beacons_lock);
2173 if (!list_empty(&reg_pending_beacons)) {
2174 list_for_each_entry_safe(reg_beacon, btmp,
2175 &reg_pending_beacons, list) {
2176 list_del(&reg_beacon->list);
2177 kfree(reg_beacon);
2178 }
2179 }
2180 spin_unlock_bh(&reg_pending_beacons_lock);
2181
2182 if (!list_empty(&reg_beacon_list)) {
2183 list_for_each_entry_safe(reg_beacon, btmp,
2184 &reg_beacon_list, list) {
2185 list_del(&reg_beacon->list);
2186 kfree(reg_beacon);
2187 }
2188 }
2189
1968 spin_lock(&reg_requests_lock); 2190 spin_lock(&reg_requests_lock);
1969 if (!list_empty(&reg_requests_list)) { 2191 if (!list_empty(&reg_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 */
58int 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}