|
@@ -1230,14 +1230,30 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
|
return have_chan_pwr;
|
|
return have_chan_pwr;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void ieee80211_find_cisco_dtpc(struct ieee80211_sub_if_data *sdata,
|
|
|
|
+ struct ieee80211_channel *channel,
|
|
|
|
+ const u8 *cisco_dtpc_ie,
|
|
|
|
+ int *pwr_level)
|
|
|
|
+{
|
|
|
|
+ /* From practical testing, the first data byte of the DTPC element
|
|
|
|
+ * seems to contain the requested dBm level, and the CLI on Cisco
|
|
|
|
+ * APs clearly state the range is -127 to 127 dBm, which indicates
|
|
|
|
+ * a signed byte, although it seemingly never actually goes negative.
|
|
|
|
+ * The other byte seems to always be zero.
|
|
|
|
+ */
|
|
|
|
+ *pwr_level = (__s8)cisco_dtpc_ie[4];
|
|
|
|
+}
|
|
|
|
+
|
|
static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
|
static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
|
struct ieee80211_channel *channel,
|
|
struct ieee80211_channel *channel,
|
|
struct ieee80211_mgmt *mgmt,
|
|
struct ieee80211_mgmt *mgmt,
|
|
const u8 *country_ie, u8 country_ie_len,
|
|
const u8 *country_ie, u8 country_ie_len,
|
|
- const u8 *pwr_constr_ie)
|
|
|
|
|
|
+ const u8 *pwr_constr_ie,
|
|
|
|
+ const u8 *cisco_dtpc_ie)
|
|
{
|
|
{
|
|
- bool has_80211h_pwr = false;
|
|
|
|
- int chan_pwr, pwr_reduction_80211h;
|
|
|
|
|
|
+ bool has_80211h_pwr = false, has_cisco_pwr = false;
|
|
|
|
+ int chan_pwr = 0, pwr_reduction_80211h = 0;
|
|
|
|
+ int pwr_level_cisco, pwr_level_80211h;
|
|
int new_ap_level;
|
|
int new_ap_level;
|
|
|
|
|
|
if (country_ie && pwr_constr_ie &&
|
|
if (country_ie && pwr_constr_ie &&
|
|
@@ -1246,16 +1262,35 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
|
has_80211h_pwr = ieee80211_find_80211h_pwr_constr(
|
|
has_80211h_pwr = ieee80211_find_80211h_pwr_constr(
|
|
sdata, channel, country_ie, country_ie_len,
|
|
sdata, channel, country_ie, country_ie_len,
|
|
pwr_constr_ie, &chan_pwr, &pwr_reduction_80211h);
|
|
pwr_constr_ie, &chan_pwr, &pwr_reduction_80211h);
|
|
- new_ap_level = max_t(int, 0, chan_pwr - pwr_reduction_80211h);
|
|
|
|
|
|
+ pwr_level_80211h =
|
|
|
|
+ max_t(int, 0, chan_pwr - pwr_reduction_80211h);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (cisco_dtpc_ie) {
|
|
|
|
+ ieee80211_find_cisco_dtpc(
|
|
|
|
+ sdata, channel, cisco_dtpc_ie, &pwr_level_cisco);
|
|
|
|
+ has_cisco_pwr = true;
|
|
}
|
|
}
|
|
|
|
|
|
- if (!has_80211h_pwr)
|
|
|
|
|
|
+ if (!has_80211h_pwr && !has_cisco_pwr)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- sdata_info(sdata,
|
|
|
|
- "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
|
|
|
|
- new_ap_level, chan_pwr, pwr_reduction_80211h,
|
|
|
|
- sdata->u.mgd.bssid);
|
|
|
|
|
|
+ /* If we have both 802.11h and Cisco DTPC, apply both limits
|
|
|
|
+ * by picking the smallest of the two power levels advertised.
|
|
|
|
+ */
|
|
|
|
+ if (has_80211h_pwr &&
|
|
|
|
+ (!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) {
|
|
|
|
+ sdata_info(sdata,
|
|
|
|
+ "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
|
|
|
|
+ pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
|
|
|
|
+ sdata->u.mgd.bssid);
|
|
|
|
+ new_ap_level = pwr_level_80211h;
|
|
|
|
+ } else { /* has_cisco_pwr is always true here. */
|
|
|
|
+ sdata_info(sdata,
|
|
|
|
+ "Limiting TX power to %d dBm as advertised by %pM\n",
|
|
|
|
+ pwr_level_cisco, sdata->u.mgd.bssid);
|
|
|
|
+ new_ap_level = pwr_level_cisco;
|
|
|
|
+ }
|
|
|
|
|
|
if (sdata->ap_power_level == new_ap_level)
|
|
if (sdata->ap_power_level == new_ap_level)
|
|
return 0;
|
|
return 0;
|
|
@@ -2923,7 +2958,9 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
|
|
/*
|
|
/*
|
|
* This is the canonical list of information elements we care about,
|
|
* This is the canonical list of information elements we care about,
|
|
* the filter code also gives us all changes to the Microsoft OUI
|
|
* the filter code also gives us all changes to the Microsoft OUI
|
|
- * (00:50:F2) vendor IE which is used for WMM which we need to track.
|
|
|
|
|
|
+ * (00:50:F2) vendor IE which is used for WMM which we need to track,
|
|
|
|
+ * as well as the DTPC IE (part of the Cisco OUI) used for signaling
|
|
|
|
+ * changes to requested client power.
|
|
*
|
|
*
|
|
* We implement beacon filtering in software since that means we can
|
|
* We implement beacon filtering in software since that means we can
|
|
* avoid processing the frame here and in cfg80211, and userspace
|
|
* avoid processing the frame here and in cfg80211, and userspace
|
|
@@ -3232,7 +3269,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
changed |= ieee80211_handle_pwr_constr(sdata, chan, mgmt,
|
|
changed |= ieee80211_handle_pwr_constr(sdata, chan, mgmt,
|
|
elems.country_elem,
|
|
elems.country_elem,
|
|
elems.country_elem_len,
|
|
elems.country_elem_len,
|
|
- elems.pwr_constr_elem);
|
|
|
|
|
|
+ elems.pwr_constr_elem,
|
|
|
|
+ elems.cisco_dtpc_elem);
|
|
|
|
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
|
}
|
|
}
|