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 | |
f0706e82 JB |
30 | static int ieee80211_ioctl_siwgenie(struct net_device *dev, |
31 | struct iw_request_info *info, | |
32 | struct iw_point *data, char *extra) | |
33 | { | |
34 | struct ieee80211_sub_if_data *sdata; | |
f0706e82 | 35 | |
ddd3d2be JB |
36 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
37 | ||
46900298 | 38 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
f698d856 | 39 | int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length); |
175427ce | 40 | if (ret && ret != -EALREADY) |
f0706e82 | 41 | return ret; |
46900298 | 42 | sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; |
636a5d36 | 43 | sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; |
3f77316c | 44 | sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; |
175427ce JB |
45 | if (ret != -EALREADY) |
46 | ieee80211_sta_req_auth(sdata); | |
f0706e82 JB |
47 | return 0; |
48 | } | |
49 | ||
f0706e82 JB |
50 | return -EOPNOTSUPP; |
51 | } | |
52 | ||
f0706e82 JB |
53 | static int ieee80211_ioctl_siwfreq(struct net_device *dev, |
54 | struct iw_request_info *info, | |
55 | struct iw_freq *freq, char *extra) | |
56 | { | |
f0706e82 | 57 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
7e9debe9 JB |
58 | struct ieee80211_local *local = sdata->local; |
59 | struct ieee80211_channel *chan; | |
f0706e82 | 60 | |
46900298 | 61 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) |
af8cdcd8 | 62 | return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); |
46900298 JB |
63 | else if (sdata->vif.type == NL80211_IFTYPE_STATION) |
64 | sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL; | |
f0706e82 JB |
65 | |
66 | /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */ | |
67 | if (freq->e == 0) { | |
68 | if (freq->m < 0) { | |
af8cdcd8 | 69 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
46900298 | 70 | sdata->u.mgd.flags |= |
d6f2da5b | 71 | IEEE80211_STA_AUTO_CHANNEL_SEL; |
f0706e82 JB |
72 | return 0; |
73 | } else | |
7e9debe9 | 74 | chan = ieee80211_get_channel(local->hw.wiphy, |
8318d78a | 75 | ieee80211_channel_to_frequency(freq->m)); |
f0706e82 JB |
76 | } else { |
77 | int i, div = 1000000; | |
78 | for (i = 0; i < freq->e; i++) | |
79 | div /= 10; | |
7e9debe9 | 80 | if (div <= 0) |
f0706e82 | 81 | return -EINVAL; |
7e9debe9 | 82 | chan = ieee80211_get_channel(local->hw.wiphy, freq->m / div); |
f0706e82 | 83 | } |
7e9debe9 JB |
84 | |
85 | if (!chan) | |
86 | return -EINVAL; | |
87 | ||
88 | if (chan->flags & IEEE80211_CHAN_DISABLED) | |
89 | return -EINVAL; | |
90 | ||
91 | /* | |
92 | * no change except maybe auto -> fixed, ignore the HT | |
93 | * setting so you can fix a channel you're on already | |
94 | */ | |
95 | if (local->oper_channel == chan) | |
96 | return 0; | |
97 | ||
98 | if (sdata->vif.type == NL80211_IFTYPE_STATION) | |
99 | ieee80211_sta_req_auth(sdata); | |
100 | ||
101 | local->oper_channel = chan; | |
102 | local->oper_channel_type = NL80211_CHAN_NO_HT; | |
103 | ieee80211_hw_config(local, 0); | |
104 | ||
105 | return 0; | |
f0706e82 JB |
106 | } |
107 | ||
108 | ||
109 | static int ieee80211_ioctl_giwfreq(struct net_device *dev, | |
110 | struct iw_request_info *info, | |
111 | struct iw_freq *freq, char *extra) | |
112 | { | |
113 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
af8cdcd8 JB |
114 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
115 | ||
116 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) | |
117 | return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); | |
f0706e82 | 118 | |
7738231f | 119 | freq->m = local->oper_channel->center_freq; |
f0706e82 JB |
120 | freq->e = 6; |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
125 | ||
126 | static int ieee80211_ioctl_siwessid(struct net_device *dev, | |
127 | struct iw_request_info *info, | |
128 | struct iw_point *data, char *ssid) | |
129 | { | |
af8cdcd8 | 130 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
f0706e82 | 131 | size_t len = data->length; |
46900298 | 132 | int ret; |
f0706e82 | 133 | |
af8cdcd8 JB |
134 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) |
135 | return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); | |
136 | ||
f0706e82 JB |
137 | /* iwconfig uses nul termination in SSID.. */ |
138 | if (len > 0 && ssid[len - 1] == '\0') | |
139 | len--; | |
140 | ||
46900298 | 141 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
d6f2da5b | 142 | if (data->flags) |
46900298 | 143 | sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; |
d6f2da5b | 144 | else |
46900298 JB |
145 | sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL; |
146 | ||
f698d856 | 147 | ret = ieee80211_sta_set_ssid(sdata, ssid, len); |
f0706e82 JB |
148 | if (ret) |
149 | return ret; | |
46900298 | 150 | |
636a5d36 | 151 | sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; |
3f77316c | 152 | sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; |
46900298 | 153 | ieee80211_sta_req_auth(sdata); |
f0706e82 | 154 | return 0; |
af8cdcd8 | 155 | } |
f0706e82 | 156 | |
f0706e82 JB |
157 | return -EOPNOTSUPP; |
158 | } | |
159 | ||
160 | ||
161 | static int ieee80211_ioctl_giwessid(struct net_device *dev, | |
162 | struct iw_request_info *info, | |
163 | struct iw_point *data, char *ssid) | |
164 | { | |
165 | size_t len; | |
f0706e82 | 166 | struct ieee80211_sub_if_data *sdata; |
af8cdcd8 | 167 | |
f0706e82 | 168 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
af8cdcd8 JB |
169 | |
170 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) | |
171 | return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); | |
172 | ||
46900298 | 173 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
f698d856 | 174 | int res = ieee80211_sta_get_ssid(sdata, ssid, &len); |
f0706e82 JB |
175 | if (res == 0) { |
176 | data->length = len; | |
177 | data->flags = 1; | |
178 | } else | |
179 | data->flags = 0; | |
180 | return res; | |
181 | } | |
182 | ||
f0706e82 JB |
183 | return -EOPNOTSUPP; |
184 | } | |
185 | ||
186 | ||
187 | static int ieee80211_ioctl_siwap(struct net_device *dev, | |
188 | struct iw_request_info *info, | |
189 | struct sockaddr *ap_addr, char *extra) | |
190 | { | |
af8cdcd8 JB |
191 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
192 | ||
193 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) | |
194 | return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra); | |
f0706e82 | 195 | |
46900298 | 196 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
f0706e82 | 197 | int ret; |
7986cf95 | 198 | |
d6f2da5b | 199 | if (is_zero_ether_addr((u8 *) &ap_addr->sa_data)) |
46900298 | 200 | sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL | |
d6f2da5b JS |
201 | IEEE80211_STA_AUTO_CHANNEL_SEL; |
202 | else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data)) | |
46900298 | 203 | sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL; |
f0706e82 | 204 | else |
46900298 | 205 | sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; |
f698d856 | 206 | ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data); |
f0706e82 JB |
207 | if (ret) |
208 | return ret; | |
636a5d36 | 209 | sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; |
3f77316c | 210 | sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; |
46900298 | 211 | ieee80211_sta_req_auth(sdata); |
f0706e82 | 212 | return 0; |
05c914fe | 213 | } else if (sdata->vif.type == NL80211_IFTYPE_WDS) { |
44213b5e JB |
214 | /* |
215 | * If it is necessary to update the WDS peer address | |
216 | * while the interface is running, then we need to do | |
217 | * more work here, namely if it is running we need to | |
218 | * add a new and remove the old STA entry, this is | |
219 | * normally handled by _open() and _stop(). | |
220 | */ | |
221 | if (netif_running(dev)) | |
222 | return -EBUSY; | |
223 | ||
224 | memcpy(&sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data, | |
225 | ETH_ALEN); | |
226 | ||
227 | return 0; | |
f0706e82 JB |
228 | } |
229 | ||
230 | return -EOPNOTSUPP; | |
231 | } | |
232 | ||
233 | ||
234 | static int ieee80211_ioctl_giwap(struct net_device *dev, | |
235 | struct iw_request_info *info, | |
236 | struct sockaddr *ap_addr, char *extra) | |
237 | { | |
af8cdcd8 JB |
238 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
239 | ||
240 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) | |
241 | return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra); | |
f0706e82 | 242 | |
46900298 JB |
243 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
244 | if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATED) { | |
d4231ca3 | 245 | ap_addr->sa_family = ARPHRD_ETHER; |
46900298 JB |
246 | memcpy(&ap_addr->sa_data, sdata->u.mgd.bssid, ETH_ALEN); |
247 | } else | |
d4231ca3 | 248 | memset(&ap_addr->sa_data, 0, ETH_ALEN); |
46900298 | 249 | return 0; |
05c914fe | 250 | } else if (sdata->vif.type == NL80211_IFTYPE_WDS) { |
f0706e82 JB |
251 | ap_addr->sa_family = ARPHRD_ETHER; |
252 | memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN); | |
253 | return 0; | |
254 | } | |
255 | ||
256 | return -EOPNOTSUPP; | |
257 | } | |
258 | ||
259 | ||
1fd5e589 LF |
260 | static int ieee80211_ioctl_siwrate(struct net_device *dev, |
261 | struct iw_request_info *info, | |
262 | struct iw_param *rate, char *extra) | |
263 | { | |
264 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
8318d78a | 265 | int i, err = -EINVAL; |
1fd5e589 LF |
266 | u32 target_rate = rate->value / 100000; |
267 | struct ieee80211_sub_if_data *sdata; | |
8318d78a | 268 | struct ieee80211_supported_band *sband; |
1fd5e589 LF |
269 | |
270 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
8318d78a JB |
271 | |
272 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | |
273 | ||
1fd5e589 LF |
274 | /* target_rate = -1, rate->fixed = 0 means auto only, so use all rates |
275 | * target_rate = X, rate->fixed = 1 means only rate X | |
276 | * target_rate = X, rate->fixed = 0 means all rates <= X */ | |
3e122be0 JB |
277 | sdata->max_ratectrl_rateidx = -1; |
278 | sdata->force_unicast_rateidx = -1; | |
1fd5e589 LF |
279 | if (rate->value < 0) |
280 | return 0; | |
8318d78a JB |
281 | |
282 | for (i=0; i< sband->n_bitrates; i++) { | |
283 | struct ieee80211_rate *brate = &sband->bitrates[i]; | |
284 | int this_rate = brate->bitrate; | |
1fd5e589 | 285 | |
1fd5e589 | 286 | if (target_rate == this_rate) { |
3e122be0 | 287 | sdata->max_ratectrl_rateidx = i; |
1fd5e589 | 288 | if (rate->fixed) |
3e122be0 | 289 | sdata->force_unicast_rateidx = i; |
8318d78a JB |
290 | err = 0; |
291 | break; | |
1fd5e589 LF |
292 | } |
293 | } | |
8318d78a | 294 | return err; |
1fd5e589 LF |
295 | } |
296 | ||
b3d88ad4 LF |
297 | static int ieee80211_ioctl_giwrate(struct net_device *dev, |
298 | struct iw_request_info *info, | |
299 | struct iw_param *rate, char *extra) | |
300 | { | |
301 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
302 | struct sta_info *sta; | |
303 | struct ieee80211_sub_if_data *sdata; | |
8318d78a | 304 | struct ieee80211_supported_band *sband; |
b3d88ad4 LF |
305 | |
306 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
8318d78a | 307 | |
05c914fe | 308 | if (sdata->vif.type != NL80211_IFTYPE_STATION) |
b3d88ad4 | 309 | return -EOPNOTSUPP; |
8318d78a JB |
310 | |
311 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | |
312 | ||
380a942b JB |
313 | rcu_read_lock(); |
314 | ||
46900298 | 315 | sta = sta_info_get(local, sdata->u.mgd.bssid); |
380a942b | 316 | |
e6a9854b JB |
317 | if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) |
318 | rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate; | |
b3d88ad4 LF |
319 | else |
320 | rate->value = 0; | |
380a942b JB |
321 | |
322 | rcu_read_unlock(); | |
323 | ||
324 | if (!sta) | |
325 | return -ENODEV; | |
326 | ||
8318d78a | 327 | rate->value *= 100000; |
d0709a65 | 328 | |
b3d88ad4 LF |
329 | return 0; |
330 | } | |
331 | ||
49292d56 SO |
332 | static int ieee80211_ioctl_siwpower(struct net_device *dev, |
333 | struct iw_request_info *info, | |
334 | struct iw_param *wrq, | |
335 | char *extra) | |
336 | { | |
e0cb686f | 337 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
49292d56 SO |
338 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); |
339 | struct ieee80211_conf *conf = &local->hw.conf; | |
965bedad | 340 | int timeout = 0; |
e0cb686f KV |
341 | bool ps; |
342 | ||
4be8c387 JB |
343 | if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) |
344 | return -EOPNOTSUPP; | |
345 | ||
e0cb686f KV |
346 | if (sdata->vif.type != NL80211_IFTYPE_STATION) |
347 | return -EINVAL; | |
49292d56 SO |
348 | |
349 | if (wrq->disabled) { | |
e0cb686f | 350 | ps = false; |
520eb820 | 351 | timeout = 0; |
e0cb686f | 352 | goto set; |
49292d56 SO |
353 | } |
354 | ||
355 | switch (wrq->flags & IW_POWER_MODE) { | |
356 | case IW_POWER_ON: /* If not specified */ | |
357 | case IW_POWER_MODE: /* If set all mask */ | |
358 | case IW_POWER_ALL_R: /* If explicitely state all */ | |
e0cb686f | 359 | ps = true; |
49292d56 | 360 | break; |
520eb820 | 361 | default: /* Otherwise we ignore */ |
e9aeabae | 362 | return -EINVAL; |
49292d56 SO |
363 | } |
364 | ||
e9aeabae JB |
365 | if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT)) |
366 | return -EINVAL; | |
367 | ||
520eb820 KV |
368 | if (wrq->flags & IW_POWER_TIMEOUT) |
369 | timeout = wrq->value / 1000; | |
e0cb686f | 370 | |
4be8c387 | 371 | set: |
965bedad JB |
372 | if (ps == sdata->u.mgd.powersave && timeout == conf->dynamic_ps_timeout) |
373 | return 0; | |
520eb820 | 374 | |
965bedad | 375 | sdata->u.mgd.powersave = ps; |
46f2c4bd | 376 | conf->dynamic_ps_timeout = timeout; |
e0cb686f | 377 | |
4be8c387 | 378 | if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) |
e255d5eb | 379 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); |
4be8c387 | 380 | |
10f644a4 | 381 | ieee80211_recalc_ps(local, -1); |
e0cb686f | 382 | |
965bedad | 383 | return 0; |
49292d56 SO |
384 | } |
385 | ||
386 | static int ieee80211_ioctl_giwpower(struct net_device *dev, | |
387 | struct iw_request_info *info, | |
388 | union iwreq_data *wrqu, | |
389 | char *extra) | |
390 | { | |
965bedad | 391 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
49292d56 | 392 | |
965bedad | 393 | wrqu->power.disabled = !sdata->u.mgd.powersave; |
49292d56 SO |
394 | |
395 | return 0; | |
396 | } | |
397 | ||
f0706e82 JB |
398 | static int ieee80211_ioctl_siwauth(struct net_device *dev, |
399 | struct iw_request_info *info, | |
400 | struct iw_param *data, char *extra) | |
401 | { | |
f0706e82 JB |
402 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
403 | int ret = 0; | |
404 | ||
405 | switch (data->flags & IW_AUTH_INDEX) { | |
406 | case IW_AUTH_WPA_VERSION: | |
f0706e82 JB |
407 | case IW_AUTH_CIPHER_GROUP: |
408 | case IW_AUTH_WPA_ENABLED: | |
409 | case IW_AUTH_RX_UNENCRYPTED_EAPOL: | |
f0706e82 | 410 | case IW_AUTH_KEY_MGMT: |
54604d3a | 411 | case IW_AUTH_CIPHER_GROUP_MGMT: |
5b98b1f7 | 412 | break; |
eb46936b VT |
413 | case IW_AUTH_CIPHER_PAIRWISE: |
414 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | |
415 | if (data->value & (IW_AUTH_CIPHER_WEP40 | | |
416 | IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_TKIP)) | |
46900298 | 417 | sdata->u.mgd.flags |= |
eb46936b VT |
418 | IEEE80211_STA_TKIP_WEP_USED; |
419 | else | |
46900298 | 420 | sdata->u.mgd.flags &= |
eb46936b VT |
421 | ~IEEE80211_STA_TKIP_WEP_USED; |
422 | } | |
423 | break; | |
b1357a81 JB |
424 | case IW_AUTH_DROP_UNENCRYPTED: |
425 | sdata->drop_unencrypted = !!data->value; | |
426 | break; | |
5b98b1f7 | 427 | case IW_AUTH_PRIVACY_INVOKED: |
05c914fe | 428 | if (sdata->vif.type != NL80211_IFTYPE_STATION) |
f0706e82 JB |
429 | ret = -EINVAL; |
430 | else { | |
46900298 | 431 | sdata->u.mgd.flags &= ~IEEE80211_STA_PRIVACY_INVOKED; |
f0706e82 | 432 | /* |
5b98b1f7 JB |
433 | * Privacy invoked by wpa_supplicant, store the |
434 | * value and allow associating to a protected | |
435 | * network without having a key up front. | |
f0706e82 | 436 | */ |
5b98b1f7 | 437 | if (data->value) |
46900298 | 438 | sdata->u.mgd.flags |= |
5b98b1f7 | 439 | IEEE80211_STA_PRIVACY_INVOKED; |
f0706e82 JB |
440 | } |
441 | break; | |
442 | case IW_AUTH_80211_AUTH_ALG: | |
46900298 JB |
443 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
444 | sdata->u.mgd.auth_algs = data->value; | |
f0706e82 JB |
445 | else |
446 | ret = -EOPNOTSUPP; | |
447 | break; | |
fdfacf0a | 448 | case IW_AUTH_MFP: |
4375d083 JM |
449 | if (!(sdata->local->hw.flags & IEEE80211_HW_MFP_CAPABLE)) { |
450 | ret = -EOPNOTSUPP; | |
451 | break; | |
452 | } | |
46900298 | 453 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
e4e5e2b0 JB |
454 | switch (data->value) { |
455 | case IW_AUTH_MFP_DISABLED: | |
46900298 | 456 | sdata->u.mgd.mfp = IEEE80211_MFP_DISABLED; |
e4e5e2b0 JB |
457 | break; |
458 | case IW_AUTH_MFP_OPTIONAL: | |
46900298 | 459 | sdata->u.mgd.mfp = IEEE80211_MFP_OPTIONAL; |
e4e5e2b0 JB |
460 | break; |
461 | case IW_AUTH_MFP_REQUIRED: | |
46900298 | 462 | sdata->u.mgd.mfp = IEEE80211_MFP_REQUIRED; |
e4e5e2b0 JB |
463 | break; |
464 | default: | |
465 | ret = -EINVAL; | |
466 | } | |
467 | } else | |
fdfacf0a JM |
468 | ret = -EOPNOTSUPP; |
469 | break; | |
f0706e82 JB |
470 | default: |
471 | ret = -EOPNOTSUPP; | |
472 | break; | |
473 | } | |
474 | return ret; | |
475 | } | |
476 | ||
477 | /* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ | |
478 | static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev) | |
479 | { | |
480 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
481 | struct iw_statistics *wstats = &local->wstats; | |
482 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
483 | struct sta_info *sta = NULL; | |
484 | ||
98dd6a57 JB |
485 | rcu_read_lock(); |
486 | ||
46900298 JB |
487 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
488 | sta = sta_info_get(local, sdata->u.mgd.bssid); | |
489 | ||
f0706e82 JB |
490 | if (!sta) { |
491 | wstats->discard.fragment = 0; | |
492 | wstats->discard.misc = 0; | |
493 | wstats->qual.qual = 0; | |
494 | wstats->qual.level = 0; | |
495 | wstats->qual.noise = 0; | |
496 | wstats->qual.updated = IW_QUAL_ALL_INVALID; | |
497 | } else { | |
24776cfd JB |
498 | wstats->qual.updated = 0; |
499 | /* | |
500 | * mirror what cfg80211 does for iwrange/scan results, | |
501 | * otherwise userspace gets confused. | |
502 | */ | |
503 | if (local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC | | |
504 | IEEE80211_HW_SIGNAL_DBM)) { | |
505 | wstats->qual.updated |= IW_QUAL_LEVEL_UPDATED; | |
506 | wstats->qual.updated |= IW_QUAL_QUAL_UPDATED; | |
507 | } else { | |
508 | wstats->qual.updated |= IW_QUAL_LEVEL_INVALID; | |
509 | wstats->qual.updated |= IW_QUAL_QUAL_INVALID; | |
510 | } | |
511 | ||
512 | if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) { | |
513 | wstats->qual.level = sta->last_signal; | |
514 | wstats->qual.qual = sta->last_signal; | |
515 | } else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { | |
516 | int sig = sta->last_signal; | |
517 | ||
518 | wstats->qual.updated |= IW_QUAL_DBM; | |
519 | wstats->qual.level = sig; | |
520 | if (sig < -110) | |
521 | sig = -110; | |
522 | else if (sig > -40) | |
523 | sig = -40; | |
524 | wstats->qual.qual = sig + 110; | |
525 | } | |
526 | ||
527 | if (local->hw.flags & IEEE80211_HW_NOISE_DBM) { | |
528 | /* | |
529 | * This assumes that if driver reports noise, it also | |
530 | * reports signal in dBm. | |
531 | */ | |
532 | wstats->qual.noise = sta->last_noise; | |
533 | wstats->qual.updated |= IW_QUAL_NOISE_UPDATED; | |
534 | } else { | |
535 | wstats->qual.updated |= IW_QUAL_NOISE_INVALID; | |
536 | } | |
f0706e82 | 537 | } |
98dd6a57 JB |
538 | |
539 | rcu_read_unlock(); | |
540 | ||
f0706e82 JB |
541 | return wstats; |
542 | } | |
543 | ||
544 | static int ieee80211_ioctl_giwauth(struct net_device *dev, | |
545 | struct iw_request_info *info, | |
546 | struct iw_param *data, char *extra) | |
547 | { | |
548 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
549 | int ret = 0; | |
550 | ||
551 | switch (data->flags & IW_AUTH_INDEX) { | |
552 | case IW_AUTH_80211_AUTH_ALG: | |
46900298 JB |
553 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
554 | data->value = sdata->u.mgd.auth_algs; | |
f0706e82 JB |
555 | else |
556 | ret = -EOPNOTSUPP; | |
557 | break; | |
558 | default: | |
559 | ret = -EOPNOTSUPP; | |
560 | break; | |
561 | } | |
562 | return ret; | |
563 | } | |
564 | ||
565 | ||
f0706e82 JB |
566 | /* Structures to export the Wireless Handlers */ |
567 | ||
568 | static const iw_handler ieee80211_handler[] = | |
569 | { | |
570 | (iw_handler) NULL, /* SIOCSIWCOMMIT */ | |
fee52678 | 571 | (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */ |
f0706e82 JB |
572 | (iw_handler) NULL, /* SIOCSIWNWID */ |
573 | (iw_handler) NULL, /* SIOCGIWNWID */ | |
574 | (iw_handler) ieee80211_ioctl_siwfreq, /* SIOCSIWFREQ */ | |
575 | (iw_handler) ieee80211_ioctl_giwfreq, /* SIOCGIWFREQ */ | |
e60c7744 JB |
576 | (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */ |
577 | (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */ | |
f0706e82 JB |
578 | (iw_handler) NULL, /* SIOCSIWSENS */ |
579 | (iw_handler) NULL, /* SIOCGIWSENS */ | |
580 | (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ | |
4aa188e1 | 581 | (iw_handler) cfg80211_wext_giwrange, /* SIOCGIWRANGE */ |
f0706e82 JB |
582 | (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ |
583 | (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ | |
584 | (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ | |
585 | (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ | |
5d4ecd93 JB |
586 | (iw_handler) NULL, /* SIOCSIWSPY */ |
587 | (iw_handler) NULL, /* SIOCGIWSPY */ | |
588 | (iw_handler) NULL, /* SIOCSIWTHRSPY */ | |
589 | (iw_handler) NULL, /* SIOCGIWTHRSPY */ | |
f0706e82 JB |
590 | (iw_handler) ieee80211_ioctl_siwap, /* SIOCSIWAP */ |
591 | (iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */ | |
691597cb | 592 | (iw_handler) cfg80211_wext_siwmlme, /* SIOCSIWMLME */ |
f0706e82 | 593 | (iw_handler) NULL, /* SIOCGIWAPLIST */ |
2a519311 JB |
594 | (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */ |
595 | (iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */ | |
f0706e82 JB |
596 | (iw_handler) ieee80211_ioctl_siwessid, /* SIOCSIWESSID */ |
597 | (iw_handler) ieee80211_ioctl_giwessid, /* SIOCGIWESSID */ | |
598 | (iw_handler) NULL, /* SIOCSIWNICKN */ | |
599 | (iw_handler) NULL, /* SIOCGIWNICKN */ | |
600 | (iw_handler) NULL, /* -- hole -- */ | |
601 | (iw_handler) NULL, /* -- hole -- */ | |
1fd5e589 | 602 | (iw_handler) ieee80211_ioctl_siwrate, /* SIOCSIWRATE */ |
b3d88ad4 | 603 | (iw_handler) ieee80211_ioctl_giwrate, /* SIOCGIWRATE */ |
b9a5f8ca JM |
604 | (iw_handler) cfg80211_wext_siwrts, /* SIOCSIWRTS */ |
605 | (iw_handler) cfg80211_wext_giwrts, /* SIOCGIWRTS */ | |
606 | (iw_handler) cfg80211_wext_siwfrag, /* SIOCSIWFRAG */ | |
607 | (iw_handler) cfg80211_wext_giwfrag, /* SIOCGIWFRAG */ | |
7643a2c3 JB |
608 | (iw_handler) cfg80211_wext_siwtxpower, /* SIOCSIWTXPOW */ |
609 | (iw_handler) cfg80211_wext_giwtxpower, /* SIOCGIWTXPOW */ | |
b9a5f8ca JM |
610 | (iw_handler) cfg80211_wext_siwretry, /* SIOCSIWRETRY */ |
611 | (iw_handler) cfg80211_wext_giwretry, /* SIOCGIWRETRY */ | |
08645126 JB |
612 | (iw_handler) cfg80211_wext_siwencode, /* SIOCSIWENCODE */ |
613 | (iw_handler) cfg80211_wext_giwencode, /* SIOCGIWENCODE */ | |
49292d56 SO |
614 | (iw_handler) ieee80211_ioctl_siwpower, /* SIOCSIWPOWER */ |
615 | (iw_handler) ieee80211_ioctl_giwpower, /* SIOCGIWPOWER */ | |
f0706e82 JB |
616 | (iw_handler) NULL, /* -- hole -- */ |
617 | (iw_handler) NULL, /* -- hole -- */ | |
618 | (iw_handler) ieee80211_ioctl_siwgenie, /* SIOCSIWGENIE */ | |
619 | (iw_handler) NULL, /* SIOCGIWGENIE */ | |
620 | (iw_handler) ieee80211_ioctl_siwauth, /* SIOCSIWAUTH */ | |
621 | (iw_handler) ieee80211_ioctl_giwauth, /* SIOCGIWAUTH */ | |
08645126 | 622 | (iw_handler) cfg80211_wext_siwencodeext, /* SIOCSIWENCODEEXT */ |
f0706e82 JB |
623 | (iw_handler) NULL, /* SIOCGIWENCODEEXT */ |
624 | (iw_handler) NULL, /* SIOCSIWPMKSA */ | |
625 | (iw_handler) NULL, /* -- hole -- */ | |
626 | }; | |
627 | ||
f0706e82 JB |
628 | const struct iw_handler_def ieee80211_iw_handler_def = |
629 | { | |
630 | .num_standard = ARRAY_SIZE(ieee80211_handler), | |
f0706e82 | 631 | .standard = (iw_handler *) ieee80211_handler, |
f0706e82 JB |
632 | .get_wireless_stats = ieee80211_get_wireless_stats, |
633 | }; |