hostapd: fix frequency selection for ap+sta
[openwrt.git] / package / network / services / hostapd / patches / 440-dynamic_20_40_mhz.patch
1 --- a/hostapd/config_file.c
2 +++ b/hostapd/config_file.c
3 @@ -2422,6 +2422,10 @@ static int hostapd_config_fill(struct ho
4                                            "ht_capab", line);
5                                 errors++;
6                         }
7 +               } else if (os_strcmp(buf, "dynamic_ht40") == 0) {
8 +                       conf->dynamic_ht40 = atoi(pos);
9 +                       if (conf->dynamic_ht40 == 1)
10 +                               conf->dynamic_ht40 = 1500;
11                 } else if (os_strcmp(buf, "require_ht") == 0) {
12                         conf->require_ht = atoi(pos);
13                 } else if (os_strcmp(buf, "obss_interval") == 0) {
14 --- a/src/ap/ap_config.h
15 +++ b/src/ap/ap_config.h
16 @@ -531,6 +531,7 @@ struct hostapd_config {
17         int ieee80211n;
18         int secondary_channel;
19         int require_ht;
20 +       int dynamic_ht40;
21         int obss_interval;
22         u32 vht_capab;
23         int ieee80211ac;
24 --- a/src/ap/hostapd.c
25 +++ b/src/ap/hostapd.c
26 @@ -23,6 +23,7 @@
27  #include "beacon.h"
28  #include "iapp.h"
29  #include "ieee802_1x.h"
30 +#include "ieee802_11.h"
31  #include "ieee802_11_auth.h"
32  #include "vlan_init.h"
33  #include "wpa_auth.h"
34 @@ -332,6 +333,7 @@ static void hostapd_cleanup(struct hosta
35  static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
36  {
37         wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
38 +       hostapd_deinit_ht(iface);
39         hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
40         iface->hw_features = NULL;
41         os_free(iface->current_rates);
42 --- a/src/ap/hostapd.h
43 +++ b/src/ap/hostapd.h
44 @@ -326,6 +326,9 @@ struct hostapd_iface {
45         /* Overlapping BSS information */
46         int olbc_ht;
47  
48 +       int force_20mhz;
49 +       struct os_time last_20mhz_trigger;
50 +
51         u16 ht_op_mode;
52  
53         /* surveying helpers */
54 --- a/src/ap/ieee802_11.c
55 +++ b/src/ap/ieee802_11.c
56 @@ -1538,6 +1538,9 @@ static void handle_beacon(struct hostapd
57                                              sizeof(mgmt->u.beacon)), &elems,
58                                       0);
59  
60 +       if (!elems.ht_capabilities)
61 +               hostapd_trigger_20mhz(hapd->iface);
62 +
63         ap_list_process_beacon(hapd->iface, mgmt, &elems, fi);
64  }
65  
66 --- a/src/ap/ieee802_11.h
67 +++ b/src/ap/ieee802_11.h
68 @@ -82,4 +82,17 @@ int hostapd_update_time_adv(struct hosta
69  void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
70  u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
71  
72 +#ifdef CONFIG_IEEE80211N
73 +void hostapd_trigger_20mhz(struct hostapd_iface *iface);
74 +void hostapd_deinit_ht(struct hostapd_iface *iface);
75 +
76 +#else
77 +static inline void hostapd_deinit_ht(struct hostapd_iface *iface)
78 +{
79 +}
80 +static inline void hostapd_trigger_20mhz(struct hostapd_iface *iface)
81 +{
82 +}
83 +#endif /* CONFIG_IEEE80211N */
84 +
85  #endif /* IEEE802_11_H */
86 --- a/src/ap/ieee802_11_ht.c
87 +++ b/src/ap/ieee802_11_ht.c
88 @@ -20,9 +20,11 @@
89  #include "drivers/driver.h"
90  #include "hostapd.h"
91  #include "ap_config.h"
92 +#include "ap_drv_ops.h"
93  #include "sta_info.h"
94  #include "beacon.h"
95  #include "ieee802_11.h"
96 +#include "utils/eloop.h"
97  
98  
99  u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
100 @@ -86,12 +88,15 @@ u8 * hostapd_eid_ht_operation(struct hos
101  
102         oper->control_chan = hapd->iconf->channel;
103         oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
104 -       if (hapd->iconf->secondary_channel == 1)
105 -               oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
106 -                       HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
107 -       if (hapd->iconf->secondary_channel == -1)
108 -               oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
109 -                       HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
110 +
111 +       if (!hapd->iface->force_20mhz) {
112 +               if (hapd->iconf->secondary_channel == 1)
113 +                       oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
114 +                               HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
115 +               if (hapd->iconf->secondary_channel == -1)
116 +                       oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
117 +                               HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
118 +       }
119  
120         pos += sizeof(*oper);
121  
122 @@ -286,3 +291,84 @@ void hostapd_get_ht_capab(struct hostapd
123  
124         neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
125  }
126 +
127 +static void hostapd_set_force_20mhz(struct hostapd_iface *iface);
128 +
129 +static void hostapd_restore_40mhz(void *eloop_data, void *user_ctx)
130 +{
131 +       struct hostapd_iface *iface = eloop_data;
132 +       struct os_time time;
133 +       int timeout;
134 +
135 +       if (!iface->last_20mhz_trigger.sec)
136 +           return;
137 +
138 +       os_get_time(&time);
139 +       timeout = iface->last_20mhz_trigger.sec + iface->conf->dynamic_ht40 -
140 +                 time.sec;
141 +
142 +       if (timeout > 0) {
143 +               eloop_register_timeout(timeout, 0, hostapd_restore_40mhz,
144 +                                      iface, NULL);
145 +               return;
146 +       }
147 +
148 +       iface->last_20mhz_trigger.sec = 0;
149 +       iface->last_20mhz_trigger.usec = 0;
150 +
151 +       iface->force_20mhz = 0;
152 +       hostapd_set_force_20mhz(iface);
153 +}
154 +
155 +static void hostapd_set_force_20mhz(struct hostapd_iface *iface)
156 +{
157 +       int secondary_channel;
158 +       int i;
159 +
160 +       ieee802_11_set_beacons(iface);
161 +
162 +       for (i = 0; i < iface->num_bss; i++) {
163 +               struct hostapd_data *hapd = iface->bss[i];
164 +
165 +               if (iface->force_20mhz)
166 +                       secondary_channel = 0;
167 +               else
168 +                       secondary_channel = hapd->iconf->secondary_channel;
169 +
170 +               if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
171 +                                    hapd->iconf->channel,
172 +                                    hapd->iconf->ieee80211n,
173 +                                    hapd->iconf->ieee80211ac,
174 +                                    secondary_channel,
175 +                                    hapd->iconf->vht_oper_chwidth,
176 +                                    hapd->iconf->vht_oper_centr_freq_seg0_idx,
177 +                                    hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
178 +                       wpa_printf(MSG_ERROR, "Could not set channel for "
179 +                                  "kernel driver");
180 +               }
181 +       }
182 +}
183 +
184 +void hostapd_deinit_ht(struct hostapd_iface *iface)
185 +{
186 +       eloop_cancel_timeout(hostapd_restore_40mhz, iface, NULL);
187 +}
188 +
189 +void hostapd_trigger_20mhz(struct hostapd_iface *iface)
190 +{
191 +       if (!iface->conf->dynamic_ht40)
192 +               return;
193 +
194 +       if (!iface->force_20mhz) {
195 +               iface->force_20mhz = 1;
196 +               hostapd_set_force_20mhz(iface);
197 +       }
198 +
199 +       if (!iface->last_20mhz_trigger.sec) {
200 +               eloop_cancel_timeout(hostapd_restore_40mhz, iface, NULL);
201 +               eloop_register_timeout(iface->conf->dynamic_ht40, 0,
202 +                                      hostapd_restore_40mhz, iface, NULL);
203 +       }
204 +
205 +       os_get_time(&iface->last_20mhz_trigger);
206 +}