wext.c 57.1 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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str)
{
	union iwreq_data iwrq;
	u8 buf[50];

	lbs_deb_enter(LBS_DEB_WEXT);

	memset(&iwrq, 0, sizeof(union iwreq_data));
	memset(buf, 0, sizeof(buf));

	snprintf(buf, sizeof(buf) - 1, "%s", str);

	iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN;

	/* Send Event to upper layer */
	lbs_deb_wext("event indication string %s\n", (char *)buf);
	lbs_deb_wext("event indication length %d\n", iwrq.data.length);
	lbs_deb_wext("sending wireless event IWEVCUSTOM for %s\n", str);

	wireless_send_event(priv->dev, IWEVCUSTOM, &iwrq, buf);

	lbs_deb_leave(LBS_DEB_WEXT);
}

72 73 74
/**
 *  @brief Find the channel frequency power info with specific channel
 *
75
 *  @param priv 	A pointer to struct lbs_private structure
76 77 78 79
 *  @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.
 */
80
struct chan_freq_power *lbs_find_cfp_by_band_and_channel(
81
	struct lbs_private *priv,
82 83
	u8 band,
	u16 channel)
84 85 86 87 88
{
	struct chan_freq_power *cfp = NULL;
	struct region_channel *rc;
	int i, j;

89 90
	for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
		rc = &priv->region_channel[j];
91 92 93 94 95 96 97 98 99 100 101 102 103 104

		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)
105
		lbs_deb_wext("lbs_find_cfp_by_band_and_channel: can't find "
106
		       "cfp by band %d / channel %d\n", band, channel);
107 108 109 110 111 112 113

	return cfp;
}

/**
 *  @brief Find the channel frequency power info with specific frequency
 *
114
 *  @param priv 	A pointer to struct lbs_private structure
115 116 117 118
 *  @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.
 */
119
static struct chan_freq_power *find_cfp_by_band_and_freq(
120
	struct lbs_private *priv,
121 122
	u8 band,
	u32 freq)
123 124 125 126 127
{
	struct chan_freq_power *cfp = NULL;
	struct region_channel *rc;
	int i, j;

128 129
	for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
		rc = &priv->region_channel[j];
130 131 132 133 134 135 136 137 138 139 140 141 142 143

		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)
144 145
		lbs_deb_wext("find_cfp_by_band_and_freql: can't find cfp by "
		       "band %d / freq %d\n", band, freq);
146 147 148 149 150

	return cfp;
}

/**
151
 *  @brief Copy active data rates based on adapter mode and status
152
 *
153
 *  @param priv              A pointer to struct lbs_private structure
154 155
 *  @param rate		        The buf to return the active rates
 */
156
static void copy_active_data_rates(struct lbs_private *priv, u8 *rates)
157
{
158
	lbs_deb_enter(LBS_DEB_WEXT);
159

160 161
	if ((priv->connect_status != LBS_CONNECTED) &&
		(priv->mesh_connect_status != LBS_CONNECTED))
162
		memcpy(rates, lbs_bg_rates, MAX_RATES);
163
	else
164
		memcpy(rates, priv->curbssparams.rates, MAX_RATES);
165

166
	lbs_deb_leave(LBS_DEB_WEXT);
167 168
}

169
static int lbs_get_name(struct net_device *dev, struct iw_request_info *info,
170 171 172
			 char *cwrq, char *extra)
{

173
	lbs_deb_enter(LBS_DEB_WEXT);
174

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

178
	lbs_deb_leave(LBS_DEB_WEXT);
179 180 181
	return 0;
}

182
static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
183 184
			 struct iw_freq *fwrq, char *extra)
{
185
	struct lbs_private *priv = dev->ml_priv;
186 187
	struct chan_freq_power *cfp;

188
	lbs_deb_enter(LBS_DEB_WEXT);
189

190
	cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
191
					   priv->channel);
192 193

	if (!cfp) {
194
		if (priv->channel)
195
			lbs_deb_wext("invalid channel %d\n",
196
			       priv->channel);
197 198 199 200 201 202
		return -EINVAL;
	}

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

203 204
	lbs_deb_wext("freq %u\n", fwrq->m);
	lbs_deb_leave(LBS_DEB_WEXT);
205 206 207
	return 0;
}

208
static int lbs_get_wap(struct net_device *dev, struct iw_request_info *info,
209 210
			struct sockaddr *awrq, char *extra)
{
211
	struct lbs_private *priv = dev->ml_priv;
212

213
	lbs_deb_enter(LBS_DEB_WEXT);
214

215 216
	if (priv->connect_status == LBS_CONNECTED) {
		memcpy(awrq->sa_data, priv->curbssparams.bssid, ETH_ALEN);
217 218 219 220 221
	} else {
		memset(awrq->sa_data, 0, ETH_ALEN);
	}
	awrq->sa_family = ARPHRD_ETHER;

222
	lbs_deb_leave(LBS_DEB_WEXT);
223 224 225
	return 0;
}

226
static int lbs_set_nick(struct net_device *dev, struct iw_request_info *info,
227 228
			 struct iw_point *dwrq, char *extra)
{
229
	struct lbs_private *priv = dev->ml_priv;
230

231
	lbs_deb_enter(LBS_DEB_WEXT);
232 233 234 235 236 237 238 239 240

	/*
	 * Check the size of the string
	 */

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

241 242 243 244
	mutex_lock(&priv->lock);
	memset(priv->nodename, 0, sizeof(priv->nodename));
	memcpy(priv->nodename, extra, dwrq->length);
	mutex_unlock(&priv->lock);
245

246
	lbs_deb_leave(LBS_DEB_WEXT);
247 248 249
	return 0;
}

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

255
	lbs_deb_enter(LBS_DEB_WEXT);
256

257 258
	dwrq->length = strlen(priv->nodename);
	memcpy(extra, priv->nodename, dwrq->length);
259
	extra[dwrq->length] = '\0';
260

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

263
	lbs_deb_leave(LBS_DEB_WEXT);
264 265 266
	return 0;
}

267 268 269
static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info,
			 struct iw_point *dwrq, char *extra)
{
270
	struct lbs_private *priv = dev->ml_priv;
271 272 273 274 275

	lbs_deb_enter(LBS_DEB_WEXT);

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

276
	if (priv->mesh_connect_status == LBS_CONNECTED) {
277 278
		strncpy(extra, "Mesh", 12);
		extra[12] = '\0';
279
		dwrq->length = strlen(extra);
280 281 282 283
	}

	else {
		extra[0] = '\0';
284
		dwrq->length = 0;
285 286 287 288 289
	}

	lbs_deb_leave(LBS_DEB_WEXT);
	return 0;
}
290

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

298
	lbs_deb_enter(LBS_DEB_WEXT);
299

300 301
	if (vwrq->disabled)
		val = MRVDRV_RTS_MAX_VALUE;
302

303
	if (val > MRVDRV_RTS_MAX_VALUE) /* min rts value is 0 */
304 305 306
		return -EINVAL;

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

308
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
309 310 311
	return ret;
}

312
static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info,
313 314
			struct iw_param *vwrq, char *extra)
{
315
	struct lbs_private *priv = dev->ml_priv;
316 317
	int ret = 0;
	u16 val = 0;
318

319
	lbs_deb_enter(LBS_DEB_WEXT);
320

321
	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, &val);
322 323
	if (ret)
		goto out;
324

325
	vwrq->value = val;
326
	vwrq->disabled = val > MRVDRV_RTS_MAX_VALUE; /* min rts value is 0 */
327 328
	vwrq->fixed = 1;

329 330 331
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
332 333
}

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

341
	lbs_deb_enter(LBS_DEB_WEXT);
342

343 344 345 346 347
	if (vwrq->disabled)
		val = MRVDRV_FRAG_MAX_VALUE;

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

349
	ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, (u16) val);
350 351

	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
352 353 354
	return ret;
}

355
static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info,
356 357
			 struct iw_param *vwrq, char *extra)
{
358
	struct lbs_private *priv = dev->ml_priv;
359 360
	int ret = 0;
	u16 val = 0;
361

362
	lbs_deb_enter(LBS_DEB_WEXT);
363

364
	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, &val);
365 366
	if (ret)
		goto out;
367

368 369 370
	vwrq->value = val;
	vwrq->disabled = ((val < MRVDRV_FRAG_MIN_VALUE)
			  || (val > MRVDRV_FRAG_MAX_VALUE));
371 372
	vwrq->fixed = 1;

373 374
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
375 376 377
	return ret;
}

378
static int lbs_get_mode(struct net_device *dev,
379 380
			 struct iw_request_info *info, u32 * uwrq, char *extra)
{
381
	struct lbs_private *priv = dev->ml_priv;
382

383
	lbs_deb_enter(LBS_DEB_WEXT);
384

385
	*uwrq = priv->mode;
386

387
	lbs_deb_leave(LBS_DEB_WEXT);
388 389 390
	return 0;
}

391 392 393 394 395 396
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);

397
	*uwrq = IW_MODE_REPEAT;
398 399 400 401 402

	lbs_deb_leave(LBS_DEB_WEXT);
	return 0;
}

403
static int lbs_get_txpow(struct net_device *dev,
404 405 406
			  struct iw_request_info *info,
			  struct iw_param *vwrq, char *extra)
{
407
	struct lbs_private *priv = dev->ml_priv;
408
	s16 curlevel = 0;
409
	int ret = 0;
410

411
	lbs_deb_enter(LBS_DEB_WEXT);
412

413 414 415 416 417 418 419
	if (!priv->radio_on) {
		lbs_deb_wext("tx power off\n");
		vwrq->value = 0;
		vwrq->disabled = 1;
		goto out;
	}

420
	ret = lbs_get_tx_power(priv, &curlevel, NULL, NULL);
421 422
	if (ret)
		goto out;
423

424 425
	lbs_deb_wext("tx power level %d dbm\n", curlevel);
	priv->txpower_cur = curlevel;
426

427
	vwrq->value = curlevel;
428
	vwrq->fixed = 1;
429 430
	vwrq->disabled = 0;
	vwrq->flags = IW_TXPOW_DBM;
431

432 433 434
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
435 436
}

437
static int lbs_set_retry(struct net_device *dev, struct iw_request_info *info,
438 439
			  struct iw_param *vwrq, char *extra)
{
440
	struct lbs_private *priv = dev->ml_priv;
441 442
	int ret = 0;
	u16 slimit = 0, llimit = 0;
443

444
	lbs_deb_enter(LBS_DEB_WEXT);
445

446 447 448 449 450
        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 */
451 452
#define TX_RETRY_MIN 0
#define TX_RETRY_MAX 14
453 454
	if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX)
		return -EINVAL;
455

456 457 458 459 460 461 462
	/* 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 */
463

464 465 466 467 468 469
	if (llimit) {
		ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_LONG_RETRY_LIMIT,
				       llimit);
		if (ret)
			goto out;
	}
470

471 472 473 474 475
	if (slimit) {
		/* txretrycount follows the short retry limit */
		priv->txretrycount = slimit;
		ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_SHORT_RETRY_LIMIT,
				       slimit);
476 477
		if (ret)
			goto out;
478 479
	}

480 481 482
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
483 484
}

485
static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info,
486 487
			  struct iw_param *vwrq, char *extra)
{
488
	struct lbs_private *priv = dev->ml_priv;
489
	int ret = 0;
490
	u16 val = 0;
491

492 493
	lbs_deb_enter(LBS_DEB_WEXT);

494
	vwrq->disabled = 0;
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510

	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;
511
		/* Subtract 1 to convert try count to retry count */
512 513
		vwrq->value = val - 1;
		vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
514 515
	}

516 517 518
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
}

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
 */
560
static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
561 562 563
			  struct iw_point *dwrq, char *extra)
{
	int i, j;
564
	struct lbs_private *priv = dev->ml_priv;
565 566
	struct iw_range *range = (struct iw_range *)extra;
	struct chan_freq_power *cfp;
567
	u8 rates[MAX_RATES + 1];
568

569
	lbs_deb_enter(LBS_DEB_WEXT);
570 571 572 573 574 575 576 577

	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));
578
	copy_active_data_rates(priv, rates);
579 580 581
	range->num_bitrates = strnlen(rates, IW_MAX_BITRATES);
	for (i = 0; i < range->num_bitrates; i++)
		range->bitrate[i] = rates[i] * 500000;
582
	range->num_bitrates = i;
583
	lbs_deb_wext("IW_MAX_BITRATES %d, num_bitrates %d\n", IW_MAX_BITRATES,
584 585 586
	       range->num_bitrates);

	range->num_frequency = 0;
587 588 589

	range->scan_capa = IW_SCAN_CAPA_ESSID;

H
Holger Schurig 已提交
590 591 592
	for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
	     && (j < ARRAY_SIZE(priv->region_channel)); j++) {
		cfp = priv->region_channel[j].CFP;
593
		for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
H
Holger Schurig 已提交
594 595 596 597 598
		     && priv->region_channel[j].valid
		     && cfp
		     && (i < priv->region_channel[j].nrcfp); i++) {
			range->freq[range->num_frequency].i =
			    (long)cfp->channel;
599
			range->freq[range->num_frequency].m =
H
Holger Schurig 已提交
600
			    (long)cfp->freq * 100000;
601
			range->freq[range->num_frequency].e = 1;
H
Holger Schurig 已提交
602
			cfp++;
603 604 605 606
			range->num_frequency++;
		}
	}

607
	lbs_deb_wext("IW_MAX_FREQUENCIES %d, num_frequency %d\n",
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
	       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;

633 634 635 636
	/*
	 * Right now we support only "iwconfig ethX power on|off"
	 */
	range->pm_capa = IW_POWER_ON;
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669

	/*
	 * 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;

670
	/* Setup the supported power level ranges */
671
	memset(range->txpower, 0, sizeof(range->txpower));
672 673 674 675
	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;
676 677 678 679 680 681

	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;

682
	if (priv->fwcapinfo & FW_CAPINFO_WPA) {
683 684 685 686 687 688
		range->enc_capa =   IW_ENC_CAPA_WPA
		                  | IW_ENC_CAPA_WPA2
		                  | IW_ENC_CAPA_CIPHER_TKIP
		                  | IW_ENC_CAPA_CIPHER_CCMP;
	}

689
	lbs_deb_leave(LBS_DEB_WEXT);
690 691 692
	return 0;
}

693
static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
694 695
			  struct iw_param *vwrq, char *extra)
{
696
	struct lbs_private *priv = dev->ml_priv;
697
	int ret = 0;
698

699
	lbs_deb_enter(LBS_DEB_WEXT);
700

701
	if (!(priv->fwcapinfo & FW_CAPINFO_PS)) {
702 703 704 705 706 707
		if (vwrq->disabled)
			return 0;
		else
			return -EINVAL;
	}

708 709 710 711 712
	/* PS is currently supported only in Infrastructure mode
	 * Remove this check if it is to be supported in IBSS mode also
	 */

	if (vwrq->disabled) {
713 714
		priv->psmode = LBS802_11POWERMODECAM;
		if (priv->psstate != PS_STATE_FULL_POWER) {
715
			lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
716 717 718 719 720 721
		}

		return 0;
	}

	if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
722 723
		lbs_deb_wext(
		       "setting power timeout is not supported\n");
724 725
		return -EINVAL;
	} else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
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 760 761 762 763 764 765 766 767 768 769 770 771 772 773
		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;
		}
774 775
	}

776
	if (priv->psmode != LBS802_11POWERMODECAM) {
777 778 779
		return 0;
	}

780
	priv->psmode = LBS802_11POWERMODEMAX_PSP;
781

782
	if (priv->connect_status == LBS_CONNECTED) {
783
		lbs_ps_sleep(priv, CMD_OPTION_WAITFORRSP);
784 785
	}

786
	lbs_deb_leave(LBS_DEB_WEXT);
787

788 789 790
	return 0;
}

791
static int lbs_get_power(struct net_device *dev, struct iw_request_info *info,
792 793
			  struct iw_param *vwrq, char *extra)
{
794
	struct lbs_private *priv = dev->ml_priv;
795

796
	lbs_deb_enter(LBS_DEB_WEXT);
797 798

	vwrq->value = 0;
799 800 801
	vwrq->flags = 0;
	vwrq->disabled = priv->psmode == LBS802_11POWERMODECAM
		|| priv->connect_status == LBS_DISCONNECTED;
802

803
	lbs_deb_leave(LBS_DEB_WEXT);
804 805 806
	return 0;
}

807
static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
808 809 810 811 812 813 814 815 816
{
	enum {
		POOR = 30,
		FAIR = 60,
		GOOD = 80,
		VERY_GOOD = 90,
		EXCELLENT = 95,
		PERFECT = 100
	};
817
	struct lbs_private *priv = dev->ml_priv;
818 819 820
	u32 rssi_qual;
	u32 tx_qual;
	u32 quality = 0;
821
	int ret, stats_valid = 0;
822 823
	u8 rssi;
	u32 tx_retries;
824
	struct cmd_ds_802_11_get_log log;
825

826
	lbs_deb_enter(LBS_DEB_WEXT);
827

828
	priv->wstats.status = priv->mode;
829 830

	/* If we're not associated, all quality values are meaningless */
831 832
	if ((priv->connect_status != LBS_CONNECTED) &&
	    (priv->mesh_connect_status != LBS_CONNECTED))
833 834 835 836
		goto out;

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

840
	if (priv->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
841 842 843
		priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
	} else {
		priv->wstats.qual.noise =
844
		    CAL_NF(priv->NF[TYPE_BEACON][TYPE_NOAVG]);
845 846
	}

847 848
	lbs_deb_wext("signal level %#x\n", priv->wstats.qual.level);
	lbs_deb_wext("noise %#x\n", priv->wstats.qual.noise);
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865

	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 */
866
	priv->wstats.discard.retries = dev->stats.tx_errors;
867

868 869
	memset(&log, 0, sizeof(log));
	log.hdr.size = cpu_to_le16(sizeof(log));
870 871 872
	ret = lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log);
	if (ret)
		goto out;
873 874

	tx_retries = le32_to_cpu(log.retry);
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889

	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);

890
	priv->wstats.discard.code = le32_to_cpu(log.wepundecryptable);
891
	priv->wstats.discard.retries = tx_retries;
892
	priv->wstats.discard.misc = le32_to_cpu(log.ackfailure);
893 894

	/* Calculate quality */
895
	priv->wstats.qual.qual = min_t(u8, quality, 100);
896 897 898 899
	priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
	stats_valid = 1;

	/* update stats asynchronously for future calls */
900
	ret = lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
901
					0, 0, NULL);
902 903
	if (ret)
		lbs_pr_err("RSSI command failed\n");
904 905 906 907 908 909 910 911 912 913 914 915
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;
	}

916
	lbs_deb_leave(LBS_DEB_WEXT);
917 918 919 920 921
	return &priv->wstats;


}

922
static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info,
923 924
		  struct iw_freq *fwrq, char *extra)
{
925
	int ret = -EINVAL;
926
	struct lbs_private *priv = dev->ml_priv;
927
	struct chan_freq_power *cfp;
928
	struct assoc_request * assoc_req;
929

930
	lbs_deb_enter(LBS_DEB_WEXT);
931

932 933
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
934 935 936 937
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}
938

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

943
		cfp = find_cfp_by_band_and_freq(priv, 0, f);
944
		if (!cfp) {
945
			lbs_deb_wext("invalid freq %ld\n", f);
946
			goto out;
947 948 949
		}

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

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

958
	cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m);
959 960
	if (!cfp) {
		goto out;
961 962
	}

963 964 965
	assoc_req->channel = fwrq->m;
	ret = 0;

966
out:
967 968
	if (ret == 0) {
		set_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags);
969
		lbs_postpone_association_work(priv);
970
	} else {
971
		lbs_cancel_association_work(priv);
972
	}
973
	mutex_unlock(&priv->lock);
974 975 976

	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
977 978
}

979 980 981 982
static int lbs_mesh_set_freq(struct net_device *dev,
			     struct iw_request_info *info,
			     struct iw_freq *fwrq, char *extra)
{
983
	struct lbs_private *priv = dev->ml_priv;
984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
	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;
	}

1013
	if (fwrq->m != priv->channel) {
1014 1015
		lbs_deb_wext("mesh channel change forces eth disconnect\n");
		if (priv->mode == IW_MODE_INFRA)
1016 1017 1018
			lbs_cmd_80211_deauthenticate(priv,
						     priv->curbssparams.bssid,
						     WLAN_REASON_DEAUTH_LEAVING);
1019
		else if (priv->mode == IW_MODE_ADHOC)
1020
			lbs_adhoc_stop(priv);
1021
	}
1022
	lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, fwrq->m);
1023
	lbs_update_channel(priv);
1024 1025 1026 1027 1028 1029 1030
	ret = 0;

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

1031
static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
1032 1033
		  struct iw_param *vwrq, char *extra)
{
1034
	struct lbs_private *priv = dev->ml_priv;
1035
	u8 new_rate = 0;
1036 1037
	int ret = -EINVAL;
	u8 rates[MAX_RATES + 1];
1038

1039
	lbs_deb_enter(LBS_DEB_WEXT);
1040

1041
	lbs_deb_wext("vwrq->value %d\n", vwrq->value);
1042 1043 1044 1045
	lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);

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

1047
	/* Auto rate? */
1048 1049 1050
	priv->enablehwauto = !vwrq->fixed;

	if (vwrq->value == -1)
1051
		priv->cur_rate = 0;
1052
	else {
1053 1054
		if (vwrq->value % 100000)
			goto out;
1055

1056 1057 1058
		new_rate = vwrq->value / 500000;
		priv->cur_rate = new_rate;
		/* the rest is only needed for lbs_set_data_rate() */
1059
		memset(rates, 0, sizeof(rates));
1060
		copy_active_data_rates(priv, rates);
1061 1062 1063 1064
		if (!memchr(rates, new_rate, sizeof(rates))) {
			lbs_pr_alert("fixed data rate 0x%X out of range\n",
				new_rate);
			goto out;
1065
		}
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
		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;
1078 1079
	}

1080 1081 1082 1083 1084 1085
	/* 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);
1086

1087
out:
1088
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1089 1090 1091
	return ret;
}

1092
static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info,
1093 1094
		  struct iw_param *vwrq, char *extra)
{
1095
	struct lbs_private *priv = dev->ml_priv;
1096

1097
	lbs_deb_enter(LBS_DEB_WEXT);
1098

1099 1100
	if (priv->connect_status == LBS_CONNECTED) {
		vwrq->value = priv->cur_rate * 500000;
1101

1102
		if (priv->enablehwauto)
1103 1104 1105 1106
			vwrq->fixed = 0;
		else
			vwrq->fixed = 1;

1107
	} else {
1108 1109
		vwrq->fixed = 0;
		vwrq->value = 0;
1110 1111
	}

1112
	lbs_deb_leave(LBS_DEB_WEXT);
1113 1114 1115
	return 0;
}

1116
static int lbs_set_mode(struct net_device *dev,
1117 1118 1119
		  struct iw_request_info *info, u32 * uwrq, char *extra)
{
	int ret = 0;
1120
	struct lbs_private *priv = dev->ml_priv;
1121 1122
	struct assoc_request * assoc_req;

1123
	lbs_deb_enter(LBS_DEB_WEXT);
1124

1125 1126 1127
	if (   (*uwrq != IW_MODE_ADHOC)
	    && (*uwrq != IW_MODE_INFRA)
	    && (*uwrq != IW_MODE_AUTO)) {
1128
		lbs_deb_wext("Invalid mode: 0x%x\n", *uwrq);
1129 1130
		ret = -EINVAL;
		goto out;
1131 1132
	}

1133 1134
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1135 1136
	if (!assoc_req) {
		ret = -ENOMEM;
1137
		lbs_cancel_association_work(priv);
1138
	} else {
1139
		assoc_req->mode = *uwrq;
1140
		set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
1141
		lbs_postpone_association_work(priv);
1142
		lbs_deb_wext("Switching to mode: 0x%x\n", *uwrq);
1143
	}
1144
	mutex_unlock(&priv->lock);
1145

1146
out:
1147
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160
	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
 */
1161
static int lbs_get_encode(struct net_device *dev,
1162 1163 1164
			   struct iw_request_info *info,
			   struct iw_point *dwrq, u8 * extra)
{
1165
	struct lbs_private *priv = dev->ml_priv;
1166 1167
	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;

1168
	lbs_deb_enter(LBS_DEB_WEXT);
1169

1170
	lbs_deb_wext("flags 0x%x, index %d, length %d, wep_tx_keyidx %d\n",
1171
	       dwrq->flags, index, dwrq->length, priv->wep_tx_keyidx);
1172 1173 1174 1175

	dwrq->flags = 0;

	/* Authentication method */
1176
	switch (priv->secinfo.auth_mode) {
1177
	case IW_AUTH_ALG_OPEN_SYSTEM:
1178 1179 1180
		dwrq->flags = IW_ENCODE_OPEN;
		break;

1181 1182
	case IW_AUTH_ALG_SHARED_KEY:
	case IW_AUTH_ALG_LEAP:
1183 1184 1185 1186 1187 1188 1189 1190 1191
		dwrq->flags = IW_ENCODE_RESTRICTED;
		break;
	default:
		dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN;
		break;
	}

	memset(extra, 0, 16);

1192
	mutex_lock(&priv->lock);
1193 1194 1195

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

1198 1199 1200 1201
	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;
1202 1203 1204 1205

		dwrq->flags |= (index + 1);
		/* Return WEP enabled */
		dwrq->flags &= ~IW_ENCODE_DISABLED;
1206 1207
	} else if ((priv->secinfo.WPAenabled)
		   || (priv->secinfo.WPA2enabled)) {
1208 1209
		/* return WPA enabled */
		dwrq->flags &= ~IW_ENCODE_DISABLED;
1210
		dwrq->flags |= IW_ENCODE_NOKEY;
1211 1212 1213 1214
	} else {
		dwrq->flags |= IW_ENCODE_DISABLED;
	}

1215
	mutex_unlock(&priv->lock);
1216

1217
	lbs_deb_wext("key: %02x:%02x:%02x:%02x:%02x:%02x, keylen %d\n",
1218 1219 1220
	       extra[0], extra[1], extra[2],
	       extra[3], extra[4], extra[5], dwrq->length);

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

1223
	lbs_deb_leave(LBS_DEB_WEXT);
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236
	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
 */
1237
static int lbs_set_wep_key(struct assoc_request *assoc_req,
1238 1239 1240 1241 1242
			    const char *key_material,
			    u16 key_length,
			    u16 index,
			    int set_tx_key)
{
1243
	int ret = 0;
1244
	struct enc_key *pkey;
1245

1246
	lbs_deb_enter(LBS_DEB_WEXT);
1247 1248 1249

	/* Paranoid validation of key index */
	if (index > 3) {
1250 1251
		ret = -EINVAL;
		goto out;
1252 1253 1254 1255
	}

	/* validate max key length */
	if (key_length > KEY_LEN_WEP_104) {
1256 1257
		ret = -EINVAL;
		goto out;
1258 1259 1260 1261 1262
	}

	pkey = &assoc_req->wep_keys[index];

	if (key_length > 0) {
1263
		memset(pkey, 0, sizeof(struct enc_key));
1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274
		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) {
1275 1276 1277
			lbs_deb_wext("key not set, so cannot enable it\n");
			ret = -EINVAL;
			goto out;
1278 1279 1280 1281
		}
		assoc_req->wep_tx_keyidx = index;
	}

1282
	assoc_req->secinfo.wep_enabled = 1;
1283

1284 1285 1286
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310
}

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;

1311 1312
	lbs_deb_enter(LBS_DEB_WEXT);

1313
	/* Set Open System auth mode */
1314
	assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1315 1316

	/* Clear WEP keys and mark WEP as disabled */
1317
	assoc_req->secinfo.wep_enabled = 0;
1318 1319 1320 1321 1322
	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);
1323 1324 1325 1326 1327 1328 1329 1330

	lbs_deb_leave(LBS_DEB_WEXT);
}

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

1331
	memset(&assoc_req->wpa_mcast_key, 0, sizeof (struct enc_key));
1332 1333 1334
	assoc_req->wpa_mcast_key.flags = KEY_INFO_WPA_MCAST;
	set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);

1335
	memset(&assoc_req->wpa_unicast_key, 0, sizeof (struct enc_key));
1336 1337 1338 1339 1340 1341 1342 1343
	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);
1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354
}

/**
 *  @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
 */
1355
static int lbs_set_encode(struct net_device *dev,
1356 1357 1358 1359
		    struct iw_request_info *info,
		    struct iw_point *dwrq, char *extra)
{
	int ret = 0;
1360
	struct lbs_private *priv = dev->ml_priv;
1361 1362 1363
	struct assoc_request * assoc_req;
	u16 is_default = 0, index = 0, set_tx_key = 0;

1364
	lbs_deb_enter(LBS_DEB_WEXT);
1365

1366 1367
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1368 1369 1370 1371 1372 1373 1374
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}

	if (dwrq->flags & IW_ENCODE_DISABLED) {
		disable_wep (assoc_req);
1375
		disable_wpa (assoc_req);
1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389
		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.
	 */
1390
	if (!assoc_req->secinfo.wep_enabled || (dwrq->length == 0 && !is_default))
1391 1392
		set_tx_key = 1;

1393
	ret = lbs_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key);
1394 1395 1396 1397 1398 1399 1400 1401 1402
	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) {
1403
		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1404
	} else if (dwrq->flags & IW_ENCODE_OPEN) {
1405
		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1406 1407 1408 1409 1410
	}

out:
	if (ret == 0) {
		set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1411
		lbs_postpone_association_work(priv);
1412
	} else {
1413
		lbs_cancel_association_work(priv);
1414
	}
1415
	mutex_unlock(&priv->lock);
1416

1417
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429
	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
 */
1430
static int lbs_get_encodeext(struct net_device *dev,
1431 1432 1433 1434 1435
			      struct iw_request_info *info,
			      struct iw_point *dwrq,
			      char *extra)
{
	int ret = -EINVAL;
1436
	struct lbs_private *priv = dev->ml_priv;
1437 1438 1439
	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
	int index, max_key_len;

1440
	lbs_deb_enter(LBS_DEB_WEXT);
1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451

	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 {
1452
		index = priv->wep_tx_keyidx;
1453 1454
	}

R
Roel Kluin 已提交
1455
	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
1456
	    ext->alg != IW_ENCODE_ALG_WEP) {
1457
		if (index != 0 || priv->mode != IW_MODE_INFRA)
1458 1459 1460 1461 1462 1463
			goto out;
	}

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

1464 1465 1466
	if (   !priv->secinfo.wep_enabled
	    && !priv->secinfo.WPAenabled
	    && !priv->secinfo.WPA2enabled) {
1467 1468 1469 1470 1471 1472
		ext->alg = IW_ENCODE_ALG_NONE;
		ext->key_len = 0;
		dwrq->flags |= IW_ENCODE_DISABLED;
	} else {
		u8 *key = NULL;

1473 1474 1475
		if (   priv->secinfo.wep_enabled
		    && !priv->secinfo.WPAenabled
		    && !priv->secinfo.WPA2enabled) {
1476
			/* WEP */
1477
			ext->alg = IW_ENCODE_ALG_WEP;
1478 1479 1480 1481 1482
			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)) {
1483
			/* WPA */
1484
			struct enc_key * pkey = NULL;
1485

1486 1487 1488 1489 1490 1491
			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;
1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504

			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;
			}
1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522
		} 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:
1523
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535
	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
 */
1536
static int lbs_set_encodeext(struct net_device *dev,
1537 1538 1539 1540 1541
			      struct iw_request_info *info,
			      struct iw_point *dwrq,
			      char *extra)
{
	int ret = 0;
1542
	struct lbs_private *priv = dev->ml_priv;
1543 1544 1545 1546
	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
	int alg = ext->alg;
	struct assoc_request * assoc_req;

1547
	lbs_deb_enter(LBS_DEB_WEXT);
1548

1549 1550
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1551 1552 1553 1554 1555 1556 1557
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}

	if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) {
		disable_wep (assoc_req);
1558
		disable_wpa (assoc_req);
1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570
	} 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.
		 */
1571
		if (   !assoc_req->secinfo.wep_enabled
1572 1573 1574 1575 1576
		    || (dwrq->length == 0 && !is_default)
		    || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY))
			set_tx_key = 1;

		/* Copy key to driver */
1577
		ret = lbs_set_wep_key(assoc_req, ext->key, ext->key_len, index,
1578 1579 1580 1581 1582
					set_tx_key);
		if (ret)
			goto out;

		if (dwrq->flags & IW_ENCODE_RESTRICTED) {
1583
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1584
		} else if (dwrq->flags & IW_ENCODE_OPEN) {
1585
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1586 1587 1588 1589 1590 1591 1592 1593 1594
		}

		/* 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)) {
1595
		struct enc_key * pkey;
1596 1597 1598 1599 1600 1601

		/* 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))) {
1602
				lbs_deb_wext("invalid size %d for key of alg "
1603
				       "type %d\n",
1604 1605 1606 1607 1608 1609
				       ext->key_len,
				       alg);
				ret = -EINVAL;
				goto out;
		}

1610
		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
1611
			pkey = &assoc_req->wpa_mcast_key;
1612 1613
			set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
		} else {
1614
			pkey = &assoc_req->wpa_unicast_key;
1615 1616
			set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
		}
1617

1618
		memset(pkey, 0, sizeof (struct enc_key));
1619 1620
		memcpy(pkey->key, ext->key, ext->key_len);
		pkey->len = ext->key_len;
1621 1622
		if (pkey->len)
			pkey->flags |= KEY_INFO_WPA_ENABLED;
1623

1624
		/* Do this after zeroing key structure */
1625 1626 1627 1628 1629 1630
		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
			pkey->flags |= KEY_INFO_WPA_MCAST;
		} else {
			pkey->flags |= KEY_INFO_WPA_UNICAST;
		}

1631
		if (alg == IW_ENCODE_ALG_TKIP) {
1632
			pkey->type = KEY_TYPE_ID_TKIP;
1633
		} else if (alg == IW_ENCODE_ALG_CCMP) {
1634
			pkey->type = KEY_TYPE_ID_AES;
1635
		}
1636 1637 1638 1639 1640 1641 1642 1643 1644

		/* 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);
		}

1645 1646 1647
		/* Only disable wep if necessary: can't waste time here. */
		if (priv->mac_control & CMD_ACT_MAC_WEP_ENABLE)
			disable_wep(assoc_req);
1648 1649 1650
	}

out:
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664
	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);
1665
	} else {
1666
		lbs_cancel_association_work(priv);
1667
	}
1668
	mutex_unlock(&priv->lock);
1669

1670
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1671 1672 1673 1674
	return ret;
}


1675
static int lbs_set_genie(struct net_device *dev,
1676 1677 1678 1679
			  struct iw_request_info *info,
			  struct iw_point *dwrq,
			  char *extra)
{
1680
	struct lbs_private *priv = dev->ml_priv;
1681 1682 1683
	int ret = 0;
	struct assoc_request * assoc_req;

1684
	lbs_deb_enter(LBS_DEB_WEXT);
1685

1686 1687
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702
	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 {
1703
		memset(&assoc_req->wpa_ie[0], 0, sizeof(priv->wpa_ie));
1704 1705 1706 1707 1708 1709
		assoc_req->wpa_ie_len = 0;
	}

out:
	if (ret == 0) {
		set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags);
1710
		lbs_postpone_association_work(priv);
1711
	} else {
1712
		lbs_cancel_association_work(priv);
1713
	}
1714
	mutex_unlock(&priv->lock);
1715

1716
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1717 1718 1719
	return ret;
}

1720
static int lbs_get_genie(struct net_device *dev,
1721 1722 1723 1724
			  struct iw_request_info *info,
			  struct iw_point *dwrq,
			  char *extra)
{
1725
	int ret = 0;
1726
	struct lbs_private *priv = dev->ml_priv;
1727

1728
	lbs_deb_enter(LBS_DEB_WEXT);
1729

1730
	if (priv->wpa_ie_len == 0) {
1731
		dwrq->length = 0;
1732
		goto out;
1733 1734
	}

1735
	if (dwrq->length < priv->wpa_ie_len) {
1736 1737
		ret = -E2BIG;
		goto out;
1738 1739
	}

1740 1741
	dwrq->length = priv->wpa_ie_len;
	memcpy(extra, &priv->wpa_ie[0], priv->wpa_ie_len);
1742

1743 1744 1745
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
1746 1747 1748
}


1749
static int lbs_set_auth(struct net_device *dev,
1750 1751 1752 1753
			 struct iw_request_info *info,
			 struct iw_param *dwrq,
			 char *extra)
{
1754
	struct lbs_private *priv = dev->ml_priv;
1755 1756 1757 1758
	struct assoc_request * assoc_req;
	int ret = 0;
	int updated = 0;

1759
	lbs_deb_enter(LBS_DEB_WEXT);
1760

1761 1762
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1763 1764 1765 1766 1767 1768
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}

	switch (dwrq->flags & IW_AUTH_INDEX) {
1769 1770
	case IW_AUTH_PRIVACY_INVOKED:
	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
1771 1772 1773
	case IW_AUTH_TKIP_COUNTERMEASURES:
	case IW_AUTH_CIPHER_PAIRWISE:
	case IW_AUTH_CIPHER_GROUP:
1774
	case IW_AUTH_DROP_UNENCRYPTED:
1775 1776 1777 1778 1779
		/*
		 * libertas does not use these parameters
		 */
		break;

1780 1781 1782 1783 1784
	case IW_AUTH_KEY_MGMT:
		assoc_req->secinfo.key_mgmt = dwrq->value;
		updated = 1;
		break;

1785 1786 1787 1788
	case IW_AUTH_WPA_VERSION:
		if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) {
			assoc_req->secinfo.WPAenabled = 0;
			assoc_req->secinfo.WPA2enabled = 0;
1789
			disable_wpa (assoc_req);
1790 1791 1792
		}
		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) {
			assoc_req->secinfo.WPAenabled = 1;
1793
			assoc_req->secinfo.wep_enabled = 0;
1794
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1795 1796 1797
		}
		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) {
			assoc_req->secinfo.WPA2enabled = 1;
1798
			assoc_req->secinfo.wep_enabled = 0;
1799
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1800 1801 1802 1803 1804 1805
		}
		updated = 1;
		break;

	case IW_AUTH_80211_AUTH_ALG:
		if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) {
1806
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1807
		} else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) {
1808
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1809
		} else if (dwrq->value & IW_AUTH_ALG_LEAP) {
1810
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_LEAP;
1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822
		} 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;
1823
				assoc_req->secinfo.wep_enabled = 0;
1824
				assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1825 1826 1827 1828
			}
		} else {
			assoc_req->secinfo.WPAenabled = 0;
			assoc_req->secinfo.WPA2enabled = 0;
1829
			disable_wpa (assoc_req);
1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842
		}
		updated = 1;
		break;

	default:
		ret = -EOPNOTSUPP;
		break;
	}

out:
	if (ret == 0) {
		if (updated)
			set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1843
		lbs_postpone_association_work(priv);
1844
	} else if (ret != -EOPNOTSUPP) {
1845
		lbs_cancel_association_work(priv);
1846
	}
1847
	mutex_unlock(&priv->lock);
1848

1849
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1850 1851 1852
	return ret;
}

1853
static int lbs_get_auth(struct net_device *dev,
1854 1855 1856 1857
			 struct iw_request_info *info,
			 struct iw_param *dwrq,
			 char *extra)
{
1858
	int ret = 0;
1859
	struct lbs_private *priv = dev->ml_priv;
1860

1861
	lbs_deb_enter(LBS_DEB_WEXT);
1862 1863

	switch (dwrq->flags & IW_AUTH_INDEX) {
1864 1865 1866 1867
	case IW_AUTH_KEY_MGMT:
		dwrq->value = priv->secinfo.key_mgmt;
		break;

1868 1869
	case IW_AUTH_WPA_VERSION:
		dwrq->value = 0;
1870
		if (priv->secinfo.WPAenabled)
1871
			dwrq->value |= IW_AUTH_WPA_VERSION_WPA;
1872
		if (priv->secinfo.WPA2enabled)
1873 1874 1875 1876 1877 1878
			dwrq->value |= IW_AUTH_WPA_VERSION_WPA2;
		if (!dwrq->value)
			dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED;
		break;

	case IW_AUTH_80211_AUTH_ALG:
1879
		dwrq->value = priv->secinfo.auth_mode;
1880 1881 1882
		break;

	case IW_AUTH_WPA_ENABLED:
1883
		if (priv->secinfo.WPAenabled && priv->secinfo.WPA2enabled)
1884 1885 1886 1887
			dwrq->value = 1;
		break;

	default:
1888
		ret = -EOPNOTSUPP;
1889 1890
	}

1891 1892
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
1893 1894 1895
}


1896
static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
1897 1898 1899
		   struct iw_param *vwrq, char *extra)
{
	int ret = 0;
1900
	struct lbs_private *priv = dev->ml_priv;
1901
	s16 dbm = (s16) vwrq->value;
1902

1903
	lbs_deb_enter(LBS_DEB_WEXT);
1904 1905

	if (vwrq->disabled) {
1906
		lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 0);
1907
		goto out;
1908 1909
	}

1910
	if (vwrq->fixed == 0) {
1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925
		/* 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;
1926 1927 1928 1929 1930 1931 1932 1933
		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;
		}
1934

1935 1936
		/* Validate requested power level against firmware allowed
		 * levels */
1937 1938 1939 1940
		if (priv->txpower_min && (dbm < priv->txpower_min)) {
			ret = -EINVAL;
			goto out;
		}
1941

1942 1943 1944 1945
		if (priv->txpower_max && (dbm > priv->txpower_max)) {
			ret = -EINVAL;
			goto out;
		}
1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957
		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;
1958
	}
1959

1960 1961 1962 1963 1964 1965
	/* 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;
	}
1966

1967
	lbs_deb_wext("txpower set %d dBm\n", dbm);
1968

1969
	ret = lbs_set_tx_power(priv, dbm);
1970

1971
out:
1972
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1973 1974 1975
	return ret;
}

1976
static int lbs_get_essid(struct net_device *dev, struct iw_request_info *info,
1977 1978
		   struct iw_point *dwrq, char *extra)
{
1979
	struct lbs_private *priv = dev->ml_priv;
1980

1981 1982
	lbs_deb_enter(LBS_DEB_WEXT);

1983 1984 1985 1986 1987 1988 1989 1990
	/*
	 * Note : if dwrq->flags != 0, we should get the relevant SSID from
	 * the SSID list...
	 */

	/*
	 * Get the current SSID
	 */
1991 1992 1993 1994
	if (priv->connect_status == LBS_CONNECTED) {
		memcpy(extra, priv->curbssparams.ssid,
		       priv->curbssparams.ssid_len);
		extra[priv->curbssparams.ssid_len] = '\0';
1995 1996
	} else {
		memset(extra, 0, 32);
1997
		extra[priv->curbssparams.ssid_len] = '\0';
1998 1999 2000 2001 2002
	}
	/*
	 * If none, we may want to get the one that was set
	 */

2003
	dwrq->length = priv->curbssparams.ssid_len;
2004 2005 2006

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

2007
	lbs_deb_leave(LBS_DEB_WEXT);
2008 2009 2010
	return 0;
}

2011
static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
2012 2013
		   struct iw_point *dwrq, char *extra)
{
2014
	struct lbs_private *priv = dev->ml_priv;
2015
	int ret = 0;
2016
	u8 ssid[IEEE80211_MAX_SSID_LEN];
2017
	u8 ssid_len = 0;
2018
	struct assoc_request * assoc_req;
2019
	int in_ssid_len = dwrq->length;
2020
	DECLARE_SSID_BUF(ssid_buf);
2021

2022
	lbs_deb_enter(LBS_DEB_WEXT);
2023

2024 2025 2026 2027 2028
	if (!priv->radio_on) {
		ret = -EINVAL;
		goto out;
	}

2029
	/* Check the size of the string */
2030
	if (in_ssid_len > IEEE80211_MAX_SSID_LEN) {
2031 2032 2033 2034
		ret = -E2BIG;
		goto out;
	}

2035
	memset(&ssid, 0, sizeof(ssid));
2036

2037
	if (!dwrq->flags || !in_ssid_len) {
2038 2039 2040
		/* "any" SSID requested; leave SSID blank */
	} else {
		/* Specific SSID requested */
2041 2042
		memcpy(&ssid, extra, in_ssid_len);
		ssid_len = in_ssid_len;
2043 2044
	}

2045 2046 2047 2048
	if (!ssid_len) {
		lbs_deb_wext("requested any SSID\n");
	} else {
		lbs_deb_wext("requested SSID '%s'\n",
2049
		             print_ssid(ssid_buf, ssid, ssid_len));
2050
	}
2051 2052

out:
2053
	mutex_lock(&priv->lock);
2054 2055
	if (ret == 0) {
		/* Get or create the current association request */
2056
		assoc_req = lbs_get_association_request(priv);
2057 2058 2059 2060
		if (!assoc_req) {
			ret = -ENOMEM;
		} else {
			/* Copy the SSID to the association request */
2061
			memcpy(&assoc_req->ssid, &ssid, IEEE80211_MAX_SSID_LEN);
2062
			assoc_req->ssid_len = ssid_len;
2063
			set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
2064
			lbs_postpone_association_work(priv);
2065 2066 2067 2068 2069
		}
	}

	/* Cancel the association request if there was an error */
	if (ret != 0) {
2070
		lbs_cancel_association_work(priv);
2071 2072
	}

2073
	mutex_unlock(&priv->lock);
2074

2075
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
2076 2077 2078
	return ret;
}

2079 2080 2081 2082
static int lbs_mesh_get_essid(struct net_device *dev,
			      struct iw_request_info *info,
			      struct iw_point *dwrq, char *extra)
{
2083
	struct lbs_private *priv = dev->ml_priv;
2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100

	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)
{
2101
	struct lbs_private *priv = dev->ml_priv;
2102 2103 2104 2105
	int ret = 0;

	lbs_deb_enter(LBS_DEB_WEXT);

2106 2107 2108 2109 2110
	if (!priv->radio_on) {
		ret = -EINVAL;
		goto out;
	}

2111
	/* Check the size of the string */
2112
	if (dwrq->length > IEEE80211_MAX_SSID_LEN) {
2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125
		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;
	}

2126
	lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
2127
			priv->channel);
2128 2129 2130 2131 2132
 out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
}

2133 2134 2135 2136 2137 2138 2139 2140 2141
/**
 *  @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
 */
2142
static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
2143 2144
		 struct sockaddr *awrq, char *extra)
{
2145
	struct lbs_private *priv = dev->ml_priv;
2146 2147 2148
	struct assoc_request * assoc_req;
	int ret = 0;

2149
	lbs_deb_enter(LBS_DEB_WEXT);
2150

2151 2152 2153
	if (!priv->radio_on)
		return -EINVAL;

2154 2155 2156
	if (awrq->sa_family != ARPHRD_ETHER)
		return -EINVAL;

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

2159
	mutex_lock(&priv->lock);
2160 2161

	/* Get or create the current association request */
2162
	assoc_req = lbs_get_association_request(priv);
2163
	if (!assoc_req) {
2164
		lbs_cancel_association_work(priv);
2165 2166 2167 2168 2169
		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);
2170
		lbs_postpone_association_work(priv);
2171 2172
	}

2173
	mutex_unlock(&priv->lock);
2174 2175 2176 2177 2178 2179 2180

	return ret;
}

/*
 * iwconfig settable callbacks
 */
2181
static const iw_handler lbs_handler[] = {
2182
	(iw_handler) NULL,	/* SIOCSIWCOMMIT */
2183
	(iw_handler) lbs_get_name,	/* SIOCGIWNAME */
2184 2185
	(iw_handler) NULL,	/* SIOCSIWNWID */
	(iw_handler) NULL,	/* SIOCGIWNWID */
2186 2187 2188 2189
	(iw_handler) lbs_set_freq,	/* SIOCSIWFREQ */
	(iw_handler) lbs_get_freq,	/* SIOCGIWFREQ */
	(iw_handler) lbs_set_mode,	/* SIOCSIWMODE */
	(iw_handler) lbs_get_mode,	/* SIOCGIWMODE */
2190 2191 2192
	(iw_handler) NULL,	/* SIOCSIWSENS */
	(iw_handler) NULL,	/* SIOCGIWSENS */
	(iw_handler) NULL,	/* SIOCSIWRANGE */
2193
	(iw_handler) lbs_get_range,	/* SIOCGIWRANGE */
2194 2195 2196 2197 2198 2199 2200 2201
	(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 */
2202 2203
	(iw_handler) lbs_set_wap,	/* SIOCSIWAP */
	(iw_handler) lbs_get_wap,	/* SIOCGIWAP */
2204 2205
	(iw_handler) NULL,	/* SIOCSIWMLME */
	(iw_handler) NULL,	/* SIOCGIWAPLIST - deprecated */
2206 2207 2208 2209 2210 2211
	(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 */
2212 2213
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227
	(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 */
2228 2229
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2230 2231 2232 2233 2234 2235
	(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 */
2236 2237 2238
	(iw_handler) NULL,		/* SIOCSIWPMKSA */
};

2239 2240
static const iw_handler mesh_wlan_handler[] = {
	(iw_handler) NULL,	/* SIOCSIWCOMMIT */
2241
	(iw_handler) lbs_get_name,	/* SIOCGIWNAME */
2242 2243
	(iw_handler) NULL,	/* SIOCSIWNWID */
	(iw_handler) NULL,	/* SIOCGIWNWID */
2244
	(iw_handler) lbs_mesh_set_freq,	/* SIOCSIWFREQ */
2245
	(iw_handler) lbs_get_freq,	/* SIOCGIWFREQ */
2246 2247 2248 2249 2250
	(iw_handler) NULL,		/* SIOCSIWMODE */
	(iw_handler) mesh_wlan_get_mode,	/* SIOCGIWMODE */
	(iw_handler) NULL,	/* SIOCSIWSENS */
	(iw_handler) NULL,	/* SIOCGIWSENS */
	(iw_handler) NULL,	/* SIOCSIWRANGE */
2251
	(iw_handler) lbs_get_range,	/* SIOCGIWRANGE */
2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263
	(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 */
2264 2265
	(iw_handler) lbs_set_scan,	/* SIOCSIWSCAN */
	(iw_handler) lbs_get_scan,	/* SIOCGIWSCAN */
2266 2267
	(iw_handler) lbs_mesh_set_essid,/* SIOCSIWESSID */
	(iw_handler) lbs_mesh_get_essid,/* SIOCGIWESSID */
2268 2269 2270 2271
	(iw_handler) NULL,		/* SIOCSIWNICKN */
	(iw_handler) mesh_get_nick,	/* SIOCGIWNICKN */
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285
	(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 */
2286 2287
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2288 2289 2290 2291 2292 2293
	(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 */
2294 2295
	(iw_handler) NULL,		/* SIOCSIWPMKSA */
};
2296 2297 2298 2299
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,
2300
};
2301 2302

struct iw_handler_def mesh_handler_def = {
2303
	.num_standard	= ARRAY_SIZE(mesh_wlan_handler),
2304
	.standard	= (iw_handler *) mesh_wlan_handler,
2305
	.get_wireless_stats = lbs_get_wireless_stats,
2306
};