]>
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. | |
ddaa83eb | 5 | * Copyright (c) 2008, Jouni Malinen <j@w1.fi> |
6fc6879b JM |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * Alternatively, this software may be distributed under the terms of BSD | |
12 | * license. | |
13 | * | |
14 | * See README and COPYING for more details. | |
15 | */ | |
16 | ||
17 | #include "includes.h" | |
18 | ||
19 | #include "hostapd.h" | |
20 | #include "hw_features.h" | |
21 | #include "driver.h" | |
22 | #include "config.h" | |
6fc6879b JM |
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++) { | |
10b83bd7 JM |
71 | /* |
72 | * Disable all channels that are marked not to allow | |
73 | * IBSS operation or active scanning. In addition, | |
74 | * disable all channels that require radar detection, | |
75 | * since that (in addition to full DFS) is not yet | |
76 | * supported. | |
77 | */ | |
78 | if (feature->channels[j].flag & | |
79 | (HOSTAPD_CHAN_NO_IBSS | | |
80 | HOSTAPD_CHAN_PASSIVE_SCAN | | |
81 | HOSTAPD_CHAN_RADAR)) | |
6fc6879b | 82 | feature->channels[j].flag |= |
10b83bd7 JM |
83 | HOSTAPD_CHAN_DISABLED; |
84 | if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) | |
85 | continue; | |
86 | wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d " | |
87 | "chan=%d freq=%d MHz", | |
88 | feature->mode, | |
89 | feature->channels[j].chan, | |
90 | feature->channels[j].freq); | |
6fc6879b JM |
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 | ||
6fc6879b | 172 | /** |
ddaa83eb | 173 | * hostapd_select_hw_mode - Select the hardware mode |
6fc6879b | 174 | * @iface: Pointer to interface data. |
ddaa83eb | 175 | * Returns: 0 on success, -1 on failure |
6fc6879b | 176 | * |
ddaa83eb JM |
177 | * Sets up the hardware mode, channel, rates, and passive scanning |
178 | * based on the configuration. | |
6fc6879b | 179 | */ |
ddaa83eb | 180 | int hostapd_select_hw_mode(struct hostapd_iface *iface) |
6fc6879b | 181 | { |
ddaa83eb | 182 | int i, j, ok, ret; |
6fc6879b JM |
183 | |
184 | if (iface->num_hw_features < 1) | |
185 | return -1; | |
186 | ||
187 | iface->current_mode = NULL; | |
188 | for (i = 0; i < iface->num_hw_features; i++) { | |
189 | struct hostapd_hw_modes *mode = &iface->hw_features[i]; | |
190 | if (mode->mode == (int) iface->conf->hw_mode) { | |
191 | iface->current_mode = mode; | |
192 | break; | |
193 | } | |
194 | } | |
195 | ||
196 | if (iface->current_mode == NULL) { | |
197 | wpa_printf(MSG_ERROR, "Hardware does not support configured " | |
198 | "mode"); | |
199 | hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, | |
200 | HOSTAPD_LEVEL_WARNING, | |
201 | "Hardware does not support configured mode " | |
202 | "(%d)", (int) iface->conf->hw_mode); | |
203 | return -1; | |
204 | } | |
205 | ||
206 | ok = 0; | |
207 | for (j = 0; j < iface->current_mode->num_channels; j++) { | |
208 | struct hostapd_channel_data *chan = | |
209 | &iface->current_mode->channels[j]; | |
10b83bd7 | 210 | if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && |
6fc6879b JM |
211 | (chan->chan == iface->conf->channel)) { |
212 | ok = 1; | |
213 | break; | |
214 | } | |
215 | } | |
216 | if (ok == 0 && iface->conf->channel != 0) { | |
217 | hostapd_logger(iface->bss[0], NULL, | |
218 | HOSTAPD_MODULE_IEEE80211, | |
219 | HOSTAPD_LEVEL_WARNING, | |
220 | "Configured channel (%d) not found from the " | |
221 | "channel list of current mode (%d) %s", | |
222 | iface->conf->channel, | |
223 | iface->current_mode->mode, | |
224 | hostapd_hw_mode_txt(iface->current_mode->mode)); | |
225 | iface->current_mode = NULL; | |
226 | } | |
227 | ||
ddaa83eb JM |
228 | if (iface->current_mode == NULL) { |
229 | hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, | |
230 | HOSTAPD_LEVEL_WARNING, | |
231 | "Hardware does not support configured channel"); | |
6fc6879b JM |
232 | return -1; |
233 | } | |
234 | ||
ddaa83eb JM |
235 | if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) { |
236 | wpa_printf(MSG_ERROR, "Failed to prepare rates table."); | |
237 | hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, | |
238 | HOSTAPD_LEVEL_WARNING, | |
239 | "Failed to prepare rates table."); | |
240 | return -1; | |
241 | } | |
6fc6879b | 242 | |
ddaa83eb JM |
243 | ret = hostapd_passive_scan(iface->bss[0], 0, |
244 | iface->conf->passive_scan_mode, | |
245 | iface->conf->passive_scan_interval, | |
246 | iface->conf->passive_scan_listen, | |
247 | NULL, NULL); | |
248 | if (ret) { | |
249 | if (ret == -1) { | |
250 | wpa_printf(MSG_DEBUG, "Passive scanning not " | |
251 | "supported"); | |
252 | } else { | |
253 | wpa_printf(MSG_ERROR, "Could not set passive " | |
254 | "scanning: %s", strerror(ret)); | |
255 | } | |
256 | ret = 0; | |
257 | } | |
6fc6879b | 258 | |
ddaa83eb | 259 | return ret; |
6fc6879b JM |
260 | } |
261 | ||
262 | ||
263 | const char * hostapd_hw_mode_txt(int mode) | |
264 | { | |
265 | switch (mode) { | |
266 | case HOSTAPD_MODE_IEEE80211A: | |
267 | return "IEEE 802.11a"; | |
268 | case HOSTAPD_MODE_IEEE80211B: | |
269 | return "IEEE 802.11b"; | |
270 | case HOSTAPD_MODE_IEEE80211G: | |
271 | return "IEEE 802.11g"; | |
272 | default: | |
273 | return "UNKNOWN"; | |
274 | } | |
275 | } | |
276 | ||
277 | ||
278 | int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) | |
279 | { | |
280 | int i; | |
281 | ||
282 | if (!hapd->iface->current_mode) | |
283 | return 0; | |
284 | ||
285 | for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { | |
286 | struct hostapd_channel_data *ch = | |
287 | &hapd->iface->current_mode->channels[i]; | |
288 | if (ch->chan == chan) | |
289 | return ch->freq; | |
290 | } | |
291 | ||
292 | return 0; | |
293 | } | |
294 | ||
295 | ||
296 | int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) | |
297 | { | |
298 | int i; | |
299 | ||
300 | if (!hapd->iface->current_mode) | |
301 | return 0; | |
302 | ||
303 | for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { | |
304 | struct hostapd_channel_data *ch = | |
305 | &hapd->iface->current_mode->channels[i]; | |
306 | if (ch->freq == freq) | |
307 | return ch->chan; | |
308 | } | |
309 | ||
310 | return 0; | |
311 | } |