| 1 | --- a/include/net/mac80211.h |
| 2 | +++ b/include/net/mac80211.h |
| 3 | @@ -851,6 +851,7 @@ enum ieee80211_smps_mode { |
| 4 | * the CONF_PS flag is set. |
| 5 | * |
| 6 | * @power_level: requested transmit power (in dBm) |
| 7 | + * @max_antenna_gain: maximum antenna gain adjusted by user config (in dBi) |
| 8 | * |
| 9 | * @channel: the channel to tune to |
| 10 | * @channel_type: the channel (HT) type |
| 11 | @@ -870,6 +871,7 @@ struct ieee80211_conf { |
| 12 | u32 flags; |
| 13 | int power_level, dynamic_ps_timeout; |
| 14 | int max_sleep_period; |
| 15 | + int max_antenna_gain; |
| 16 | |
| 17 | u16 listen_interval; |
| 18 | u8 ps_dtim_period; |
| 19 | --- a/net/mac80211/main.c |
| 20 | +++ b/net/mac80211/main.c |
| 21 | @@ -101,7 +101,7 @@ int ieee80211_hw_config(struct ieee80211 |
| 22 | { |
| 23 | struct ieee80211_channel *chan; |
| 24 | int ret = 0; |
| 25 | - int power; |
| 26 | + int power, ant_gain, max_power; |
| 27 | enum nl80211_channel_type channel_type; |
| 28 | u32 offchannel_flag; |
| 29 | |
| 30 | @@ -152,19 +152,31 @@ int ieee80211_hw_config(struct ieee80211 |
| 31 | changed |= IEEE80211_CONF_CHANGE_SMPS; |
| 32 | } |
| 33 | |
| 34 | - if (test_bit(SCAN_SW_SCANNING, &local->scanning) || |
| 35 | - test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) || |
| 36 | - test_bit(SCAN_HW_SCANNING, &local->scanning) || |
| 37 | - !local->ap_power_level) |
| 38 | - power = chan->max_power; |
| 39 | - else |
| 40 | - power = min(chan->max_power, local->ap_power_level); |
| 41 | + max_power = chan->max_reg_power; |
| 42 | + if (!test_bit(SCAN_SW_SCANNING, &local->scanning) && |
| 43 | + !test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) && |
| 44 | + !test_bit(SCAN_HW_SCANNING, &local->scanning) && |
| 45 | + local->ap_power_level) |
| 46 | + max_power = min(max_power, local->ap_power_level); |
| 47 | + |
| 48 | + ant_gain = chan->max_antenna_gain; |
| 49 | + if (local->user_antenna_gain > 0) { |
| 50 | + if (local->user_antenna_gain > ant_gain) { |
| 51 | + max_power -= local->user_antenna_gain - ant_gain; |
| 52 | + ant_gain = 0; |
| 53 | + } else |
| 54 | + ant_gain -= local->user_antenna_gain; |
| 55 | + } |
| 56 | + |
| 57 | + power = min(chan->max_power, max_power); |
| 58 | |
| 59 | if (local->user_power_level >= 0) |
| 60 | power = min(power, local->user_power_level); |
| 61 | |
| 62 | - if (local->hw.conf.power_level != power) { |
| 63 | + if (local->hw.conf.power_level != power || |
| 64 | + local->hw.conf.max_antenna_gain != ant_gain) { |
| 65 | changed |= IEEE80211_CONF_CHANGE_POWER; |
| 66 | + local->hw.conf.max_antenna_gain = ant_gain; |
| 67 | local->hw.cur_power_level = power; |
| 68 | local->hw.conf.power_level = power; |
| 69 | } |
| 70 | @@ -620,6 +632,7 @@ struct ieee80211_hw *ieee80211_alloc_hw( |
| 71 | IEEE80211_RADIOTAP_MCS_HAVE_GI | |
| 72 | IEEE80211_RADIOTAP_MCS_HAVE_BW; |
| 73 | local->user_power_level = -1; |
| 74 | + local->user_antenna_gain = -1; |
| 75 | wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; |
| 76 | |
| 77 | INIT_LIST_HEAD(&local->interfaces); |
| 78 | --- a/net/mac80211/ieee80211_i.h |
| 79 | +++ b/net/mac80211/ieee80211_i.h |
| 80 | @@ -1063,6 +1063,7 @@ struct ieee80211_local { |
| 81 | |
| 82 | int user_power_level; /* in dBm */ |
| 83 | int ap_power_level; /* in dBm */ |
| 84 | + int user_antenna_gain; /* in dBi */ |
| 85 | |
| 86 | enum ieee80211_smps_mode smps_mode; |
| 87 | |
| 88 | --- a/include/linux/nl80211.h |
| 89 | +++ b/include/linux/nl80211.h |
| 90 | @@ -1517,6 +1517,8 @@ enum nl80211_attrs { |
| 91 | |
| 92 | NL80211_ATTR_USER_REG_HINT_TYPE, |
| 93 | |
| 94 | + NL80211_ATTR_WIPHY_ANTENNA_GAIN, |
| 95 | + |
| 96 | /* add attributes here, update the policy in nl80211.c */ |
| 97 | |
| 98 | __NL80211_ATTR_AFTER_LAST, |
| 99 | --- a/net/wireless/nl80211.c |
| 100 | +++ b/net/wireless/nl80211.c |
| 101 | @@ -355,6 +355,7 @@ static const struct nla_policy nl80211_p |
| 102 | [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, |
| 103 | [NL80211_ATTR_WDEV] = { .type = NLA_U64 }, |
| 104 | [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 }, |
| 105 | + [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 }, |
| 106 | }; |
| 107 | |
| 108 | /* policy for the key attributes */ |
| 109 | @@ -1604,6 +1605,22 @@ static int nl80211_set_wiphy(struct sk_b |
| 110 | goto bad_res; |
| 111 | } |
| 112 | |
| 113 | + if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_GAIN]) { |
| 114 | + int idx, dbi = 0; |
| 115 | + |
| 116 | + if (!rdev->ops->set_antenna_gain) { |
| 117 | + result = -EOPNOTSUPP; |
| 118 | + goto bad_res; |
| 119 | + } |
| 120 | + |
| 121 | + idx = NL80211_ATTR_WIPHY_ANTENNA_GAIN; |
| 122 | + dbi = nla_get_u32(info->attrs[idx]); |
| 123 | + |
| 124 | + result = rdev->ops->set_antenna_gain(&rdev->wiphy, dbi); |
| 125 | + if (result) |
| 126 | + goto bad_res; |
| 127 | + } |
| 128 | + |
| 129 | if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] && |
| 130 | info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) { |
| 131 | u32 tx_ant, rx_ant; |
| 132 | --- a/net/mac80211/cfg.c |
| 133 | +++ b/net/mac80211/cfg.c |
| 134 | @@ -1984,6 +1984,19 @@ static int ieee80211_get_tx_power(struct |
| 135 | return 0; |
| 136 | } |
| 137 | |
| 138 | +static int ieee80211_set_antenna_gain(struct wiphy *wiphy, int dbi) |
| 139 | +{ |
| 140 | + struct ieee80211_local *local = wiphy_priv(wiphy); |
| 141 | + |
| 142 | + if (dbi < 0) |
| 143 | + return -EINVAL; |
| 144 | + |
| 145 | + local->user_antenna_gain = dbi; |
| 146 | + ieee80211_hw_config(local, 0); |
| 147 | + |
| 148 | + return 0; |
| 149 | +} |
| 150 | + |
| 151 | static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev, |
| 152 | const u8 *addr) |
| 153 | { |
| 154 | @@ -3082,6 +3095,7 @@ struct cfg80211_ops mac80211_config_ops |
| 155 | .set_wiphy_params = ieee80211_set_wiphy_params, |
| 156 | .set_tx_power = ieee80211_set_tx_power, |
| 157 | .get_tx_power = ieee80211_get_tx_power, |
| 158 | + .set_antenna_gain = ieee80211_set_antenna_gain, |
| 159 | .set_wds_peer = ieee80211_set_wds_peer, |
| 160 | .rfkill_poll = ieee80211_rfkill_poll, |
| 161 | CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) |
| 162 | --- a/include/net/cfg80211.h |
| 163 | +++ b/include/net/cfg80211.h |
| 164 | @@ -1551,6 +1551,7 @@ struct cfg80211_gtk_rekey_data { |
| 165 | * the power passed is in mBm, to get dBm use MBM_TO_DBM(). |
| 166 | * @get_tx_power: store the current TX power into the dbm variable; |
| 167 | * return 0 if successful |
| 168 | + * @set_antenna_gain: set antenna gain to reduce maximum tx power if necessary |
| 169 | * |
| 170 | * @set_wds_peer: set the WDS peer for a WDS interface |
| 171 | * |
| 172 | @@ -1750,6 +1751,7 @@ struct cfg80211_ops { |
| 173 | int (*set_tx_power)(struct wiphy *wiphy, |
| 174 | enum nl80211_tx_power_setting type, int mbm); |
| 175 | int (*get_tx_power)(struct wiphy *wiphy, int *dbm); |
| 176 | + int (*set_antenna_gain)(struct wiphy *wiphy, int dbi); |
| 177 | |
| 178 | int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev, |
| 179 | const u8 *addr); |
| 180 | |