1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2156,6 +2156,7 @@ struct cfg80211_qos_map {
* (as advertised by the nl80211 feature flag.)
* @get_tx_power: store the current TX power into the dbm variable;
* return 0 if successful
+ * @set_antenna_gain: set antenna gain to reduce maximum tx power if necessary
*
* @set_wds_peer: set the WDS peer for a WDS interface
*
@@ -2380,6 +2381,7 @@ struct cfg80211_ops {
enum nl80211_tx_power_setting type, int mbm);
int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
int *dbm);
+ int (*set_antenna_gain)(struct wiphy *wiphy, int dbi);
int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev,
const u8 *addr);
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1033,6 +1033,7 @@ enum ieee80211_smps_mode {
*
* @power_level: requested transmit power (in dBm), backward compatibility
* value only that is set to the minimum of all interfaces
+ * @max_antenna_gain: maximum antenna gain adjusted by user config (in dBi)
*
* @chandef: the channel definition to tune to
* @radar_enabled: whether radar detection is enabled
@@ -1054,6 +1055,7 @@ struct ieee80211_conf {
u32 flags;
int power_level, dynamic_ps_timeout;
int max_sleep_period;
+ int max_antenna_gain;
u16 listen_interval;
u8 ps_dtim_period;
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1555,6 +1555,9 @@ enum nl80211_commands {
* data is in the format defined for the payload of the QoS Map Set element
* in IEEE Std 802.11-2012, 8.4.2.97.
*
+ * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
+ * transmit power to stay within regulatory limits. u32, dBi.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1883,6 +1886,8 @@ enum nl80211_attrs {
NL80211_ATTR_QOS_MAP,
+ NL80211_ATTR_WIPHY_ANTENNA_GAIN,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2336,6 +2336,19 @@ static int ieee80211_get_tx_power(struct
return 0;
}
+static int ieee80211_set_antenna_gain(struct wiphy *wiphy, int dbi)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+
+ if (dbi < 0)
+ return -EINVAL;
+
+ local->user_antenna_gain = dbi;
+ ieee80211_hw_config(local, 0);
+
+ return 0;
+}
+
static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev,
const u8 *addr)
{
@@ -3921,6 +3934,7 @@ struct cfg80211_ops mac80211_config_ops
.set_wiphy_params = ieee80211_set_wiphy_params,
.set_tx_power = ieee80211_set_tx_power,
.get_tx_power = ieee80211_get_tx_power,
+ .set_antenna_gain = ieee80211_set_antenna_gain,
.set_wds_peer = ieee80211_set_wds_peer,
.rfkill_poll = ieee80211_rfkill_poll,
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1221,6 +1221,7 @@ struct ieee80211_local {
int dynamic_ps_forced_timeout;
int user_power_level; /* in dBm, for all interfaces */
+ int user_antenna_gain; /* in dBi */
enum ieee80211_smps_mode smps_mode;
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -101,7 +101,7 @@ static u32 ieee80211_hw_conf_chan(struct
struct ieee80211_sub_if_data *sdata;
struct cfg80211_chan_def chandef = {};
u32 changed = 0;
- int power;
+ int power, ant_gain, max_power;
u32 offchannel_flag;
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
@@ -156,8 +156,21 @@ static u32 ieee80211_hw_conf_chan(struct
}
rcu_read_unlock();
- if (local->hw.conf.power_level != power) {
+ max_power = chandef.chan->max_reg_power;
+ ant_gain = chandef.chan->max_antenna_gain;
+ if (local->user_antenna_gain > 0) {
+ if (local->user_antenna_gain > ant_gain) {
+ max_power -= local->user_antenna_gain - ant_gain;
+ ant_gain = 0;
+ } else
+ ant_gain -= local->user_antenna_gain;
+ power = min(power, max_power);
+ }
+
+ if (local->hw.conf.power_level != power ||
+ local->hw.conf.max_antenna_gain != ant_gain) {
changed |= IEEE80211_CONF_CHANGE_POWER;
+ local->hw.conf.max_antenna_gain = ant_gain;
local->hw.cur_power_level = power;
local->hw.conf.power_level = power;
}
@@ -584,6 +597,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(
IEEE80211_RADIOTAP_MCS_HAVE_BW;
local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+ local->user_antenna_gain = 0;
local->hw.uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES;
local->hw.uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN;
local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -384,6 +384,7 @@ static const struct nla_policy nl80211_p
[NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
[NL80211_ATTR_QOS_MAP] = { .type = NLA_BINARY,
.len = IEEE80211_QOS_MAP_LEN_MAX },
+ [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 },
};
/* policy for the key attributes */
@@ -2105,6 +2106,22 @@ static int nl80211_set_wiphy(struct sk_b
goto bad_res;
}
+ if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_GAIN]) {
+ int idx, dbi = 0;
+
+ if (!rdev->ops->set_antenna_gain) {
+ result = -EOPNOTSUPP;
+ goto bad_res;
+ }
+
+ idx = NL80211_ATTR_WIPHY_ANTENNA_GAIN;
+ dbi = nla_get_u32(info->attrs[idx]);
+
+ result = rdev->ops->set_antenna_gain(&rdev->wiphy, dbi);
+ if (result)
+ goto bad_res;
+ }
+
if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
u32 tx_ant, rx_ant;
|