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
180
181
182
|
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1901,6 +1901,10 @@ struct hostapd_config * hostapd_config_r
"ht_capab", line);
errors++;
}
+ } else if (os_strcmp(buf, "dynamic_ht40") == 0) {
+ conf->dynamic_ht40 = atoi(pos);
+ if (conf->dynamic_ht40 == 1)
+ conf->dynamic_ht40 = 1500;
} else if (os_strcmp(buf, "require_ht") == 0) {
conf->require_ht = atoi(pos);
#endif /* CONFIG_IEEE80211N */
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -393,6 +393,7 @@ struct hostapd_config {
int ieee80211n;
int secondary_channel;
int require_ht;
+ int dynamic_ht40;
};
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -285,6 +285,7 @@ static void hostapd_cleanup_iface_pre(st
*/
static void hostapd_cleanup_iface(struct hostapd_iface *iface)
{
+ hostapd_deinit_ht(iface);
hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
iface->hw_features = NULL;
os_free(iface->current_rates);
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -220,6 +220,9 @@ struct hostapd_iface {
/* Overlapping BSS information */
int olbc_ht;
+ int force_20mhz;
+ struct os_time last_20mhz_trigger;
+
u16 ht_op_mode;
void (*scan_cb)(struct hostapd_iface *iface);
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1242,6 +1242,9 @@ static void handle_beacon(struct hostapd
sizeof(mgmt->u.beacon)), &elems,
0);
+ if (!elems.ht_capabilities)
+ hostapd_trigger_20mhz(hapd->iface);
+
ap_list_process_beacon(hapd->iface, mgmt, &elems, fi);
}
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -65,4 +65,17 @@ void hostapd_tx_status(struct hostapd_da
void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
int wds);
+#ifdef CONFIG_IEEE80211N
+void hostapd_trigger_20mhz(struct hostapd_iface *iface);
+void hostapd_deinit_ht(struct hostapd_iface *iface);
+
+#else
+static inline void hostapd_deinit_ht(struct hostapd_iface *iface)
+{
+}
+static inline void hostapd_trigger_20mhz(struct hostapd_iface *iface)
+{
+}
+#endif /* CONFIG_IEEE80211N */
+
#endif /* IEEE802_11_H */
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -70,12 +70,15 @@ u8 * hostapd_eid_ht_operation(struct hos
oper->control_chan = hapd->iconf->channel;
oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
- if (hapd->iconf->secondary_channel == 1)
- oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
- HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
- if (hapd->iconf->secondary_channel == -1)
- oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
- HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
+
+ if (!hapd->iface->force_20mhz) {
+ if (hapd->iconf->secondary_channel == 1)
+ oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
+ HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
+ if (hapd->iconf->secondary_channel == -1)
+ oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
+ HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
+ }
pos += sizeof(*oper);
@@ -265,3 +268,80 @@ void hostapd_get_ht_capab(struct hostapd
neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
}
+
+static int hostapd_set_force_20mhz(struct hostapd_iface *iface);
+
+static int hostapd_restore_40mhz(void *eloop_data, void *user_ctx)
+{
+ struct hostapd_iface *iface = eloop_data;
+ struct os_time time;
+ int timeout;
+
+ if (!iface->last_20mhz_trigger.sec)
+ return;
+
+ os_get_time(&time);
+ timeout = iface->last_20mhz_trigger.sec + iface->conf->dynamic_ht40 -
+ time.sec;
+
+ if (timeout > 0) {
+ eloop_register_timeout(timeout, 0, hostapd_restore_40mhz,
+ iface, NULL);
+ return;
+ }
+
+ iface->last_20mhz_trigger.sec = 0;
+ iface->last_20mhz_trigger.usec = 0;
+
+ iface->force_20mhz = 0;
+ hostapd_set_force_20mhz(iface);
+}
+
+static int hostapd_set_force_20mhz(struct hostapd_iface *iface)
+{
+ int secondary_channel;
+ int i;
+
+ ieee802_11_set_beacons(iface);
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+
+ if (iface->force_20mhz)
+ secondary_channel = 0;
+ else
+ secondary_channel = hapd->iconf->secondary_channel;
+
+ if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+ hapd->iconf->channel,
+ hapd->iconf->ieee80211n,
+ secondary_channel)) {
+ wpa_printf(MSG_ERROR, "Could not set channel for "
+ "kernel driver");
+ }
+ }
+}
+
+void hostapd_deinit_ht(struct hostapd_iface *iface)
+{
+ eloop_cancel_timeout(hostapd_restore_40mhz, iface, NULL);
+}
+
+void hostapd_trigger_20mhz(struct hostapd_iface *iface)
+{
+ if (!iface->conf->dynamic_ht40)
+ return;
+
+ if (!iface->force_20mhz) {
+ iface->force_20mhz = 1;
+ hostapd_set_force_20mhz(iface);
+ }
+
+ if (!iface->last_20mhz_trigger.sec) {
+ eloop_cancel_timeout(hostapd_restore_40mhz, iface, NULL);
+ eloop_register_timeout(iface->conf->dynamic_ht40, 0,
+ hostapd_restore_40mhz, iface, NULL);
+ }
+
+ os_get_time(&iface->last_20mhz_trigger);
+}
|