wext.c 57.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/**
  * This file contains ioctl functions
  */
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <linux/bitops.h>

11
#include <net/lib80211.h>
12 13 14 15 16 17 18 19
#include <net/iw_handler.h>

#include "host.h"
#include "radiotap.h"
#include "decl.h"
#include "defs.h"
#include "dev.h"
#include "wext.h"
20
#include "scan.h"
21
#include "assoc.h"
22
#include "cmd.h"
23 24


25
static inline void lbs_postpone_association_work(struct lbs_private *priv)
26
{
27
	if (priv->surpriseremoved)
28 29 30 31 32
		return;
	cancel_delayed_work(&priv->assoc_work);
	queue_delayed_work(priv->work_thread, &priv->assoc_work, HZ / 2);
}

33 34 35 36 37 38 39 40
static inline void lbs_do_association_work(struct lbs_private *priv)
{
	if (priv->surpriseremoved)
		return;
	cancel_delayed_work(&priv->assoc_work);
	queue_delayed_work(priv->work_thread, &priv->assoc_work, 0);
}

41
static inline void lbs_cancel_association_work(struct lbs_private *priv)
42 43
{
	cancel_delayed_work(&priv->assoc_work);
44 45
	kfree(priv->pending_assoc_req);
	priv->pending_assoc_req = NULL;
46 47
}

48 49 50
/**
 *  @brief Find the channel frequency power info with specific channel
 *
51
 *  @param priv 	A pointer to struct lbs_private structure
52 53 54 55
 *  @param band		it can be BAND_A, BAND_G or BAND_B
 *  @param channel      the channel for looking
 *  @return 	   	A pointer to struct chan_freq_power structure or NULL if not find.
 */
56
struct chan_freq_power *lbs_find_cfp_by_band_and_channel(
57
	struct lbs_private *priv,
58 59
	u8 band,
	u16 channel)
60 61 62 63 64
{
	struct chan_freq_power *cfp = NULL;
	struct region_channel *rc;
	int i, j;

65 66
	for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
		rc = &priv->region_channel[j];
67

68 69
		if (priv->enable11d)
			rc = &priv->universal_channel[j];
70 71 72 73 74 75 76 77 78 79 80 81 82
		if (!rc->valid || !rc->CFP)
			continue;
		if (rc->band != band)
			continue;
		for (i = 0; i < rc->nrcfp; i++) {
			if (rc->CFP[i].channel == channel) {
				cfp = &rc->CFP[i];
				break;
			}
		}
	}

	if (!cfp && channel)
83
		lbs_deb_wext("lbs_find_cfp_by_band_and_channel: can't find "
84
		       "cfp by band %d / channel %d\n", band, channel);
85 86 87 88 89 90 91

	return cfp;
}

/**
 *  @brief Find the channel frequency power info with specific frequency
 *
92
 *  @param priv 	A pointer to struct lbs_private structure
93 94 95 96
 *  @param band		it can be BAND_A, BAND_G or BAND_B
 *  @param freq	        the frequency for looking
 *  @return 	   	A pointer to struct chan_freq_power structure or NULL if not find.
 */
97
static struct chan_freq_power *find_cfp_by_band_and_freq(
98
	struct lbs_private *priv,
99 100
	u8 band,
	u32 freq)
101 102 103 104 105
{
	struct chan_freq_power *cfp = NULL;
	struct region_channel *rc;
	int i, j;

106 107
	for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
		rc = &priv->region_channel[j];
108

109 110
		if (priv->enable11d)
			rc = &priv->universal_channel[j];
111 112 113 114 115 116 117 118 119 120 121 122 123
		if (!rc->valid || !rc->CFP)
			continue;
		if (rc->band != band)
			continue;
		for (i = 0; i < rc->nrcfp; i++) {
			if (rc->CFP[i].freq == freq) {
				cfp = &rc->CFP[i];
				break;
			}
		}
	}

	if (!cfp && freq)
124 125
		lbs_deb_wext("find_cfp_by_band_and_freql: can't find cfp by "
		       "band %d / freq %d\n", band, freq);
126 127 128 129 130

	return cfp;
}

/**
131
 *  @brief Copy active data rates based on adapter mode and status
132
 *
133
 *  @param priv              A pointer to struct lbs_private structure
134 135
 *  @param rate		        The buf to return the active rates
 */
136
static void copy_active_data_rates(struct lbs_private *priv, u8 *rates)
137
{
138
	lbs_deb_enter(LBS_DEB_WEXT);
139

140 141
	if ((priv->connect_status != LBS_CONNECTED) &&
		(priv->mesh_connect_status != LBS_CONNECTED))
142
		memcpy(rates, lbs_bg_rates, MAX_RATES);
143
	else
144
		memcpy(rates, priv->curbssparams.rates, MAX_RATES);
145

146
	lbs_deb_leave(LBS_DEB_WEXT);
147 148
}

149
static int lbs_get_name(struct net_device *dev, struct iw_request_info *info,
150 151 152
			 char *cwrq, char *extra)
{

153
	lbs_deb_enter(LBS_DEB_WEXT);
154

155 156
	/* We could add support for 802.11n here as needed. Jean II */
	snprintf(cwrq, IFNAMSIZ, "IEEE 802.11b/g");
157

158
	lbs_deb_leave(LBS_DEB_WEXT);
159 160 161
	return 0;
}

162
static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
163 164
			 struct iw_freq *fwrq, char *extra)
{
165
	struct lbs_private *priv = dev->ml_priv;
166 167
	struct chan_freq_power *cfp;

168
	lbs_deb_enter(LBS_DEB_WEXT);
169

170 171
	cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
					   priv->curbssparams.channel);
172 173

	if (!cfp) {
174
		if (priv->curbssparams.channel)
175
			lbs_deb_wext("invalid channel %d\n",
176
			       priv->curbssparams.channel);
177 178 179 180 181 182
		return -EINVAL;
	}

	fwrq->m = (long)cfp->freq * 100000;
	fwrq->e = 1;

183 184
	lbs_deb_wext("freq %u\n", fwrq->m);
	lbs_deb_leave(LBS_DEB_WEXT);
185 186 187
	return 0;
}

188
static int lbs_get_wap(struct net_device *dev, struct iw_request_info *info,
189 190
			struct sockaddr *awrq, char *extra)
{
191
	struct lbs_private *priv = dev->ml_priv;
192

193
	lbs_deb_enter(LBS_DEB_WEXT);
194

195 196
	if (priv->connect_status == LBS_CONNECTED) {
		memcpy(awrq->sa_data, priv->curbssparams.bssid, ETH_ALEN);
197 198 199 200 201
	} else {
		memset(awrq->sa_data, 0, ETH_ALEN);
	}
	awrq->sa_family = ARPHRD_ETHER;

202
	lbs_deb_leave(LBS_DEB_WEXT);
203 204 205
	return 0;
}

206
static int lbs_set_nick(struct net_device *dev, struct iw_request_info *info,
207 208
			 struct iw_point *dwrq, char *extra)
{
209
	struct lbs_private *priv = dev->ml_priv;
210

211
	lbs_deb_enter(LBS_DEB_WEXT);
212 213 214 215 216 217 218 219 220

	/*
	 * Check the size of the string
	 */

	if (dwrq->length > 16) {
		return -E2BIG;
	}

221 222 223 224
	mutex_lock(&priv->lock);
	memset(priv->nodename, 0, sizeof(priv->nodename));
	memcpy(priv->nodename, extra, dwrq->length);
	mutex_unlock(&priv->lock);
225

226
	lbs_deb_leave(LBS_DEB_WEXT);
227 228 229
	return 0;
}

230
static int lbs_get_nick(struct net_device *dev, struct iw_request_info *info,
231 232
			 struct iw_point *dwrq, char *extra)
{
233
	struct lbs_private *priv = dev->ml_priv;
234

235
	lbs_deb_enter(LBS_DEB_WEXT);
236

237 238
	dwrq->length = strlen(priv->nodename);
	memcpy(extra, priv->nodename, dwrq->length);
239
	extra[dwrq->length] = '\0';
240

241
	dwrq->flags = 1;	/* active */
242

243
	lbs_deb_leave(LBS_DEB_WEXT);
244 245 246
	return 0;
}

247 248 249
static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info,
			 struct iw_point *dwrq, char *extra)
{
250
	struct lbs_private *priv = dev->ml_priv;
251 252 253 254 255

	lbs_deb_enter(LBS_DEB_WEXT);

	/* Use nickname to indicate that mesh is on */

256
	if (priv->mesh_connect_status == LBS_CONNECTED) {
257 258
		strncpy(extra, "Mesh", 12);
		extra[12] = '\0';
259
		dwrq->length = strlen(extra);
260 261 262 263
	}

	else {
		extra[0] = '\0';
264
		dwrq->length = 0;
265 266 267 268 269
	}

	lbs_deb_leave(LBS_DEB_WEXT);
	return 0;
}
270

271
static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info,
272 273 274
			struct iw_param *vwrq, char *extra)
{
	int ret = 0;
275
	struct lbs_private *priv = dev->ml_priv;
276
	u32 val = vwrq->value;
277

278
	lbs_deb_enter(LBS_DEB_WEXT);
279

280 281
	if (vwrq->disabled)
		val = MRVDRV_RTS_MAX_VALUE;
282

283
	if (val > MRVDRV_RTS_MAX_VALUE) /* min rts value is 0 */
284 285 286
		return -EINVAL;

	ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, (u16) val);
287

288
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
289 290 291
	return ret;
}

292
static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info,
293 294
			struct iw_param *vwrq, char *extra)
{
295
	struct lbs_private *priv = dev->ml_priv;
296 297
	int ret = 0;
	u16 val = 0;
298

299
	lbs_deb_enter(LBS_DEB_WEXT);
300

301
	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, &val);
302 303
	if (ret)
		goto out;
304

305
	vwrq->value = val;
306
	vwrq->disabled = val > MRVDRV_RTS_MAX_VALUE; /* min rts value is 0 */
307 308
	vwrq->fixed = 1;

309 310 311
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
312 313
}

314
static int lbs_set_frag(struct net_device *dev, struct iw_request_info *info,
315 316
			 struct iw_param *vwrq, char *extra)
{
317
	struct lbs_private *priv = dev->ml_priv;
318 319
	int ret = 0;
	u32 val = vwrq->value;
320

321
	lbs_deb_enter(LBS_DEB_WEXT);
322

323 324 325 326 327
	if (vwrq->disabled)
		val = MRVDRV_FRAG_MAX_VALUE;

	if (val < MRVDRV_FRAG_MIN_VALUE || val > MRVDRV_FRAG_MAX_VALUE)
		return -EINVAL;
328

329
	ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, (u16) val);
330 331

	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
332 333 334
	return ret;
}

335
static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info,
336 337
			 struct iw_param *vwrq, char *extra)
{
338
	struct lbs_private *priv = dev->ml_priv;
339 340
	int ret = 0;
	u16 val = 0;
341

342
	lbs_deb_enter(LBS_DEB_WEXT);
343

344
	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, &val);
345 346
	if (ret)
		goto out;
347

348 349 350
	vwrq->value = val;
	vwrq->disabled = ((val < MRVDRV_FRAG_MIN_VALUE)
			  || (val > MRVDRV_FRAG_MAX_VALUE));
351 352
	vwrq->fixed = 1;

353 354
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
355 356 357
	return ret;
}

358
static int lbs_get_mode(struct net_device *dev,
359 360
			 struct iw_request_info *info, u32 * uwrq, char *extra)
{
361
	struct lbs_private *priv = dev->ml_priv;
362

363
	lbs_deb_enter(LBS_DEB_WEXT);
364

365
	*uwrq = priv->mode;
366

367
	lbs_deb_leave(LBS_DEB_WEXT);
368 369 370
	return 0;
}

371 372 373 374 375 376
static int mesh_wlan_get_mode(struct net_device *dev,
		              struct iw_request_info *info, u32 * uwrq,
			      char *extra)
{
	lbs_deb_enter(LBS_DEB_WEXT);

377
	*uwrq = IW_MODE_REPEAT;
378 379 380 381 382

	lbs_deb_leave(LBS_DEB_WEXT);
	return 0;
}

383
static int lbs_get_txpow(struct net_device *dev,
384 385 386
			  struct iw_request_info *info,
			  struct iw_param *vwrq, char *extra)
{
387
	struct lbs_private *priv = dev->ml_priv;
388
	s16 curlevel = 0;
389
	int ret = 0;
390

391
	lbs_deb_enter(LBS_DEB_WEXT);
392

393 394 395 396 397 398 399
	if (!priv->radio_on) {
		lbs_deb_wext("tx power off\n");
		vwrq->value = 0;
		vwrq->disabled = 1;
		goto out;
	}

400
	ret = lbs_get_tx_power(priv, &curlevel, NULL, NULL);
401 402
	if (ret)
		goto out;
403

404 405
	lbs_deb_wext("tx power level %d dbm\n", curlevel);
	priv->txpower_cur = curlevel;
406

407
	vwrq->value = curlevel;
408
	vwrq->fixed = 1;
409 410
	vwrq->disabled = 0;
	vwrq->flags = IW_TXPOW_DBM;
411

412 413 414
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
415 416
}

417
static int lbs_set_retry(struct net_device *dev, struct iw_request_info *info,
418 419
			  struct iw_param *vwrq, char *extra)
{
420
	struct lbs_private *priv = dev->ml_priv;
421 422
	int ret = 0;
	u16 slimit = 0, llimit = 0;
423

424
	lbs_deb_enter(LBS_DEB_WEXT);
425

426 427 428 429 430
        if ((vwrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
                return -EOPNOTSUPP;

	/* The MAC has a 4-bit Total_Tx_Count register
	   Total_Tx_Count = 1 + Tx_Retry_Count */
431 432
#define TX_RETRY_MIN 0
#define TX_RETRY_MAX 14
433 434
	if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX)
		return -EINVAL;
435

436 437 438 439 440 441 442
	/* Add 1 to convert retry count to try count */
	if (vwrq->flags & IW_RETRY_SHORT)
		slimit = (u16) (vwrq->value + 1);
	else if (vwrq->flags & IW_RETRY_LONG)
		llimit = (u16) (vwrq->value + 1);
	else
		slimit = llimit = (u16) (vwrq->value + 1); /* set both */
443

444 445 446 447 448 449
	if (llimit) {
		ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_LONG_RETRY_LIMIT,
				       llimit);
		if (ret)
			goto out;
	}
450

451 452 453 454 455
	if (slimit) {
		/* txretrycount follows the short retry limit */
		priv->txretrycount = slimit;
		ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_SHORT_RETRY_LIMIT,
				       slimit);
456 457
		if (ret)
			goto out;
458 459
	}

460 461 462
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
463 464
}

465
static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info,
466 467
			  struct iw_param *vwrq, char *extra)
{
468
	struct lbs_private *priv = dev->ml_priv;
469
	int ret = 0;
470
	u16 val = 0;
471

472 473
	lbs_deb_enter(LBS_DEB_WEXT);

474
	vwrq->disabled = 0;
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490

	if (vwrq->flags & IW_RETRY_LONG) {
		ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_LONG_RETRY_LIMIT, &val);
		if (ret)
			goto out;

		/* Subtract 1 to convert try count to retry count */
		vwrq->value = val - 1;
		vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
	} else {
		ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_SHORT_RETRY_LIMIT, &val);
		if (ret)
			goto out;

		/* txretry count follows the short retry limit */
		priv->txretrycount = val;
491
		/* Subtract 1 to convert try count to retry count */
492 493
		vwrq->value = val - 1;
		vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
494 495
	}

496 497 498
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
}

static inline void sort_channels(struct iw_freq *freq, int num)
{
	int i, j;
	struct iw_freq temp;

	for (i = 0; i < num; i++)
		for (j = i + 1; j < num; j++)
			if (freq[i].i > freq[j].i) {
				temp.i = freq[i].i;
				temp.m = freq[i].m;

				freq[i].i = freq[j].i;
				freq[i].m = freq[j].m;

				freq[j].i = temp.i;
				freq[j].m = temp.m;
			}
}

/* data rate listing
	MULTI_BANDS:
		abg		a	b	b/g
   Infra 	G(12)		A(8)	B(4)	G(12)
   Adhoc 	A+B(12)		A(8)	B(4)	B(4)

	non-MULTI_BANDS:
					b	b/g
   Infra 	     		    	B(4)	G(12)
   Adhoc 	      		    	B(4)	B(4)
 */
/**
 *  @brief Get Range Info
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info			A pointer to iw_request_info structure
 *  @param vwrq 		A pointer to iw_param structure
 *  @param extra		A pointer to extra data buf
 *  @return 	   		0 --success, otherwise fail
 */
540
static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
541 542 543
			  struct iw_point *dwrq, char *extra)
{
	int i, j;
544
	struct lbs_private *priv = dev->ml_priv;
545 546
	struct iw_range *range = (struct iw_range *)extra;
	struct chan_freq_power *cfp;
547
	u8 rates[MAX_RATES + 1];
548 549 550

	u8 flag = 0;

551
	lbs_deb_enter(LBS_DEB_WEXT);
552 553 554 555 556 557 558 559

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

	range->min_nwid = 0;
	range->max_nwid = 0;

	memset(rates, 0, sizeof(rates));
560
	copy_active_data_rates(priv, rates);
561 562 563
	range->num_bitrates = strnlen(rates, IW_MAX_BITRATES);
	for (i = 0; i < range->num_bitrates; i++)
		range->bitrate[i] = rates[i] * 500000;
564
	range->num_bitrates = i;
565
	lbs_deb_wext("IW_MAX_BITRATES %d, num_bitrates %d\n", IW_MAX_BITRATES,
566 567 568
	       range->num_bitrates);

	range->num_frequency = 0;
569 570 571

	range->scan_capa = IW_SCAN_CAPA_ESSID;

572 573 574
	if (priv->enable11d &&
	    (priv->connect_status == LBS_CONNECTED ||
	    priv->mesh_connect_status == LBS_CONNECTED)) {
575 576 577 578
		u8 chan_no;
		u8 band;

		struct parsed_region_chan_11d *parsed_region_chan =
579
		    &priv->parsed_region_chan;
580 581

		if (parsed_region_chan == NULL) {
582 583
			lbs_deb_wext("11d: parsed_region_chan is NULL\n");
			goto out;
584 585
		}
		band = parsed_region_chan->band;
586
		lbs_deb_wext("band %d, nr_char %d\n", band,
587 588 589 590 591
		       parsed_region_chan->nr_chan);

		for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
		     && (i < parsed_region_chan->nr_chan); i++) {
			chan_no = parsed_region_chan->chanpwr[i].chan;
592
			lbs_deb_wext("chan_no %d\n", chan_no);
593 594
			range->freq[range->num_frequency].i = (long)chan_no;
			range->freq[range->num_frequency].m =
595
			    (long)lbs_chan_2_freq(chan_no) * 100000;
596 597 598 599 600 601 602
			range->freq[range->num_frequency].e = 1;
			range->num_frequency++;
		}
		flag = 1;
	}
	if (!flag) {
		for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
603 604
		     && (j < ARRAY_SIZE(priv->region_channel)); j++) {
			cfp = priv->region_channel[j].CFP;
605
			for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
606
			     && priv->region_channel[j].valid
607
			     && cfp
608
			     && (i < priv->region_channel[j].nrcfp); i++) {
609 610 611 612 613 614 615 616 617 618 619
				range->freq[range->num_frequency].i =
				    (long)cfp->channel;
				range->freq[range->num_frequency].m =
				    (long)cfp->freq * 100000;
				range->freq[range->num_frequency].e = 1;
				cfp++;
				range->num_frequency++;
			}
		}
	}

620
	lbs_deb_wext("IW_MAX_FREQUENCIES %d, num_frequency %d\n",
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
	       IW_MAX_FREQUENCIES, range->num_frequency);

	range->num_channels = range->num_frequency;

	sort_channels(&range->freq[0], range->num_frequency);

	/*
	 * Set an indication of the max TCP throughput in bit/s that we can
	 * expect using this interface
	 */
	if (i > 2)
		range->throughput = 5000 * 1000;
	else
		range->throughput = 1500 * 1000;

	range->min_rts = MRVDRV_RTS_MIN_VALUE;
	range->max_rts = MRVDRV_RTS_MAX_VALUE;
	range->min_frag = MRVDRV_FRAG_MIN_VALUE;
	range->max_frag = MRVDRV_FRAG_MAX_VALUE;

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

646 647 648 649
	/*
	 * Right now we support only "iwconfig ethX power on|off"
	 */
	range->pm_capa = IW_POWER_ON;
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682

	/*
	 * Minimum version we recommend
	 */
	range->we_version_source = 15;

	/*
	 * Version we are compiled with
	 */
	range->we_version_compiled = WIRELESS_EXT;

	range->retry_capa = IW_RETRY_LIMIT;
	range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX;

	range->min_retry = TX_RETRY_MIN;
	range->max_retry = TX_RETRY_MAX;

	/*
	 * Set the qual, level and noise range values
	 */
	range->max_qual.qual = 100;
	range->max_qual.level = 0;
	range->max_qual.noise = 0;
	range->max_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;

	range->avg_qual.qual = 70;
	/* TODO: Find real 'good' to 'bad' threshold value for RSSI */
	range->avg_qual.level = 0;
	range->avg_qual.noise = 0;
	range->avg_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;

	range->sensitivity = 0;

683
	/* Setup the supported power level ranges */
684
	memset(range->txpower, 0, sizeof(range->txpower));
685 686 687 688
	range->txpower_capa = IW_TXPOW_DBM | IW_TXPOW_RANGE;
	range->txpower[0] = priv->txpower_min;
	range->txpower[1] = priv->txpower_max;
	range->num_txpower = 2;
689 690 691 692 693 694

	range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
				IW_EVENT_CAPA_MASK(SIOCGIWAP) |
				IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
	range->event_capa[1] = IW_EVENT_CAPA_K_1;

695
	if (priv->fwcapinfo & FW_CAPINFO_WPA) {
696 697 698 699 700 701
		range->enc_capa =   IW_ENC_CAPA_WPA
		                  | IW_ENC_CAPA_WPA2
		                  | IW_ENC_CAPA_CIPHER_TKIP
		                  | IW_ENC_CAPA_CIPHER_CCMP;
	}

702 703
out:
	lbs_deb_leave(LBS_DEB_WEXT);
704 705 706
	return 0;
}

707
static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
708 709
			  struct iw_param *vwrq, char *extra)
{
710
	struct lbs_private *priv = dev->ml_priv;
711
	int ret = 0;
712

713
	lbs_deb_enter(LBS_DEB_WEXT);
714

715
	if (!(priv->fwcapinfo & FW_CAPINFO_PS)) {
716 717 718 719 720 721
		if (vwrq->disabled)
			return 0;
		else
			return -EINVAL;
	}

722 723 724 725 726
	/* PS is currently supported only in Infrastructure mode
	 * Remove this check if it is to be supported in IBSS mode also
	 */

	if (vwrq->disabled) {
727 728
		priv->psmode = LBS802_11POWERMODECAM;
		if (priv->psstate != PS_STATE_FULL_POWER) {
729
			lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
730 731 732 733 734 735
		}

		return 0;
	}

	if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
736 737
		lbs_deb_wext(
		       "setting power timeout is not supported\n");
738 739
		return -EINVAL;
	} else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
		vwrq->value = vwrq->value / 1000;
		if (!priv->enter_deep_sleep) {
			lbs_pr_err("deep sleep feature is not implemented "
					"for this interface driver\n");
			return -EINVAL;
		}

		if (priv->connect_status == LBS_CONNECTED) {
			if ((priv->is_auto_deep_sleep_enabled) &&
						(vwrq->value == -1000)) {
				lbs_exit_auto_deep_sleep(priv);
				return 0;
			} else {
				lbs_pr_err("can't use deep sleep cmd in "
						"connected state\n");
				return -EINVAL;
			}
		}

		if ((vwrq->value < 0) && (vwrq->value != -1000)) {
			lbs_pr_err("unknown option\n");
			return -EINVAL;
		}

		if (vwrq->value > 0) {
			if (!priv->is_auto_deep_sleep_enabled) {
				priv->is_activity_detected = 0;
				priv->auto_deep_sleep_timeout = vwrq->value;
				lbs_enter_auto_deep_sleep(priv);
			} else {
				priv->auto_deep_sleep_timeout = vwrq->value;
				lbs_deb_debugfs("auto deep sleep: "
						"already enabled\n");
			}
			return 0;
		} else {
			if (priv->is_auto_deep_sleep_enabled) {
				lbs_exit_auto_deep_sleep(priv);
				/* Try to exit deep sleep if auto */
				/*deep sleep disabled */
				ret = lbs_set_deep_sleep(priv, 0);
			}
			if (vwrq->value == 0)
				ret = lbs_set_deep_sleep(priv, 1);
			else if (vwrq->value == -1000)
				ret = lbs_set_deep_sleep(priv, 0);
			return ret;
		}
788 789
	}

790
	if (priv->psmode != LBS802_11POWERMODECAM) {
791 792 793
		return 0;
	}

794
	priv->psmode = LBS802_11POWERMODEMAX_PSP;
795

796
	if (priv->connect_status == LBS_CONNECTED) {
797
		lbs_ps_sleep(priv, CMD_OPTION_WAITFORRSP);
798 799
	}

800
	lbs_deb_leave(LBS_DEB_WEXT);
801

802 803 804
	return 0;
}

805
static int lbs_get_power(struct net_device *dev, struct iw_request_info *info,
806 807
			  struct iw_param *vwrq, char *extra)
{
808
	struct lbs_private *priv = dev->ml_priv;
809

810
	lbs_deb_enter(LBS_DEB_WEXT);
811 812

	vwrq->value = 0;
813 814 815
	vwrq->flags = 0;
	vwrq->disabled = priv->psmode == LBS802_11POWERMODECAM
		|| priv->connect_status == LBS_DISCONNECTED;
816

817
	lbs_deb_leave(LBS_DEB_WEXT);
818 819 820
	return 0;
}

821
static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
822 823 824 825 826 827 828 829 830
{
	enum {
		POOR = 30,
		FAIR = 60,
		GOOD = 80,
		VERY_GOOD = 90,
		EXCELLENT = 95,
		PERFECT = 100
	};
831
	struct lbs_private *priv = dev->ml_priv;
832 833 834 835 836 837
	u32 rssi_qual;
	u32 tx_qual;
	u32 quality = 0;
	int stats_valid = 0;
	u8 rssi;
	u32 tx_retries;
838
	struct cmd_ds_802_11_get_log log;
839

840
	lbs_deb_enter(LBS_DEB_WEXT);
841

842
	priv->wstats.status = priv->mode;
843 844

	/* If we're not associated, all quality values are meaningless */
845 846
	if ((priv->connect_status != LBS_CONNECTED) &&
	    (priv->mesh_connect_status != LBS_CONNECTED))
847 848 849 850
		goto out;

	/* Quality by RSSI */
	priv->wstats.qual.level =
851 852
	    CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG],
	     priv->NF[TYPE_BEACON][TYPE_NOAVG]);
853

854
	if (priv->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
855 856 857
		priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
	} else {
		priv->wstats.qual.noise =
858
		    CAL_NF(priv->NF[TYPE_BEACON][TYPE_NOAVG]);
859 860
	}

861 862
	lbs_deb_wext("signal level %#x\n", priv->wstats.qual.level);
	lbs_deb_wext("noise %#x\n", priv->wstats.qual.noise);
863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879

	rssi = priv->wstats.qual.level - priv->wstats.qual.noise;
	if (rssi < 15)
		rssi_qual = rssi * POOR / 10;
	else if (rssi < 20)
		rssi_qual = (rssi - 15) * (FAIR - POOR) / 5 + POOR;
	else if (rssi < 30)
		rssi_qual = (rssi - 20) * (GOOD - FAIR) / 5 + FAIR;
	else if (rssi < 40)
		rssi_qual = (rssi - 30) * (VERY_GOOD - GOOD) /
		    10 + GOOD;
	else
		rssi_qual = (rssi - 40) * (PERFECT - VERY_GOOD) /
		    10 + VERY_GOOD;
	quality = rssi_qual;

	/* Quality by TX errors */
880
	priv->wstats.discard.retries = dev->stats.tx_errors;
881

882 883 884 885 886
	memset(&log, 0, sizeof(log));
	log.hdr.size = cpu_to_le16(sizeof(log));
	lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log);

	tx_retries = le32_to_cpu(log.retry);
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901

	if (tx_retries > 75)
		tx_qual = (90 - tx_retries) * POOR / 15;
	else if (tx_retries > 70)
		tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR;
	else if (tx_retries > 65)
		tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR;
	else if (tx_retries > 50)
		tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) /
		    15 + GOOD;
	else
		tx_qual = (50 - tx_retries) *
		    (PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
	quality = min(quality, tx_qual);

902
	priv->wstats.discard.code = le32_to_cpu(log.wepundecryptable);
903
	priv->wstats.discard.retries = tx_retries;
904
	priv->wstats.discard.misc = le32_to_cpu(log.ackfailure);
905 906

	/* Calculate quality */
907
	priv->wstats.qual.qual = min_t(u8, quality, 100);
908 909 910 911
	priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
	stats_valid = 1;

	/* update stats asynchronously for future calls */
912
	lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
913 914 915 916 917 918 919 920 921 922 923 924 925
					0, 0, NULL);
out:
	if (!stats_valid) {
		priv->wstats.miss.beacon = 0;
		priv->wstats.discard.retries = 0;
		priv->wstats.qual.qual = 0;
		priv->wstats.qual.level = 0;
		priv->wstats.qual.noise = 0;
		priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED;
		priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID |
		    IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
	}

926
	lbs_deb_leave(LBS_DEB_WEXT);
927 928 929 930 931
	return &priv->wstats;


}

932
static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info,
933 934
		  struct iw_freq *fwrq, char *extra)
{
935
	int ret = -EINVAL;
936
	struct lbs_private *priv = dev->ml_priv;
937
	struct chan_freq_power *cfp;
938
	struct assoc_request * assoc_req;
939

940
	lbs_deb_enter(LBS_DEB_WEXT);
941

942 943
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
944 945 946 947
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}
948

949 950
	/* If setting by frequency, convert to a channel */
	if (fwrq->e == 1) {
951 952
		long f = fwrq->m / 100000;

953
		cfp = find_cfp_by_band_and_freq(priv, 0, f);
954
		if (!cfp) {
955
			lbs_deb_wext("invalid freq %ld\n", f);
956
			goto out;
957 958 959
		}

		fwrq->e = 0;
960
		fwrq->m = (int) cfp->channel;
961 962
	}

963
	/* Setting by channel number */
964
	if (fwrq->m > 1000 || fwrq->e > 0) {
965 966
		goto out;
	}
967

968
	cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m);
969 970
	if (!cfp) {
		goto out;
971 972
	}

973 974 975
	assoc_req->channel = fwrq->m;
	ret = 0;

976
out:
977 978
	if (ret == 0) {
		set_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags);
979
		lbs_postpone_association_work(priv);
980
	} else {
981
		lbs_cancel_association_work(priv);
982
	}
983
	mutex_unlock(&priv->lock);
984 985 986

	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
987 988
}

989 990 991 992
static int lbs_mesh_set_freq(struct net_device *dev,
			     struct iw_request_info *info,
			     struct iw_freq *fwrq, char *extra)
{
993
	struct lbs_private *priv = dev->ml_priv;
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
	struct chan_freq_power *cfp;
	int ret = -EINVAL;

	lbs_deb_enter(LBS_DEB_WEXT);

	/* If setting by frequency, convert to a channel */
	if (fwrq->e == 1) {
		long f = fwrq->m / 100000;

		cfp = find_cfp_by_band_and_freq(priv, 0, f);
		if (!cfp) {
			lbs_deb_wext("invalid freq %ld\n", f);
			goto out;
		}

		fwrq->e = 0;
		fwrq->m = (int) cfp->channel;
	}

	/* Setting by channel number */
	if (fwrq->m > 1000 || fwrq->e > 0) {
		goto out;
	}

	cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m);
	if (!cfp) {
		goto out;
	}

	if (fwrq->m != priv->curbssparams.channel) {
		lbs_deb_wext("mesh channel change forces eth disconnect\n");
		if (priv->mode == IW_MODE_INFRA)
1026 1027 1028
			lbs_cmd_80211_deauthenticate(priv,
						     priv->curbssparams.bssid,
						     WLAN_REASON_DEAUTH_LEAVING);
1029
		else if (priv->mode == IW_MODE_ADHOC)
1030
			lbs_adhoc_stop(priv);
1031
	}
1032
	lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, fwrq->m);
1033
	lbs_update_channel(priv);
1034 1035 1036 1037 1038 1039 1040
	ret = 0;

out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
}

1041
static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
1042 1043
		  struct iw_param *vwrq, char *extra)
{
1044
	struct lbs_private *priv = dev->ml_priv;
1045
	u8 new_rate = 0;
1046 1047
	int ret = -EINVAL;
	u8 rates[MAX_RATES + 1];
1048

1049
	lbs_deb_enter(LBS_DEB_WEXT);
1050

1051
	lbs_deb_wext("vwrq->value %d\n", vwrq->value);
1052 1053 1054 1055
	lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);

	if (vwrq->fixed && vwrq->value == -1)
		goto out;
1056

1057
	/* Auto rate? */
1058 1059 1060
	priv->enablehwauto = !vwrq->fixed;

	if (vwrq->value == -1)
1061
		priv->cur_rate = 0;
1062
	else {
1063 1064
		if (vwrq->value % 100000)
			goto out;
1065

1066 1067 1068
		new_rate = vwrq->value / 500000;
		priv->cur_rate = new_rate;
		/* the rest is only needed for lbs_set_data_rate() */
1069
		memset(rates, 0, sizeof(rates));
1070
		copy_active_data_rates(priv, rates);
1071 1072 1073 1074
		if (!memchr(rates, new_rate, sizeof(rates))) {
			lbs_pr_alert("fixed data rate 0x%X out of range\n",
				new_rate);
			goto out;
1075
		}
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
		if (priv->fwrelease < 0x09000000) {
			ret = lbs_set_power_adapt_cfg(priv, 0,
					POW_ADAPT_DEFAULT_P0,
					POW_ADAPT_DEFAULT_P1,
					POW_ADAPT_DEFAULT_P2);
			if (ret)
				goto out;
		}
		ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
				TPC_DEFAULT_P2, 1);
		if (ret)
			goto out;
1088 1089
	}

1090 1091 1092 1093 1094 1095
	/* Try the newer command first (Firmware Spec 5.1 and above) */
	ret = lbs_cmd_802_11_rate_adapt_rateset(priv, CMD_ACT_SET);

	/* Fallback to older version */
	if (ret)
		ret = lbs_set_data_rate(priv, new_rate);
1096

1097
out:
1098
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1099 1100 1101
	return ret;
}

1102
static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info,
1103 1104
		  struct iw_param *vwrq, char *extra)
{
1105
	struct lbs_private *priv = dev->ml_priv;
1106

1107
	lbs_deb_enter(LBS_DEB_WEXT);
1108

1109 1110
	if (priv->connect_status == LBS_CONNECTED) {
		vwrq->value = priv->cur_rate * 500000;
1111

1112
		if (priv->enablehwauto)
1113 1114 1115 1116
			vwrq->fixed = 0;
		else
			vwrq->fixed = 1;

1117
	} else {
1118 1119
		vwrq->fixed = 0;
		vwrq->value = 0;
1120 1121
	}

1122
	lbs_deb_leave(LBS_DEB_WEXT);
1123 1124 1125
	return 0;
}

1126
static int lbs_set_mode(struct net_device *dev,
1127 1128 1129
		  struct iw_request_info *info, u32 * uwrq, char *extra)
{
	int ret = 0;
1130
	struct lbs_private *priv = dev->ml_priv;
1131 1132
	struct assoc_request * assoc_req;

1133
	lbs_deb_enter(LBS_DEB_WEXT);
1134

1135 1136 1137
	if (   (*uwrq != IW_MODE_ADHOC)
	    && (*uwrq != IW_MODE_INFRA)
	    && (*uwrq != IW_MODE_AUTO)) {
1138
		lbs_deb_wext("Invalid mode: 0x%x\n", *uwrq);
1139 1140
		ret = -EINVAL;
		goto out;
1141 1142
	}

1143 1144
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1145 1146
	if (!assoc_req) {
		ret = -ENOMEM;
1147
		lbs_cancel_association_work(priv);
1148
	} else {
1149
		assoc_req->mode = *uwrq;
1150
		set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
1151
		lbs_postpone_association_work(priv);
1152
		lbs_deb_wext("Switching to mode: 0x%x\n", *uwrq);
1153
	}
1154
	mutex_unlock(&priv->lock);
1155

1156
out:
1157
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
	return ret;
}


/**
 *  @brief Get Encryption key
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info			A pointer to iw_request_info structure
 *  @param vwrq 		A pointer to iw_param structure
 *  @param extra		A pointer to extra data buf
 *  @return 	   		0 --success, otherwise fail
 */
1171
static int lbs_get_encode(struct net_device *dev,
1172 1173 1174
			   struct iw_request_info *info,
			   struct iw_point *dwrq, u8 * extra)
{
1175
	struct lbs_private *priv = dev->ml_priv;
1176 1177
	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;

1178
	lbs_deb_enter(LBS_DEB_WEXT);
1179

1180
	lbs_deb_wext("flags 0x%x, index %d, length %d, wep_tx_keyidx %d\n",
1181
	       dwrq->flags, index, dwrq->length, priv->wep_tx_keyidx);
1182 1183 1184 1185

	dwrq->flags = 0;

	/* Authentication method */
1186
	switch (priv->secinfo.auth_mode) {
1187
	case IW_AUTH_ALG_OPEN_SYSTEM:
1188 1189 1190
		dwrq->flags = IW_ENCODE_OPEN;
		break;

1191 1192
	case IW_AUTH_ALG_SHARED_KEY:
	case IW_AUTH_ALG_LEAP:
1193 1194 1195 1196 1197 1198 1199 1200 1201
		dwrq->flags = IW_ENCODE_RESTRICTED;
		break;
	default:
		dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN;
		break;
	}

	memset(extra, 0, 16);

1202
	mutex_lock(&priv->lock);
1203 1204 1205

	/* Default to returning current transmit key */
	if (index < 0)
1206
		index = priv->wep_tx_keyidx;
1207

1208 1209 1210 1211
	if ((priv->wep_keys[index].len) && priv->secinfo.wep_enabled) {
		memcpy(extra, priv->wep_keys[index].key,
		       priv->wep_keys[index].len);
		dwrq->length = priv->wep_keys[index].len;
1212 1213 1214 1215

		dwrq->flags |= (index + 1);
		/* Return WEP enabled */
		dwrq->flags &= ~IW_ENCODE_DISABLED;
1216 1217
	} else if ((priv->secinfo.WPAenabled)
		   || (priv->secinfo.WPA2enabled)) {
1218 1219
		/* return WPA enabled */
		dwrq->flags &= ~IW_ENCODE_DISABLED;
1220
		dwrq->flags |= IW_ENCODE_NOKEY;
1221 1222 1223 1224
	} else {
		dwrq->flags |= IW_ENCODE_DISABLED;
	}

1225
	mutex_unlock(&priv->lock);
1226

1227
	lbs_deb_wext("key: %02x:%02x:%02x:%02x:%02x:%02x, keylen %d\n",
1228 1229 1230
	       extra[0], extra[1], extra[2],
	       extra[3], extra[4], extra[5], dwrq->length);

1231
	lbs_deb_wext("return flags 0x%x\n", dwrq->flags);
1232

1233
	lbs_deb_leave(LBS_DEB_WEXT);
1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
	return 0;
}

/**
 *  @brief Set Encryption key (internal)
 *
 *  @param priv			A pointer to private card structure
 *  @param key_material		A pointer to key material
 *  @param key_length		length of key material
 *  @param index		key index to set
 *  @param set_tx_key		Force set TX key (1 = yes, 0 = no)
 *  @return 	   		0 --success, otherwise fail
 */
1247
static int lbs_set_wep_key(struct assoc_request *assoc_req,
1248 1249 1250 1251 1252
			    const char *key_material,
			    u16 key_length,
			    u16 index,
			    int set_tx_key)
{
1253
	int ret = 0;
1254
	struct enc_key *pkey;
1255

1256
	lbs_deb_enter(LBS_DEB_WEXT);
1257 1258 1259

	/* Paranoid validation of key index */
	if (index > 3) {
1260 1261
		ret = -EINVAL;
		goto out;
1262 1263 1264 1265
	}

	/* validate max key length */
	if (key_length > KEY_LEN_WEP_104) {
1266 1267
		ret = -EINVAL;
		goto out;
1268 1269 1270 1271 1272
	}

	pkey = &assoc_req->wep_keys[index];

	if (key_length > 0) {
1273
		memset(pkey, 0, sizeof(struct enc_key));
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
		pkey->type = KEY_TYPE_ID_WEP;

		/* Standardize the key length */
		pkey->len = (key_length > KEY_LEN_WEP_40) ?
		                KEY_LEN_WEP_104 : KEY_LEN_WEP_40;
		memcpy(pkey->key, key_material, key_length);
	}

	if (set_tx_key) {
		/* Ensure the chosen key is valid */
		if (!pkey->len) {
1285 1286 1287
			lbs_deb_wext("key not set, so cannot enable it\n");
			ret = -EINVAL;
			goto out;
1288 1289 1290 1291
		}
		assoc_req->wep_tx_keyidx = index;
	}

1292
	assoc_req->secinfo.wep_enabled = 1;
1293

1294 1295 1296
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320
}

static int validate_key_index(u16 def_index, u16 raw_index,
			      u16 *out_index, u16 *is_default)
{
	if (!out_index || !is_default)
		return -EINVAL;

	/* Verify index if present, otherwise use default TX key index */
	if (raw_index > 0) {
		if (raw_index > 4)
			return -EINVAL;
		*out_index = raw_index - 1;
	} else {
		*out_index = def_index;
		*is_default = 1;
	}
	return 0;
}

static void disable_wep(struct assoc_request *assoc_req)
{
	int i;

1321 1322
	lbs_deb_enter(LBS_DEB_WEXT);

1323
	/* Set Open System auth mode */
1324
	assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1325 1326

	/* Clear WEP keys and mark WEP as disabled */
1327
	assoc_req->secinfo.wep_enabled = 0;
1328 1329 1330 1331 1332
	for (i = 0; i < 4; i++)
		assoc_req->wep_keys[i].len = 0;

	set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
	set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
1333 1334 1335 1336 1337 1338 1339 1340

	lbs_deb_leave(LBS_DEB_WEXT);
}

static void disable_wpa(struct assoc_request *assoc_req)
{
	lbs_deb_enter(LBS_DEB_WEXT);

1341
	memset(&assoc_req->wpa_mcast_key, 0, sizeof (struct enc_key));
1342 1343 1344
	assoc_req->wpa_mcast_key.flags = KEY_INFO_WPA_MCAST;
	set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);

1345
	memset(&assoc_req->wpa_unicast_key, 0, sizeof (struct enc_key));
1346 1347 1348 1349 1350 1351 1352 1353
	assoc_req->wpa_unicast_key.flags = KEY_INFO_WPA_UNICAST;
	set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);

	assoc_req->secinfo.WPAenabled = 0;
	assoc_req->secinfo.WPA2enabled = 0;
	set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);

	lbs_deb_leave(LBS_DEB_WEXT);
1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364
}

/**
 *  @brief Set Encryption key
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info			A pointer to iw_request_info structure
 *  @param vwrq 		A pointer to iw_param structure
 *  @param extra		A pointer to extra data buf
 *  @return 	   		0 --success, otherwise fail
 */
1365
static int lbs_set_encode(struct net_device *dev,
1366 1367 1368 1369
		    struct iw_request_info *info,
		    struct iw_point *dwrq, char *extra)
{
	int ret = 0;
1370
	struct lbs_private *priv = dev->ml_priv;
1371 1372 1373
	struct assoc_request * assoc_req;
	u16 is_default = 0, index = 0, set_tx_key = 0;

1374
	lbs_deb_enter(LBS_DEB_WEXT);
1375

1376 1377
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1378 1379 1380 1381 1382 1383 1384
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}

	if (dwrq->flags & IW_ENCODE_DISABLED) {
		disable_wep (assoc_req);
1385
		disable_wpa (assoc_req);
1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399
		goto out;
	}

	ret = validate_key_index(assoc_req->wep_tx_keyidx,
	                         (dwrq->flags & IW_ENCODE_INDEX),
	                         &index, &is_default);
	if (ret) {
		ret = -EINVAL;
		goto out;
	}

	/* If WEP isn't enabled, or if there is no key data but a valid
	 * index, set the TX key.
	 */
1400
	if (!assoc_req->secinfo.wep_enabled || (dwrq->length == 0 && !is_default))
1401 1402
		set_tx_key = 1;

1403
	ret = lbs_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key);
1404 1405 1406 1407 1408 1409 1410 1411 1412
	if (ret)
		goto out;

	if (dwrq->length)
		set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
	if (set_tx_key)
		set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);

	if (dwrq->flags & IW_ENCODE_RESTRICTED) {
1413
		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1414
	} else if (dwrq->flags & IW_ENCODE_OPEN) {
1415
		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1416 1417 1418 1419 1420
	}

out:
	if (ret == 0) {
		set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1421
		lbs_postpone_association_work(priv);
1422
	} else {
1423
		lbs_cancel_association_work(priv);
1424
	}
1425
	mutex_unlock(&priv->lock);
1426

1427
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439
	return ret;
}

/**
 *  @brief Get Extended Encryption key (WPA/802.1x and WEP)
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info			A pointer to iw_request_info structure
 *  @param vwrq 		A pointer to iw_param structure
 *  @param extra		A pointer to extra data buf
 *  @return 	   		0 on success, otherwise failure
 */
1440
static int lbs_get_encodeext(struct net_device *dev,
1441 1442 1443 1444 1445
			      struct iw_request_info *info,
			      struct iw_point *dwrq,
			      char *extra)
{
	int ret = -EINVAL;
1446
	struct lbs_private *priv = dev->ml_priv;
1447 1448 1449
	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
	int index, max_key_len;

1450
	lbs_deb_enter(LBS_DEB_WEXT);
1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461

	max_key_len = dwrq->length - sizeof(*ext);
	if (max_key_len < 0)
		goto out;

	index = dwrq->flags & IW_ENCODE_INDEX;
	if (index) {
		if (index < 1 || index > 4)
			goto out;
		index--;
	} else {
1462
		index = priv->wep_tx_keyidx;
1463 1464
	}

R
Roel Kluin 已提交
1465
	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
1466
	    ext->alg != IW_ENCODE_ALG_WEP) {
1467
		if (index != 0 || priv->mode != IW_MODE_INFRA)
1468 1469 1470 1471 1472 1473
			goto out;
	}

	dwrq->flags = index + 1;
	memset(ext, 0, sizeof(*ext));

1474 1475 1476
	if (   !priv->secinfo.wep_enabled
	    && !priv->secinfo.WPAenabled
	    && !priv->secinfo.WPA2enabled) {
1477 1478 1479 1480 1481 1482
		ext->alg = IW_ENCODE_ALG_NONE;
		ext->key_len = 0;
		dwrq->flags |= IW_ENCODE_DISABLED;
	} else {
		u8 *key = NULL;

1483 1484 1485
		if (   priv->secinfo.wep_enabled
		    && !priv->secinfo.WPAenabled
		    && !priv->secinfo.WPA2enabled) {
1486
			/* WEP */
1487
			ext->alg = IW_ENCODE_ALG_WEP;
1488 1489 1490 1491 1492
			ext->key_len = priv->wep_keys[index].len;
			key = &priv->wep_keys[index].key[0];
		} else if (   !priv->secinfo.wep_enabled
		           && (priv->secinfo.WPAenabled ||
		               priv->secinfo.WPA2enabled)) {
1493
			/* WPA */
1494
			struct enc_key * pkey = NULL;
1495

1496 1497 1498 1499 1500 1501
			if (   priv->wpa_mcast_key.len
			    && (priv->wpa_mcast_key.flags & KEY_INFO_WPA_ENABLED))
				pkey = &priv->wpa_mcast_key;
			else if (   priv->wpa_unicast_key.len
			         && (priv->wpa_unicast_key.flags & KEY_INFO_WPA_ENABLED))
				pkey = &priv->wpa_unicast_key;
1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514

			if (pkey) {
				if (pkey->type == KEY_TYPE_ID_AES) {
					ext->alg = IW_ENCODE_ALG_CCMP;
				} else {
					ext->alg = IW_ENCODE_ALG_TKIP;
				}
				ext->key_len = pkey->len;
				key = &pkey->key[0];
			} else {
				ext->alg = IW_ENCODE_ALG_TKIP;
				ext->key_len = 0;
			}
1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532
		} else {
			goto out;
		}

		if (ext->key_len > max_key_len) {
			ret = -E2BIG;
			goto out;
		}

		if (ext->key_len)
			memcpy(ext->key, key, ext->key_len);
		else
			dwrq->flags |= IW_ENCODE_NOKEY;
		dwrq->flags |= IW_ENCODE_ENABLED;
	}
	ret = 0;

out:
1533
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545
	return ret;
}

/**
 *  @brief Set Encryption key Extended (WPA/802.1x and WEP)
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info			A pointer to iw_request_info structure
 *  @param vwrq 		A pointer to iw_param structure
 *  @param extra		A pointer to extra data buf
 *  @return 	   		0 --success, otherwise fail
 */
1546
static int lbs_set_encodeext(struct net_device *dev,
1547 1548 1549 1550 1551
			      struct iw_request_info *info,
			      struct iw_point *dwrq,
			      char *extra)
{
	int ret = 0;
1552
	struct lbs_private *priv = dev->ml_priv;
1553 1554 1555 1556
	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
	int alg = ext->alg;
	struct assoc_request * assoc_req;

1557
	lbs_deb_enter(LBS_DEB_WEXT);
1558

1559 1560
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1561 1562 1563 1564 1565 1566 1567
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}

	if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) {
		disable_wep (assoc_req);
1568
		disable_wpa (assoc_req);
1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580
	} else if (alg == IW_ENCODE_ALG_WEP) {
		u16 is_default = 0, index, set_tx_key = 0;

		ret = validate_key_index(assoc_req->wep_tx_keyidx,
		                         (dwrq->flags & IW_ENCODE_INDEX),
		                         &index, &is_default);
		if (ret)
			goto out;

		/* If WEP isn't enabled, or if there is no key data but a valid
		 * index, or if the set-TX-key flag was passed, set the TX key.
		 */
1581
		if (   !assoc_req->secinfo.wep_enabled
1582 1583 1584 1585 1586
		    || (dwrq->length == 0 && !is_default)
		    || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY))
			set_tx_key = 1;

		/* Copy key to driver */
1587
		ret = lbs_set_wep_key(assoc_req, ext->key, ext->key_len, index,
1588 1589 1590 1591 1592
					set_tx_key);
		if (ret)
			goto out;

		if (dwrq->flags & IW_ENCODE_RESTRICTED) {
1593
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1594
		} else if (dwrq->flags & IW_ENCODE_OPEN) {
1595
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1596 1597 1598 1599 1600 1601 1602 1603 1604
		}

		/* Mark the various WEP bits as modified */
		set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
		if (dwrq->length)
			set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
		if (set_tx_key)
			set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
	} else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) {
1605
		struct enc_key * pkey;
1606 1607 1608 1609 1610 1611

		/* validate key length */
		if (((alg == IW_ENCODE_ALG_TKIP)
			&& (ext->key_len != KEY_LEN_WPA_TKIP))
		    || ((alg == IW_ENCODE_ALG_CCMP)
		        && (ext->key_len != KEY_LEN_WPA_AES))) {
1612
				lbs_deb_wext("invalid size %d for key of alg "
1613
				       "type %d\n",
1614 1615 1616 1617 1618 1619
				       ext->key_len,
				       alg);
				ret = -EINVAL;
				goto out;
		}

1620
		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
1621
			pkey = &assoc_req->wpa_mcast_key;
1622 1623
			set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
		} else {
1624
			pkey = &assoc_req->wpa_unicast_key;
1625 1626
			set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
		}
1627

1628
		memset(pkey, 0, sizeof (struct enc_key));
1629 1630
		memcpy(pkey->key, ext->key, ext->key_len);
		pkey->len = ext->key_len;
1631 1632
		if (pkey->len)
			pkey->flags |= KEY_INFO_WPA_ENABLED;
1633

1634
		/* Do this after zeroing key structure */
1635 1636 1637 1638 1639 1640
		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
			pkey->flags |= KEY_INFO_WPA_MCAST;
		} else {
			pkey->flags |= KEY_INFO_WPA_UNICAST;
		}

1641
		if (alg == IW_ENCODE_ALG_TKIP) {
1642
			pkey->type = KEY_TYPE_ID_TKIP;
1643
		} else if (alg == IW_ENCODE_ALG_CCMP) {
1644
			pkey->type = KEY_TYPE_ID_AES;
1645
		}
1646 1647 1648 1649 1650 1651 1652 1653 1654

		/* If WPA isn't enabled yet, do that now */
		if (   assoc_req->secinfo.WPAenabled == 0
		    && assoc_req->secinfo.WPA2enabled == 0) {
			assoc_req->secinfo.WPAenabled = 1;
			assoc_req->secinfo.WPA2enabled = 1;
			set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
		}

1655 1656 1657
		/* Only disable wep if necessary: can't waste time here. */
		if (priv->mac_control & CMD_ACT_MAC_WEP_ENABLE)
			disable_wep(assoc_req);
1658 1659 1660
	}

out:
1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674
	if (ret == 0) {
		/* 802.1x and WPA rekeying must happen as quickly as possible,
		 * especially during the 4-way handshake; thus if in
		 * infrastructure mode, and either (a) 802.1x is enabled or
		 * (b) WPA is being used, set the key right away.
		 */
		if (assoc_req->mode == IW_MODE_INFRA &&
		    ((assoc_req->secinfo.key_mgmt & IW_AUTH_KEY_MGMT_802_1X) ||
		     (assoc_req->secinfo.key_mgmt & IW_AUTH_KEY_MGMT_PSK) ||
		      assoc_req->secinfo.WPAenabled ||
		      assoc_req->secinfo.WPA2enabled)) {
			lbs_do_association_work(priv);
		} else
			lbs_postpone_association_work(priv);
1675
	} else {
1676
		lbs_cancel_association_work(priv);
1677
	}
1678
	mutex_unlock(&priv->lock);
1679

1680
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1681 1682 1683 1684
	return ret;
}


1685
static int lbs_set_genie(struct net_device *dev,
1686 1687 1688 1689
			  struct iw_request_info *info,
			  struct iw_point *dwrq,
			  char *extra)
{
1690
	struct lbs_private *priv = dev->ml_priv;
1691 1692 1693
	int ret = 0;
	struct assoc_request * assoc_req;

1694
	lbs_deb_enter(LBS_DEB_WEXT);
1695

1696 1697
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}

	if (dwrq->length > MAX_WPA_IE_LEN ||
	    (dwrq->length && extra == NULL)) {
		ret = -EINVAL;
		goto out;
	}

	if (dwrq->length) {
		memcpy(&assoc_req->wpa_ie[0], extra, dwrq->length);
		assoc_req->wpa_ie_len = dwrq->length;
	} else {
1713
		memset(&assoc_req->wpa_ie[0], 0, sizeof(priv->wpa_ie));
1714 1715 1716 1717 1718 1719
		assoc_req->wpa_ie_len = 0;
	}

out:
	if (ret == 0) {
		set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags);
1720
		lbs_postpone_association_work(priv);
1721
	} else {
1722
		lbs_cancel_association_work(priv);
1723
	}
1724
	mutex_unlock(&priv->lock);
1725

1726
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1727 1728 1729
	return ret;
}

1730
static int lbs_get_genie(struct net_device *dev,
1731 1732 1733 1734
			  struct iw_request_info *info,
			  struct iw_point *dwrq,
			  char *extra)
{
1735
	int ret = 0;
1736
	struct lbs_private *priv = dev->ml_priv;
1737

1738
	lbs_deb_enter(LBS_DEB_WEXT);
1739

1740
	if (priv->wpa_ie_len == 0) {
1741
		dwrq->length = 0;
1742
		goto out;
1743 1744
	}

1745
	if (dwrq->length < priv->wpa_ie_len) {
1746 1747
		ret = -E2BIG;
		goto out;
1748 1749
	}

1750 1751
	dwrq->length = priv->wpa_ie_len;
	memcpy(extra, &priv->wpa_ie[0], priv->wpa_ie_len);
1752

1753 1754 1755
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
1756 1757 1758
}


1759
static int lbs_set_auth(struct net_device *dev,
1760 1761 1762 1763
			 struct iw_request_info *info,
			 struct iw_param *dwrq,
			 char *extra)
{
1764
	struct lbs_private *priv = dev->ml_priv;
1765 1766 1767 1768
	struct assoc_request * assoc_req;
	int ret = 0;
	int updated = 0;

1769
	lbs_deb_enter(LBS_DEB_WEXT);
1770

1771 1772
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1773 1774 1775 1776 1777 1778
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}

	switch (dwrq->flags & IW_AUTH_INDEX) {
1779 1780
	case IW_AUTH_PRIVACY_INVOKED:
	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
1781 1782 1783
	case IW_AUTH_TKIP_COUNTERMEASURES:
	case IW_AUTH_CIPHER_PAIRWISE:
	case IW_AUTH_CIPHER_GROUP:
1784
	case IW_AUTH_DROP_UNENCRYPTED:
1785 1786 1787 1788 1789
		/*
		 * libertas does not use these parameters
		 */
		break;

1790 1791 1792 1793 1794
	case IW_AUTH_KEY_MGMT:
		assoc_req->secinfo.key_mgmt = dwrq->value;
		updated = 1;
		break;

1795 1796 1797 1798
	case IW_AUTH_WPA_VERSION:
		if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) {
			assoc_req->secinfo.WPAenabled = 0;
			assoc_req->secinfo.WPA2enabled = 0;
1799
			disable_wpa (assoc_req);
1800 1801 1802
		}
		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) {
			assoc_req->secinfo.WPAenabled = 1;
1803
			assoc_req->secinfo.wep_enabled = 0;
1804
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1805 1806 1807
		}
		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) {
			assoc_req->secinfo.WPA2enabled = 1;
1808
			assoc_req->secinfo.wep_enabled = 0;
1809
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1810 1811 1812 1813 1814 1815
		}
		updated = 1;
		break;

	case IW_AUTH_80211_AUTH_ALG:
		if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) {
1816
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1817
		} else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) {
1818
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1819
		} else if (dwrq->value & IW_AUTH_ALG_LEAP) {
1820
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_LEAP;
1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832
		} else {
			ret = -EINVAL;
		}
		updated = 1;
		break;

	case IW_AUTH_WPA_ENABLED:
		if (dwrq->value) {
			if (!assoc_req->secinfo.WPAenabled &&
			    !assoc_req->secinfo.WPA2enabled) {
				assoc_req->secinfo.WPAenabled = 1;
				assoc_req->secinfo.WPA2enabled = 1;
1833
				assoc_req->secinfo.wep_enabled = 0;
1834
				assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1835 1836 1837 1838
			}
		} else {
			assoc_req->secinfo.WPAenabled = 0;
			assoc_req->secinfo.WPA2enabled = 0;
1839
			disable_wpa (assoc_req);
1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852
		}
		updated = 1;
		break;

	default:
		ret = -EOPNOTSUPP;
		break;
	}

out:
	if (ret == 0) {
		if (updated)
			set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1853
		lbs_postpone_association_work(priv);
1854
	} else if (ret != -EOPNOTSUPP) {
1855
		lbs_cancel_association_work(priv);
1856
	}
1857
	mutex_unlock(&priv->lock);
1858

1859
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1860 1861 1862
	return ret;
}

1863
static int lbs_get_auth(struct net_device *dev,
1864 1865 1866 1867
			 struct iw_request_info *info,
			 struct iw_param *dwrq,
			 char *extra)
{
1868
	int ret = 0;
1869
	struct lbs_private *priv = dev->ml_priv;
1870

1871
	lbs_deb_enter(LBS_DEB_WEXT);
1872 1873

	switch (dwrq->flags & IW_AUTH_INDEX) {
1874 1875 1876 1877
	case IW_AUTH_KEY_MGMT:
		dwrq->value = priv->secinfo.key_mgmt;
		break;

1878 1879
	case IW_AUTH_WPA_VERSION:
		dwrq->value = 0;
1880
		if (priv->secinfo.WPAenabled)
1881
			dwrq->value |= IW_AUTH_WPA_VERSION_WPA;
1882
		if (priv->secinfo.WPA2enabled)
1883 1884 1885 1886 1887 1888
			dwrq->value |= IW_AUTH_WPA_VERSION_WPA2;
		if (!dwrq->value)
			dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED;
		break;

	case IW_AUTH_80211_AUTH_ALG:
1889
		dwrq->value = priv->secinfo.auth_mode;
1890 1891 1892
		break;

	case IW_AUTH_WPA_ENABLED:
1893
		if (priv->secinfo.WPAenabled && priv->secinfo.WPA2enabled)
1894 1895 1896 1897
			dwrq->value = 1;
		break;

	default:
1898
		ret = -EOPNOTSUPP;
1899 1900
	}

1901 1902
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
1903 1904 1905
}


1906
static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
1907 1908 1909
		   struct iw_param *vwrq, char *extra)
{
	int ret = 0;
1910
	struct lbs_private *priv = dev->ml_priv;
1911
	s16 dbm = (s16) vwrq->value;
1912

1913
	lbs_deb_enter(LBS_DEB_WEXT);
1914 1915

	if (vwrq->disabled) {
1916
		lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 0);
1917
		goto out;
1918 1919
	}

1920
	if (vwrq->fixed == 0) {
1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935
		/* User requests automatic tx power control, however there are
		 * many auto tx settings.  For now use firmware defaults until
		 * we come up with a good way to expose these to the user. */
		if (priv->fwrelease < 0x09000000) {
			ret = lbs_set_power_adapt_cfg(priv, 1,
					POW_ADAPT_DEFAULT_P0,
					POW_ADAPT_DEFAULT_P1,
					POW_ADAPT_DEFAULT_P2);
			if (ret)
				goto out;
		}
		ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
				TPC_DEFAULT_P2, 1);
		if (ret)
			goto out;
1936 1937 1938 1939 1940 1941 1942 1943
		dbm = priv->txpower_max;
	} else {
		/* Userspace check in iwrange if it should use dBm or mW,
		 * therefore this should never happen... Jean II */
		if ((vwrq->flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) {
			ret = -EOPNOTSUPP;
			goto out;
		}
1944

1945 1946
		/* Validate requested power level against firmware allowed
		 * levels */
1947 1948 1949 1950
		if (priv->txpower_min && (dbm < priv->txpower_min)) {
			ret = -EINVAL;
			goto out;
		}
1951

1952 1953 1954 1955
		if (priv->txpower_max && (dbm > priv->txpower_max)) {
			ret = -EINVAL;
			goto out;
		}
1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967
		if (priv->fwrelease < 0x09000000) {
			ret = lbs_set_power_adapt_cfg(priv, 0,
					POW_ADAPT_DEFAULT_P0,
					POW_ADAPT_DEFAULT_P1,
					POW_ADAPT_DEFAULT_P2);
			if (ret)
				goto out;
		}
		ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
				TPC_DEFAULT_P2, 1);
		if (ret)
			goto out;
1968
	}
1969

1970 1971 1972 1973 1974 1975
	/* If the radio was off, turn it on */
	if (!priv->radio_on) {
		ret = lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 1);
		if (ret)
			goto out;
	}
1976

1977
	lbs_deb_wext("txpower set %d dBm\n", dbm);
1978

1979
	ret = lbs_set_tx_power(priv, dbm);
1980

1981
out:
1982
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1983 1984 1985
	return ret;
}

1986
static int lbs_get_essid(struct net_device *dev, struct iw_request_info *info,
1987 1988
		   struct iw_point *dwrq, char *extra)
{
1989
	struct lbs_private *priv = dev->ml_priv;
1990

1991 1992
	lbs_deb_enter(LBS_DEB_WEXT);

1993 1994 1995 1996 1997 1998 1999 2000
	/*
	 * Note : if dwrq->flags != 0, we should get the relevant SSID from
	 * the SSID list...
	 */

	/*
	 * Get the current SSID
	 */
2001 2002 2003 2004
	if (priv->connect_status == LBS_CONNECTED) {
		memcpy(extra, priv->curbssparams.ssid,
		       priv->curbssparams.ssid_len);
		extra[priv->curbssparams.ssid_len] = '\0';
2005 2006
	} else {
		memset(extra, 0, 32);
2007
		extra[priv->curbssparams.ssid_len] = '\0';
2008 2009 2010 2011 2012
	}
	/*
	 * If none, we may want to get the one that was set
	 */

2013
	dwrq->length = priv->curbssparams.ssid_len;
2014 2015 2016

	dwrq->flags = 1;	/* active */

2017
	lbs_deb_leave(LBS_DEB_WEXT);
2018 2019 2020
	return 0;
}

2021
static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
2022 2023
		   struct iw_point *dwrq, char *extra)
{
2024
	struct lbs_private *priv = dev->ml_priv;
2025
	int ret = 0;
2026 2027
	u8 ssid[IW_ESSID_MAX_SIZE];
	u8 ssid_len = 0;
2028
	struct assoc_request * assoc_req;
2029
	int in_ssid_len = dwrq->length;
2030
	DECLARE_SSID_BUF(ssid_buf);
2031

2032
	lbs_deb_enter(LBS_DEB_WEXT);
2033

2034 2035 2036 2037 2038
	if (!priv->radio_on) {
		ret = -EINVAL;
		goto out;
	}

2039
	/* Check the size of the string */
2040
	if (in_ssid_len > IW_ESSID_MAX_SIZE) {
2041 2042 2043 2044
		ret = -E2BIG;
		goto out;
	}

2045
	memset(&ssid, 0, sizeof(ssid));
2046

2047
	if (!dwrq->flags || !in_ssid_len) {
2048 2049 2050
		/* "any" SSID requested; leave SSID blank */
	} else {
		/* Specific SSID requested */
2051 2052
		memcpy(&ssid, extra, in_ssid_len);
		ssid_len = in_ssid_len;
2053 2054
	}

2055 2056 2057 2058
	if (!ssid_len) {
		lbs_deb_wext("requested any SSID\n");
	} else {
		lbs_deb_wext("requested SSID '%s'\n",
2059
		             print_ssid(ssid_buf, ssid, ssid_len));
2060
	}
2061 2062

out:
2063
	mutex_lock(&priv->lock);
2064 2065
	if (ret == 0) {
		/* Get or create the current association request */
2066
		assoc_req = lbs_get_association_request(priv);
2067 2068 2069 2070
		if (!assoc_req) {
			ret = -ENOMEM;
		} else {
			/* Copy the SSID to the association request */
2071 2072
			memcpy(&assoc_req->ssid, &ssid, IW_ESSID_MAX_SIZE);
			assoc_req->ssid_len = ssid_len;
2073
			set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
2074
			lbs_postpone_association_work(priv);
2075 2076 2077 2078 2079
		}
	}

	/* Cancel the association request if there was an error */
	if (ret != 0) {
2080
		lbs_cancel_association_work(priv);
2081 2082
	}

2083
	mutex_unlock(&priv->lock);
2084

2085
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
2086 2087 2088
	return ret;
}

2089 2090 2091 2092
static int lbs_mesh_get_essid(struct net_device *dev,
			      struct iw_request_info *info,
			      struct iw_point *dwrq, char *extra)
{
2093
	struct lbs_private *priv = dev->ml_priv;
2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110

	lbs_deb_enter(LBS_DEB_WEXT);

	memcpy(extra, priv->mesh_ssid, priv->mesh_ssid_len);

	dwrq->length = priv->mesh_ssid_len;

	dwrq->flags = 1;	/* active */

	lbs_deb_leave(LBS_DEB_WEXT);
	return 0;
}

static int lbs_mesh_set_essid(struct net_device *dev,
			      struct iw_request_info *info,
			      struct iw_point *dwrq, char *extra)
{
2111
	struct lbs_private *priv = dev->ml_priv;
2112 2113 2114 2115
	int ret = 0;

	lbs_deb_enter(LBS_DEB_WEXT);

2116 2117 2118 2119 2120
	if (!priv->radio_on) {
		ret = -EINVAL;
		goto out;
	}

2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135
	/* Check the size of the string */
	if (dwrq->length > IW_ESSID_MAX_SIZE) {
		ret = -E2BIG;
		goto out;
	}

	if (!dwrq->flags || !dwrq->length) {
		ret = -EINVAL;
		goto out;
	} else {
		/* Specific SSID requested */
		memcpy(priv->mesh_ssid, extra, dwrq->length);
		priv->mesh_ssid_len = dwrq->length;
	}

2136 2137
	lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
			priv->curbssparams.channel);
2138 2139 2140 2141 2142
 out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
}

2143 2144 2145 2146 2147 2148 2149 2150 2151
/**
 *  @brief Connect to the AP or Ad-hoc Network with specific bssid
 *
 *  @param dev          A pointer to net_device structure
 *  @param info         A pointer to iw_request_info structure
 *  @param awrq         A pointer to iw_param structure
 *  @param extra        A pointer to extra data buf
 *  @return             0 --success, otherwise fail
 */
2152
static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
2153 2154
		 struct sockaddr *awrq, char *extra)
{
2155
	struct lbs_private *priv = dev->ml_priv;
2156 2157 2158
	struct assoc_request * assoc_req;
	int ret = 0;

2159
	lbs_deb_enter(LBS_DEB_WEXT);
2160

2161 2162 2163
	if (!priv->radio_on)
		return -EINVAL;

2164 2165 2166
	if (awrq->sa_family != ARPHRD_ETHER)
		return -EINVAL;

J
Johannes Berg 已提交
2167
	lbs_deb_wext("ASSOC: WAP: sa_data %pM\n", awrq->sa_data);
2168

2169
	mutex_lock(&priv->lock);
2170 2171

	/* Get or create the current association request */
2172
	assoc_req = lbs_get_association_request(priv);
2173
	if (!assoc_req) {
2174
		lbs_cancel_association_work(priv);
2175 2176 2177 2178 2179
		ret = -ENOMEM;
	} else {
		/* Copy the BSSID to the association request */
		memcpy(&assoc_req->bssid, awrq->sa_data, ETH_ALEN);
		set_bit(ASSOC_FLAG_BSSID, &assoc_req->flags);
2180
		lbs_postpone_association_work(priv);
2181 2182
	}

2183
	mutex_unlock(&priv->lock);
2184 2185 2186 2187 2188 2189 2190

	return ret;
}

/*
 * iwconfig settable callbacks
 */
2191
static const iw_handler lbs_handler[] = {
2192
	(iw_handler) NULL,	/* SIOCSIWCOMMIT */
2193
	(iw_handler) lbs_get_name,	/* SIOCGIWNAME */
2194 2195
	(iw_handler) NULL,	/* SIOCSIWNWID */
	(iw_handler) NULL,	/* SIOCGIWNWID */
2196 2197 2198 2199
	(iw_handler) lbs_set_freq,	/* SIOCSIWFREQ */
	(iw_handler) lbs_get_freq,	/* SIOCGIWFREQ */
	(iw_handler) lbs_set_mode,	/* SIOCSIWMODE */
	(iw_handler) lbs_get_mode,	/* SIOCGIWMODE */
2200 2201 2202
	(iw_handler) NULL,	/* SIOCSIWSENS */
	(iw_handler) NULL,	/* SIOCGIWSENS */
	(iw_handler) NULL,	/* SIOCSIWRANGE */
2203
	(iw_handler) lbs_get_range,	/* SIOCGIWRANGE */
2204 2205 2206 2207 2208 2209 2210 2211
	(iw_handler) NULL,	/* SIOCSIWPRIV */
	(iw_handler) NULL,	/* SIOCGIWPRIV */
	(iw_handler) NULL,	/* SIOCSIWSTATS */
	(iw_handler) NULL,	/* SIOCGIWSTATS */
	iw_handler_set_spy,	/* SIOCSIWSPY */
	iw_handler_get_spy,	/* SIOCGIWSPY */
	iw_handler_set_thrspy,	/* SIOCSIWTHRSPY */
	iw_handler_get_thrspy,	/* SIOCGIWTHRSPY */
2212 2213
	(iw_handler) lbs_set_wap,	/* SIOCSIWAP */
	(iw_handler) lbs_get_wap,	/* SIOCGIWAP */
2214 2215
	(iw_handler) NULL,	/* SIOCSIWMLME */
	(iw_handler) NULL,	/* SIOCGIWAPLIST - deprecated */
2216 2217 2218 2219 2220 2221
	(iw_handler) lbs_set_scan,	/* SIOCSIWSCAN */
	(iw_handler) lbs_get_scan,	/* SIOCGIWSCAN */
	(iw_handler) lbs_set_essid,	/* SIOCSIWESSID */
	(iw_handler) lbs_get_essid,	/* SIOCGIWESSID */
	(iw_handler) lbs_set_nick,	/* SIOCSIWNICKN */
	(iw_handler) lbs_get_nick,	/* SIOCGIWNICKN */
2222 2223
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237
	(iw_handler) lbs_set_rate,	/* SIOCSIWRATE */
	(iw_handler) lbs_get_rate,	/* SIOCGIWRATE */
	(iw_handler) lbs_set_rts,	/* SIOCSIWRTS */
	(iw_handler) lbs_get_rts,	/* SIOCGIWRTS */
	(iw_handler) lbs_set_frag,	/* SIOCSIWFRAG */
	(iw_handler) lbs_get_frag,	/* SIOCGIWFRAG */
	(iw_handler) lbs_set_txpow,	/* SIOCSIWTXPOW */
	(iw_handler) lbs_get_txpow,	/* SIOCGIWTXPOW */
	(iw_handler) lbs_set_retry,	/* SIOCSIWRETRY */
	(iw_handler) lbs_get_retry,	/* SIOCGIWRETRY */
	(iw_handler) lbs_set_encode,	/* SIOCSIWENCODE */
	(iw_handler) lbs_get_encode,	/* SIOCGIWENCODE */
	(iw_handler) lbs_set_power,	/* SIOCSIWPOWER */
	(iw_handler) lbs_get_power,	/* SIOCGIWPOWER */
2238 2239
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2240 2241 2242 2243 2244 2245
	(iw_handler) lbs_set_genie,	/* SIOCSIWGENIE */
	(iw_handler) lbs_get_genie,	/* SIOCGIWGENIE */
	(iw_handler) lbs_set_auth,	/* SIOCSIWAUTH */
	(iw_handler) lbs_get_auth,	/* SIOCGIWAUTH */
	(iw_handler) lbs_set_encodeext,/* SIOCSIWENCODEEXT */
	(iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */
2246 2247 2248
	(iw_handler) NULL,		/* SIOCSIWPMKSA */
};

2249 2250
static const iw_handler mesh_wlan_handler[] = {
	(iw_handler) NULL,	/* SIOCSIWCOMMIT */
2251
	(iw_handler) lbs_get_name,	/* SIOCGIWNAME */
2252 2253
	(iw_handler) NULL,	/* SIOCSIWNWID */
	(iw_handler) NULL,	/* SIOCGIWNWID */
2254
	(iw_handler) lbs_mesh_set_freq,	/* SIOCSIWFREQ */
2255
	(iw_handler) lbs_get_freq,	/* SIOCGIWFREQ */
2256 2257 2258 2259 2260
	(iw_handler) NULL,		/* SIOCSIWMODE */
	(iw_handler) mesh_wlan_get_mode,	/* SIOCGIWMODE */
	(iw_handler) NULL,	/* SIOCSIWSENS */
	(iw_handler) NULL,	/* SIOCGIWSENS */
	(iw_handler) NULL,	/* SIOCSIWRANGE */
2261
	(iw_handler) lbs_get_range,	/* SIOCGIWRANGE */
2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273
	(iw_handler) NULL,	/* SIOCSIWPRIV */
	(iw_handler) NULL,	/* SIOCGIWPRIV */
	(iw_handler) NULL,	/* SIOCSIWSTATS */
	(iw_handler) NULL,	/* SIOCGIWSTATS */
	iw_handler_set_spy,	/* SIOCSIWSPY */
	iw_handler_get_spy,	/* SIOCGIWSPY */
	iw_handler_set_thrspy,	/* SIOCSIWTHRSPY */
	iw_handler_get_thrspy,	/* SIOCGIWTHRSPY */
	(iw_handler) NULL,	/* SIOCSIWAP */
	(iw_handler) NULL,	/* SIOCGIWAP */
	(iw_handler) NULL,	/* SIOCSIWMLME */
	(iw_handler) NULL,	/* SIOCGIWAPLIST - deprecated */
2274 2275
	(iw_handler) lbs_set_scan,	/* SIOCSIWSCAN */
	(iw_handler) lbs_get_scan,	/* SIOCGIWSCAN */
2276 2277
	(iw_handler) lbs_mesh_set_essid,/* SIOCSIWESSID */
	(iw_handler) lbs_mesh_get_essid,/* SIOCGIWESSID */
2278 2279 2280 2281
	(iw_handler) NULL,		/* SIOCSIWNICKN */
	(iw_handler) mesh_get_nick,	/* SIOCGIWNICKN */
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295
	(iw_handler) lbs_set_rate,	/* SIOCSIWRATE */
	(iw_handler) lbs_get_rate,	/* SIOCGIWRATE */
	(iw_handler) lbs_set_rts,	/* SIOCSIWRTS */
	(iw_handler) lbs_get_rts,	/* SIOCGIWRTS */
	(iw_handler) lbs_set_frag,	/* SIOCSIWFRAG */
	(iw_handler) lbs_get_frag,	/* SIOCGIWFRAG */
	(iw_handler) lbs_set_txpow,	/* SIOCSIWTXPOW */
	(iw_handler) lbs_get_txpow,	/* SIOCGIWTXPOW */
	(iw_handler) lbs_set_retry,	/* SIOCSIWRETRY */
	(iw_handler) lbs_get_retry,	/* SIOCGIWRETRY */
	(iw_handler) lbs_set_encode,	/* SIOCSIWENCODE */
	(iw_handler) lbs_get_encode,	/* SIOCGIWENCODE */
	(iw_handler) lbs_set_power,	/* SIOCSIWPOWER */
	(iw_handler) lbs_get_power,	/* SIOCGIWPOWER */
2296 2297
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2298 2299 2300 2301 2302 2303
	(iw_handler) lbs_set_genie,	/* SIOCSIWGENIE */
	(iw_handler) lbs_get_genie,	/* SIOCGIWGENIE */
	(iw_handler) lbs_set_auth,	/* SIOCSIWAUTH */
	(iw_handler) lbs_get_auth,	/* SIOCGIWAUTH */
	(iw_handler) lbs_set_encodeext,/* SIOCSIWENCODEEXT */
	(iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */
2304 2305
	(iw_handler) NULL,		/* SIOCSIWPMKSA */
};
2306 2307 2308 2309
struct iw_handler_def lbs_handler_def = {
	.num_standard	= ARRAY_SIZE(lbs_handler),
	.standard	= (iw_handler *) lbs_handler,
	.get_wireless_stats = lbs_get_wireless_stats,
2310
};
2311 2312

struct iw_handler_def mesh_handler_def = {
2313
	.num_standard	= ARRAY_SIZE(mesh_wlan_handler),
2314
	.standard	= (iw_handler *) mesh_wlan_handler,
2315
	.get_wireless_stats = lbs_get_wireless_stats,
2316
};