]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - net/mac80211/wext.c
cfg80211: implement iwpower
[mirror_ubuntu-bionic-kernel.git] / net / mac80211 / wext.c
CommitLineData
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
30static 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
80static 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
99static 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
114static 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
131static 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
164static 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
186static 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
223static 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 */
259static 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
327static 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
387const 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};