Commit | Line | Data |
---|---|---|
f0706e82 JB |
1 | /* |
2 | * Copyright 2002-2005, Instant802 Networks, Inc. | |
3 | * Copyright 2005-2006, Devicescape Software, Inc. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | */ | |
9 | ||
10 | #include <linux/module.h> | |
11 | #include <linux/init.h> | |
12 | #include <linux/netdevice.h> | |
13 | #include <linux/types.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/skbuff.h> | |
16 | #include <linux/etherdevice.h> | |
17 | #include <linux/if_arp.h> | |
18 | #include <linux/wireless.h> | |
19 | #include <net/iw_handler.h> | |
20 | #include <asm/uaccess.h> | |
21 | ||
22 | #include <net/mac80211.h> | |
23 | #include "ieee80211_i.h" | |
2c8dccc7 JB |
24 | #include "led.h" |
25 | #include "rate.h" | |
f0706e82 JB |
26 | #include "wpa.h" |
27 | #include "aes_ccm.h" | |
f0706e82 | 28 | |
b708e610 | 29 | |
f698d856 | 30 | static int ieee80211_set_encryption(struct ieee80211_sub_if_data *sdata, u8 *sta_addr, |
628a140b JB |
31 | int idx, int alg, int remove, |
32 | int set_tx_key, const u8 *_key, | |
33 | size_t key_len) | |
f0706e82 | 34 | { |
f698d856 | 35 | struct ieee80211_local *local = sdata->local; |
d0709a65 | 36 | struct sta_info *sta; |
11a843b7 | 37 | struct ieee80211_key *key; |
3b96766f | 38 | int err; |
f0706e82 | 39 | |
22787dba JM |
40 | if (alg == ALG_AES_CMAC) { |
41 | if (idx < NUM_DEFAULT_KEYS || | |
42 | idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) { | |
43 | printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d " | |
44 | "(BIP)\n", sdata->dev->name, idx); | |
45 | return -EINVAL; | |
46 | } | |
47 | } else if (idx < 0 || idx >= NUM_DEFAULT_KEYS) { | |
139c3a04 | 48 | printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d\n", |
f698d856 | 49 | sdata->dev->name, idx); |
139c3a04 VB |
50 | return -EINVAL; |
51 | } | |
52 | ||
db4d1169 | 53 | if (remove) { |
3b96766f JB |
54 | rcu_read_lock(); |
55 | ||
56 | err = 0; | |
57 | ||
db4d1169 JB |
58 | if (is_broadcast_ether_addr(sta_addr)) { |
59 | key = sdata->keys[idx]; | |
60 | } else { | |
61 | sta = sta_info_get(local, sta_addr); | |
3b96766f JB |
62 | if (!sta) { |
63 | err = -ENOENT; | |
64 | goto out_unlock; | |
65 | } | |
db4d1169 | 66 | key = sta->key; |
f0706e82 JB |
67 | } |
68 | ||
d0709a65 | 69 | ieee80211_key_free(key); |
db4d1169 JB |
70 | } else { |
71 | key = ieee80211_key_alloc(alg, idx, key_len, _key); | |
72 | if (!key) | |
73 | return -ENOMEM; | |
74 | ||
d0709a65 | 75 | sta = NULL; |
3b96766f JB |
76 | err = 0; |
77 | ||
78 | rcu_read_lock(); | |
d0709a65 | 79 | |
db4d1169 JB |
80 | if (!is_broadcast_ether_addr(sta_addr)) { |
81 | set_tx_key = 0; | |
82 | /* | |
83 | * According to the standard, the key index of a | |
84 | * pairwise key must be zero. However, some AP are | |
85 | * broken when it comes to WEP key indices, so we | |
86 | * work around this. | |
87 | */ | |
88 | if (idx != 0 && alg != ALG_WEP) { | |
d0709a65 | 89 | ieee80211_key_free(key); |
3b96766f JB |
90 | err = -EINVAL; |
91 | goto out_unlock; | |
db4d1169 | 92 | } |
f0706e82 | 93 | |
db4d1169 JB |
94 | sta = sta_info_get(local, sta_addr); |
95 | if (!sta) { | |
d0709a65 | 96 | ieee80211_key_free(key); |
3b96766f JB |
97 | err = -ENOENT; |
98 | goto out_unlock; | |
db4d1169 | 99 | } |
f0706e82 JB |
100 | } |
101 | ||
23976efe EG |
102 | if (alg == ALG_WEP && |
103 | key_len != LEN_WEP40 && key_len != LEN_WEP104) { | |
104 | ieee80211_key_free(key); | |
105 | err = -EINVAL; | |
106 | goto out_unlock; | |
107 | } | |
108 | ||
db4d1169 | 109 | ieee80211_key_link(key, sdata, sta); |
f0706e82 | 110 | |
db4d1169 JB |
111 | if (set_tx_key || (!sta && !sdata->default_key && key)) |
112 | ieee80211_set_default_key(sdata, idx); | |
22787dba JM |
113 | if (alg == ALG_AES_CMAC && |
114 | (set_tx_key || (!sta && !sdata->default_mgmt_key && key))) | |
115 | ieee80211_set_default_mgmt_key(sdata, idx); | |
db4d1169 | 116 | } |
f0706e82 | 117 | |
3b96766f JB |
118 | out_unlock: |
119 | rcu_read_unlock(); | |
120 | ||
121 | return err; | |
f0706e82 JB |
122 | } |
123 | ||
124 | static int ieee80211_ioctl_siwgenie(struct net_device *dev, | |
125 | struct iw_request_info *info, | |
126 | struct iw_point *data, char *extra) | |
127 | { | |
128 | struct ieee80211_sub_if_data *sdata; | |
f0706e82 | 129 | |
ddd3d2be JB |
130 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
131 | ||
132 | if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) | |
f0706e82 JB |
133 | return -EOPNOTSUPP; |
134 | ||
46900298 | 135 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
f698d856 | 136 | int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length); |
f0706e82 JB |
137 | if (ret) |
138 | return ret; | |
46900298 JB |
139 | sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; |
140 | ieee80211_sta_req_auth(sdata); | |
f0706e82 JB |
141 | return 0; |
142 | } | |
143 | ||
f0706e82 JB |
144 | return -EOPNOTSUPP; |
145 | } | |
146 | ||
9a03d6d7 JB |
147 | static u8 ieee80211_get_wstats_flags(struct ieee80211_local *local) |
148 | { | |
149 | u8 wstats_flags = 0; | |
150 | ||
151 | wstats_flags |= local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC | | |
152 | IEEE80211_HW_SIGNAL_DBM) ? | |
153 | IW_QUAL_QUAL_UPDATED : IW_QUAL_QUAL_INVALID; | |
154 | wstats_flags |= local->hw.flags & IEEE80211_HW_NOISE_DBM ? | |
155 | IW_QUAL_NOISE_UPDATED : IW_QUAL_NOISE_INVALID; | |
156 | if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) | |
157 | wstats_flags |= IW_QUAL_DBM; | |
158 | ||
159 | return wstats_flags; | |
160 | } | |
161 | ||
f0706e82 JB |
162 | static int ieee80211_ioctl_giwrange(struct net_device *dev, |
163 | struct iw_request_info *info, | |
164 | struct iw_point *data, char *extra) | |
165 | { | |
166 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
167 | struct iw_range *range = (struct iw_range *) extra; | |
8318d78a | 168 | enum ieee80211_band band; |
333af2f0 | 169 | int c = 0; |
f0706e82 JB |
170 | |
171 | data->length = sizeof(struct iw_range); | |
172 | memset(range, 0, sizeof(struct iw_range)); | |
173 | ||
174 | range->we_version_compiled = WIRELESS_EXT; | |
175 | range->we_version_source = 21; | |
176 | range->retry_capa = IW_RETRY_LIMIT; | |
177 | range->retry_flags = IW_RETRY_LIMIT; | |
178 | range->min_retry = 0; | |
179 | range->max_retry = 255; | |
180 | range->min_rts = 0; | |
181 | range->max_rts = 2347; | |
182 | range->min_frag = 256; | |
183 | range->max_frag = 2346; | |
184 | ||
185 | range->encoding_size[0] = 5; | |
186 | range->encoding_size[1] = 13; | |
187 | range->num_encoding_sizes = 2; | |
188 | range->max_encoding_tokens = NUM_DEFAULT_KEYS; | |
189 | ||
2a519311 | 190 | /* cfg80211 requires this, and enforces 0..100 */ |
7fee5372 | 191 | if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) |
2a519311 | 192 | range->max_qual.level = 100; |
566bfe5a BR |
193 | else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) |
194 | range->max_qual.level = -110; | |
195 | else | |
196 | range->max_qual.level = 0; | |
197 | ||
198 | if (local->hw.flags & IEEE80211_HW_NOISE_DBM) | |
199 | range->max_qual.noise = -110; | |
200 | else | |
201 | range->max_qual.noise = 0; | |
202 | ||
203 | range->max_qual.qual = 100; | |
9a03d6d7 | 204 | range->max_qual.updated = ieee80211_get_wstats_flags(local); |
f0706e82 | 205 | |
566bfe5a BR |
206 | range->avg_qual.qual = 50; |
207 | /* not always true but better than nothing */ | |
208 | range->avg_qual.level = range->max_qual.level / 2; | |
209 | range->avg_qual.noise = range->max_qual.noise / 2; | |
9a03d6d7 | 210 | range->avg_qual.updated = ieee80211_get_wstats_flags(local); |
f0706e82 JB |
211 | |
212 | range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | | |
213 | IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; | |
214 | ||
333af2f0 | 215 | |
8318d78a JB |
216 | for (band = 0; band < IEEE80211_NUM_BANDS; band ++) { |
217 | int i; | |
218 | struct ieee80211_supported_band *sband; | |
219 | ||
220 | sband = local->hw.wiphy->bands[band]; | |
221 | ||
222 | if (!sband) | |
333af2f0 HL |
223 | continue; |
224 | ||
8318d78a JB |
225 | for (i = 0; i < sband->n_channels && c < IW_MAX_FREQUENCIES; i++) { |
226 | struct ieee80211_channel *chan = &sband->channels[i]; | |
333af2f0 | 227 | |
8318d78a JB |
228 | if (!(chan->flags & IEEE80211_CHAN_DISABLED)) { |
229 | range->freq[c].i = | |
230 | ieee80211_frequency_to_channel( | |
231 | chan->center_freq); | |
232 | range->freq[c].m = chan->center_freq; | |
233 | range->freq[c].e = 6; | |
333af2f0 HL |
234 | c++; |
235 | } | |
333af2f0 HL |
236 | } |
237 | } | |
238 | range->num_channels = c; | |
239 | range->num_frequency = c; | |
240 | ||
f0706e82 | 241 | IW_EVENT_CAPA_SET_KERNEL(range->event_capa); |
f0706e82 JB |
242 | IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); |
243 | IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); | |
244 | ||
374fdfbc DW |
245 | range->scan_capa |= IW_SCAN_CAPA_ESSID; |
246 | ||
f0706e82 JB |
247 | return 0; |
248 | } | |
249 | ||
250 | ||
f0706e82 JB |
251 | static int ieee80211_ioctl_siwfreq(struct net_device *dev, |
252 | struct iw_request_info *info, | |
253 | struct iw_freq *freq, char *extra) | |
254 | { | |
f0706e82 JB |
255 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
256 | ||
46900298 JB |
257 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) |
258 | sdata->u.ibss.flags &= ~IEEE80211_IBSS_AUTO_CHANNEL_SEL; | |
259 | else if (sdata->vif.type == NL80211_IFTYPE_STATION) | |
260 | sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL; | |
f0706e82 JB |
261 | |
262 | /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */ | |
263 | if (freq->e == 0) { | |
264 | if (freq->m < 0) { | |
46900298 JB |
265 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) |
266 | sdata->u.ibss.flags |= | |
267 | IEEE80211_IBSS_AUTO_CHANNEL_SEL; | |
268 | else if (sdata->vif.type == NL80211_IFTYPE_STATION) | |
269 | sdata->u.mgd.flags |= | |
d6f2da5b | 270 | IEEE80211_STA_AUTO_CHANNEL_SEL; |
f0706e82 JB |
271 | return 0; |
272 | } else | |
f698d856 | 273 | return ieee80211_set_freq(sdata, |
8318d78a | 274 | ieee80211_channel_to_frequency(freq->m)); |
f0706e82 JB |
275 | } else { |
276 | int i, div = 1000000; | |
277 | for (i = 0; i < freq->e; i++) | |
278 | div /= 10; | |
279 | if (div > 0) | |
f698d856 | 280 | return ieee80211_set_freq(sdata, freq->m / div); |
f0706e82 JB |
281 | else |
282 | return -EINVAL; | |
283 | } | |
284 | } | |
285 | ||
286 | ||
287 | static int ieee80211_ioctl_giwfreq(struct net_device *dev, | |
288 | struct iw_request_info *info, | |
289 | struct iw_freq *freq, char *extra) | |
290 | { | |
291 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
292 | ||
8318d78a | 293 | freq->m = local->hw.conf.channel->center_freq; |
f0706e82 JB |
294 | freq->e = 6; |
295 | ||
296 | return 0; | |
297 | } | |
298 | ||
299 | ||
300 | static int ieee80211_ioctl_siwessid(struct net_device *dev, | |
301 | struct iw_request_info *info, | |
302 | struct iw_point *data, char *ssid) | |
303 | { | |
f0706e82 JB |
304 | struct ieee80211_sub_if_data *sdata; |
305 | size_t len = data->length; | |
46900298 | 306 | int ret; |
f0706e82 JB |
307 | |
308 | /* iwconfig uses nul termination in SSID.. */ | |
309 | if (len > 0 && ssid[len - 1] == '\0') | |
310 | len--; | |
311 | ||
312 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
46900298 | 313 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
ddd3d2be | 314 | if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) { |
f0706e82 JB |
315 | if (len > IEEE80211_MAX_SSID_LEN) |
316 | return -EINVAL; | |
46900298 JB |
317 | memcpy(sdata->u.mgd.ssid, ssid, len); |
318 | sdata->u.mgd.ssid_len = len; | |
f0706e82 JB |
319 | return 0; |
320 | } | |
46900298 | 321 | |
d6f2da5b | 322 | if (data->flags) |
46900298 | 323 | sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; |
d6f2da5b | 324 | else |
46900298 JB |
325 | sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL; |
326 | ||
f698d856 | 327 | ret = ieee80211_sta_set_ssid(sdata, ssid, len); |
f0706e82 JB |
328 | if (ret) |
329 | return ret; | |
46900298 JB |
330 | |
331 | ieee80211_sta_req_auth(sdata); | |
f0706e82 | 332 | return 0; |
46900298 JB |
333 | } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) |
334 | return ieee80211_ibss_set_ssid(sdata, ssid, len); | |
f0706e82 | 335 | |
f0706e82 JB |
336 | return -EOPNOTSUPP; |
337 | } | |
338 | ||
339 | ||
340 | static int ieee80211_ioctl_giwessid(struct net_device *dev, | |
341 | struct iw_request_info *info, | |
342 | struct iw_point *data, char *ssid) | |
343 | { | |
344 | size_t len; | |
345 | ||
346 | struct ieee80211_sub_if_data *sdata; | |
347 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
46900298 | 348 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
f698d856 | 349 | int res = ieee80211_sta_get_ssid(sdata, ssid, &len); |
f0706e82 JB |
350 | if (res == 0) { |
351 | data->length = len; | |
352 | data->flags = 1; | |
353 | } else | |
354 | data->flags = 0; | |
355 | return res; | |
46900298 JB |
356 | } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { |
357 | int res = ieee80211_ibss_get_ssid(sdata, ssid, &len); | |
358 | if (res == 0) { | |
359 | data->length = len; | |
360 | data->flags = 1; | |
361 | } else | |
362 | data->flags = 0; | |
363 | return res; | |
f0706e82 JB |
364 | } |
365 | ||
f0706e82 JB |
366 | return -EOPNOTSUPP; |
367 | } | |
368 | ||
369 | ||
370 | static int ieee80211_ioctl_siwap(struct net_device *dev, | |
371 | struct iw_request_info *info, | |
372 | struct sockaddr *ap_addr, char *extra) | |
373 | { | |
f0706e82 JB |
374 | struct ieee80211_sub_if_data *sdata; |
375 | ||
376 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
46900298 | 377 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
f0706e82 | 378 | int ret; |
ddd3d2be | 379 | if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) { |
46900298 | 380 | memcpy(sdata->u.mgd.bssid, (u8 *) &ap_addr->sa_data, |
f0706e82 JB |
381 | ETH_ALEN); |
382 | return 0; | |
383 | } | |
d6f2da5b | 384 | if (is_zero_ether_addr((u8 *) &ap_addr->sa_data)) |
46900298 | 385 | sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL | |
d6f2da5b JS |
386 | IEEE80211_STA_AUTO_CHANNEL_SEL; |
387 | else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data)) | |
46900298 | 388 | sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL; |
f0706e82 | 389 | else |
46900298 | 390 | sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; |
f698d856 | 391 | ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data); |
f0706e82 JB |
392 | if (ret) |
393 | return ret; | |
46900298 | 394 | ieee80211_sta_req_auth(sdata); |
f0706e82 | 395 | return 0; |
46900298 JB |
396 | } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { |
397 | if (is_zero_ether_addr((u8 *) &ap_addr->sa_data)) | |
398 | sdata->u.ibss.flags |= IEEE80211_IBSS_AUTO_BSSID_SEL | | |
399 | IEEE80211_IBSS_AUTO_CHANNEL_SEL; | |
400 | else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data)) | |
401 | sdata->u.ibss.flags |= IEEE80211_IBSS_AUTO_BSSID_SEL; | |
402 | else | |
403 | sdata->u.ibss.flags &= ~IEEE80211_IBSS_AUTO_BSSID_SEL; | |
404 | ||
405 | return ieee80211_ibss_set_bssid(sdata, (u8 *) &ap_addr->sa_data); | |
05c914fe | 406 | } else if (sdata->vif.type == NL80211_IFTYPE_WDS) { |
44213b5e JB |
407 | /* |
408 | * If it is necessary to update the WDS peer address | |
409 | * while the interface is running, then we need to do | |
410 | * more work here, namely if it is running we need to | |
411 | * add a new and remove the old STA entry, this is | |
412 | * normally handled by _open() and _stop(). | |
413 | */ | |
414 | if (netif_running(dev)) | |
415 | return -EBUSY; | |
416 | ||
417 | memcpy(&sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data, | |
418 | ETH_ALEN); | |
419 | ||
420 | return 0; | |
f0706e82 JB |
421 | } |
422 | ||
423 | return -EOPNOTSUPP; | |
424 | } | |
425 | ||
426 | ||
427 | static int ieee80211_ioctl_giwap(struct net_device *dev, | |
428 | struct iw_request_info *info, | |
429 | struct sockaddr *ap_addr, char *extra) | |
430 | { | |
431 | struct ieee80211_sub_if_data *sdata; | |
432 | ||
433 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
46900298 JB |
434 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
435 | if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATED) { | |
d4231ca3 | 436 | ap_addr->sa_family = ARPHRD_ETHER; |
46900298 JB |
437 | memcpy(&ap_addr->sa_data, sdata->u.mgd.bssid, ETH_ALEN); |
438 | } else | |
d4231ca3 | 439 | memset(&ap_addr->sa_data, 0, ETH_ALEN); |
46900298 JB |
440 | return 0; |
441 | } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { | |
442 | if (sdata->u.ibss.state == IEEE80211_IBSS_MLME_JOINED) { | |
443 | ap_addr->sa_family = ARPHRD_ETHER; | |
444 | memcpy(&ap_addr->sa_data, sdata->u.ibss.bssid, ETH_ALEN); | |
445 | } else | |
446 | memset(&ap_addr->sa_data, 0, ETH_ALEN); | |
447 | return 0; | |
05c914fe | 448 | } else if (sdata->vif.type == NL80211_IFTYPE_WDS) { |
f0706e82 JB |
449 | ap_addr->sa_family = ARPHRD_ETHER; |
450 | memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN); | |
451 | return 0; | |
452 | } | |
453 | ||
454 | return -EOPNOTSUPP; | |
455 | } | |
456 | ||
457 | ||
1fd5e589 LF |
458 | static int ieee80211_ioctl_siwrate(struct net_device *dev, |
459 | struct iw_request_info *info, | |
460 | struct iw_param *rate, char *extra) | |
461 | { | |
462 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
8318d78a | 463 | int i, err = -EINVAL; |
1fd5e589 LF |
464 | u32 target_rate = rate->value / 100000; |
465 | struct ieee80211_sub_if_data *sdata; | |
8318d78a | 466 | struct ieee80211_supported_band *sband; |
1fd5e589 LF |
467 | |
468 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
8318d78a JB |
469 | |
470 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | |
471 | ||
1fd5e589 LF |
472 | /* target_rate = -1, rate->fixed = 0 means auto only, so use all rates |
473 | * target_rate = X, rate->fixed = 1 means only rate X | |
474 | * target_rate = X, rate->fixed = 0 means all rates <= X */ | |
3e122be0 JB |
475 | sdata->max_ratectrl_rateidx = -1; |
476 | sdata->force_unicast_rateidx = -1; | |
1fd5e589 LF |
477 | if (rate->value < 0) |
478 | return 0; | |
8318d78a JB |
479 | |
480 | for (i=0; i< sband->n_bitrates; i++) { | |
481 | struct ieee80211_rate *brate = &sband->bitrates[i]; | |
482 | int this_rate = brate->bitrate; | |
1fd5e589 | 483 | |
1fd5e589 | 484 | if (target_rate == this_rate) { |
3e122be0 | 485 | sdata->max_ratectrl_rateidx = i; |
1fd5e589 | 486 | if (rate->fixed) |
3e122be0 | 487 | sdata->force_unicast_rateidx = i; |
8318d78a JB |
488 | err = 0; |
489 | break; | |
1fd5e589 LF |
490 | } |
491 | } | |
8318d78a | 492 | return err; |
1fd5e589 LF |
493 | } |
494 | ||
b3d88ad4 LF |
495 | static int ieee80211_ioctl_giwrate(struct net_device *dev, |
496 | struct iw_request_info *info, | |
497 | struct iw_param *rate, char *extra) | |
498 | { | |
499 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
500 | struct sta_info *sta; | |
501 | struct ieee80211_sub_if_data *sdata; | |
8318d78a | 502 | struct ieee80211_supported_band *sband; |
b3d88ad4 LF |
503 | |
504 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
8318d78a | 505 | |
05c914fe | 506 | if (sdata->vif.type != NL80211_IFTYPE_STATION) |
b3d88ad4 | 507 | return -EOPNOTSUPP; |
8318d78a JB |
508 | |
509 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | |
510 | ||
380a942b JB |
511 | rcu_read_lock(); |
512 | ||
46900298 | 513 | sta = sta_info_get(local, sdata->u.mgd.bssid); |
380a942b | 514 | |
e6a9854b JB |
515 | if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) |
516 | rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate; | |
b3d88ad4 LF |
517 | else |
518 | rate->value = 0; | |
380a942b JB |
519 | |
520 | rcu_read_unlock(); | |
521 | ||
522 | if (!sta) | |
523 | return -ENODEV; | |
524 | ||
8318d78a | 525 | rate->value *= 100000; |
d0709a65 | 526 | |
b3d88ad4 LF |
527 | return 0; |
528 | } | |
529 | ||
61609bc0 MB |
530 | static int ieee80211_ioctl_siwtxpower(struct net_device *dev, |
531 | struct iw_request_info *info, | |
532 | union iwreq_data *data, char *extra) | |
533 | { | |
534 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
d9d29257 | 535 | struct ieee80211_channel* chan = local->hw.conf.channel; |
e8975581 | 536 | u32 reconf_flags = 0; |
8318d78a | 537 | int new_power_level; |
61609bc0 MB |
538 | |
539 | if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) | |
540 | return -EINVAL; | |
541 | if (data->txpower.flags & IW_TXPOW_RANGE) | |
542 | return -EINVAL; | |
d9d29257 LR |
543 | if (!chan) |
544 | return -EINVAL; | |
61609bc0 | 545 | |
d9d29257 LR |
546 | if (data->txpower.fixed) |
547 | new_power_level = min(data->txpower.value, chan->max_power); | |
548 | else /* Automatic power level setting */ | |
8318d78a | 549 | new_power_level = chan->max_power; |
6a432955 | 550 | |
2bf30fab | 551 | local->user_power_level = new_power_level; |
e3c92df0 | 552 | if (local->hw.conf.power_level != new_power_level) |
e8975581 | 553 | reconf_flags |= IEEE80211_CONF_CHANGE_POWER; |
6a432955 | 554 | |
61609bc0 MB |
555 | if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) { |
556 | local->hw.conf.radio_enabled = !(data->txpower.disabled); | |
e8975581 | 557 | reconf_flags |= IEEE80211_CONF_CHANGE_RADIO_ENABLED; |
cdcb006f | 558 | ieee80211_led_radio(local, local->hw.conf.radio_enabled); |
61609bc0 | 559 | } |
6a432955 | 560 | |
e8975581 JB |
561 | if (reconf_flags) |
562 | ieee80211_hw_config(local, reconf_flags); | |
61609bc0 MB |
563 | |
564 | return 0; | |
565 | } | |
566 | ||
fe6aa301 LF |
567 | static int ieee80211_ioctl_giwtxpower(struct net_device *dev, |
568 | struct iw_request_info *info, | |
569 | union iwreq_data *data, char *extra) | |
570 | { | |
571 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
572 | ||
573 | data->txpower.fixed = 1; | |
574 | data->txpower.disabled = !(local->hw.conf.radio_enabled); | |
575 | data->txpower.value = local->hw.conf.power_level; | |
576 | data->txpower.flags = IW_TXPOW_DBM; | |
577 | ||
578 | return 0; | |
579 | } | |
580 | ||
f0706e82 JB |
581 | static int ieee80211_ioctl_siwrts(struct net_device *dev, |
582 | struct iw_request_info *info, | |
583 | struct iw_param *rts, char *extra) | |
584 | { | |
585 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
586 | ||
587 | if (rts->disabled) | |
588 | local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; | |
fa6adfe9 EG |
589 | else if (!rts->fixed) |
590 | /* if the rts value is not fixed, then take default */ | |
591 | local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; | |
f0706e82 JB |
592 | else if (rts->value < 0 || rts->value > IEEE80211_MAX_RTS_THRESHOLD) |
593 | return -EINVAL; | |
594 | else | |
595 | local->rts_threshold = rts->value; | |
596 | ||
597 | /* If the wlan card performs RTS/CTS in hardware/firmware, | |
598 | * configure it here */ | |
599 | ||
600 | if (local->ops->set_rts_threshold) | |
601 | local->ops->set_rts_threshold(local_to_hw(local), | |
602 | local->rts_threshold); | |
603 | ||
604 | return 0; | |
605 | } | |
606 | ||
607 | static int ieee80211_ioctl_giwrts(struct net_device *dev, | |
608 | struct iw_request_info *info, | |
609 | struct iw_param *rts, char *extra) | |
610 | { | |
611 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
612 | ||
613 | rts->value = local->rts_threshold; | |
614 | rts->disabled = (rts->value >= IEEE80211_MAX_RTS_THRESHOLD); | |
615 | rts->fixed = 1; | |
616 | ||
617 | return 0; | |
618 | } | |
619 | ||
620 | ||
621 | static int ieee80211_ioctl_siwfrag(struct net_device *dev, | |
622 | struct iw_request_info *info, | |
623 | struct iw_param *frag, char *extra) | |
624 | { | |
625 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
626 | ||
627 | if (frag->disabled) | |
628 | local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD; | |
e4abd4d4 EG |
629 | else if (!frag->fixed) |
630 | local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD; | |
f0706e82 JB |
631 | else if (frag->value < 256 || |
632 | frag->value > IEEE80211_MAX_FRAG_THRESHOLD) | |
633 | return -EINVAL; | |
634 | else { | |
635 | /* Fragment length must be even, so strip LSB. */ | |
636 | local->fragmentation_threshold = frag->value & ~0x1; | |
637 | } | |
638 | ||
f0706e82 JB |
639 | return 0; |
640 | } | |
641 | ||
642 | static int ieee80211_ioctl_giwfrag(struct net_device *dev, | |
643 | struct iw_request_info *info, | |
644 | struct iw_param *frag, char *extra) | |
645 | { | |
646 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
647 | ||
648 | frag->value = local->fragmentation_threshold; | |
649 | frag->disabled = (frag->value >= IEEE80211_MAX_RTS_THRESHOLD); | |
650 | frag->fixed = 1; | |
651 | ||
652 | return 0; | |
653 | } | |
654 | ||
655 | ||
656 | static int ieee80211_ioctl_siwretry(struct net_device *dev, | |
657 | struct iw_request_info *info, | |
658 | struct iw_param *retry, char *extra) | |
659 | { | |
660 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
661 | ||
662 | if (retry->disabled || | |
663 | (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) | |
664 | return -EINVAL; | |
665 | ||
9124b077 JB |
666 | if (retry->flags & IW_RETRY_MAX) { |
667 | local->hw.conf.long_frame_max_tx_count = retry->value; | |
668 | } else if (retry->flags & IW_RETRY_MIN) { | |
669 | local->hw.conf.short_frame_max_tx_count = retry->value; | |
670 | } else { | |
671 | local->hw.conf.long_frame_max_tx_count = retry->value; | |
672 | local->hw.conf.short_frame_max_tx_count = retry->value; | |
f0706e82 JB |
673 | } |
674 | ||
9124b077 | 675 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS); |
f0706e82 JB |
676 | |
677 | return 0; | |
678 | } | |
679 | ||
680 | ||
681 | static int ieee80211_ioctl_giwretry(struct net_device *dev, | |
682 | struct iw_request_info *info, | |
683 | struct iw_param *retry, char *extra) | |
684 | { | |
685 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
686 | ||
687 | retry->disabled = 0; | |
688 | if (retry->flags == 0 || retry->flags & IW_RETRY_MIN) { | |
689 | /* first return min value, iwconfig will ask max value | |
690 | * later if needed */ | |
691 | retry->flags |= IW_RETRY_LIMIT; | |
9124b077 JB |
692 | retry->value = local->hw.conf.short_frame_max_tx_count; |
693 | if (local->hw.conf.long_frame_max_tx_count != | |
694 | local->hw.conf.short_frame_max_tx_count) | |
f0706e82 JB |
695 | retry->flags |= IW_RETRY_MIN; |
696 | return 0; | |
697 | } | |
698 | if (retry->flags & IW_RETRY_MAX) { | |
699 | retry->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; | |
9124b077 | 700 | retry->value = local->hw.conf.long_frame_max_tx_count; |
f0706e82 JB |
701 | } |
702 | ||
703 | return 0; | |
704 | } | |
705 | ||
f0706e82 JB |
706 | static int ieee80211_ioctl_siwmlme(struct net_device *dev, |
707 | struct iw_request_info *info, | |
708 | struct iw_point *data, char *extra) | |
709 | { | |
710 | struct ieee80211_sub_if_data *sdata; | |
711 | struct iw_mlme *mlme = (struct iw_mlme *) extra; | |
712 | ||
713 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
46900298 | 714 | if (!(sdata->vif.type == NL80211_IFTYPE_STATION)) |
f0706e82 JB |
715 | return -EINVAL; |
716 | ||
717 | switch (mlme->cmd) { | |
718 | case IW_MLME_DEAUTH: | |
719 | /* TODO: mlme->addr.sa_data */ | |
f698d856 | 720 | return ieee80211_sta_deauthenticate(sdata, mlme->reason_code); |
f0706e82 JB |
721 | case IW_MLME_DISASSOC: |
722 | /* TODO: mlme->addr.sa_data */ | |
f698d856 | 723 | return ieee80211_sta_disassociate(sdata, mlme->reason_code); |
f0706e82 JB |
724 | default: |
725 | return -EOPNOTSUPP; | |
726 | } | |
727 | } | |
728 | ||
729 | ||
730 | static int ieee80211_ioctl_siwencode(struct net_device *dev, | |
731 | struct iw_request_info *info, | |
732 | struct iw_point *erq, char *keybuf) | |
733 | { | |
734 | struct ieee80211_sub_if_data *sdata; | |
735 | int idx, i, alg = ALG_WEP; | |
736 | u8 bcaddr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | |
628a140b | 737 | int remove = 0; |
f0706e82 JB |
738 | |
739 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
740 | ||
741 | idx = erq->flags & IW_ENCODE_INDEX; | |
742 | if (idx == 0) { | |
743 | if (sdata->default_key) | |
744 | for (i = 0; i < NUM_DEFAULT_KEYS; i++) { | |
745 | if (sdata->default_key == sdata->keys[i]) { | |
746 | idx = i; | |
747 | break; | |
748 | } | |
749 | } | |
750 | } else if (idx < 1 || idx > 4) | |
751 | return -EINVAL; | |
752 | else | |
753 | idx--; | |
754 | ||
755 | if (erq->flags & IW_ENCODE_DISABLED) | |
628a140b | 756 | remove = 1; |
f0706e82 JB |
757 | else if (erq->length == 0) { |
758 | /* No key data - just set the default TX key index */ | |
11a843b7 | 759 | ieee80211_set_default_key(sdata, idx); |
f0706e82 JB |
760 | return 0; |
761 | } | |
762 | ||
763 | return ieee80211_set_encryption( | |
f698d856 | 764 | sdata, bcaddr, |
628a140b | 765 | idx, alg, remove, |
f0706e82 JB |
766 | !sdata->default_key, |
767 | keybuf, erq->length); | |
768 | } | |
769 | ||
770 | ||
771 | static int ieee80211_ioctl_giwencode(struct net_device *dev, | |
772 | struct iw_request_info *info, | |
773 | struct iw_point *erq, char *key) | |
774 | { | |
775 | struct ieee80211_sub_if_data *sdata; | |
776 | int idx, i; | |
777 | ||
778 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
779 | ||
780 | idx = erq->flags & IW_ENCODE_INDEX; | |
781 | if (idx < 1 || idx > 4) { | |
782 | idx = -1; | |
783 | if (!sdata->default_key) | |
784 | idx = 0; | |
785 | else for (i = 0; i < NUM_DEFAULT_KEYS; i++) { | |
786 | if (sdata->default_key == sdata->keys[i]) { | |
787 | idx = i; | |
788 | break; | |
789 | } | |
790 | } | |
791 | if (idx < 0) | |
792 | return -EINVAL; | |
793 | } else | |
794 | idx--; | |
795 | ||
796 | erq->flags = idx + 1; | |
797 | ||
798 | if (!sdata->keys[idx]) { | |
799 | erq->length = 0; | |
800 | erq->flags |= IW_ENCODE_DISABLED; | |
801 | return 0; | |
802 | } | |
803 | ||
8f20fc24 | 804 | memcpy(key, sdata->keys[idx]->conf.key, |
11a843b7 | 805 | min_t(int, erq->length, sdata->keys[idx]->conf.keylen)); |
8f20fc24 | 806 | erq->length = sdata->keys[idx]->conf.keylen; |
f0706e82 JB |
807 | erq->flags |= IW_ENCODE_ENABLED; |
808 | ||
05c914fe | 809 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
46900298 | 810 | switch (sdata->u.mgd.auth_alg) { |
b9fcc4f2 EG |
811 | case WLAN_AUTH_OPEN: |
812 | case WLAN_AUTH_LEAP: | |
813 | erq->flags |= IW_ENCODE_OPEN; | |
814 | break; | |
815 | case WLAN_AUTH_SHARED_KEY: | |
816 | erq->flags |= IW_ENCODE_RESTRICTED; | |
817 | break; | |
818 | } | |
819 | } | |
820 | ||
f0706e82 JB |
821 | return 0; |
822 | } | |
823 | ||
49292d56 SO |
824 | static int ieee80211_ioctl_siwpower(struct net_device *dev, |
825 | struct iw_request_info *info, | |
826 | struct iw_param *wrq, | |
827 | char *extra) | |
828 | { | |
e0cb686f | 829 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
49292d56 SO |
830 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); |
831 | struct ieee80211_conf *conf = &local->hw.conf; | |
520eb820 | 832 | int ret = 0, timeout = 0; |
e0cb686f KV |
833 | bool ps; |
834 | ||
4be8c387 JB |
835 | if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) |
836 | return -EOPNOTSUPP; | |
837 | ||
e0cb686f KV |
838 | if (sdata->vif.type != NL80211_IFTYPE_STATION) |
839 | return -EINVAL; | |
49292d56 SO |
840 | |
841 | if (wrq->disabled) { | |
e0cb686f | 842 | ps = false; |
520eb820 | 843 | timeout = 0; |
e0cb686f | 844 | goto set; |
49292d56 SO |
845 | } |
846 | ||
847 | switch (wrq->flags & IW_POWER_MODE) { | |
848 | case IW_POWER_ON: /* If not specified */ | |
849 | case IW_POWER_MODE: /* If set all mask */ | |
850 | case IW_POWER_ALL_R: /* If explicitely state all */ | |
e0cb686f | 851 | ps = true; |
49292d56 | 852 | break; |
520eb820 | 853 | default: /* Otherwise we ignore */ |
e9aeabae | 854 | return -EINVAL; |
49292d56 SO |
855 | } |
856 | ||
e9aeabae JB |
857 | if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT)) |
858 | return -EINVAL; | |
859 | ||
520eb820 KV |
860 | if (wrq->flags & IW_POWER_TIMEOUT) |
861 | timeout = wrq->value / 1000; | |
e0cb686f | 862 | |
4be8c387 | 863 | set: |
46f2c4bd | 864 | if (ps == local->powersave && timeout == conf->dynamic_ps_timeout) |
520eb820 KV |
865 | return ret; |
866 | ||
e0cb686f | 867 | local->powersave = ps; |
46f2c4bd | 868 | conf->dynamic_ps_timeout = timeout; |
e0cb686f | 869 | |
4be8c387 | 870 | if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) |
46f2c4bd JB |
871 | ret = ieee80211_hw_config(local, |
872 | IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT); | |
4be8c387 | 873 | |
46900298 | 874 | if (!(sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED)) |
4be8c387 JB |
875 | return ret; |
876 | ||
877 | if (conf->dynamic_ps_timeout > 0 && | |
878 | !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) { | |
879 | mod_timer(&local->dynamic_ps_timer, jiffies + | |
880 | msecs_to_jiffies(conf->dynamic_ps_timeout)); | |
881 | } else { | |
882 | if (local->powersave) { | |
883 | if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) | |
a97b77b9 | 884 | ieee80211_send_nullfunc(local, sdata, 1); |
4be8c387 JB |
885 | conf->flags |= IEEE80211_CONF_PS; |
886 | ret = ieee80211_hw_config(local, | |
887 | IEEE80211_CONF_CHANGE_PS); | |
888 | } else { | |
889 | conf->flags &= ~IEEE80211_CONF_PS; | |
890 | ret = ieee80211_hw_config(local, | |
891 | IEEE80211_CONF_CHANGE_PS); | |
892 | if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) | |
a97b77b9 | 893 | ieee80211_send_nullfunc(local, sdata, 0); |
b8abde45 VN |
894 | del_timer_sync(&local->dynamic_ps_timer); |
895 | cancel_work_sync(&local->dynamic_ps_enable_work); | |
520eb820 | 896 | } |
e0cb686f KV |
897 | } |
898 | ||
899 | return ret; | |
49292d56 SO |
900 | } |
901 | ||
902 | static int ieee80211_ioctl_giwpower(struct net_device *dev, | |
903 | struct iw_request_info *info, | |
904 | union iwreq_data *wrqu, | |
905 | char *extra) | |
906 | { | |
907 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
49292d56 | 908 | |
e0cb686f | 909 | wrqu->power.disabled = !local->powersave; |
49292d56 SO |
910 | |
911 | return 0; | |
912 | } | |
913 | ||
f0706e82 JB |
914 | static int ieee80211_ioctl_siwauth(struct net_device *dev, |
915 | struct iw_request_info *info, | |
916 | struct iw_param *data, char *extra) | |
917 | { | |
f0706e82 JB |
918 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
919 | int ret = 0; | |
920 | ||
921 | switch (data->flags & IW_AUTH_INDEX) { | |
922 | case IW_AUTH_WPA_VERSION: | |
f0706e82 JB |
923 | case IW_AUTH_CIPHER_GROUP: |
924 | case IW_AUTH_WPA_ENABLED: | |
925 | case IW_AUTH_RX_UNENCRYPTED_EAPOL: | |
f0706e82 | 926 | case IW_AUTH_KEY_MGMT: |
54604d3a | 927 | case IW_AUTH_CIPHER_GROUP_MGMT: |
5b98b1f7 | 928 | break; |
eb46936b VT |
929 | case IW_AUTH_CIPHER_PAIRWISE: |
930 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | |
931 | if (data->value & (IW_AUTH_CIPHER_WEP40 | | |
932 | IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_TKIP)) | |
46900298 | 933 | sdata->u.mgd.flags |= |
eb46936b VT |
934 | IEEE80211_STA_TKIP_WEP_USED; |
935 | else | |
46900298 | 936 | sdata->u.mgd.flags &= |
eb46936b VT |
937 | ~IEEE80211_STA_TKIP_WEP_USED; |
938 | } | |
939 | break; | |
b1357a81 JB |
940 | case IW_AUTH_DROP_UNENCRYPTED: |
941 | sdata->drop_unencrypted = !!data->value; | |
942 | break; | |
5b98b1f7 | 943 | case IW_AUTH_PRIVACY_INVOKED: |
05c914fe | 944 | if (sdata->vif.type != NL80211_IFTYPE_STATION) |
f0706e82 JB |
945 | ret = -EINVAL; |
946 | else { | |
46900298 | 947 | sdata->u.mgd.flags &= ~IEEE80211_STA_PRIVACY_INVOKED; |
f0706e82 | 948 | /* |
5b98b1f7 JB |
949 | * Privacy invoked by wpa_supplicant, store the |
950 | * value and allow associating to a protected | |
951 | * network without having a key up front. | |
f0706e82 | 952 | */ |
5b98b1f7 | 953 | if (data->value) |
46900298 | 954 | sdata->u.mgd.flags |= |
5b98b1f7 | 955 | IEEE80211_STA_PRIVACY_INVOKED; |
f0706e82 JB |
956 | } |
957 | break; | |
958 | case IW_AUTH_80211_AUTH_ALG: | |
46900298 JB |
959 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
960 | sdata->u.mgd.auth_algs = data->value; | |
f0706e82 JB |
961 | else |
962 | ret = -EOPNOTSUPP; | |
963 | break; | |
fdfacf0a | 964 | case IW_AUTH_MFP: |
4375d083 JM |
965 | if (!(sdata->local->hw.flags & IEEE80211_HW_MFP_CAPABLE)) { |
966 | ret = -EOPNOTSUPP; | |
967 | break; | |
968 | } | |
46900298 | 969 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
e4e5e2b0 JB |
970 | switch (data->value) { |
971 | case IW_AUTH_MFP_DISABLED: | |
46900298 | 972 | sdata->u.mgd.mfp = IEEE80211_MFP_DISABLED; |
e4e5e2b0 JB |
973 | break; |
974 | case IW_AUTH_MFP_OPTIONAL: | |
46900298 | 975 | sdata->u.mgd.mfp = IEEE80211_MFP_OPTIONAL; |
e4e5e2b0 JB |
976 | break; |
977 | case IW_AUTH_MFP_REQUIRED: | |
46900298 | 978 | sdata->u.mgd.mfp = IEEE80211_MFP_REQUIRED; |
e4e5e2b0 JB |
979 | break; |
980 | default: | |
981 | ret = -EINVAL; | |
982 | } | |
983 | } else | |
fdfacf0a JM |
984 | ret = -EOPNOTSUPP; |
985 | break; | |
f0706e82 JB |
986 | default: |
987 | ret = -EOPNOTSUPP; | |
988 | break; | |
989 | } | |
990 | return ret; | |
991 | } | |
992 | ||
993 | /* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ | |
994 | static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev) | |
995 | { | |
996 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
997 | struct iw_statistics *wstats = &local->wstats; | |
998 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
999 | struct sta_info *sta = NULL; | |
1000 | ||
98dd6a57 JB |
1001 | rcu_read_lock(); |
1002 | ||
46900298 JB |
1003 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
1004 | sta = sta_info_get(local, sdata->u.mgd.bssid); | |
1005 | ||
f0706e82 JB |
1006 | if (!sta) { |
1007 | wstats->discard.fragment = 0; | |
1008 | wstats->discard.misc = 0; | |
1009 | wstats->qual.qual = 0; | |
1010 | wstats->qual.level = 0; | |
1011 | wstats->qual.noise = 0; | |
1012 | wstats->qual.updated = IW_QUAL_ALL_INVALID; | |
1013 | } else { | |
566bfe5a BR |
1014 | wstats->qual.level = sta->last_signal; |
1015 | wstats->qual.qual = sta->last_qual; | |
f0706e82 | 1016 | wstats->qual.noise = sta->last_noise; |
9a03d6d7 | 1017 | wstats->qual.updated = ieee80211_get_wstats_flags(local); |
f0706e82 | 1018 | } |
98dd6a57 JB |
1019 | |
1020 | rcu_read_unlock(); | |
1021 | ||
f0706e82 JB |
1022 | return wstats; |
1023 | } | |
1024 | ||
1025 | static int ieee80211_ioctl_giwauth(struct net_device *dev, | |
1026 | struct iw_request_info *info, | |
1027 | struct iw_param *data, char *extra) | |
1028 | { | |
1029 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
1030 | int ret = 0; | |
1031 | ||
1032 | switch (data->flags & IW_AUTH_INDEX) { | |
1033 | case IW_AUTH_80211_AUTH_ALG: | |
46900298 JB |
1034 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
1035 | data->value = sdata->u.mgd.auth_algs; | |
f0706e82 JB |
1036 | else |
1037 | ret = -EOPNOTSUPP; | |
1038 | break; | |
1039 | default: | |
1040 | ret = -EOPNOTSUPP; | |
1041 | break; | |
1042 | } | |
1043 | return ret; | |
1044 | } | |
1045 | ||
1046 | ||
1047 | static int ieee80211_ioctl_siwencodeext(struct net_device *dev, | |
1048 | struct iw_request_info *info, | |
1049 | struct iw_point *erq, char *extra) | |
1050 | { | |
1051 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
1052 | struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; | |
628a140b | 1053 | int uninitialized_var(alg), idx, i, remove = 0; |
f0706e82 JB |
1054 | |
1055 | switch (ext->alg) { | |
1056 | case IW_ENCODE_ALG_NONE: | |
628a140b | 1057 | remove = 1; |
f0706e82 JB |
1058 | break; |
1059 | case IW_ENCODE_ALG_WEP: | |
1060 | alg = ALG_WEP; | |
1061 | break; | |
1062 | case IW_ENCODE_ALG_TKIP: | |
1063 | alg = ALG_TKIP; | |
1064 | break; | |
1065 | case IW_ENCODE_ALG_CCMP: | |
1066 | alg = ALG_CCMP; | |
1067 | break; | |
22787dba JM |
1068 | case IW_ENCODE_ALG_AES_CMAC: |
1069 | alg = ALG_AES_CMAC; | |
1070 | break; | |
f0706e82 JB |
1071 | default: |
1072 | return -EOPNOTSUPP; | |
1073 | } | |
1074 | ||
1075 | if (erq->flags & IW_ENCODE_DISABLED) | |
628a140b | 1076 | remove = 1; |
f0706e82 JB |
1077 | |
1078 | idx = erq->flags & IW_ENCODE_INDEX; | |
22787dba JM |
1079 | if (alg == ALG_AES_CMAC) { |
1080 | if (idx < NUM_DEFAULT_KEYS + 1 || | |
1081 | idx > NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) { | |
1082 | idx = -1; | |
1083 | if (!sdata->default_mgmt_key) | |
1084 | idx = 0; | |
1085 | else for (i = NUM_DEFAULT_KEYS; | |
1086 | i < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS; | |
1087 | i++) { | |
1088 | if (sdata->default_mgmt_key == sdata->keys[i]) | |
1089 | { | |
1090 | idx = i; | |
1091 | break; | |
1092 | } | |
f0706e82 | 1093 | } |
22787dba JM |
1094 | if (idx < 0) |
1095 | return -EINVAL; | |
1096 | } else | |
1097 | idx--; | |
1098 | } else { | |
1099 | if (idx < 1 || idx > 4) { | |
1100 | idx = -1; | |
1101 | if (!sdata->default_key) | |
1102 | idx = 0; | |
1103 | else for (i = 0; i < NUM_DEFAULT_KEYS; i++) { | |
1104 | if (sdata->default_key == sdata->keys[i]) { | |
1105 | idx = i; | |
1106 | break; | |
1107 | } | |
1108 | } | |
1109 | if (idx < 0) | |
1110 | return -EINVAL; | |
1111 | } else | |
1112 | idx--; | |
1113 | } | |
f0706e82 | 1114 | |
f698d856 | 1115 | return ieee80211_set_encryption(sdata, ext->addr.sa_data, idx, alg, |
628a140b | 1116 | remove, |
f0706e82 JB |
1117 | ext->ext_flags & |
1118 | IW_ENCODE_EXT_SET_TX_KEY, | |
1119 | ext->key, ext->key_len); | |
1120 | } | |
1121 | ||
1122 | ||
f0706e82 JB |
1123 | /* Structures to export the Wireless Handlers */ |
1124 | ||
1125 | static const iw_handler ieee80211_handler[] = | |
1126 | { | |
1127 | (iw_handler) NULL, /* SIOCSIWCOMMIT */ | |
fee52678 | 1128 | (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */ |
f0706e82 JB |
1129 | (iw_handler) NULL, /* SIOCSIWNWID */ |
1130 | (iw_handler) NULL, /* SIOCGIWNWID */ | |
1131 | (iw_handler) ieee80211_ioctl_siwfreq, /* SIOCSIWFREQ */ | |
1132 | (iw_handler) ieee80211_ioctl_giwfreq, /* SIOCGIWFREQ */ | |
e60c7744 JB |
1133 | (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */ |
1134 | (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */ | |
f0706e82 JB |
1135 | (iw_handler) NULL, /* SIOCSIWSENS */ |
1136 | (iw_handler) NULL, /* SIOCGIWSENS */ | |
1137 | (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ | |
1138 | (iw_handler) ieee80211_ioctl_giwrange, /* SIOCGIWRANGE */ | |
1139 | (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ | |
1140 | (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ | |
1141 | (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ | |
1142 | (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ | |
5d4ecd93 JB |
1143 | (iw_handler) NULL, /* SIOCSIWSPY */ |
1144 | (iw_handler) NULL, /* SIOCGIWSPY */ | |
1145 | (iw_handler) NULL, /* SIOCSIWTHRSPY */ | |
1146 | (iw_handler) NULL, /* SIOCGIWTHRSPY */ | |
f0706e82 JB |
1147 | (iw_handler) ieee80211_ioctl_siwap, /* SIOCSIWAP */ |
1148 | (iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */ | |
1149 | (iw_handler) ieee80211_ioctl_siwmlme, /* SIOCSIWMLME */ | |
1150 | (iw_handler) NULL, /* SIOCGIWAPLIST */ | |
2a519311 JB |
1151 | (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */ |
1152 | (iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */ | |
f0706e82 JB |
1153 | (iw_handler) ieee80211_ioctl_siwessid, /* SIOCSIWESSID */ |
1154 | (iw_handler) ieee80211_ioctl_giwessid, /* SIOCGIWESSID */ | |
1155 | (iw_handler) NULL, /* SIOCSIWNICKN */ | |
1156 | (iw_handler) NULL, /* SIOCGIWNICKN */ | |
1157 | (iw_handler) NULL, /* -- hole -- */ | |
1158 | (iw_handler) NULL, /* -- hole -- */ | |
1fd5e589 | 1159 | (iw_handler) ieee80211_ioctl_siwrate, /* SIOCSIWRATE */ |
b3d88ad4 | 1160 | (iw_handler) ieee80211_ioctl_giwrate, /* SIOCGIWRATE */ |
f0706e82 JB |
1161 | (iw_handler) ieee80211_ioctl_siwrts, /* SIOCSIWRTS */ |
1162 | (iw_handler) ieee80211_ioctl_giwrts, /* SIOCGIWRTS */ | |
1163 | (iw_handler) ieee80211_ioctl_siwfrag, /* SIOCSIWFRAG */ | |
1164 | (iw_handler) ieee80211_ioctl_giwfrag, /* SIOCGIWFRAG */ | |
61609bc0 | 1165 | (iw_handler) ieee80211_ioctl_siwtxpower, /* SIOCSIWTXPOW */ |
fe6aa301 | 1166 | (iw_handler) ieee80211_ioctl_giwtxpower, /* SIOCGIWTXPOW */ |
f0706e82 JB |
1167 | (iw_handler) ieee80211_ioctl_siwretry, /* SIOCSIWRETRY */ |
1168 | (iw_handler) ieee80211_ioctl_giwretry, /* SIOCGIWRETRY */ | |
1169 | (iw_handler) ieee80211_ioctl_siwencode, /* SIOCSIWENCODE */ | |
1170 | (iw_handler) ieee80211_ioctl_giwencode, /* SIOCGIWENCODE */ | |
49292d56 SO |
1171 | (iw_handler) ieee80211_ioctl_siwpower, /* SIOCSIWPOWER */ |
1172 | (iw_handler) ieee80211_ioctl_giwpower, /* SIOCGIWPOWER */ | |
f0706e82 JB |
1173 | (iw_handler) NULL, /* -- hole -- */ |
1174 | (iw_handler) NULL, /* -- hole -- */ | |
1175 | (iw_handler) ieee80211_ioctl_siwgenie, /* SIOCSIWGENIE */ | |
1176 | (iw_handler) NULL, /* SIOCGIWGENIE */ | |
1177 | (iw_handler) ieee80211_ioctl_siwauth, /* SIOCSIWAUTH */ | |
1178 | (iw_handler) ieee80211_ioctl_giwauth, /* SIOCGIWAUTH */ | |
1179 | (iw_handler) ieee80211_ioctl_siwencodeext, /* SIOCSIWENCODEEXT */ | |
1180 | (iw_handler) NULL, /* SIOCGIWENCODEEXT */ | |
1181 | (iw_handler) NULL, /* SIOCSIWPMKSA */ | |
1182 | (iw_handler) NULL, /* -- hole -- */ | |
1183 | }; | |
1184 | ||
f0706e82 JB |
1185 | const struct iw_handler_def ieee80211_iw_handler_def = |
1186 | { | |
1187 | .num_standard = ARRAY_SIZE(ieee80211_handler), | |
f0706e82 | 1188 | .standard = (iw_handler *) ieee80211_handler, |
f0706e82 JB |
1189 | .get_wireless_stats = ieee80211_get_wireless_stats, |
1190 | }; |