]>
Commit | Line | Data |
---|---|---|
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" | |
24 | #include "led.h" | |
25 | #include "rate.h" | |
26 | #include "wpa.h" | |
27 | #include "aes_ccm.h" | |
28 | ||
29 | ||
30 | static int ieee80211_ioctl_siwfreq(struct net_device *dev, | |
31 | struct iw_request_info *info, | |
32 | struct iw_freq *freq, char *extra) | |
33 | { | |
34 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
35 | struct ieee80211_local *local = sdata->local; | |
36 | struct ieee80211_channel *chan; | |
37 | ||
38 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) | |
39 | return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); | |
40 | else if (sdata->vif.type == NL80211_IFTYPE_STATION) | |
41 | return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra); | |
42 | ||
43 | /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */ | |
44 | if (freq->e == 0) { | |
45 | if (freq->m < 0) | |
46 | return -EINVAL; | |
47 | else | |
48 | chan = ieee80211_get_channel(local->hw.wiphy, | |
49 | ieee80211_channel_to_frequency(freq->m)); | |
50 | } else { | |
51 | int i, div = 1000000; | |
52 | for (i = 0; i < freq->e; i++) | |
53 | div /= 10; | |
54 | if (div <= 0) | |
55 | return -EINVAL; | |
56 | chan = ieee80211_get_channel(local->hw.wiphy, freq->m / div); | |
57 | } | |
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 | ||
72 | local->oper_channel = chan; | |
73 | local->oper_channel_type = NL80211_CHAN_NO_HT; | |
74 | ieee80211_hw_config(local, 0); | |
75 | ||
76 | return 0; | |
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); | |
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); | |
89 | else if (sdata->vif.type == NL80211_IFTYPE_STATION) | |
90 | return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); | |
91 | ||
92 | freq->m = local->oper_channel->center_freq; | |
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 | { | |
103 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
104 | ||
105 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) | |
106 | return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); | |
107 | else if (sdata->vif.type == NL80211_IFTYPE_STATION) | |
108 | return cfg80211_mgd_wext_siwessid(dev, info, data, ssid); | |
109 | ||
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 | { | |
118 | struct ieee80211_sub_if_data *sdata; | |
119 | ||
120 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
121 | ||
122 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) | |
123 | return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); | |
124 | else if (sdata->vif.type == NL80211_IFTYPE_STATION) | |
125 | return cfg80211_mgd_wext_giwessid(dev, info, data, ssid); | |
126 | ||
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 | { | |
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); | |
139 | ||
140 | if (sdata->vif.type == NL80211_IFTYPE_STATION) | |
141 | return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra); | |
142 | ||
143 | if (sdata->vif.type == NL80211_IFTYPE_WDS) { | |
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; | |
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 | { | |
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); | |
172 | ||
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) { | |
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 | ||
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); | |
191 | int i, err = -EINVAL; | |
192 | u32 target_rate = rate->value / 100000; | |
193 | struct ieee80211_sub_if_data *sdata; | |
194 | struct ieee80211_supported_band *sband; | |
195 | ||
196 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
197 | ||
198 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | |
199 | ||
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 */ | |
203 | sdata->max_ratectrl_rateidx = -1; | |
204 | sdata->force_unicast_rateidx = -1; | |
205 | if (rate->value < 0) | |
206 | return 0; | |
207 | ||
208 | for (i=0; i< sband->n_bitrates; i++) { | |
209 | struct ieee80211_rate *brate = &sband->bitrates[i]; | |
210 | int this_rate = brate->bitrate; | |
211 | ||
212 | if (target_rate == this_rate) { | |
213 | sdata->max_ratectrl_rateidx = i; | |
214 | if (rate->fixed) | |
215 | sdata->force_unicast_rateidx = i; | |
216 | err = 0; | |
217 | break; | |
218 | } | |
219 | } | |
220 | return err; | |
221 | } | |
222 | ||
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; | |
230 | struct ieee80211_supported_band *sband; | |
231 | ||
232 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
233 | ||
234 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | |
235 | return -EOPNOTSUPP; | |
236 | ||
237 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | |
238 | ||
239 | rcu_read_lock(); | |
240 | ||
241 | sta = sta_info_get(local, sdata->u.mgd.bssid); | |
242 | ||
243 | if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) | |
244 | rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate; | |
245 | else | |
246 | rate->value = 0; | |
247 | ||
248 | rcu_read_unlock(); | |
249 | ||
250 | if (!sta) | |
251 | return -ENODEV; | |
252 | ||
253 | rate->value *= 100000; | |
254 | ||
255 | return 0; | |
256 | } | |
257 | ||
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 | ||
266 | rcu_read_lock(); | |
267 | ||
268 | if (sdata->vif.type == NL80211_IFTYPE_STATION) | |
269 | sta = sta_info_get(local, sdata->u.mgd.bssid); | |
270 | ||
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 { | |
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 | } | |
318 | } | |
319 | ||
320 | rcu_read_unlock(); | |
321 | ||
322 | return wstats; | |
323 | } | |
324 | ||
325 | /* Structures to export the Wireless Handlers */ | |
326 | ||
327 | static const iw_handler ieee80211_handler[] = | |
328 | { | |
329 | (iw_handler) NULL, /* SIOCSIWCOMMIT */ | |
330 | (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */ | |
331 | (iw_handler) NULL, /* SIOCSIWNWID */ | |
332 | (iw_handler) NULL, /* SIOCGIWNWID */ | |
333 | (iw_handler) ieee80211_ioctl_siwfreq, /* SIOCSIWFREQ */ | |
334 | (iw_handler) ieee80211_ioctl_giwfreq, /* SIOCGIWFREQ */ | |
335 | (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */ | |
336 | (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */ | |
337 | (iw_handler) NULL, /* SIOCSIWSENS */ | |
338 | (iw_handler) NULL, /* SIOCGIWSENS */ | |
339 | (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ | |
340 | (iw_handler) cfg80211_wext_giwrange, /* SIOCGIWRANGE */ | |
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 */ | |
345 | (iw_handler) NULL, /* SIOCSIWSPY */ | |
346 | (iw_handler) NULL, /* SIOCGIWSPY */ | |
347 | (iw_handler) NULL, /* SIOCSIWTHRSPY */ | |
348 | (iw_handler) NULL, /* SIOCGIWTHRSPY */ | |
349 | (iw_handler) ieee80211_ioctl_siwap, /* SIOCSIWAP */ | |
350 | (iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */ | |
351 | (iw_handler) cfg80211_wext_siwmlme, /* SIOCSIWMLME */ | |
352 | (iw_handler) NULL, /* SIOCGIWAPLIST */ | |
353 | (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */ | |
354 | (iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */ | |
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 -- */ | |
361 | (iw_handler) ieee80211_ioctl_siwrate, /* SIOCSIWRATE */ | |
362 | (iw_handler) ieee80211_ioctl_giwrate, /* SIOCGIWRATE */ | |
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 */ | |
367 | (iw_handler) cfg80211_wext_siwtxpower, /* SIOCSIWTXPOW */ | |
368 | (iw_handler) cfg80211_wext_giwtxpower, /* SIOCGIWTXPOW */ | |
369 | (iw_handler) cfg80211_wext_siwretry, /* SIOCSIWRETRY */ | |
370 | (iw_handler) cfg80211_wext_giwretry, /* SIOCGIWRETRY */ | |
371 | (iw_handler) cfg80211_wext_siwencode, /* SIOCSIWENCODE */ | |
372 | (iw_handler) cfg80211_wext_giwencode, /* SIOCGIWENCODE */ | |
373 | (iw_handler) cfg80211_wext_siwpower, /* SIOCSIWPOWER */ | |
374 | (iw_handler) cfg80211_wext_giwpower, /* SIOCGIWPOWER */ | |
375 | (iw_handler) NULL, /* -- hole -- */ | |
376 | (iw_handler) NULL, /* -- hole -- */ | |
377 | (iw_handler) cfg80211_wext_siwgenie, /* SIOCSIWGENIE */ | |
378 | (iw_handler) NULL, /* SIOCGIWGENIE */ | |
379 | (iw_handler) cfg80211_wext_siwauth, /* SIOCSIWAUTH */ | |
380 | (iw_handler) cfg80211_wext_giwauth, /* SIOCGIWAUTH */ | |
381 | (iw_handler) cfg80211_wext_siwencodeext, /* SIOCSIWENCODEEXT */ | |
382 | (iw_handler) NULL, /* SIOCGIWENCODEEXT */ | |
383 | (iw_handler) NULL, /* SIOCSIWPMKSA */ | |
384 | (iw_handler) NULL, /* -- hole -- */ | |
385 | }; | |
386 | ||
387 | const struct iw_handler_def ieee80211_iw_handler_def = | |
388 | { | |
389 | .num_standard = ARRAY_SIZE(ieee80211_handler), | |
390 | .standard = (iw_handler *) ieee80211_handler, | |
391 | .get_wireless_stats = ieee80211_get_wireless_stats, | |
392 | }; |