summaryrefslogtreecommitdiffstats
path: root/package/mac80211/patches/524-mac80211_configure_antenna_gain.patch
blob: b3b4ebb2f38db9aab5c9b5d490564640ea0b16ec (plain)
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
173
174
175
176
177
178
179
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -851,6 +851,7 @@ enum ieee80211_smps_mode {
  *	the CONF_PS flag is set.
  *
  * @power_level: requested transmit power (in dBm)
+ * @max_antenna_gain: maximum antenna gain adjusted by user config (in dBi)
  *
  * @channel: the channel to tune to
  * @channel_type: the channel (HT) type
@@ -870,6 +871,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/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -101,7 +101,7 @@ int ieee80211_hw_config(struct ieee80211
 {
 	struct ieee80211_channel *chan;
 	int ret = 0;
-	int power;
+	int power, ant_gain, max_power;
 	enum nl80211_channel_type channel_type;
 	u32 offchannel_flag;
 
@@ -152,19 +152,31 @@ int ieee80211_hw_config(struct ieee80211
 		changed |= IEEE80211_CONF_CHANGE_SMPS;
 	}
 
-	if (test_bit(SCAN_SW_SCANNING, &local->scanning) ||
-	    test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
-	    test_bit(SCAN_HW_SCANNING, &local->scanning) ||
-	    !local->ap_power_level)
-		power = chan->max_power;
-	else
-		power = min(chan->max_power, local->ap_power_level);
+	max_power = chan->max_reg_power;
+	if (!test_bit(SCAN_SW_SCANNING, &local->scanning) &&
+	    !test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) &&
+	    !test_bit(SCAN_HW_SCANNING, &local->scanning) &&
+	    local->ap_power_level)
+		max_power = min(max_power, local->ap_power_level);
+
+	ant_gain = 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(chan->max_power, max_power);
 
 	if (local->user_power_level >= 0)
 		power = min(power, local->user_power_level);
 
-	if (local->hw.conf.power_level != 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;
 	}
@@ -620,6 +632,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(
 					 IEEE80211_RADIOTAP_MCS_HAVE_GI |
 					 IEEE80211_RADIOTAP_MCS_HAVE_BW;
 	local->user_power_level = -1;
+	local->user_antenna_gain = -1;
 	wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
 
 	INIT_LIST_HEAD(&local->interfaces);
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1063,6 +1063,7 @@ struct ieee80211_local {
 
 	int user_power_level; /* in dBm */
 	int ap_power_level; /* in dBm */
+	int user_antenna_gain; /* in dBi */
 
 	enum ieee80211_smps_mode smps_mode;
 
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -1517,6 +1517,8 @@ enum nl80211_attrs {
 
 	NL80211_ATTR_USER_REG_HINT_TYPE,
 
+	NL80211_ATTR_WIPHY_ANTENNA_GAIN,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -355,6 +355,7 @@ static const struct nla_policy nl80211_p
 	[NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
 	[NL80211_ATTR_WDEV] = { .type = NLA_U64 },
 	[NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 },
+	[NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 },
 };
 
 /* policy for the key attributes */
@@ -1604,6 +1605,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;
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1984,6 +1984,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)
 {
@@ -3082,6 +3095,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/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1552,6 +1552,7 @@ struct cfg80211_gtk_rekey_data {
  *	the power passed is in mBm, to get dBm use MBM_TO_DBM().
  * @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
  *
@@ -1751,6 +1752,7 @@ struct cfg80211_ops {
 	int	(*set_tx_power)(struct wiphy *wiphy,
 				enum nl80211_tx_power_setting type, int mbm);
 	int	(*get_tx_power)(struct wiphy *wiphy, 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);