mac80211: fix antenna gain and add support for user-defined antenna gain configuration
[openwrt/openwrt.git] / package / mac80211 / patches / 524-mac80211_configure_antenna_gain.patch
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 @@ -1986,6 +1986,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 @@ -3084,6 +3097,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);