]>
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_siwfreq(struct net_device *dev, |
31 | struct iw_request_info *info, | |
32 | struct iw_freq *freq, char *extra) | |
33 | { | |
f0706e82 | 34 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
7e9debe9 JB |
35 | struct ieee80211_local *local = sdata->local; |
36 | struct ieee80211_channel *chan; | |
f0706e82 | 37 | |
46900298 | 38 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) |
af8cdcd8 | 39 | return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); |
46900298 | 40 | else if (sdata->vif.type == NL80211_IFTYPE_STATION) |
f2129354 | 41 | return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra); |
f0706e82 JB |
42 | |
43 | /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */ | |
44 | if (freq->e == 0) { | |
f2129354 JB |
45 | if (freq->m < 0) |
46 | return -EINVAL; | |
47 | else | |
7e9debe9 | 48 | chan = ieee80211_get_channel(local->hw.wiphy, |
8318d78a | 49 | ieee80211_channel_to_frequency(freq->m)); |
f0706e82 JB |
50 | } else { |
51 | int i, div = 1000000; | |
52 | for (i = 0; i < freq->e; i++) | |
53 | div /= 10; | |
7e9debe9 | 54 | if (div <= 0) |
f0706e82 | 55 | return -EINVAL; |
7e9debe9 | 56 | chan = ieee80211_get_channel(local->hw.wiphy, freq->m / div); |
f0706e82 | 57 | } |
7e9debe9 JB |
58 | |
59 | if (!chan) | |
60 | return -EINVAL; | |
61 | ||
62 | if (chan->flags & IEEE80211_CHAN_DISABLED) | |
63 | return -EINVAL; | |
64 | ||
65 | /* | |
66 | * no change except maybe auto -> fixed, ignore the HT | |
67 | * setting so you can fix a channel you're on already | |
68 | */ | |
69 | if (local->oper_channel == chan) | |
70 | return 0; | |
71 | ||
7e9debe9 JB |
72 | local->oper_channel = chan; |
73 | local->oper_channel_type = NL80211_CHAN_NO_HT; | |
74 | ieee80211_hw_config(local, 0); | |
75 | ||
76 | return 0; | |
f0706e82 JB |
77 | } |
78 | ||
79 | ||
80 | static int ieee80211_ioctl_giwfreq(struct net_device *dev, | |
81 | struct iw_request_info *info, | |
82 | struct iw_freq *freq, char *extra) | |
83 | { | |
84 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
af8cdcd8 JB |
85 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
86 | ||
87 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) | |
88 | return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); | |
f2129354 JB |
89 | else if (sdata->vif.type == NL80211_IFTYPE_STATION) |
90 | return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); | |
f0706e82 | 91 | |
7738231f | 92 | freq->m = local->oper_channel->center_freq; |
f0706e82 JB |
93 | freq->e = 6; |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
98 | ||
99 | static int ieee80211_ioctl_siwessid(struct net_device *dev, | |
100 | struct iw_request_info *info, | |
101 | struct iw_point *data, char *ssid) | |
102 | { | |
af8cdcd8 | 103 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
f0706e82 | 104 | |
af8cdcd8 JB |
105 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) |
106 | return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); | |
f2129354 JB |
107 | else if (sdata->vif.type == NL80211_IFTYPE_STATION) |
108 | return cfg80211_mgd_wext_siwessid(dev, info, data, ssid); | |
f0706e82 | 109 | |
f0706e82 JB |
110 | return -EOPNOTSUPP; |
111 | } | |
112 | ||
113 | ||
114 | static int ieee80211_ioctl_giwessid(struct net_device *dev, | |
115 | struct iw_request_info *info, | |
116 | struct iw_point *data, char *ssid) | |
117 | { | |
f0706e82 | 118 | struct ieee80211_sub_if_data *sdata; |
af8cdcd8 | 119 | |
f0706e82 | 120 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
af8cdcd8 JB |
121 | |
122 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) | |
123 | return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); | |
f2129354 JB |
124 | else if (sdata->vif.type == NL80211_IFTYPE_STATION) |
125 | return cfg80211_mgd_wext_giwessid(dev, info, data, ssid); | |
f0706e82 | 126 | |
f0706e82 JB |
127 | return -EOPNOTSUPP; |
128 | } | |
129 | ||
130 | ||
131 | static int ieee80211_ioctl_siwap(struct net_device *dev, | |
132 | struct iw_request_info *info, | |
133 | struct sockaddr *ap_addr, char *extra) | |
134 | { | |
af8cdcd8 JB |
135 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
136 | ||
137 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) | |
138 | return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra); | |
f0706e82 | 139 | |
f2129354 JB |
140 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
141 | return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra); | |
7986cf95 | 142 | |
f2129354 | 143 | if (sdata->vif.type == NL80211_IFTYPE_WDS) { |
44213b5e JB |
144 | /* |
145 | * If it is necessary to update the WDS peer address | |
146 | * while the interface is running, then we need to do | |
147 | * more work here, namely if it is running we need to | |
148 | * add a new and remove the old STA entry, this is | |
149 | * normally handled by _open() and _stop(). | |
150 | */ | |
151 | if (netif_running(dev)) | |
152 | return -EBUSY; | |
153 | ||
154 | memcpy(&sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data, | |
155 | ETH_ALEN); | |
156 | ||
157 | return 0; | |
f0706e82 JB |
158 | } |
159 | ||
160 | return -EOPNOTSUPP; | |
161 | } | |
162 | ||
163 | ||
164 | static int ieee80211_ioctl_giwap(struct net_device *dev, | |
165 | struct iw_request_info *info, | |
166 | struct sockaddr *ap_addr, char *extra) | |
167 | { | |
af8cdcd8 JB |
168 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
169 | ||
170 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) | |
171 | return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra); | |
f0706e82 | 172 | |
f2129354 JB |
173 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
174 | return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra); | |
175 | ||
176 | if (sdata->vif.type == NL80211_IFTYPE_WDS) { | |
f0706e82 JB |
177 | ap_addr->sa_family = ARPHRD_ETHER; |
178 | memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN); | |
179 | return 0; | |
180 | } | |
181 | ||
182 | return -EOPNOTSUPP; | |
183 | } | |
184 | ||
185 | ||
1fd5e589 LF |
186 | static int ieee80211_ioctl_siwrate(struct net_device *dev, |
187 | struct iw_request_info *info, | |
188 | struct iw_param *rate, char *extra) | |
189 | { | |
190 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
8318d78a | 191 | int i, err = -EINVAL; |
1fd5e589 LF |
192 | u32 target_rate = rate->value / 100000; |
193 | struct ieee80211_sub_if_data *sdata; | |
8318d78a | 194 | struct ieee80211_supported_band *sband; |
1fd5e589 LF |
195 | |
196 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
8318d78a JB |
197 | |
198 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | |
199 | ||
1fd5e589 LF |
200 | /* target_rate = -1, rate->fixed = 0 means auto only, so use all rates |
201 | * target_rate = X, rate->fixed = 1 means only rate X | |
202 | * target_rate = X, rate->fixed = 0 means all rates <= X */ | |
3e122be0 JB |
203 | sdata->max_ratectrl_rateidx = -1; |
204 | sdata->force_unicast_rateidx = -1; | |
1fd5e589 LF |
205 | if (rate->value < 0) |
206 | return 0; | |
8318d78a JB |
207 | |
208 | for (i=0; i< sband->n_bitrates; i++) { | |
209 | struct ieee80211_rate *brate = &sband->bitrates[i]; | |
210 | int this_rate = brate->bitrate; | |
1fd5e589 | 211 | |
1fd5e589 | 212 | if (target_rate == this_rate) { |
3e122be0 | 213 | sdata->max_ratectrl_rateidx = i; |
1fd5e589 | 214 | if (rate->fixed) |
3e122be0 | 215 | sdata->force_unicast_rateidx = i; |
8318d78a JB |
216 | err = 0; |
217 | break; | |
1fd5e589 LF |
218 | } |
219 | } | |
8318d78a | 220 | return err; |
1fd5e589 LF |
221 | } |
222 | ||
b3d88ad4 LF |
223 | static int ieee80211_ioctl_giwrate(struct net_device *dev, |
224 | struct iw_request_info *info, | |
225 | struct iw_param *rate, char *extra) | |
226 | { | |
227 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
228 | struct sta_info *sta; | |
229 | struct ieee80211_sub_if_data *sdata; | |
8318d78a | 230 | struct ieee80211_supported_band *sband; |
b3d88ad4 LF |
231 | |
232 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
8318d78a | 233 | |
05c914fe | 234 | if (sdata->vif.type != NL80211_IFTYPE_STATION) |
b3d88ad4 | 235 | return -EOPNOTSUPP; |
8318d78a JB |
236 | |
237 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | |
238 | ||
380a942b JB |
239 | rcu_read_lock(); |
240 | ||
46900298 | 241 | sta = sta_info_get(local, sdata->u.mgd.bssid); |
380a942b | 242 | |
e6a9854b JB |
243 | if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) |
244 | rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate; | |
b3d88ad4 LF |
245 | else |
246 | rate->value = 0; | |
380a942b JB |
247 | |
248 | rcu_read_unlock(); | |
249 | ||
250 | if (!sta) | |
251 | return -ENODEV; | |
252 | ||
8318d78a | 253 | rate->value *= 100000; |
d0709a65 | 254 | |
b3d88ad4 LF |
255 | return 0; |
256 | } | |
257 | ||
f0706e82 JB |
258 | /* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ |
259 | static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev) | |
260 | { | |
261 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
262 | struct iw_statistics *wstats = &local->wstats; | |
263 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
264 | struct sta_info *sta = NULL; | |
265 | ||
98dd6a57 JB |
266 | rcu_read_lock(); |
267 | ||
46900298 JB |
268 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
269 | sta = sta_info_get(local, sdata->u.mgd.bssid); | |
270 | ||
f0706e82 JB |
271 | if (!sta) { |
272 | wstats->discard.fragment = 0; | |
273 | wstats->discard.misc = 0; | |
274 | wstats->qual.qual = 0; | |
275 | wstats->qual.level = 0; | |
276 | wstats->qual.noise = 0; | |
277 | wstats->qual.updated = IW_QUAL_ALL_INVALID; | |
278 | } else { | |
24776cfd JB |
279 | wstats->qual.updated = 0; |
280 | /* | |
281 | * mirror what cfg80211 does for iwrange/scan results, | |
282 | * otherwise userspace gets confused. | |
283 | */ | |
284 | if (local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC | | |
285 | IEEE80211_HW_SIGNAL_DBM)) { | |
286 | wstats->qual.updated |= IW_QUAL_LEVEL_UPDATED; | |
287 | wstats->qual.updated |= IW_QUAL_QUAL_UPDATED; | |
288 | } else { | |
289 | wstats->qual.updated |= IW_QUAL_LEVEL_INVALID; | |
290 | wstats->qual.updated |= IW_QUAL_QUAL_INVALID; | |
291 | } | |
292 | ||
293 | if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) { | |
294 | wstats->qual.level = sta->last_signal; | |
295 | wstats->qual.qual = sta->last_signal; | |
296 | } else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { | |
297 | int sig = sta->last_signal; | |
298 | ||
299 | wstats->qual.updated |= IW_QUAL_DBM; | |
300 | wstats->qual.level = sig; | |
301 | if (sig < -110) | |
302 | sig = -110; | |
303 | else if (sig > -40) | |
304 | sig = -40; | |
305 | wstats->qual.qual = sig + 110; | |
306 | } | |
307 | ||
308 | if (local->hw.flags & IEEE80211_HW_NOISE_DBM) { | |
309 | /* | |
310 | * This assumes that if driver reports noise, it also | |
311 | * reports signal in dBm. | |
312 | */ | |
313 | wstats->qual.noise = sta->last_noise; | |
314 | wstats->qual.updated |= IW_QUAL_NOISE_UPDATED; | |
315 | } else { | |
316 | wstats->qual.updated |= IW_QUAL_NOISE_INVALID; | |
317 | } | |
f0706e82 | 318 | } |
98dd6a57 JB |
319 | |
320 | rcu_read_unlock(); | |
321 | ||
f0706e82 JB |
322 | return wstats; |
323 | } | |
324 | ||
f0706e82 JB |
325 | /* Structures to export the Wireless Handlers */ |
326 | ||
327 | static const iw_handler ieee80211_handler[] = | |
328 | { | |
329 | (iw_handler) NULL, /* SIOCSIWCOMMIT */ | |
fee52678 | 330 | (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */ |
f0706e82 JB |
331 | (iw_handler) NULL, /* SIOCSIWNWID */ |
332 | (iw_handler) NULL, /* SIOCGIWNWID */ | |
333 | (iw_handler) ieee80211_ioctl_siwfreq, /* SIOCSIWFREQ */ | |
334 | (iw_handler) ieee80211_ioctl_giwfreq, /* SIOCGIWFREQ */ | |
e60c7744 JB |
335 | (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */ |
336 | (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */ | |
f0706e82 JB |
337 | (iw_handler) NULL, /* SIOCSIWSENS */ |
338 | (iw_handler) NULL, /* SIOCGIWSENS */ | |
339 | (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ | |
4aa188e1 | 340 | (iw_handler) cfg80211_wext_giwrange, /* SIOCGIWRANGE */ |
f0706e82 JB |
341 | (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ |
342 | (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ | |
343 | (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ | |
344 | (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ | |
5d4ecd93 JB |
345 | (iw_handler) NULL, /* SIOCSIWSPY */ |
346 | (iw_handler) NULL, /* SIOCGIWSPY */ | |
347 | (iw_handler) NULL, /* SIOCSIWTHRSPY */ | |
348 | (iw_handler) NULL, /* SIOCGIWTHRSPY */ | |
f0706e82 JB |
349 | (iw_handler) ieee80211_ioctl_siwap, /* SIOCSIWAP */ |
350 | (iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */ | |
691597cb | 351 | (iw_handler) cfg80211_wext_siwmlme, /* SIOCSIWMLME */ |
f0706e82 | 352 | (iw_handler) NULL, /* SIOCGIWAPLIST */ |
2a519311 JB |
353 | (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */ |
354 | (iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */ | |
f0706e82 JB |
355 | (iw_handler) ieee80211_ioctl_siwessid, /* SIOCSIWESSID */ |
356 | (iw_handler) ieee80211_ioctl_giwessid, /* SIOCGIWESSID */ | |
357 | (iw_handler) NULL, /* SIOCSIWNICKN */ | |
358 | (iw_handler) NULL, /* SIOCGIWNICKN */ | |
359 | (iw_handler) NULL, /* -- hole -- */ | |
360 | (iw_handler) NULL, /* -- hole -- */ | |
1fd5e589 | 361 | (iw_handler) ieee80211_ioctl_siwrate, /* SIOCSIWRATE */ |
b3d88ad4 | 362 | (iw_handler) ieee80211_ioctl_giwrate, /* SIOCGIWRATE */ |
b9a5f8ca JM |
363 | (iw_handler) cfg80211_wext_siwrts, /* SIOCSIWRTS */ |
364 | (iw_handler) cfg80211_wext_giwrts, /* SIOCGIWRTS */ | |
365 | (iw_handler) cfg80211_wext_siwfrag, /* SIOCSIWFRAG */ | |
366 | (iw_handler) cfg80211_wext_giwfrag, /* SIOCGIWFRAG */ | |
7643a2c3 JB |
367 | (iw_handler) cfg80211_wext_siwtxpower, /* SIOCSIWTXPOW */ |
368 | (iw_handler) cfg80211_wext_giwtxpower, /* SIOCGIWTXPOW */ | |
b9a5f8ca JM |
369 | (iw_handler) cfg80211_wext_siwretry, /* SIOCSIWRETRY */ |
370 | (iw_handler) cfg80211_wext_giwretry, /* SIOCGIWRETRY */ | |
08645126 JB |
371 | (iw_handler) cfg80211_wext_siwencode, /* SIOCSIWENCODE */ |
372 | (iw_handler) cfg80211_wext_giwencode, /* SIOCGIWENCODE */ | |
bc92afd9 JB |
373 | (iw_handler) cfg80211_wext_siwpower, /* SIOCSIWPOWER */ |
374 | (iw_handler) cfg80211_wext_giwpower, /* SIOCGIWPOWER */ | |
f0706e82 JB |
375 | (iw_handler) NULL, /* -- hole -- */ |
376 | (iw_handler) NULL, /* -- hole -- */ | |
f2129354 | 377 | (iw_handler) cfg80211_wext_siwgenie, /* SIOCSIWGENIE */ |
f0706e82 | 378 | (iw_handler) NULL, /* SIOCGIWGENIE */ |
f2129354 JB |
379 | (iw_handler) cfg80211_wext_siwauth, /* SIOCSIWAUTH */ |
380 | (iw_handler) cfg80211_wext_giwauth, /* SIOCGIWAUTH */ | |
08645126 | 381 | (iw_handler) cfg80211_wext_siwencodeext, /* SIOCSIWENCODEEXT */ |
f0706e82 JB |
382 | (iw_handler) NULL, /* SIOCGIWENCODEEXT */ |
383 | (iw_handler) NULL, /* SIOCSIWPMKSA */ | |
384 | (iw_handler) NULL, /* -- hole -- */ | |
385 | }; | |
386 | ||
f0706e82 JB |
387 | const struct iw_handler_def ieee80211_iw_handler_def = |
388 | { | |
389 | .num_standard = ARRAY_SIZE(ieee80211_handler), | |
f0706e82 | 390 | .standard = (iw_handler *) ieee80211_handler, |
f0706e82 JB |
391 | .get_wireless_stats = ieee80211_get_wireless_stats, |
392 | }; |