]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * hostapd / Hardware feature query and different modes | |
3 | * Copyright 2002-2003, Instant802 Networks, Inc. | |
4 | * Copyright 2005-2006, Devicescape Software, Inc. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * Alternatively, this software may be distributed under the terms of BSD | |
11 | * license. | |
12 | * | |
13 | * See README and COPYING for more details. | |
14 | */ | |
15 | ||
16 | #include "includes.h" | |
17 | ||
18 | #include "hostapd.h" | |
19 | #include "hw_features.h" | |
20 | #include "driver.h" | |
21 | #include "config.h" | |
22 | #include "eloop.h" | |
23 | ||
24 | ||
25 | void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, | |
26 | size_t num_hw_features) | |
27 | { | |
28 | size_t i; | |
29 | ||
30 | if (hw_features == NULL) | |
31 | return; | |
32 | ||
33 | for (i = 0; i < num_hw_features; i++) { | |
34 | os_free(hw_features[i].channels); | |
35 | os_free(hw_features[i].rates); | |
36 | } | |
37 | ||
38 | os_free(hw_features); | |
39 | } | |
40 | ||
41 | ||
42 | int hostapd_get_hw_features(struct hostapd_iface *iface) | |
43 | { | |
44 | struct hostapd_data *hapd = iface->bss[0]; | |
45 | int ret = 0, i, j; | |
46 | u16 num_modes, flags; | |
47 | struct hostapd_hw_modes *modes; | |
48 | ||
85141289 JM |
49 | if (hostapd_drv_none(hapd)) |
50 | return -1; | |
6fc6879b JM |
51 | modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags); |
52 | if (modes == NULL) { | |
53 | hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, | |
54 | HOSTAPD_LEVEL_DEBUG, | |
55 | "Fetching hardware channel/rate support not " | |
56 | "supported."); | |
57 | return -1; | |
58 | } | |
59 | ||
60 | iface->hw_flags = flags; | |
61 | ||
62 | hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); | |
63 | iface->hw_features = modes; | |
64 | iface->num_hw_features = num_modes; | |
65 | ||
66 | for (i = 0; i < num_modes; i++) { | |
67 | struct hostapd_hw_modes *feature = &modes[i]; | |
68 | /* set flag for channels we can use in current regulatory | |
69 | * domain */ | |
70 | for (j = 0; j < feature->num_channels; j++) { | |
71 | /* TODO: add regulatory domain lookup */ | |
72 | unsigned char power_level = 0; | |
73 | unsigned char antenna_max = 0; | |
74 | ||
75 | if ((feature->mode == HOSTAPD_MODE_IEEE80211G || | |
76 | feature->mode == HOSTAPD_MODE_IEEE80211B) && | |
77 | feature->channels[j].chan >= 1 && | |
78 | feature->channels[j].chan <= 11) { | |
79 | power_level = 20; | |
80 | feature->channels[j].flag |= | |
81 | HOSTAPD_CHAN_W_SCAN; | |
82 | } else | |
83 | feature->channels[j].flag &= | |
84 | ~HOSTAPD_CHAN_W_SCAN; | |
85 | ||
86 | hostapd_set_channel_flag(hapd, feature->mode, | |
87 | feature->channels[j].chan, | |
88 | feature->channels[j].flag, | |
89 | power_level, | |
90 | antenna_max); | |
91 | } | |
92 | } | |
93 | ||
94 | return ret; | |
95 | } | |
96 | ||
97 | ||
98 | static int hostapd_prepare_rates(struct hostapd_data *hapd, | |
99 | struct hostapd_hw_modes *mode) | |
100 | { | |
101 | int i, num_basic_rates = 0; | |
102 | int basic_rates_a[] = { 60, 120, 240, -1 }; | |
103 | int basic_rates_b[] = { 10, 20, -1 }; | |
104 | int basic_rates_g[] = { 10, 20, 55, 110, -1 }; | |
105 | int *basic_rates; | |
106 | ||
107 | if (hapd->iconf->basic_rates) | |
108 | basic_rates = hapd->iconf->basic_rates; | |
109 | else switch (mode->mode) { | |
110 | case HOSTAPD_MODE_IEEE80211A: | |
111 | basic_rates = basic_rates_a; | |
112 | break; | |
113 | case HOSTAPD_MODE_IEEE80211B: | |
114 | basic_rates = basic_rates_b; | |
115 | break; | |
116 | case HOSTAPD_MODE_IEEE80211G: | |
117 | basic_rates = basic_rates_g; | |
118 | break; | |
119 | default: | |
120 | return -1; | |
121 | } | |
122 | ||
123 | if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates, | |
124 | basic_rates, mode->mode)) { | |
125 | wpa_printf(MSG_ERROR, "Failed to update rate sets in kernel " | |
126 | "module"); | |
127 | } | |
128 | ||
129 | os_free(hapd->iface->current_rates); | |
130 | hapd->iface->num_rates = 0; | |
131 | ||
132 | hapd->iface->current_rates = | |
133 | os_malloc(mode->num_rates * sizeof(struct hostapd_rate_data)); | |
134 | if (!hapd->iface->current_rates) { | |
135 | wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " | |
136 | "table."); | |
137 | return -1; | |
138 | } | |
139 | ||
140 | for (i = 0; i < mode->num_rates; i++) { | |
141 | struct hostapd_rate_data *rate; | |
142 | ||
143 | if (hapd->iconf->supported_rates && | |
144 | !hostapd_rate_found(hapd->iconf->supported_rates, | |
145 | mode->rates[i].rate)) | |
146 | continue; | |
147 | ||
148 | rate = &hapd->iface->current_rates[hapd->iface->num_rates]; | |
149 | os_memcpy(rate, &mode->rates[i], | |
150 | sizeof(struct hostapd_rate_data)); | |
151 | if (hostapd_rate_found(basic_rates, rate->rate)) { | |
152 | rate->flags |= HOSTAPD_RATE_BASIC; | |
153 | num_basic_rates++; | |
154 | } else | |
155 | rate->flags &= ~HOSTAPD_RATE_BASIC; | |
156 | wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", | |
157 | hapd->iface->num_rates, rate->rate, rate->flags); | |
158 | hapd->iface->num_rates++; | |
159 | } | |
160 | ||
161 | if (hapd->iface->num_rates == 0 || num_basic_rates == 0) { | |
162 | wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " | |
163 | "rate sets (%d,%d).", | |
164 | hapd->iface->num_rates, num_basic_rates); | |
165 | return -1; | |
166 | } | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
171 | ||
172 | static void select_hw_mode_start(void *eloop_data, void *user_ctx); | |
173 | static void select_hw_mode2_handler(void *eloop_data, void *user_ctx); | |
174 | ||
175 | /** | |
176 | * select_hw_mode_finalize - Finish select HW mode & call the callback | |
177 | * @iface: Pointer to interface data. | |
178 | * @status: Status of the select HW mode (0 on success; -1 on failure). | |
179 | * Returns: 0 on success; -1 on failure (e.g., was not in progress). | |
180 | */ | |
181 | static int select_hw_mode_finalize(struct hostapd_iface *iface, int status) | |
182 | { | |
183 | hostapd_iface_cb cb; | |
184 | ||
185 | if (!iface->hw_mode_sel_cb) | |
186 | return -1; | |
187 | ||
188 | eloop_cancel_timeout(select_hw_mode_start, iface, NULL); | |
189 | eloop_cancel_timeout(select_hw_mode2_handler, iface, NULL); | |
190 | ||
191 | cb = iface->hw_mode_sel_cb; | |
192 | ||
193 | iface->hw_mode_sel_cb = NULL; | |
194 | ||
195 | cb(iface, status); | |
196 | ||
197 | return 0; | |
198 | } | |
199 | ||
200 | ||
201 | /** | |
202 | * select_hw_mode2 - Select the hardware mode (part 2) | |
203 | * @iface: Pointer to interface data. | |
204 | * @status: Status of auto chanel selection. | |
205 | * | |
206 | * Setup the rates and passive scanning based on the configuration. | |
207 | */ | |
208 | static void select_hw_mode2(struct hostapd_iface *iface, int status) | |
209 | { | |
210 | int ret = status; | |
211 | if (ret) | |
212 | goto fail; | |
213 | ||
214 | if (iface->current_mode == NULL) { | |
215 | hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, | |
216 | HOSTAPD_LEVEL_WARNING, | |
217 | "Hardware does not support configured channel"); | |
218 | ret = -1; | |
219 | goto fail; | |
220 | } | |
221 | ||
222 | if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) { | |
223 | wpa_printf(MSG_ERROR, "Failed to prepare rates table."); | |
224 | hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, | |
225 | HOSTAPD_LEVEL_WARNING, | |
226 | "Failed to prepare rates table."); | |
227 | ret = -1; | |
228 | goto fail; | |
229 | } | |
230 | ||
231 | ret = hostapd_passive_scan(iface->bss[0], 0, | |
232 | iface->conf->passive_scan_mode, | |
233 | iface->conf->passive_scan_interval, | |
234 | iface->conf->passive_scan_listen, | |
235 | NULL, NULL); | |
236 | if (ret) { | |
237 | wpa_printf(MSG_ERROR, "Could not set passive scanning: %s", | |
238 | strerror(ret)); | |
239 | ret = 0; | |
240 | } | |
241 | ||
242 | fail: | |
243 | select_hw_mode_finalize(iface, ret); | |
244 | } | |
245 | ||
246 | ||
247 | /** | |
248 | * select_hw_mode2_handler - Calls select_hw_mode2 when auto chan isn't used | |
249 | * @eloop_data: Stores the struct hostapd_iface * for the interface. | |
250 | * @user_ctx: Unused. | |
251 | */ | |
252 | static void select_hw_mode2_handler(void *eloop_data, void *user_ctx) | |
253 | { | |
254 | struct hostapd_iface *iface = eloop_data; | |
255 | ||
256 | select_hw_mode2(iface, 0); | |
257 | } | |
258 | ||
259 | ||
260 | /** | |
261 | * select_hw_mode1 - Select the hardware mode (part 1) | |
262 | * @iface: Pointer to interface data. | |
263 | * Returns: 0 on success; -1 on failure. | |
264 | * | |
265 | * Setup the hardware mode and channel based on the configuration. | |
266 | * Schedules select_hw_mode2() to be called immediately or after automatic | |
267 | * channel selection takes place. | |
268 | */ | |
269 | static int select_hw_mode1(struct hostapd_iface *iface) | |
270 | { | |
271 | int i, j, ok; | |
272 | ||
273 | if (iface->num_hw_features < 1) | |
274 | return -1; | |
275 | ||
276 | iface->current_mode = NULL; | |
277 | for (i = 0; i < iface->num_hw_features; i++) { | |
278 | struct hostapd_hw_modes *mode = &iface->hw_features[i]; | |
279 | if (mode->mode == (int) iface->conf->hw_mode) { | |
280 | iface->current_mode = mode; | |
281 | break; | |
282 | } | |
283 | } | |
284 | ||
285 | if (iface->current_mode == NULL) { | |
286 | wpa_printf(MSG_ERROR, "Hardware does not support configured " | |
287 | "mode"); | |
288 | hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, | |
289 | HOSTAPD_LEVEL_WARNING, | |
290 | "Hardware does not support configured mode " | |
291 | "(%d)", (int) iface->conf->hw_mode); | |
292 | return -1; | |
293 | } | |
294 | ||
295 | ok = 0; | |
296 | for (j = 0; j < iface->current_mode->num_channels; j++) { | |
297 | struct hostapd_channel_data *chan = | |
298 | &iface->current_mode->channels[j]; | |
299 | if ((chan->flag & HOSTAPD_CHAN_W_SCAN) && | |
300 | (chan->chan == iface->conf->channel)) { | |
301 | ok = 1; | |
302 | break; | |
303 | } | |
304 | } | |
305 | if (ok == 0 && iface->conf->channel != 0) { | |
306 | hostapd_logger(iface->bss[0], NULL, | |
307 | HOSTAPD_MODULE_IEEE80211, | |
308 | HOSTAPD_LEVEL_WARNING, | |
309 | "Configured channel (%d) not found from the " | |
310 | "channel list of current mode (%d) %s", | |
311 | iface->conf->channel, | |
312 | iface->current_mode->mode, | |
313 | hostapd_hw_mode_txt(iface->current_mode->mode)); | |
314 | iface->current_mode = NULL; | |
315 | } | |
316 | ||
317 | /* | |
318 | * Calls select_hw_mode2() via a handler, so that the function is | |
319 | * always executed from eloop. | |
320 | */ | |
321 | eloop_register_timeout(0, 0, select_hw_mode2_handler, iface, NULL); | |
322 | return 0; | |
323 | } | |
324 | ||
325 | ||
326 | /** | |
327 | * select_hw_mode_start - Handler to start select HW mode | |
328 | * @eloop_data: Stores the struct hostapd_iface * for the interface. | |
329 | * @user_ctx: Unused. | |
330 | * | |
331 | * An eloop handler is used so that all errors can be processed by the | |
332 | * callback without introducing stack recursion. | |
333 | */ | |
334 | static void select_hw_mode_start(void *eloop_data, void *user_ctx) | |
335 | { | |
336 | struct hostapd_iface *iface = (struct hostapd_iface *)eloop_data; | |
337 | ||
338 | int ret; | |
339 | ||
340 | ret = select_hw_mode1(iface); | |
341 | if (ret) | |
342 | select_hw_mode_finalize(iface, ret); | |
343 | } | |
344 | ||
345 | ||
346 | /** | |
347 | * hostapd_select_hw_mode_start - Start selection of the hardware mode | |
348 | * @iface: Pointer to interface data. | |
349 | * @cb: The function to callback when done. | |
350 | * Returns: 0 if it starts successfully; cb will be called when done. | |
351 | * -1 on failure; cb will not be called. | |
352 | * | |
353 | * Sets up the hardware mode, channel, rates, and passive scanning | |
354 | * based on the configuration. | |
355 | */ | |
356 | int hostapd_select_hw_mode_start(struct hostapd_iface *iface, | |
357 | hostapd_iface_cb cb) | |
358 | { | |
359 | if (iface->hw_mode_sel_cb) { | |
360 | wpa_printf(MSG_DEBUG, | |
361 | "%s: Hardware mode select already in progress.", | |
362 | iface->bss[0]->conf->iface); | |
363 | return -1; | |
364 | } | |
365 | ||
366 | iface->hw_mode_sel_cb = cb; | |
367 | ||
368 | eloop_register_timeout(0, 0, select_hw_mode_start, iface, NULL); | |
369 | ||
370 | return 0; | |
371 | } | |
372 | ||
373 | ||
374 | /** | |
375 | * hostapd_auto_chan_select_stop - Stops automatic channel selection | |
376 | * @iface: Pointer to interface data. | |
377 | * Returns: 0 if successfully stopped; | |
378 | * -1 on failure (i.e., was not in progress) | |
379 | */ | |
380 | int hostapd_select_hw_mode_stop(struct hostapd_iface *iface) | |
381 | { | |
382 | return select_hw_mode_finalize(iface, -1); | |
383 | } | |
384 | ||
385 | ||
386 | const char * hostapd_hw_mode_txt(int mode) | |
387 | { | |
388 | switch (mode) { | |
389 | case HOSTAPD_MODE_IEEE80211A: | |
390 | return "IEEE 802.11a"; | |
391 | case HOSTAPD_MODE_IEEE80211B: | |
392 | return "IEEE 802.11b"; | |
393 | case HOSTAPD_MODE_IEEE80211G: | |
394 | return "IEEE 802.11g"; | |
395 | default: | |
396 | return "UNKNOWN"; | |
397 | } | |
398 | } | |
399 | ||
400 | ||
401 | int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) | |
402 | { | |
403 | int i; | |
404 | ||
405 | if (!hapd->iface->current_mode) | |
406 | return 0; | |
407 | ||
408 | for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { | |
409 | struct hostapd_channel_data *ch = | |
410 | &hapd->iface->current_mode->channels[i]; | |
411 | if (ch->chan == chan) | |
412 | return ch->freq; | |
413 | } | |
414 | ||
415 | return 0; | |
416 | } | |
417 | ||
418 | ||
419 | int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) | |
420 | { | |
421 | int i; | |
422 | ||
423 | if (!hapd->iface->current_mode) | |
424 | return 0; | |
425 | ||
426 | for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { | |
427 | struct hostapd_channel_data *ch = | |
428 | &hapd->iface->current_mode->channels[i]; | |
429 | if (ch->freq == freq) | |
430 | return ch->chan; | |
431 | } | |
432 | ||
433 | return 0; | |
434 | } |