wext.c 31.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright 2002-2005, Instant802 Networks, Inc.
 * Copyright 2005-2006, Devicescape Software, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <net/iw_handler.h>
#include <asm/uaccess.h>

#include <net/mac80211.h>
#include "ieee80211_i.h"
J
Johannes Berg 已提交
24 25
#include "led.h"
#include "rate.h"
26 27 28
#include "wpa.h"
#include "aes_ccm.h"

J
Johannes Berg 已提交
29

30
static int ieee80211_set_encryption(struct ieee80211_sub_if_data *sdata, u8 *sta_addr,
J
Johannes Berg 已提交
31 32 33
				    int idx, int alg, int remove,
				    int set_tx_key, const u8 *_key,
				    size_t key_len)
34
{
35
	struct ieee80211_local *local = sdata->local;
36
	struct sta_info *sta;
J
Johannes Berg 已提交
37
	struct ieee80211_key *key;
38
	int err;
39

40 41
	if (idx < 0 || idx >= NUM_DEFAULT_KEYS) {
		printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d\n",
42
		       sdata->dev->name, idx);
43 44 45
		return -EINVAL;
	}

46
	if (remove) {
47 48 49 50
		rcu_read_lock();

		err = 0;

51 52 53 54
		if (is_broadcast_ether_addr(sta_addr)) {
			key = sdata->keys[idx];
		} else {
			sta = sta_info_get(local, sta_addr);
55 56 57 58
			if (!sta) {
				err = -ENOENT;
				goto out_unlock;
			}
59
			key = sta->key;
60 61
		}

62
		ieee80211_key_free(key);
63 64 65 66 67
	} else {
		key = ieee80211_key_alloc(alg, idx, key_len, _key);
		if (!key)
			return -ENOMEM;

68
		sta = NULL;
69 70 71
		err = 0;

		rcu_read_lock();
72

73 74 75 76 77 78 79 80 81
		if (!is_broadcast_ether_addr(sta_addr)) {
			set_tx_key = 0;
			/*
			 * According to the standard, the key index of a
			 * pairwise key must be zero. However, some AP are
			 * broken when it comes to WEP key indices, so we
			 * work around this.
			 */
			if (idx != 0 && alg != ALG_WEP) {
82
				ieee80211_key_free(key);
83 84
				err = -EINVAL;
				goto out_unlock;
85
			}
86

87 88
			sta = sta_info_get(local, sta_addr);
			if (!sta) {
89
				ieee80211_key_free(key);
90 91
				err = -ENOENT;
				goto out_unlock;
92
			}
93 94
		}

95 96 97 98 99 100 101
		if (alg == ALG_WEP &&
			key_len != LEN_WEP40 && key_len != LEN_WEP104) {
			ieee80211_key_free(key);
			err = -EINVAL;
			goto out_unlock;
		}

102
		ieee80211_key_link(key, sdata, sta);
103

104 105 106
		if (set_tx_key || (!sta && !sdata->default_key && key))
			ieee80211_set_default_key(sdata, idx);
	}
107

108 109 110 111
 out_unlock:
	rcu_read_unlock();

	return err;
112 113 114 115 116 117 118 119
}

static int ieee80211_ioctl_siwgenie(struct net_device *dev,
				    struct iw_request_info *info,
				    struct iw_point *data, char *extra)
{
	struct ieee80211_sub_if_data *sdata;

120 121 122
	sdata = IEEE80211_DEV_TO_SUB_IF(dev);

	if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
123 124
		return -EOPNOTSUPP;

125 126
	if (sdata->vif.type == NL80211_IFTYPE_STATION ||
	    sdata->vif.type == NL80211_IFTYPE_ADHOC) {
127
		int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length);
128 129
		if (ret)
			return ret;
130
		sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
131
		ieee80211_sta_req_auth(sdata, &sdata->u.sta);
132 133 134 135 136 137 138 139 140 141
		return 0;
	}

	return -EOPNOTSUPP;
}

static int ieee80211_ioctl_giwname(struct net_device *dev,
				   struct iw_request_info *info,
				   char *name, char *extra)
{
142 143 144 145 146 147 148 149
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
	struct ieee80211_supported_band *sband;
	u8 is_ht = 0, is_a = 0, is_b = 0, is_g = 0;


	sband = local->hw.wiphy->bands[IEEE80211_BAND_5GHZ];
	if (sband) {
		is_a = 1;
J
Johannes Berg 已提交
150
		is_ht |= sband->ht_cap.ht_supported;
151 152 153 154 155 156 157 158 159 160 161 162
	}

	sband = local->hw.wiphy->bands[IEEE80211_BAND_2GHZ];
	if (sband) {
		int i;
		/* Check for mandatory rates */
		for (i = 0; i < sband->n_bitrates; i++) {
			if (sband->bitrates[i].bitrate == 10)
				is_b = 1;
			if (sband->bitrates[i].bitrate == 60)
				is_g = 1;
		}
J
Johannes Berg 已提交
163
		is_ht |= sband->ht_cap.ht_supported;
164 165
	}

166
	strcpy(name, "IEEE 802.11");
167 168 169 170 171 172 173 174
	if (is_a)
		strcat(name, "a");
	if (is_b)
		strcat(name, "b");
	if (is_g)
		strcat(name, "g");
	if (is_ht)
		strcat(name, "n");
175 176 177 178 179 180 181 182 183 184 185

	return 0;
}


static int ieee80211_ioctl_giwrange(struct net_device *dev,
				 struct iw_request_info *info,
				 struct iw_point *data, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
	struct iw_range *range = (struct iw_range *) extra;
186
	enum ieee80211_band band;
187
	int c = 0;
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207

	data->length = sizeof(struct iw_range);
	memset(range, 0, sizeof(struct iw_range));

	range->we_version_compiled = WIRELESS_EXT;
	range->we_version_source = 21;
	range->retry_capa = IW_RETRY_LIMIT;
	range->retry_flags = IW_RETRY_LIMIT;
	range->min_retry = 0;
	range->max_retry = 255;
	range->min_rts = 0;
	range->max_rts = 2347;
	range->min_frag = 256;
	range->max_frag = 2346;

	range->encoding_size[0] = 5;
	range->encoding_size[1] = 13;
	range->num_encoding_sizes = 2;
	range->max_encoding_tokens = NUM_DEFAULT_KEYS;

208 209 210 211 212 213 214 215 216 217 218 219 220 221
	if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC ||
	    local->hw.flags & IEEE80211_HW_SIGNAL_DB)
		range->max_qual.level = local->hw.max_signal;
	else if  (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
		range->max_qual.level = -110;
	else
		range->max_qual.level = 0;

	if (local->hw.flags & IEEE80211_HW_NOISE_DBM)
		range->max_qual.noise = -110;
	else
		range->max_qual.noise = 0;

	range->max_qual.qual = 100;
222 223
	range->max_qual.updated = local->wstats_flags;

224 225 226 227
	range->avg_qual.qual = 50;
	/* not always true but better than nothing */
	range->avg_qual.level = range->max_qual.level / 2;
	range->avg_qual.noise = range->max_qual.noise / 2;
228 229 230 231 232
	range->avg_qual.updated = local->wstats_flags;

	range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
			  IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;

233

234 235 236 237 238 239 240
	for (band = 0; band < IEEE80211_NUM_BANDS; band ++) {
		int i;
		struct ieee80211_supported_band *sband;

		sband = local->hw.wiphy->bands[band];

		if (!sband)
241 242
			continue;

243 244
		for (i = 0; i < sband->n_channels && c < IW_MAX_FREQUENCIES; i++) {
			struct ieee80211_channel *chan = &sband->channels[i];
245

246 247 248 249 250 251
			if (!(chan->flags & IEEE80211_CHAN_DISABLED)) {
				range->freq[c].i =
					ieee80211_frequency_to_channel(
						chan->center_freq);
				range->freq[c].m = chan->center_freq;
				range->freq[c].e = 6;
252 253 254 255 256 257 258
				c++;
			}
		}
	}
	range->num_channels = c;
	range->num_frequency = c;

259 260 261 262
	IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);

D
Dan Williams 已提交
263 264
	range->scan_capa |= IW_SCAN_CAPA_ESSID;

265 266 267 268 269 270 271 272 273 274 275
	return 0;
}


static int ieee80211_ioctl_siwmode(struct net_device *dev,
				   struct iw_request_info *info,
				   __u32 *mode, char *extra)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	int type;

276
	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
277 278 279 280
		return -EOPNOTSUPP;

	switch (*mode) {
	case IW_MODE_INFRA:
281
		type = NL80211_IFTYPE_STATION;
282 283
		break;
	case IW_MODE_ADHOC:
284
		type = NL80211_IFTYPE_ADHOC;
285
		break;
J
Johannes Berg 已提交
286
	case IW_MODE_REPEAT:
287
		type = NL80211_IFTYPE_WDS;
J
Johannes Berg 已提交
288
		break;
289
	case IW_MODE_MONITOR:
290
		type = NL80211_IFTYPE_MONITOR;
291 292 293 294 295
		break;
	default:
		return -EINVAL;
	}

296
	return ieee80211_if_change_type(sdata, type);
297 298 299 300 301 302 303 304 305 306
}


static int ieee80211_ioctl_giwmode(struct net_device *dev,
				   struct iw_request_info *info,
				   __u32 *mode, char *extra)
{
	struct ieee80211_sub_if_data *sdata;

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
307
	switch (sdata->vif.type) {
308
	case NL80211_IFTYPE_AP:
309 310
		*mode = IW_MODE_MASTER;
		break;
311
	case NL80211_IFTYPE_STATION:
312 313
		*mode = IW_MODE_INFRA;
		break;
314
	case NL80211_IFTYPE_ADHOC:
315 316
		*mode = IW_MODE_ADHOC;
		break;
317
	case NL80211_IFTYPE_MONITOR:
318 319
		*mode = IW_MODE_MONITOR;
		break;
320
	case NL80211_IFTYPE_WDS:
321 322
		*mode = IW_MODE_REPEAT;
		break;
323
	case NL80211_IFTYPE_AP_VLAN:
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
		*mode = IW_MODE_SECOND;		/* FIXME */
		break;
	default:
		*mode = IW_MODE_AUTO;
		break;
	}
	return 0;
}

static int ieee80211_ioctl_siwfreq(struct net_device *dev,
				   struct iw_request_info *info,
				   struct iw_freq *freq, char *extra)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);

339
	if (sdata->vif.type == NL80211_IFTYPE_STATION)
340
		sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL;
341 342 343 344

	/* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */
	if (freq->e == 0) {
		if (freq->m < 0) {
345
			if (sdata->vif.type == NL80211_IFTYPE_STATION)
346 347
				sdata->u.sta.flags |=
					IEEE80211_STA_AUTO_CHANNEL_SEL;
348 349
			return 0;
		} else
350
			return ieee80211_set_freq(sdata,
351
				ieee80211_channel_to_frequency(freq->m));
352 353 354 355 356
	} else {
		int i, div = 1000000;
		for (i = 0; i < freq->e; i++)
			div /= 10;
		if (div > 0)
357
			return ieee80211_set_freq(sdata, freq->m / div);
358 359 360 361 362 363 364 365 366 367 368 369
		else
			return -EINVAL;
	}
}


static int ieee80211_ioctl_giwfreq(struct net_device *dev,
				   struct iw_request_info *info,
				   struct iw_freq *freq, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);

370
	freq->m = local->hw.conf.channel->center_freq;
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
	freq->e = 6;

	return 0;
}


static int ieee80211_ioctl_siwessid(struct net_device *dev,
				    struct iw_request_info *info,
				    struct iw_point *data, char *ssid)
{
	struct ieee80211_sub_if_data *sdata;
	size_t len = data->length;

	/* iwconfig uses nul termination in SSID.. */
	if (len > 0 && ssid[len - 1] == '\0')
		len--;

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
389 390
	if (sdata->vif.type == NL80211_IFTYPE_STATION ||
	    sdata->vif.type == NL80211_IFTYPE_ADHOC) {
391
		int ret;
392
		if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
393 394 395 396 397 398
			if (len > IEEE80211_MAX_SSID_LEN)
				return -EINVAL;
			memcpy(sdata->u.sta.ssid, ssid, len);
			sdata->u.sta.ssid_len = len;
			return 0;
		}
399 400 401 402
		if (data->flags)
			sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
		else
			sdata->u.sta.flags |= IEEE80211_STA_AUTO_SSID_SEL;
403
		ret = ieee80211_sta_set_ssid(sdata, ssid, len);
404 405
		if (ret)
			return ret;
406
		ieee80211_sta_req_auth(sdata, &sdata->u.sta);
407 408 409
		return 0;
	}

410
	if (sdata->vif.type == NL80211_IFTYPE_AP) {
411 412 413 414
		memcpy(sdata->u.ap.ssid, ssid, len);
		memset(sdata->u.ap.ssid + len, 0,
		       IEEE80211_MAX_SSID_LEN - len);
		sdata->u.ap.ssid_len = len;
415
		return ieee80211_if_config(sdata, IEEE80211_IFCC_SSID);
416 417 418 419 420 421 422 423 424 425 426 427 428
	}
	return -EOPNOTSUPP;
}


static int ieee80211_ioctl_giwessid(struct net_device *dev,
				    struct iw_request_info *info,
				    struct iw_point *data, char *ssid)
{
	size_t len;

	struct ieee80211_sub_if_data *sdata;
	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
429 430
	if (sdata->vif.type == NL80211_IFTYPE_STATION ||
	    sdata->vif.type == NL80211_IFTYPE_ADHOC) {
431
		int res = ieee80211_sta_get_ssid(sdata, ssid, &len);
432 433 434 435 436 437 438 439
		if (res == 0) {
			data->length = len;
			data->flags = 1;
		} else
			data->flags = 0;
		return res;
	}

440
	if (sdata->vif.type == NL80211_IFTYPE_AP) {
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
		len = sdata->u.ap.ssid_len;
		if (len > IW_ESSID_MAX_SIZE)
			len = IW_ESSID_MAX_SIZE;
		memcpy(ssid, sdata->u.ap.ssid, len);
		data->length = len;
		data->flags = 1;
		return 0;
	}
	return -EOPNOTSUPP;
}


static int ieee80211_ioctl_siwap(struct net_device *dev,
				 struct iw_request_info *info,
				 struct sockaddr *ap_addr, char *extra)
{
	struct ieee80211_sub_if_data *sdata;

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
460 461
	if (sdata->vif.type == NL80211_IFTYPE_STATION ||
	    sdata->vif.type == NL80211_IFTYPE_ADHOC) {
462
		int ret;
463
		if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
464 465 466 467
			memcpy(sdata->u.sta.bssid, (u8 *) &ap_addr->sa_data,
			       ETH_ALEN);
			return 0;
		}
468 469 470 471 472
		if (is_zero_ether_addr((u8 *) &ap_addr->sa_data))
			sdata->u.sta.flags |= IEEE80211_STA_AUTO_BSSID_SEL |
				IEEE80211_STA_AUTO_CHANNEL_SEL;
		else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data))
			sdata->u.sta.flags |= IEEE80211_STA_AUTO_BSSID_SEL;
473
		else
474
			sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
475
		ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data);
476 477
		if (ret)
			return ret;
478
		ieee80211_sta_req_auth(sdata, &sdata->u.sta);
479
		return 0;
480
	} else if (sdata->vif.type == NL80211_IFTYPE_WDS) {
481 482 483 484 485 486 487 488 489 490 491 492 493 494
		/*
		 * If it is necessary to update the WDS peer address
		 * while the interface is running, then we need to do
		 * more work here, namely if it is running we need to
		 * add a new and remove the old STA entry, this is
		 * normally handled by _open() and _stop().
		 */
		if (netif_running(dev))
			return -EBUSY;

		memcpy(&sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
		       ETH_ALEN);

		return 0;
495 496 497 498 499 500 501 502 503 504 505 506 507
	}

	return -EOPNOTSUPP;
}


static int ieee80211_ioctl_giwap(struct net_device *dev,
				 struct iw_request_info *info,
				 struct sockaddr *ap_addr, char *extra)
{
	struct ieee80211_sub_if_data *sdata;

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
508 509
	if (sdata->vif.type == NL80211_IFTYPE_STATION ||
	    sdata->vif.type == NL80211_IFTYPE_ADHOC) {
510 511
		if (sdata->u.sta.state == IEEE80211_STA_MLME_ASSOCIATED ||
		    sdata->u.sta.state == IEEE80211_STA_MLME_IBSS_JOINED) {
512 513 514 515 516 517 518
			ap_addr->sa_family = ARPHRD_ETHER;
			memcpy(&ap_addr->sa_data, sdata->u.sta.bssid, ETH_ALEN);
			return 0;
		} else {
			memset(&ap_addr->sa_data, 0, ETH_ALEN);
			return 0;
		}
519
	} else if (sdata->vif.type == NL80211_IFTYPE_WDS) {
520 521 522 523 524 525 526 527 528 529 530
		ap_addr->sa_family = ARPHRD_ETHER;
		memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN);
		return 0;
	}

	return -EOPNOTSUPP;
}


static int ieee80211_ioctl_siwscan(struct net_device *dev,
				   struct iw_request_info *info,
531
				   union iwreq_data *wrqu, char *extra)
532 533
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
534
	struct iw_scan_req *req = NULL;
535 536 537 538 539 540
	u8 *ssid = NULL;
	size_t ssid_len = 0;

	if (!netif_running(dev))
		return -ENETDOWN;

541 542 543 544
	if (sdata->vif.type != NL80211_IFTYPE_STATION &&
	    sdata->vif.type != NL80211_IFTYPE_ADHOC &&
	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
	    sdata->vif.type != NL80211_IFTYPE_AP)
545 546 547
		return -EOPNOTSUPP;

	/* if SSID was specified explicitly then use that */
548 549 550 551 552
	if (wrqu->data.length == sizeof(struct iw_scan_req) &&
	    wrqu->data.flags & IW_SCAN_THIS_ESSID) {
		req = (struct iw_scan_req *)extra;
		ssid = req->essid;
		ssid_len = req->essid_len;
553
	}
554

555
	return ieee80211_request_scan(sdata, ssid, ssid_len);
556 557 558 559 560 561 562 563 564
}


static int ieee80211_ioctl_giwscan(struct net_device *dev,
				   struct iw_request_info *info,
				   struct iw_point *data, char *extra)
{
	int res;
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
565 566 567
	struct ieee80211_sub_if_data *sdata;

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
Z
Zhu Yi 已提交
568

569
	if (local->sw_scanning || local->hw_scanning)
570
		return -EAGAIN;
Z
Zhu Yi 已提交
571

572
	res = ieee80211_scan_results(local, info, extra, data->length);
573 574 575 576 577 578 579 580 581
	if (res >= 0) {
		data->length = res;
		return 0;
	}
	data->length = 0;
	return res;
}


582 583 584 585 586
static int ieee80211_ioctl_siwrate(struct net_device *dev,
				  struct iw_request_info *info,
				  struct iw_param *rate, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
587
	int i, err = -EINVAL;
588 589
	u32 target_rate = rate->value / 100000;
	struct ieee80211_sub_if_data *sdata;
590
	struct ieee80211_supported_band *sband;
591 592

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
593 594 595

	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];

596 597 598
	/* target_rate = -1, rate->fixed = 0 means auto only, so use all rates
	 * target_rate = X, rate->fixed = 1 means only rate X
	 * target_rate = X, rate->fixed = 0 means all rates <= X */
599 600
	sdata->max_ratectrl_rateidx = -1;
	sdata->force_unicast_rateidx = -1;
601 602
	if (rate->value < 0)
		return 0;
603 604 605 606

	for (i=0; i< sband->n_bitrates; i++) {
		struct ieee80211_rate *brate = &sband->bitrates[i];
		int this_rate = brate->bitrate;
607 608

		if (target_rate == this_rate) {
609
			sdata->max_ratectrl_rateidx = i;
610
			if (rate->fixed)
611
				sdata->force_unicast_rateidx = i;
612 613
			err = 0;
			break;
614 615
		}
	}
616
	return err;
617 618
}

619 620 621 622 623 624 625
static int ieee80211_ioctl_giwrate(struct net_device *dev,
				  struct iw_request_info *info,
				  struct iw_param *rate, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
	struct sta_info *sta;
	struct ieee80211_sub_if_data *sdata;
626
	struct ieee80211_supported_band *sband;
627 628

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
629

630
	if (sdata->vif.type != NL80211_IFTYPE_STATION)
631
		return -EOPNOTSUPP;
632 633 634

	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];

635 636 637 638
	rcu_read_lock();

	sta = sta_info_get(local, sdata->u.sta.bssid);

639 640
	if (sta && sta->last_txrate_idx < sband->n_bitrates)
		rate->value = sband->bitrates[sta->last_txrate_idx].bitrate;
641 642
	else
		rate->value = 0;
643 644 645 646 647 648

	rcu_read_unlock();

	if (!sta)
		return -ENODEV;

649
	rate->value *= 100000;
650

651 652 653
	return 0;
}

654 655 656 657 658
static int ieee80211_ioctl_siwtxpower(struct net_device *dev,
				      struct iw_request_info *info,
				      union iwreq_data *data, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
659
	u32 reconf_flags = 0;
660
	int new_power_level;
661 662 663 664 665 666

	if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
		return -EINVAL;
	if (data->txpower.flags & IW_TXPOW_RANGE)
		return -EINVAL;

667 668 669
	if (data->txpower.fixed) {
		new_power_level = data->txpower.value;
	} else {
670 671 672 673 674
		/*
		 * Automatic power level. Use maximum power for the current
		 * channel. Should be part of rate control.
		 */
		struct ieee80211_channel* chan = local->hw.conf.channel;
675 676 677
		if (!chan)
			return -EINVAL;

678
		new_power_level = chan->max_power;
679 680 681 682
	}

	if (local->hw.conf.power_level != new_power_level) {
		local->hw.conf.power_level = new_power_level;
683
		reconf_flags |= IEEE80211_CONF_CHANGE_POWER;
684
	}
685

686 687
	if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) {
		local->hw.conf.radio_enabled = !(data->txpower.disabled);
688
		reconf_flags |= IEEE80211_CONF_CHANGE_RADIO_ENABLED;
I
Ivo van Doorn 已提交
689
		ieee80211_led_radio(local, local->hw.conf.radio_enabled);
690
	}
691

692 693
	if (reconf_flags)
		ieee80211_hw_config(local, reconf_flags);
694 695 696 697

	return 0;
}

698 699 700 701 702 703 704 705 706 707 708 709 710 711
static int ieee80211_ioctl_giwtxpower(struct net_device *dev,
				   struct iw_request_info *info,
				   union iwreq_data *data, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);

	data->txpower.fixed = 1;
	data->txpower.disabled = !(local->hw.conf.radio_enabled);
	data->txpower.value = local->hw.conf.power_level;
	data->txpower.flags = IW_TXPOW_DBM;

	return 0;
}

712 713 714 715 716 717 718 719
static int ieee80211_ioctl_siwrts(struct net_device *dev,
				  struct iw_request_info *info,
				  struct iw_param *rts, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);

	if (rts->disabled)
		local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
720 721 722
	else if (!rts->fixed)
		/* if the rts value is not fixed, then take default */
		local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
	else if (rts->value < 0 || rts->value > IEEE80211_MAX_RTS_THRESHOLD)
		return -EINVAL;
	else
		local->rts_threshold = rts->value;

	/* If the wlan card performs RTS/CTS in hardware/firmware,
	 * configure it here */

	if (local->ops->set_rts_threshold)
		local->ops->set_rts_threshold(local_to_hw(local),
					     local->rts_threshold);

	return 0;
}

static int ieee80211_ioctl_giwrts(struct net_device *dev,
				  struct iw_request_info *info,
				  struct iw_param *rts, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);

	rts->value = local->rts_threshold;
	rts->disabled = (rts->value >= IEEE80211_MAX_RTS_THRESHOLD);
	rts->fixed = 1;

	return 0;
}


static int ieee80211_ioctl_siwfrag(struct net_device *dev,
				   struct iw_request_info *info,
				   struct iw_param *frag, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);

	if (frag->disabled)
		local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
760 761
	else if (!frag->fixed)
		local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
762 763 764 765 766 767 768 769 770 771 772 773
	else if (frag->value < 256 ||
		 frag->value > IEEE80211_MAX_FRAG_THRESHOLD)
		return -EINVAL;
	else {
		/* Fragment length must be even, so strip LSB. */
		local->fragmentation_threshold = frag->value & ~0x1;
	}

	/* If the wlan card performs fragmentation in hardware/firmware,
	 * configure it here */

	if (local->ops->set_frag_threshold)
774
		return local->ops->set_frag_threshold(
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
			local_to_hw(local),
			local->fragmentation_threshold);

	return 0;
}

static int ieee80211_ioctl_giwfrag(struct net_device *dev,
				   struct iw_request_info *info,
				   struct iw_param *frag, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);

	frag->value = local->fragmentation_threshold;
	frag->disabled = (frag->value >= IEEE80211_MAX_RTS_THRESHOLD);
	frag->fixed = 1;

	return 0;
}


static int ieee80211_ioctl_siwretry(struct net_device *dev,
				    struct iw_request_info *info,
				    struct iw_param *retry, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);

	if (retry->disabled ||
	    (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
		return -EINVAL;

805 806 807 808 809 810 811
	if (retry->flags & IW_RETRY_MAX) {
		local->hw.conf.long_frame_max_tx_count = retry->value;
	} else if (retry->flags & IW_RETRY_MIN) {
		local->hw.conf.short_frame_max_tx_count = retry->value;
	} else {
		local->hw.conf.long_frame_max_tx_count = retry->value;
		local->hw.conf.short_frame_max_tx_count = retry->value;
812 813
	}

814
	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830

	return 0;
}


static int ieee80211_ioctl_giwretry(struct net_device *dev,
				    struct iw_request_info *info,
				    struct iw_param *retry, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);

	retry->disabled = 0;
	if (retry->flags == 0 || retry->flags & IW_RETRY_MIN) {
		/* first return min value, iwconfig will ask max value
		 * later if needed */
		retry->flags |= IW_RETRY_LIMIT;
831 832 833
		retry->value = local->hw.conf.short_frame_max_tx_count;
		if (local->hw.conf.long_frame_max_tx_count !=
		    local->hw.conf.short_frame_max_tx_count)
834 835 836 837 838
			retry->flags |= IW_RETRY_MIN;
		return 0;
	}
	if (retry->flags & IW_RETRY_MAX) {
		retry->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
839
		retry->value = local->hw.conf.long_frame_max_tx_count;
840 841 842 843 844 845 846 847 848 849 850 851 852
	}

	return 0;
}

static int ieee80211_ioctl_siwmlme(struct net_device *dev,
				   struct iw_request_info *info,
				   struct iw_point *data, char *extra)
{
	struct ieee80211_sub_if_data *sdata;
	struct iw_mlme *mlme = (struct iw_mlme *) extra;

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
853 854
	if (sdata->vif.type != NL80211_IFTYPE_STATION &&
	    sdata->vif.type != NL80211_IFTYPE_ADHOC)
855 856 857 858 859
		return -EINVAL;

	switch (mlme->cmd) {
	case IW_MLME_DEAUTH:
		/* TODO: mlme->addr.sa_data */
860
		return ieee80211_sta_deauthenticate(sdata, mlme->reason_code);
861 862
	case IW_MLME_DISASSOC:
		/* TODO: mlme->addr.sa_data */
863
		return ieee80211_sta_disassociate(sdata, mlme->reason_code);
864 865 866 867 868 869 870 871 872 873 874 875 876
	default:
		return -EOPNOTSUPP;
	}
}


static int ieee80211_ioctl_siwencode(struct net_device *dev,
				     struct iw_request_info *info,
				     struct iw_point *erq, char *keybuf)
{
	struct ieee80211_sub_if_data *sdata;
	int idx, i, alg = ALG_WEP;
	u8 bcaddr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
J
Johannes Berg 已提交
877
	int remove = 0;
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);

	idx = erq->flags & IW_ENCODE_INDEX;
	if (idx == 0) {
		if (sdata->default_key)
			for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
				if (sdata->default_key == sdata->keys[i]) {
					idx = i;
					break;
				}
			}
	} else if (idx < 1 || idx > 4)
		return -EINVAL;
	else
		idx--;

	if (erq->flags & IW_ENCODE_DISABLED)
J
Johannes Berg 已提交
896
		remove = 1;
897 898
	else if (erq->length == 0) {
		/* No key data - just set the default TX key index */
J
Johannes Berg 已提交
899
		ieee80211_set_default_key(sdata, idx);
900 901 902 903
		return 0;
	}

	return ieee80211_set_encryption(
904
		sdata, bcaddr,
J
Johannes Berg 已提交
905
		idx, alg, remove,
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
		!sdata->default_key,
		keybuf, erq->length);
}


static int ieee80211_ioctl_giwencode(struct net_device *dev,
				     struct iw_request_info *info,
				     struct iw_point *erq, char *key)
{
	struct ieee80211_sub_if_data *sdata;
	int idx, i;

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);

	idx = erq->flags & IW_ENCODE_INDEX;
	if (idx < 1 || idx > 4) {
		idx = -1;
		if (!sdata->default_key)
			idx = 0;
		else for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
			if (sdata->default_key == sdata->keys[i]) {
				idx = i;
				break;
			}
		}
		if (idx < 0)
			return -EINVAL;
	} else
		idx--;

	erq->flags = idx + 1;

	if (!sdata->keys[idx]) {
		erq->length = 0;
		erq->flags |= IW_ENCODE_DISABLED;
		return 0;
	}

944
	memcpy(key, sdata->keys[idx]->conf.key,
J
Johannes Berg 已提交
945
	       min_t(int, erq->length, sdata->keys[idx]->conf.keylen));
946
	erq->length = sdata->keys[idx]->conf.keylen;
947 948
	erq->flags |= IW_ENCODE_ENABLED;

949
	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
950 951 952 953 954 955 956 957 958 959 960 961
		struct ieee80211_if_sta *ifsta = &sdata->u.sta;
		switch (ifsta->auth_alg) {
		case WLAN_AUTH_OPEN:
		case WLAN_AUTH_LEAP:
			erq->flags |= IW_ENCODE_OPEN;
			break;
		case WLAN_AUTH_SHARED_KEY:
			erq->flags |= IW_ENCODE_RESTRICTED;
			break;
		}
	}

962 963 964
	return 0;
}

965 966 967 968 969 970 971 972 973 974
static int ieee80211_ioctl_siwpower(struct net_device *dev,
				    struct iw_request_info *info,
				    struct iw_param *wrq,
				    char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
	struct ieee80211_conf *conf = &local->hw.conf;

	if (wrq->disabled) {
		conf->flags &= ~IEEE80211_CONF_PS;
975
		return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
976 977 978 979 980 981 982 983 984 985 986 987
	}

	switch (wrq->flags & IW_POWER_MODE) {
	case IW_POWER_ON:       /* If not specified */
	case IW_POWER_MODE:     /* If set all mask */
	case IW_POWER_ALL_R:    /* If explicitely state all */
		conf->flags |= IEEE80211_CONF_PS;
		break;
	default:                /* Otherwise we don't support it */
		return -EINVAL;
	}

988
	return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
}

static int ieee80211_ioctl_giwpower(struct net_device *dev,
				    struct iw_request_info *info,
				    union iwreq_data *wrqu,
				    char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
	struct ieee80211_conf *conf = &local->hw.conf;

	wrqu->power.disabled = !(conf->flags & IEEE80211_CONF_PS);

	return 0;
}

1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
static int ieee80211_ioctl_siwauth(struct net_device *dev,
				   struct iw_request_info *info,
				   struct iw_param *data, char *extra)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	int ret = 0;

	switch (data->flags & IW_AUTH_INDEX) {
	case IW_AUTH_WPA_VERSION:
	case IW_AUTH_CIPHER_PAIRWISE:
	case IW_AUTH_CIPHER_GROUP:
	case IW_AUTH_WPA_ENABLED:
	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
	case IW_AUTH_KEY_MGMT:
1018
		break;
1019 1020 1021
	case IW_AUTH_DROP_UNENCRYPTED:
		sdata->drop_unencrypted = !!data->value;
		break;
1022
	case IW_AUTH_PRIVACY_INVOKED:
1023
		if (sdata->vif.type != NL80211_IFTYPE_STATION)
1024 1025
			ret = -EINVAL;
		else {
1026
			sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
1027
			/*
1028 1029 1030
			 * Privacy invoked by wpa_supplicant, store the
			 * value and allow associating to a protected
			 * network without having a key up front.
1031
			 */
1032 1033 1034
			if (data->value)
				sdata->u.sta.flags |=
					IEEE80211_STA_PRIVACY_INVOKED;
1035 1036 1037
		}
		break;
	case IW_AUTH_80211_AUTH_ALG:
1038 1039
		if (sdata->vif.type == NL80211_IFTYPE_STATION ||
		    sdata->vif.type == NL80211_IFTYPE_ADHOC)
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
			sdata->u.sta.auth_algs = data->value;
		else
			ret = -EOPNOTSUPP;
		break;
	default:
		ret = -EOPNOTSUPP;
		break;
	}
	return ret;
}

/* Get wireless statistics.  Called by /proc/net/wireless and by SIOCGIWSTATS */
static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
	struct iw_statistics *wstats = &local->wstats;
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct sta_info *sta = NULL;

J
Johannes Berg 已提交
1059 1060
	rcu_read_lock();

1061 1062
	if (sdata->vif.type == NL80211_IFTYPE_STATION ||
	    sdata->vif.type == NL80211_IFTYPE_ADHOC)
1063 1064 1065 1066 1067 1068 1069 1070 1071
		sta = sta_info_get(local, sdata->u.sta.bssid);
	if (!sta) {
		wstats->discard.fragment = 0;
		wstats->discard.misc = 0;
		wstats->qual.qual = 0;
		wstats->qual.level = 0;
		wstats->qual.noise = 0;
		wstats->qual.updated = IW_QUAL_ALL_INVALID;
	} else {
1072 1073
		wstats->qual.level = sta->last_signal;
		wstats->qual.qual = sta->last_qual;
1074 1075 1076
		wstats->qual.noise = sta->last_noise;
		wstats->qual.updated = local->wstats_flags;
	}
J
Johannes Berg 已提交
1077 1078 1079

	rcu_read_unlock();

1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
	return wstats;
}

static int ieee80211_ioctl_giwauth(struct net_device *dev,
				   struct iw_request_info *info,
				   struct iw_param *data, char *extra)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	int ret = 0;

	switch (data->flags & IW_AUTH_INDEX) {
	case IW_AUTH_80211_AUTH_ALG:
1092 1093
		if (sdata->vif.type == NL80211_IFTYPE_STATION ||
		    sdata->vif.type == NL80211_IFTYPE_ADHOC)
1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
			data->value = sdata->u.sta.auth_algs;
		else
			ret = -EOPNOTSUPP;
		break;
	default:
		ret = -EOPNOTSUPP;
		break;
	}
	return ret;
}


static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
					struct iw_request_info *info,
					struct iw_point *erq, char *extra)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
J
Johannes Berg 已提交
1112
	int uninitialized_var(alg), idx, i, remove = 0;
1113 1114 1115

	switch (ext->alg) {
	case IW_ENCODE_ALG_NONE:
J
Johannes Berg 已提交
1116
		remove = 1;
1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
		break;
	case IW_ENCODE_ALG_WEP:
		alg = ALG_WEP;
		break;
	case IW_ENCODE_ALG_TKIP:
		alg = ALG_TKIP;
		break;
	case IW_ENCODE_ALG_CCMP:
		alg = ALG_CCMP;
		break;
	default:
		return -EOPNOTSUPP;
	}

	if (erq->flags & IW_ENCODE_DISABLED)
J
Johannes Berg 已提交
1132
		remove = 1;
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149

	idx = erq->flags & IW_ENCODE_INDEX;
	if (idx < 1 || idx > 4) {
		idx = -1;
		if (!sdata->default_key)
			idx = 0;
		else for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
			if (sdata->default_key == sdata->keys[i]) {
				idx = i;
				break;
			}
		}
		if (idx < 0)
			return -EINVAL;
	} else
		idx--;

1150
	return ieee80211_set_encryption(sdata, ext->addr.sa_data, idx, alg,
J
Johannes Berg 已提交
1151
					remove,
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177
					ext->ext_flags &
					IW_ENCODE_EXT_SET_TX_KEY,
					ext->key, ext->key_len);
}


/* Structures to export the Wireless Handlers */

static const iw_handler ieee80211_handler[] =
{
	(iw_handler) NULL,				/* SIOCSIWCOMMIT */
	(iw_handler) ieee80211_ioctl_giwname,		/* SIOCGIWNAME */
	(iw_handler) NULL,				/* SIOCSIWNWID */
	(iw_handler) NULL,				/* SIOCGIWNWID */
	(iw_handler) ieee80211_ioctl_siwfreq,		/* SIOCSIWFREQ */
	(iw_handler) ieee80211_ioctl_giwfreq,		/* SIOCGIWFREQ */
	(iw_handler) ieee80211_ioctl_siwmode,		/* SIOCSIWMODE */
	(iw_handler) ieee80211_ioctl_giwmode,		/* SIOCGIWMODE */
	(iw_handler) NULL,				/* SIOCSIWSENS */
	(iw_handler) NULL,				/* SIOCGIWSENS */
	(iw_handler) NULL /* not used */,		/* SIOCSIWRANGE */
	(iw_handler) ieee80211_ioctl_giwrange,		/* SIOCGIWRANGE */
	(iw_handler) NULL /* not used */,		/* SIOCSIWPRIV */
	(iw_handler) NULL /* kernel code */,		/* SIOCGIWPRIV */
	(iw_handler) NULL /* not used */,		/* SIOCSIWSTATS */
	(iw_handler) NULL /* kernel code */,		/* SIOCGIWSTATS */
J
Johannes Berg 已提交
1178 1179 1180 1181
	(iw_handler) NULL,				/* SIOCSIWSPY */
	(iw_handler) NULL,				/* SIOCGIWSPY */
	(iw_handler) NULL,				/* SIOCSIWTHRSPY */
	(iw_handler) NULL,				/* SIOCGIWTHRSPY */
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193
	(iw_handler) ieee80211_ioctl_siwap,		/* SIOCSIWAP */
	(iw_handler) ieee80211_ioctl_giwap,		/* SIOCGIWAP */
	(iw_handler) ieee80211_ioctl_siwmlme,		/* SIOCSIWMLME */
	(iw_handler) NULL,				/* SIOCGIWAPLIST */
	(iw_handler) ieee80211_ioctl_siwscan,		/* SIOCSIWSCAN */
	(iw_handler) ieee80211_ioctl_giwscan,		/* SIOCGIWSCAN */
	(iw_handler) ieee80211_ioctl_siwessid,		/* SIOCSIWESSID */
	(iw_handler) ieee80211_ioctl_giwessid,		/* SIOCGIWESSID */
	(iw_handler) NULL,				/* SIOCSIWNICKN */
	(iw_handler) NULL,				/* SIOCGIWNICKN */
	(iw_handler) NULL,				/* -- hole -- */
	(iw_handler) NULL,				/* -- hole -- */
1194
	(iw_handler) ieee80211_ioctl_siwrate,		/* SIOCSIWRATE */
1195
	(iw_handler) ieee80211_ioctl_giwrate,		/* SIOCGIWRATE */
1196 1197 1198 1199
	(iw_handler) ieee80211_ioctl_siwrts,		/* SIOCSIWRTS */
	(iw_handler) ieee80211_ioctl_giwrts,		/* SIOCGIWRTS */
	(iw_handler) ieee80211_ioctl_siwfrag,		/* SIOCSIWFRAG */
	(iw_handler) ieee80211_ioctl_giwfrag,		/* SIOCGIWFRAG */
1200
	(iw_handler) ieee80211_ioctl_siwtxpower,	/* SIOCSIWTXPOW */
1201
	(iw_handler) ieee80211_ioctl_giwtxpower,	/* SIOCGIWTXPOW */
1202 1203 1204 1205
	(iw_handler) ieee80211_ioctl_siwretry,		/* SIOCSIWRETRY */
	(iw_handler) ieee80211_ioctl_giwretry,		/* SIOCGIWRETRY */
	(iw_handler) ieee80211_ioctl_siwencode,		/* SIOCSIWENCODE */
	(iw_handler) ieee80211_ioctl_giwencode,		/* SIOCGIWENCODE */
1206 1207
	(iw_handler) ieee80211_ioctl_siwpower,		/* SIOCSIWPOWER */
	(iw_handler) ieee80211_ioctl_giwpower,		/* SIOCGIWPOWER */
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225
	(iw_handler) NULL,				/* -- hole -- */
	(iw_handler) NULL,				/* -- hole -- */
	(iw_handler) ieee80211_ioctl_siwgenie,		/* SIOCSIWGENIE */
	(iw_handler) NULL,				/* SIOCGIWGENIE */
	(iw_handler) ieee80211_ioctl_siwauth,		/* SIOCSIWAUTH */
	(iw_handler) ieee80211_ioctl_giwauth,		/* SIOCGIWAUTH */
	(iw_handler) ieee80211_ioctl_siwencodeext,	/* SIOCSIWENCODEEXT */
	(iw_handler) NULL,				/* SIOCGIWENCODEEXT */
	(iw_handler) NULL,				/* SIOCSIWPMKSA */
	(iw_handler) NULL,				/* -- hole -- */
};

const struct iw_handler_def ieee80211_iw_handler_def =
{
	.num_standard	= ARRAY_SIZE(ieee80211_handler),
	.standard	= (iw_handler *) ieee80211_handler,
	.get_wireless_stats = ieee80211_get_wireless_stats,
};