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