wext.c 57.8 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
void lbs_send_disconnect_notification(struct lbs_private *priv)
{
	union iwreq_data wrqu;

	memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN);
	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
	wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
}

57
static void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str)
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
{
	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);
}

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
/**
 *  @brief This function handles MIC failure event.
 *
 *  @param priv    A pointer to struct lbs_private structure
 *  @para  event   the event id
 *  @return 	   n/a
 */
void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event)
{
	char buf[50];

	lbs_deb_enter(LBS_DEB_CMD);
	memset(buf, 0, sizeof(buf));

	sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication ");

	if (event == MACREG_INT_CODE_MIC_ERR_UNICAST)
		strcat(buf, "unicast ");
	else
		strcat(buf, "multicast ");

	lbs_send_iwevcustom_event(priv, buf);
	lbs_deb_leave(LBS_DEB_CMD);
}

106 107 108
/**
 *  @brief Find the channel frequency power info with specific channel
 *
109
 *  @param priv 	A pointer to struct lbs_private structure
110 111 112 113
 *  @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.
 */
114
struct chan_freq_power *lbs_find_cfp_by_band_and_channel(
115
	struct lbs_private *priv,
116 117
	u8 band,
	u16 channel)
118 119 120 121 122
{
	struct chan_freq_power *cfp = NULL;
	struct region_channel *rc;
	int i, j;

123 124
	for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
		rc = &priv->region_channel[j];
125 126 127 128 129 130 131 132 133 134 135 136 137 138

		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)
139
		lbs_deb_wext("lbs_find_cfp_by_band_and_channel: can't find "
140
		       "cfp by band %d / channel %d\n", band, channel);
141 142 143 144 145 146 147

	return cfp;
}

/**
 *  @brief Find the channel frequency power info with specific frequency
 *
148
 *  @param priv 	A pointer to struct lbs_private structure
149 150 151 152
 *  @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.
 */
153
static struct chan_freq_power *find_cfp_by_band_and_freq(
154
	struct lbs_private *priv,
155 156
	u8 band,
	u32 freq)
157 158 159 160 161
{
	struct chan_freq_power *cfp = NULL;
	struct region_channel *rc;
	int i, j;

162 163
	for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
		rc = &priv->region_channel[j];
164 165 166 167 168 169 170 171 172 173 174 175 176 177

		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)
178 179
		lbs_deb_wext("find_cfp_by_band_and_freql: can't find cfp by "
		       "band %d / freq %d\n", band, freq);
180 181 182 183 184

	return cfp;
}

/**
185
 *  @brief Copy active data rates based on adapter mode and status
186
 *
187
 *  @param priv              A pointer to struct lbs_private structure
188 189
 *  @param rate		        The buf to return the active rates
 */
190
static void copy_active_data_rates(struct lbs_private *priv, u8 *rates)
191
{
192
	lbs_deb_enter(LBS_DEB_WEXT);
193

194
	if ((priv->connect_status != LBS_CONNECTED) &&
195
		!lbs_mesh_connected(priv))
196
		memcpy(rates, lbs_bg_rates, MAX_RATES);
197
	else
198
		memcpy(rates, priv->curbssparams.rates, MAX_RATES);
199

200
	lbs_deb_leave(LBS_DEB_WEXT);
201 202
}

203
static int lbs_get_name(struct net_device *dev, struct iw_request_info *info,
204 205 206
			 char *cwrq, char *extra)
{

207
	lbs_deb_enter(LBS_DEB_WEXT);
208

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

212
	lbs_deb_leave(LBS_DEB_WEXT);
213 214 215
	return 0;
}

216
static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
217 218
			 struct iw_freq *fwrq, char *extra)
{
219
	struct lbs_private *priv = dev->ml_priv;
220 221
	struct chan_freq_power *cfp;

222
	lbs_deb_enter(LBS_DEB_WEXT);
223

224
	cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
225
					   priv->channel);
226 227

	if (!cfp) {
228
		if (priv->channel)
229
			lbs_deb_wext("invalid channel %d\n",
230
			       priv->channel);
231 232 233 234 235 236
		return -EINVAL;
	}

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

237 238
	lbs_deb_wext("freq %u\n", fwrq->m);
	lbs_deb_leave(LBS_DEB_WEXT);
239 240 241
	return 0;
}

242
static int lbs_get_wap(struct net_device *dev, struct iw_request_info *info,
243 244
			struct sockaddr *awrq, char *extra)
{
245
	struct lbs_private *priv = dev->ml_priv;
246

247
	lbs_deb_enter(LBS_DEB_WEXT);
248

249 250
	if (priv->connect_status == LBS_CONNECTED) {
		memcpy(awrq->sa_data, priv->curbssparams.bssid, ETH_ALEN);
251 252 253 254 255
	} else {
		memset(awrq->sa_data, 0, ETH_ALEN);
	}
	awrq->sa_family = ARPHRD_ETHER;

256
	lbs_deb_leave(LBS_DEB_WEXT);
257 258 259
	return 0;
}

260
static int lbs_set_nick(struct net_device *dev, struct iw_request_info *info,
261 262
			 struct iw_point *dwrq, char *extra)
{
263
	struct lbs_private *priv = dev->ml_priv;
264

265
	lbs_deb_enter(LBS_DEB_WEXT);
266 267 268 269 270 271 272 273 274

	/*
	 * Check the size of the string
	 */

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

275 276 277 278
	mutex_lock(&priv->lock);
	memset(priv->nodename, 0, sizeof(priv->nodename));
	memcpy(priv->nodename, extra, dwrq->length);
	mutex_unlock(&priv->lock);
279

280
	lbs_deb_leave(LBS_DEB_WEXT);
281 282 283
	return 0;
}

284
static int lbs_get_nick(struct net_device *dev, struct iw_request_info *info,
285 286
			 struct iw_point *dwrq, char *extra)
{
287
	struct lbs_private *priv = dev->ml_priv;
288

289
	lbs_deb_enter(LBS_DEB_WEXT);
290

291 292
	dwrq->length = strlen(priv->nodename);
	memcpy(extra, priv->nodename, dwrq->length);
293
	extra[dwrq->length] = '\0';
294

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

297
	lbs_deb_leave(LBS_DEB_WEXT);
298 299 300
	return 0;
}

301 302 303
static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info,
			 struct iw_point *dwrq, char *extra)
{
304
	struct lbs_private *priv = dev->ml_priv;
305 306 307 308 309

	lbs_deb_enter(LBS_DEB_WEXT);

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

310
	if (lbs_mesh_connected(priv)) {
311 312
		strncpy(extra, "Mesh", 12);
		extra[12] = '\0';
313
		dwrq->length = strlen(extra);
314 315 316 317
	}

	else {
		extra[0] = '\0';
318
		dwrq->length = 0;
319 320 321 322 323
	}

	lbs_deb_leave(LBS_DEB_WEXT);
	return 0;
}
324

325
static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info,
326 327 328
			struct iw_param *vwrq, char *extra)
{
	int ret = 0;
329
	struct lbs_private *priv = dev->ml_priv;
330
	u32 val = vwrq->value;
331

332
	lbs_deb_enter(LBS_DEB_WEXT);
333

334 335
	if (vwrq->disabled)
		val = MRVDRV_RTS_MAX_VALUE;
336

337
	if (val > MRVDRV_RTS_MAX_VALUE) /* min rts value is 0 */
338 339 340
		return -EINVAL;

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

342
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
343 344 345
	return ret;
}

346
static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info,
347 348
			struct iw_param *vwrq, char *extra)
{
349
	struct lbs_private *priv = dev->ml_priv;
350 351
	int ret = 0;
	u16 val = 0;
352

353
	lbs_deb_enter(LBS_DEB_WEXT);
354

355
	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, &val);
356 357
	if (ret)
		goto out;
358

359
	vwrq->value = val;
360
	vwrq->disabled = val > MRVDRV_RTS_MAX_VALUE; /* min rts value is 0 */
361 362
	vwrq->fixed = 1;

363 364 365
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
366 367
}

368
static int lbs_set_frag(struct net_device *dev, struct iw_request_info *info,
369 370
			 struct iw_param *vwrq, char *extra)
{
371
	struct lbs_private *priv = dev->ml_priv;
372 373
	int ret = 0;
	u32 val = vwrq->value;
374

375
	lbs_deb_enter(LBS_DEB_WEXT);
376

377 378 379 380 381
	if (vwrq->disabled)
		val = MRVDRV_FRAG_MAX_VALUE;

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

383
	ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, (u16) val);
384 385

	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
386 387 388
	return ret;
}

389
static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info,
390 391
			 struct iw_param *vwrq, char *extra)
{
392
	struct lbs_private *priv = dev->ml_priv;
393 394
	int ret = 0;
	u16 val = 0;
395

396
	lbs_deb_enter(LBS_DEB_WEXT);
397

398
	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, &val);
399 400
	if (ret)
		goto out;
401

402 403 404
	vwrq->value = val;
	vwrq->disabled = ((val < MRVDRV_FRAG_MIN_VALUE)
			  || (val > MRVDRV_FRAG_MAX_VALUE));
405 406
	vwrq->fixed = 1;

407 408
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
409 410 411
	return ret;
}

412
static int lbs_get_mode(struct net_device *dev,
413 414
			 struct iw_request_info *info, u32 * uwrq, char *extra)
{
415
	struct lbs_private *priv = dev->ml_priv;
416

417
	lbs_deb_enter(LBS_DEB_WEXT);
418

419
	*uwrq = priv->mode;
420

421
	lbs_deb_leave(LBS_DEB_WEXT);
422 423 424
	return 0;
}

425 426 427 428 429 430
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);

431
	*uwrq = IW_MODE_REPEAT;
432 433 434 435 436

	lbs_deb_leave(LBS_DEB_WEXT);
	return 0;
}

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

445
	lbs_deb_enter(LBS_DEB_WEXT);
446

447 448 449 450 451 452 453
	if (!priv->radio_on) {
		lbs_deb_wext("tx power off\n");
		vwrq->value = 0;
		vwrq->disabled = 1;
		goto out;
	}

454
	ret = lbs_get_tx_power(priv, &curlevel, NULL, NULL);
455 456
	if (ret)
		goto out;
457

458 459
	lbs_deb_wext("tx power level %d dbm\n", curlevel);
	priv->txpower_cur = curlevel;
460

461
	vwrq->value = curlevel;
462
	vwrq->fixed = 1;
463 464
	vwrq->disabled = 0;
	vwrq->flags = IW_TXPOW_DBM;
465

466 467 468
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
469 470
}

471
static int lbs_set_retry(struct net_device *dev, struct iw_request_info *info,
472 473
			  struct iw_param *vwrq, char *extra)
{
474
	struct lbs_private *priv = dev->ml_priv;
475 476
	int ret = 0;
	u16 slimit = 0, llimit = 0;
477

478
	lbs_deb_enter(LBS_DEB_WEXT);
479

480 481 482 483 484
        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 */
485 486
#define TX_RETRY_MIN 0
#define TX_RETRY_MAX 14
487 488
	if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX)
		return -EINVAL;
489

490 491 492 493 494 495 496
	/* 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 */
497

498 499 500 501 502 503
	if (llimit) {
		ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_LONG_RETRY_LIMIT,
				       llimit);
		if (ret)
			goto out;
	}
504

505 506 507 508 509
	if (slimit) {
		/* txretrycount follows the short retry limit */
		priv->txretrycount = slimit;
		ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_SHORT_RETRY_LIMIT,
				       slimit);
510 511
		if (ret)
			goto out;
512 513
	}

514 515 516
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
517 518
}

519
static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info,
520 521
			  struct iw_param *vwrq, char *extra)
{
522
	struct lbs_private *priv = dev->ml_priv;
523
	int ret = 0;
524
	u16 val = 0;
525

526 527
	lbs_deb_enter(LBS_DEB_WEXT);

528
	vwrq->disabled = 0;
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544

	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;
545
		/* Subtract 1 to convert try count to retry count */
546 547
		vwrq->value = val - 1;
		vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
548 549
	}

550 551 552
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
}

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
 */
594
static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
595 596 597
			  struct iw_point *dwrq, char *extra)
{
	int i, j;
598
	struct lbs_private *priv = dev->ml_priv;
599 600
	struct iw_range *range = (struct iw_range *)extra;
	struct chan_freq_power *cfp;
601
	u8 rates[MAX_RATES + 1];
602

603
	lbs_deb_enter(LBS_DEB_WEXT);
604 605 606 607 608 609 610 611

	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));
612
	copy_active_data_rates(priv, rates);
613 614 615
	range->num_bitrates = strnlen(rates, IW_MAX_BITRATES);
	for (i = 0; i < range->num_bitrates; i++)
		range->bitrate[i] = rates[i] * 500000;
616
	range->num_bitrates = i;
617
	lbs_deb_wext("IW_MAX_BITRATES %d, num_bitrates %d\n", IW_MAX_BITRATES,
618 619 620
	       range->num_bitrates);

	range->num_frequency = 0;
621 622 623

	range->scan_capa = IW_SCAN_CAPA_ESSID;

624 625 626
	for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
	     && (j < ARRAY_SIZE(priv->region_channel)); j++) {
		cfp = priv->region_channel[j].CFP;
627
		for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
628 629 630 631 632
		     && priv->region_channel[j].valid
		     && cfp
		     && (i < priv->region_channel[j].nrcfp); i++) {
			range->freq[range->num_frequency].i =
			    (long)cfp->channel;
633
			range->freq[range->num_frequency].m =
634
			    (long)cfp->freq * 100000;
635
			range->freq[range->num_frequency].e = 1;
636
			cfp++;
637 638 639 640
			range->num_frequency++;
		}
	}

641
	lbs_deb_wext("IW_MAX_FREQUENCIES %d, num_frequency %d\n",
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
	       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;

667 668 669 670
	/*
	 * Right now we support only "iwconfig ethX power on|off"
	 */
	range->pm_capa = IW_POWER_ON;
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703

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

704
	/* Setup the supported power level ranges */
705
	memset(range->txpower, 0, sizeof(range->txpower));
706 707 708 709
	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;
710 711 712 713 714 715

	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;

716
	if (priv->fwcapinfo & FW_CAPINFO_WPA) {
717 718 719 720 721 722
		range->enc_capa =   IW_ENC_CAPA_WPA
		                  | IW_ENC_CAPA_WPA2
		                  | IW_ENC_CAPA_CIPHER_TKIP
		                  | IW_ENC_CAPA_CIPHER_CCMP;
	}

723
	lbs_deb_leave(LBS_DEB_WEXT);
724 725 726
	return 0;
}

727
static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
728 729
			  struct iw_param *vwrq, char *extra)
{
730
	struct lbs_private *priv = dev->ml_priv;
731
	int ret = 0;
732

733
	lbs_deb_enter(LBS_DEB_WEXT);
734

735
	if (!(priv->fwcapinfo & FW_CAPINFO_PS)) {
736 737 738 739 740 741
		if (vwrq->disabled)
			return 0;
		else
			return -EINVAL;
	}

742 743 744 745 746
	/* PS is currently supported only in Infrastructure mode
	 * Remove this check if it is to be supported in IBSS mode also
	 */

	if (vwrq->disabled) {
747 748
		priv->psmode = LBS802_11POWERMODECAM;
		if (priv->psstate != PS_STATE_FULL_POWER) {
749
			lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
750 751 752 753 754 755
		}

		return 0;
	}

	if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
756 757
		lbs_deb_wext(
		       "setting power timeout is not supported\n");
758 759
		return -EINVAL;
	} else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
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 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
		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;
		}
808 809
	}

810
	if (priv->psmode != LBS802_11POWERMODECAM) {
811 812 813
		return 0;
	}

814
	priv->psmode = LBS802_11POWERMODEMAX_PSP;
815

816
	if (priv->connect_status == LBS_CONNECTED) {
817
		lbs_ps_sleep(priv, CMD_OPTION_WAITFORRSP);
818 819
	}

820
	lbs_deb_leave(LBS_DEB_WEXT);
821

822 823 824
	return 0;
}

825
static int lbs_get_power(struct net_device *dev, struct iw_request_info *info,
826 827
			  struct iw_param *vwrq, char *extra)
{
828
	struct lbs_private *priv = dev->ml_priv;
829

830
	lbs_deb_enter(LBS_DEB_WEXT);
831 832

	vwrq->value = 0;
833 834 835
	vwrq->flags = 0;
	vwrq->disabled = priv->psmode == LBS802_11POWERMODECAM
		|| priv->connect_status == LBS_DISCONNECTED;
836

837
	lbs_deb_leave(LBS_DEB_WEXT);
838 839 840
	return 0;
}

841
static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
842 843 844 845 846 847 848 849 850
{
	enum {
		POOR = 30,
		FAIR = 60,
		GOOD = 80,
		VERY_GOOD = 90,
		EXCELLENT = 95,
		PERFECT = 100
	};
851
	struct lbs_private *priv = dev->ml_priv;
852 853 854
	u32 rssi_qual;
	u32 tx_qual;
	u32 quality = 0;
855
	int ret, stats_valid = 0;
856 857
	u8 rssi;
	u32 tx_retries;
858
	struct cmd_ds_802_11_get_log log;
859

860
	lbs_deb_enter(LBS_DEB_WEXT);
861

862
	priv->wstats.status = priv->mode;
863 864

	/* If we're not associated, all quality values are meaningless */
865
	if ((priv->connect_status != LBS_CONNECTED) &&
866
	    !lbs_mesh_connected(priv))
867 868 869 870
		goto out;

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

874
	if (priv->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
875 876 877
		priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
	} else {
		priv->wstats.qual.noise =
878
		    CAL_NF(priv->NF[TYPE_BEACON][TYPE_NOAVG]);
879 880
	}

881 882
	lbs_deb_wext("signal level %#x\n", priv->wstats.qual.level);
	lbs_deb_wext("noise %#x\n", priv->wstats.qual.noise);
883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899

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

902 903
	memset(&log, 0, sizeof(log));
	log.hdr.size = cpu_to_le16(sizeof(log));
904 905 906
	ret = lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log);
	if (ret)
		goto out;
907 908

	tx_retries = le32_to_cpu(log.retry);
909 910 911 912 913 914 915 916 917 918 919 920 921 922 923

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

924
	priv->wstats.discard.code = le32_to_cpu(log.wepundecryptable);
925
	priv->wstats.discard.retries = tx_retries;
926
	priv->wstats.discard.misc = le32_to_cpu(log.ackfailure);
927 928

	/* Calculate quality */
929
	priv->wstats.qual.qual = min_t(u8, quality, 100);
930 931 932 933
	priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
	stats_valid = 1;

	/* update stats asynchronously for future calls */
934
	ret = lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
935
					0, 0, NULL);
936 937
	if (ret)
		lbs_pr_err("RSSI command failed\n");
938 939 940 941 942 943 944 945 946 947 948 949
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;
	}

950
	lbs_deb_leave(LBS_DEB_WEXT);
951 952 953 954 955
	return &priv->wstats;


}

956
static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info,
957 958
		  struct iw_freq *fwrq, char *extra)
{
959
	int ret = -EINVAL;
960
	struct lbs_private *priv = dev->ml_priv;
961
	struct chan_freq_power *cfp;
962
	struct assoc_request * assoc_req;
963

964
	lbs_deb_enter(LBS_DEB_WEXT);
965

966 967
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
968 969 970 971
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}
972

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

977
		cfp = find_cfp_by_band_and_freq(priv, 0, f);
978
		if (!cfp) {
979
			lbs_deb_wext("invalid freq %ld\n", f);
980
			goto out;
981 982 983
		}

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

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

992
	cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m);
993 994
	if (!cfp) {
		goto out;
995 996
	}

997 998 999
	assoc_req->channel = fwrq->m;
	ret = 0;

1000
out:
1001 1002
	if (ret == 0) {
		set_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags);
1003
		lbs_postpone_association_work(priv);
1004
	} else {
1005
		lbs_cancel_association_work(priv);
1006
	}
1007
	mutex_unlock(&priv->lock);
1008 1009 1010

	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
1011 1012
}

1013 1014 1015 1016
static int lbs_mesh_set_freq(struct net_device *dev,
			     struct iw_request_info *info,
			     struct iw_freq *fwrq, char *extra)
{
1017
	struct lbs_private *priv = dev->ml_priv;
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
	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;
	}

1047
	if (fwrq->m != priv->channel) {
1048 1049
		lbs_deb_wext("mesh channel change forces eth disconnect\n");
		if (priv->mode == IW_MODE_INFRA)
1050 1051 1052
			lbs_cmd_80211_deauthenticate(priv,
						     priv->curbssparams.bssid,
						     WLAN_REASON_DEAUTH_LEAVING);
1053
		else if (priv->mode == IW_MODE_ADHOC)
1054
			lbs_adhoc_stop(priv);
1055
	}
1056
	lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, fwrq->m);
1057
	lbs_update_channel(priv);
1058 1059 1060 1061 1062 1063 1064
	ret = 0;

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

1065
static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
1066 1067
		  struct iw_param *vwrq, char *extra)
{
1068
	struct lbs_private *priv = dev->ml_priv;
1069
	u8 new_rate = 0;
1070 1071
	int ret = -EINVAL;
	u8 rates[MAX_RATES + 1];
1072

1073
	lbs_deb_enter(LBS_DEB_WEXT);
1074

1075
	lbs_deb_wext("vwrq->value %d\n", vwrq->value);
1076 1077 1078 1079
	lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);

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

1081
	/* Auto rate? */
1082 1083 1084
	priv->enablehwauto = !vwrq->fixed;

	if (vwrq->value == -1)
1085
		priv->cur_rate = 0;
1086
	else {
1087 1088
		if (vwrq->value % 100000)
			goto out;
1089

1090 1091 1092
		new_rate = vwrq->value / 500000;
		priv->cur_rate = new_rate;
		/* the rest is only needed for lbs_set_data_rate() */
1093
		memset(rates, 0, sizeof(rates));
1094
		copy_active_data_rates(priv, rates);
1095 1096 1097 1098
		if (!memchr(rates, new_rate, sizeof(rates))) {
			lbs_pr_alert("fixed data rate 0x%X out of range\n",
				new_rate);
			goto out;
1099
		}
1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
		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;
1112 1113
	}

1114 1115 1116 1117 1118 1119
	/* 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);
1120

1121
out:
1122
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1123 1124 1125
	return ret;
}

1126
static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info,
1127 1128
		  struct iw_param *vwrq, char *extra)
{
1129
	struct lbs_private *priv = dev->ml_priv;
1130

1131
	lbs_deb_enter(LBS_DEB_WEXT);
1132

1133 1134
	if (priv->connect_status == LBS_CONNECTED) {
		vwrq->value = priv->cur_rate * 500000;
1135

1136
		if (priv->enablehwauto)
1137 1138 1139 1140
			vwrq->fixed = 0;
		else
			vwrq->fixed = 1;

1141
	} else {
1142 1143
		vwrq->fixed = 0;
		vwrq->value = 0;
1144 1145
	}

1146
	lbs_deb_leave(LBS_DEB_WEXT);
1147 1148 1149
	return 0;
}

1150
static int lbs_set_mode(struct net_device *dev,
1151 1152 1153
		  struct iw_request_info *info, u32 * uwrq, char *extra)
{
	int ret = 0;
1154
	struct lbs_private *priv = dev->ml_priv;
1155 1156
	struct assoc_request * assoc_req;

1157
	lbs_deb_enter(LBS_DEB_WEXT);
1158

1159 1160 1161
	if (   (*uwrq != IW_MODE_ADHOC)
	    && (*uwrq != IW_MODE_INFRA)
	    && (*uwrq != IW_MODE_AUTO)) {
1162
		lbs_deb_wext("Invalid mode: 0x%x\n", *uwrq);
1163 1164
		ret = -EINVAL;
		goto out;
1165 1166
	}

1167 1168
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1169 1170
	if (!assoc_req) {
		ret = -ENOMEM;
1171
		lbs_cancel_association_work(priv);
1172
	} else {
1173
		assoc_req->mode = *uwrq;
1174
		set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
1175
		lbs_postpone_association_work(priv);
1176
		lbs_deb_wext("Switching to mode: 0x%x\n", *uwrq);
1177
	}
1178
	mutex_unlock(&priv->lock);
1179

1180
out:
1181
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
	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
 */
1195
static int lbs_get_encode(struct net_device *dev,
1196 1197 1198
			   struct iw_request_info *info,
			   struct iw_point *dwrq, u8 * extra)
{
1199
	struct lbs_private *priv = dev->ml_priv;
1200 1201
	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;

1202
	lbs_deb_enter(LBS_DEB_WEXT);
1203

1204
	lbs_deb_wext("flags 0x%x, index %d, length %d, wep_tx_keyidx %d\n",
1205
	       dwrq->flags, index, dwrq->length, priv->wep_tx_keyidx);
1206 1207 1208 1209

	dwrq->flags = 0;

	/* Authentication method */
1210
	switch (priv->secinfo.auth_mode) {
1211
	case IW_AUTH_ALG_OPEN_SYSTEM:
1212 1213 1214
		dwrq->flags = IW_ENCODE_OPEN;
		break;

1215 1216
	case IW_AUTH_ALG_SHARED_KEY:
	case IW_AUTH_ALG_LEAP:
1217 1218 1219 1220 1221 1222 1223 1224 1225
		dwrq->flags = IW_ENCODE_RESTRICTED;
		break;
	default:
		dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN;
		break;
	}

	memset(extra, 0, 16);

1226
	mutex_lock(&priv->lock);
1227 1228 1229

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

1232 1233 1234 1235
	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;
1236 1237 1238 1239

		dwrq->flags |= (index + 1);
		/* Return WEP enabled */
		dwrq->flags &= ~IW_ENCODE_DISABLED;
1240 1241
	} else if ((priv->secinfo.WPAenabled)
		   || (priv->secinfo.WPA2enabled)) {
1242 1243
		/* return WPA enabled */
		dwrq->flags &= ~IW_ENCODE_DISABLED;
1244
		dwrq->flags |= IW_ENCODE_NOKEY;
1245 1246 1247 1248
	} else {
		dwrq->flags |= IW_ENCODE_DISABLED;
	}

1249
	mutex_unlock(&priv->lock);
1250

1251
	lbs_deb_wext("key: %02x:%02x:%02x:%02x:%02x:%02x, keylen %d\n",
1252 1253 1254
	       extra[0], extra[1], extra[2],
	       extra[3], extra[4], extra[5], dwrq->length);

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

1257
	lbs_deb_leave(LBS_DEB_WEXT);
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270
	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
 */
1271
static int lbs_set_wep_key(struct assoc_request *assoc_req,
1272 1273 1274 1275 1276
			    const char *key_material,
			    u16 key_length,
			    u16 index,
			    int set_tx_key)
{
1277
	int ret = 0;
1278
	struct enc_key *pkey;
1279

1280
	lbs_deb_enter(LBS_DEB_WEXT);
1281 1282 1283

	/* Paranoid validation of key index */
	if (index > 3) {
1284 1285
		ret = -EINVAL;
		goto out;
1286 1287 1288 1289
	}

	/* validate max key length */
	if (key_length > KEY_LEN_WEP_104) {
1290 1291
		ret = -EINVAL;
		goto out;
1292 1293 1294 1295 1296
	}

	pkey = &assoc_req->wep_keys[index];

	if (key_length > 0) {
1297
		memset(pkey, 0, sizeof(struct enc_key));
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308
		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) {
1309 1310 1311
			lbs_deb_wext("key not set, so cannot enable it\n");
			ret = -EINVAL;
			goto out;
1312 1313 1314 1315
		}
		assoc_req->wep_tx_keyidx = index;
	}

1316
	assoc_req->secinfo.wep_enabled = 1;
1317

1318 1319 1320
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344
}

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;

1345 1346
	lbs_deb_enter(LBS_DEB_WEXT);

1347
	/* Set Open System auth mode */
1348
	assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1349 1350

	/* Clear WEP keys and mark WEP as disabled */
1351
	assoc_req->secinfo.wep_enabled = 0;
1352 1353 1354 1355 1356
	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);
1357 1358 1359 1360 1361 1362 1363 1364

	lbs_deb_leave(LBS_DEB_WEXT);
}

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

1365
	memset(&assoc_req->wpa_mcast_key, 0, sizeof (struct enc_key));
1366 1367 1368
	assoc_req->wpa_mcast_key.flags = KEY_INFO_WPA_MCAST;
	set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);

1369
	memset(&assoc_req->wpa_unicast_key, 0, sizeof (struct enc_key));
1370 1371 1372 1373 1374 1375 1376 1377
	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);
1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388
}

/**
 *  @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
 */
1389
static int lbs_set_encode(struct net_device *dev,
1390 1391 1392 1393
		    struct iw_request_info *info,
		    struct iw_point *dwrq, char *extra)
{
	int ret = 0;
1394
	struct lbs_private *priv = dev->ml_priv;
1395 1396 1397
	struct assoc_request * assoc_req;
	u16 is_default = 0, index = 0, set_tx_key = 0;

1398
	lbs_deb_enter(LBS_DEB_WEXT);
1399

1400 1401
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1402 1403 1404 1405 1406 1407 1408
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}

	if (dwrq->flags & IW_ENCODE_DISABLED) {
		disable_wep (assoc_req);
1409
		disable_wpa (assoc_req);
1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423
		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.
	 */
1424
	if (!assoc_req->secinfo.wep_enabled || (dwrq->length == 0 && !is_default))
1425 1426
		set_tx_key = 1;

1427
	ret = lbs_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key);
1428 1429 1430 1431 1432 1433 1434 1435 1436
	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) {
1437
		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1438
	} else if (dwrq->flags & IW_ENCODE_OPEN) {
1439
		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1440 1441 1442 1443 1444
	}

out:
	if (ret == 0) {
		set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1445
		lbs_postpone_association_work(priv);
1446
	} else {
1447
		lbs_cancel_association_work(priv);
1448
	}
1449
	mutex_unlock(&priv->lock);
1450

1451
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463
	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
 */
1464
static int lbs_get_encodeext(struct net_device *dev,
1465 1466 1467 1468 1469
			      struct iw_request_info *info,
			      struct iw_point *dwrq,
			      char *extra)
{
	int ret = -EINVAL;
1470
	struct lbs_private *priv = dev->ml_priv;
1471 1472 1473
	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
	int index, max_key_len;

1474
	lbs_deb_enter(LBS_DEB_WEXT);
1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485

	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 {
1486
		index = priv->wep_tx_keyidx;
1487 1488
	}

1489
	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
1490
	    ext->alg != IW_ENCODE_ALG_WEP) {
1491
		if (index != 0 || priv->mode != IW_MODE_INFRA)
1492 1493 1494 1495 1496 1497
			goto out;
	}

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

1498 1499 1500
	if (   !priv->secinfo.wep_enabled
	    && !priv->secinfo.WPAenabled
	    && !priv->secinfo.WPA2enabled) {
1501 1502 1503 1504 1505 1506
		ext->alg = IW_ENCODE_ALG_NONE;
		ext->key_len = 0;
		dwrq->flags |= IW_ENCODE_DISABLED;
	} else {
		u8 *key = NULL;

1507 1508 1509
		if (   priv->secinfo.wep_enabled
		    && !priv->secinfo.WPAenabled
		    && !priv->secinfo.WPA2enabled) {
1510
			/* WEP */
1511
			ext->alg = IW_ENCODE_ALG_WEP;
1512 1513 1514 1515 1516
			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)) {
1517
			/* WPA */
1518
			struct enc_key * pkey = NULL;
1519

1520 1521 1522 1523 1524 1525
			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;
1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538

			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;
			}
1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556
		} 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:
1557
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569
	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
 */
1570
static int lbs_set_encodeext(struct net_device *dev,
1571 1572 1573 1574 1575
			      struct iw_request_info *info,
			      struct iw_point *dwrq,
			      char *extra)
{
	int ret = 0;
1576
	struct lbs_private *priv = dev->ml_priv;
1577 1578 1579 1580
	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
	int alg = ext->alg;
	struct assoc_request * assoc_req;

1581
	lbs_deb_enter(LBS_DEB_WEXT);
1582

1583 1584
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1585 1586 1587 1588 1589 1590 1591
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}

	if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) {
		disable_wep (assoc_req);
1592
		disable_wpa (assoc_req);
1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604
	} 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.
		 */
1605
		if (   !assoc_req->secinfo.wep_enabled
1606 1607 1608 1609 1610
		    || (dwrq->length == 0 && !is_default)
		    || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY))
			set_tx_key = 1;

		/* Copy key to driver */
1611
		ret = lbs_set_wep_key(assoc_req, ext->key, ext->key_len, index,
1612 1613 1614 1615 1616
					set_tx_key);
		if (ret)
			goto out;

		if (dwrq->flags & IW_ENCODE_RESTRICTED) {
1617
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1618
		} else if (dwrq->flags & IW_ENCODE_OPEN) {
1619
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1620 1621 1622 1623 1624 1625 1626 1627 1628
		}

		/* 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)) {
1629
		struct enc_key * pkey;
1630 1631 1632 1633 1634 1635

		/* 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))) {
1636
				lbs_deb_wext("invalid size %d for key of alg "
1637
				       "type %d\n",
1638 1639 1640 1641 1642 1643
				       ext->key_len,
				       alg);
				ret = -EINVAL;
				goto out;
		}

1644
		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
1645
			pkey = &assoc_req->wpa_mcast_key;
1646 1647
			set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
		} else {
1648
			pkey = &assoc_req->wpa_unicast_key;
1649 1650
			set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
		}
1651

1652
		memset(pkey, 0, sizeof (struct enc_key));
1653 1654
		memcpy(pkey->key, ext->key, ext->key_len);
		pkey->len = ext->key_len;
1655 1656
		if (pkey->len)
			pkey->flags |= KEY_INFO_WPA_ENABLED;
1657

1658
		/* Do this after zeroing key structure */
1659 1660 1661 1662 1663 1664
		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
			pkey->flags |= KEY_INFO_WPA_MCAST;
		} else {
			pkey->flags |= KEY_INFO_WPA_UNICAST;
		}

1665
		if (alg == IW_ENCODE_ALG_TKIP) {
1666
			pkey->type = KEY_TYPE_ID_TKIP;
1667
		} else if (alg == IW_ENCODE_ALG_CCMP) {
1668
			pkey->type = KEY_TYPE_ID_AES;
1669
		}
1670 1671 1672 1673 1674 1675 1676 1677 1678

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

1679 1680 1681
		/* Only disable wep if necessary: can't waste time here. */
		if (priv->mac_control & CMD_ACT_MAC_WEP_ENABLE)
			disable_wep(assoc_req);
1682 1683 1684
	}

out:
1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698
	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);
1699
	} else {
1700
		lbs_cancel_association_work(priv);
1701
	}
1702
	mutex_unlock(&priv->lock);
1703

1704
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1705 1706 1707 1708
	return ret;
}


1709
static int lbs_set_genie(struct net_device *dev,
1710 1711 1712 1713
			  struct iw_request_info *info,
			  struct iw_point *dwrq,
			  char *extra)
{
1714
	struct lbs_private *priv = dev->ml_priv;
1715 1716 1717
	int ret = 0;
	struct assoc_request * assoc_req;

1718
	lbs_deb_enter(LBS_DEB_WEXT);
1719

1720 1721
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736
	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 {
1737
		memset(&assoc_req->wpa_ie[0], 0, sizeof(priv->wpa_ie));
1738 1739 1740 1741 1742 1743
		assoc_req->wpa_ie_len = 0;
	}

out:
	if (ret == 0) {
		set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags);
1744
		lbs_postpone_association_work(priv);
1745
	} else {
1746
		lbs_cancel_association_work(priv);
1747
	}
1748
	mutex_unlock(&priv->lock);
1749

1750
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1751 1752 1753
	return ret;
}

1754
static int lbs_get_genie(struct net_device *dev,
1755 1756 1757 1758
			  struct iw_request_info *info,
			  struct iw_point *dwrq,
			  char *extra)
{
1759
	int ret = 0;
1760
	struct lbs_private *priv = dev->ml_priv;
1761

1762
	lbs_deb_enter(LBS_DEB_WEXT);
1763

1764
	if (priv->wpa_ie_len == 0) {
1765
		dwrq->length = 0;
1766
		goto out;
1767 1768
	}

1769
	if (dwrq->length < priv->wpa_ie_len) {
1770 1771
		ret = -E2BIG;
		goto out;
1772 1773
	}

1774 1775
	dwrq->length = priv->wpa_ie_len;
	memcpy(extra, &priv->wpa_ie[0], priv->wpa_ie_len);
1776

1777 1778 1779
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
1780 1781 1782
}


1783
static int lbs_set_auth(struct net_device *dev,
1784 1785 1786 1787
			 struct iw_request_info *info,
			 struct iw_param *dwrq,
			 char *extra)
{
1788
	struct lbs_private *priv = dev->ml_priv;
1789 1790 1791 1792
	struct assoc_request * assoc_req;
	int ret = 0;
	int updated = 0;

1793
	lbs_deb_enter(LBS_DEB_WEXT);
1794

1795 1796
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1797 1798 1799 1800 1801 1802
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}

	switch (dwrq->flags & IW_AUTH_INDEX) {
1803 1804
	case IW_AUTH_PRIVACY_INVOKED:
	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
1805 1806 1807
	case IW_AUTH_TKIP_COUNTERMEASURES:
	case IW_AUTH_CIPHER_PAIRWISE:
	case IW_AUTH_CIPHER_GROUP:
1808
	case IW_AUTH_DROP_UNENCRYPTED:
1809 1810 1811 1812 1813
		/*
		 * libertas does not use these parameters
		 */
		break;

1814 1815 1816 1817 1818
	case IW_AUTH_KEY_MGMT:
		assoc_req->secinfo.key_mgmt = dwrq->value;
		updated = 1;
		break;

1819 1820 1821 1822
	case IW_AUTH_WPA_VERSION:
		if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) {
			assoc_req->secinfo.WPAenabled = 0;
			assoc_req->secinfo.WPA2enabled = 0;
1823
			disable_wpa (assoc_req);
1824 1825 1826
		}
		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) {
			assoc_req->secinfo.WPAenabled = 1;
1827
			assoc_req->secinfo.wep_enabled = 0;
1828
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1829 1830 1831
		}
		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) {
			assoc_req->secinfo.WPA2enabled = 1;
1832
			assoc_req->secinfo.wep_enabled = 0;
1833
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1834 1835 1836 1837 1838 1839
		}
		updated = 1;
		break;

	case IW_AUTH_80211_AUTH_ALG:
		if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) {
1840
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1841
		} else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) {
1842
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1843
		} else if (dwrq->value & IW_AUTH_ALG_LEAP) {
1844
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_LEAP;
1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856
		} 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;
1857
				assoc_req->secinfo.wep_enabled = 0;
1858
				assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1859 1860 1861 1862
			}
		} else {
			assoc_req->secinfo.WPAenabled = 0;
			assoc_req->secinfo.WPA2enabled = 0;
1863
			disable_wpa (assoc_req);
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876
		}
		updated = 1;
		break;

	default:
		ret = -EOPNOTSUPP;
		break;
	}

out:
	if (ret == 0) {
		if (updated)
			set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1877
		lbs_postpone_association_work(priv);
1878
	} else if (ret != -EOPNOTSUPP) {
1879
		lbs_cancel_association_work(priv);
1880
	}
1881
	mutex_unlock(&priv->lock);
1882

1883
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1884 1885 1886
	return ret;
}

1887
static int lbs_get_auth(struct net_device *dev,
1888 1889 1890 1891
			 struct iw_request_info *info,
			 struct iw_param *dwrq,
			 char *extra)
{
1892
	int ret = 0;
1893
	struct lbs_private *priv = dev->ml_priv;
1894

1895
	lbs_deb_enter(LBS_DEB_WEXT);
1896 1897

	switch (dwrq->flags & IW_AUTH_INDEX) {
1898 1899 1900 1901
	case IW_AUTH_KEY_MGMT:
		dwrq->value = priv->secinfo.key_mgmt;
		break;

1902 1903
	case IW_AUTH_WPA_VERSION:
		dwrq->value = 0;
1904
		if (priv->secinfo.WPAenabled)
1905
			dwrq->value |= IW_AUTH_WPA_VERSION_WPA;
1906
		if (priv->secinfo.WPA2enabled)
1907 1908 1909 1910 1911 1912
			dwrq->value |= IW_AUTH_WPA_VERSION_WPA2;
		if (!dwrq->value)
			dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED;
		break;

	case IW_AUTH_80211_AUTH_ALG:
1913
		dwrq->value = priv->secinfo.auth_mode;
1914 1915 1916
		break;

	case IW_AUTH_WPA_ENABLED:
1917
		if (priv->secinfo.WPAenabled && priv->secinfo.WPA2enabled)
1918 1919 1920 1921
			dwrq->value = 1;
		break;

	default:
1922
		ret = -EOPNOTSUPP;
1923 1924
	}

1925 1926
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
1927 1928 1929
}


1930
static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
1931 1932 1933
		   struct iw_param *vwrq, char *extra)
{
	int ret = 0;
1934
	struct lbs_private *priv = dev->ml_priv;
1935
	s16 dbm = (s16) vwrq->value;
1936

1937
	lbs_deb_enter(LBS_DEB_WEXT);
1938 1939

	if (vwrq->disabled) {
1940
		lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 0);
1941
		goto out;
1942 1943
	}

1944
	if (vwrq->fixed == 0) {
1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959
		/* 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;
1960 1961 1962 1963 1964 1965 1966 1967
		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;
		}
1968

1969 1970
		/* Validate requested power level against firmware allowed
		 * levels */
1971 1972 1973 1974
		if (priv->txpower_min && (dbm < priv->txpower_min)) {
			ret = -EINVAL;
			goto out;
		}
1975

1976 1977 1978 1979
		if (priv->txpower_max && (dbm > priv->txpower_max)) {
			ret = -EINVAL;
			goto out;
		}
1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991
		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;
1992
	}
1993

1994 1995 1996 1997 1998 1999
	/* 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;
	}
2000

2001
	lbs_deb_wext("txpower set %d dBm\n", dbm);
2002

2003
	ret = lbs_set_tx_power(priv, dbm);
2004

2005
out:
2006
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
2007 2008 2009
	return ret;
}

2010
static int lbs_get_essid(struct net_device *dev, struct iw_request_info *info,
2011 2012
		   struct iw_point *dwrq, char *extra)
{
2013
	struct lbs_private *priv = dev->ml_priv;
2014

2015 2016
	lbs_deb_enter(LBS_DEB_WEXT);

2017 2018 2019 2020 2021 2022 2023 2024
	/*
	 * Note : if dwrq->flags != 0, we should get the relevant SSID from
	 * the SSID list...
	 */

	/*
	 * Get the current SSID
	 */
2025 2026 2027 2028
	if (priv->connect_status == LBS_CONNECTED) {
		memcpy(extra, priv->curbssparams.ssid,
		       priv->curbssparams.ssid_len);
		extra[priv->curbssparams.ssid_len] = '\0';
2029 2030
	} else {
		memset(extra, 0, 32);
2031
		extra[priv->curbssparams.ssid_len] = '\0';
2032 2033 2034 2035 2036
	}
	/*
	 * If none, we may want to get the one that was set
	 */

2037
	dwrq->length = priv->curbssparams.ssid_len;
2038 2039 2040

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

2041
	lbs_deb_leave(LBS_DEB_WEXT);
2042 2043 2044
	return 0;
}

2045
static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
2046 2047
		   struct iw_point *dwrq, char *extra)
{
2048
	struct lbs_private *priv = dev->ml_priv;
2049
	int ret = 0;
2050
	u8 ssid[IEEE80211_MAX_SSID_LEN];
2051
	u8 ssid_len = 0;
2052
	struct assoc_request * assoc_req;
2053
	int in_ssid_len = dwrq->length;
2054
	DECLARE_SSID_BUF(ssid_buf);
2055

2056
	lbs_deb_enter(LBS_DEB_WEXT);
2057

2058 2059 2060 2061 2062
	if (!priv->radio_on) {
		ret = -EINVAL;
		goto out;
	}

2063
	/* Check the size of the string */
2064
	if (in_ssid_len > IEEE80211_MAX_SSID_LEN) {
2065 2066 2067 2068
		ret = -E2BIG;
		goto out;
	}

2069
	memset(&ssid, 0, sizeof(ssid));
2070

2071
	if (!dwrq->flags || !in_ssid_len) {
2072 2073 2074
		/* "any" SSID requested; leave SSID blank */
	} else {
		/* Specific SSID requested */
2075 2076
		memcpy(&ssid, extra, in_ssid_len);
		ssid_len = in_ssid_len;
2077 2078
	}

2079 2080 2081 2082
	if (!ssid_len) {
		lbs_deb_wext("requested any SSID\n");
	} else {
		lbs_deb_wext("requested SSID '%s'\n",
2083
		             print_ssid(ssid_buf, ssid, ssid_len));
2084
	}
2085 2086

out:
2087
	mutex_lock(&priv->lock);
2088 2089
	if (ret == 0) {
		/* Get or create the current association request */
2090
		assoc_req = lbs_get_association_request(priv);
2091 2092 2093 2094
		if (!assoc_req) {
			ret = -ENOMEM;
		} else {
			/* Copy the SSID to the association request */
2095
			memcpy(&assoc_req->ssid, &ssid, IEEE80211_MAX_SSID_LEN);
2096
			assoc_req->ssid_len = ssid_len;
2097
			set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
2098
			lbs_postpone_association_work(priv);
2099 2100 2101 2102 2103
		}
	}

	/* Cancel the association request if there was an error */
	if (ret != 0) {
2104
		lbs_cancel_association_work(priv);
2105 2106
	}

2107
	mutex_unlock(&priv->lock);
2108

2109
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
2110 2111 2112
	return ret;
}

2113 2114 2115 2116
static int lbs_mesh_get_essid(struct net_device *dev,
			      struct iw_request_info *info,
			      struct iw_point *dwrq, char *extra)
{
2117
	struct lbs_private *priv = dev->ml_priv;
2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134

	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)
{
2135
	struct lbs_private *priv = dev->ml_priv;
2136 2137 2138 2139
	int ret = 0;

	lbs_deb_enter(LBS_DEB_WEXT);

2140 2141 2142 2143 2144
	if (!priv->radio_on) {
		ret = -EINVAL;
		goto out;
	}

2145
	/* Check the size of the string */
2146
	if (dwrq->length > IEEE80211_MAX_SSID_LEN) {
2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159
		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;
	}

2160
	lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
2161
			priv->channel);
2162 2163 2164 2165 2166
 out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
}

2167 2168 2169 2170 2171 2172 2173 2174 2175
/**
 *  @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
 */
2176
static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
2177 2178
		 struct sockaddr *awrq, char *extra)
{
2179
	struct lbs_private *priv = dev->ml_priv;
2180 2181 2182
	struct assoc_request * assoc_req;
	int ret = 0;

2183
	lbs_deb_enter(LBS_DEB_WEXT);
2184

2185 2186 2187
	if (!priv->radio_on)
		return -EINVAL;

2188 2189 2190
	if (awrq->sa_family != ARPHRD_ETHER)
		return -EINVAL;

2191
	lbs_deb_wext("ASSOC: WAP: sa_data %pM\n", awrq->sa_data);
2192

2193
	mutex_lock(&priv->lock);
2194 2195

	/* Get or create the current association request */
2196
	assoc_req = lbs_get_association_request(priv);
2197
	if (!assoc_req) {
2198
		lbs_cancel_association_work(priv);
2199 2200 2201 2202 2203
		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);
2204
		lbs_postpone_association_work(priv);
2205 2206
	}

2207
	mutex_unlock(&priv->lock);
2208 2209 2210 2211 2212 2213 2214

	return ret;
}

/*
 * iwconfig settable callbacks
 */
2215
static const iw_handler lbs_handler[] = {
2216
	(iw_handler) NULL,	/* SIOCSIWCOMMIT */
2217
	(iw_handler) lbs_get_name,	/* SIOCGIWNAME */
2218 2219
	(iw_handler) NULL,	/* SIOCSIWNWID */
	(iw_handler) NULL,	/* SIOCGIWNWID */
2220 2221 2222 2223
	(iw_handler) lbs_set_freq,	/* SIOCSIWFREQ */
	(iw_handler) lbs_get_freq,	/* SIOCGIWFREQ */
	(iw_handler) lbs_set_mode,	/* SIOCSIWMODE */
	(iw_handler) lbs_get_mode,	/* SIOCGIWMODE */
2224 2225 2226
	(iw_handler) NULL,	/* SIOCSIWSENS */
	(iw_handler) NULL,	/* SIOCGIWSENS */
	(iw_handler) NULL,	/* SIOCSIWRANGE */
2227
	(iw_handler) lbs_get_range,	/* SIOCGIWRANGE */
2228 2229 2230 2231 2232 2233 2234 2235
	(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 */
2236 2237
	(iw_handler) lbs_set_wap,	/* SIOCSIWAP */
	(iw_handler) lbs_get_wap,	/* SIOCGIWAP */
2238 2239
	(iw_handler) NULL,	/* SIOCSIWMLME */
	(iw_handler) NULL,	/* SIOCGIWAPLIST - deprecated */
2240 2241 2242 2243 2244 2245
	(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 */
2246 2247
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261
	(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 */
2262 2263
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2264 2265 2266 2267 2268 2269
	(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 */
2270 2271 2272
	(iw_handler) NULL,		/* SIOCSIWPMKSA */
};

2273 2274
static const iw_handler mesh_wlan_handler[] = {
	(iw_handler) NULL,	/* SIOCSIWCOMMIT */
2275
	(iw_handler) lbs_get_name,	/* SIOCGIWNAME */
2276 2277
	(iw_handler) NULL,	/* SIOCSIWNWID */
	(iw_handler) NULL,	/* SIOCGIWNWID */
2278
	(iw_handler) lbs_mesh_set_freq,	/* SIOCSIWFREQ */
2279
	(iw_handler) lbs_get_freq,	/* SIOCGIWFREQ */
2280 2281 2282 2283 2284
	(iw_handler) NULL,		/* SIOCSIWMODE */
	(iw_handler) mesh_wlan_get_mode,	/* SIOCGIWMODE */
	(iw_handler) NULL,	/* SIOCSIWSENS */
	(iw_handler) NULL,	/* SIOCGIWSENS */
	(iw_handler) NULL,	/* SIOCSIWRANGE */
2285
	(iw_handler) lbs_get_range,	/* SIOCGIWRANGE */
2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297
	(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 */
2298 2299
	(iw_handler) lbs_set_scan,	/* SIOCSIWSCAN */
	(iw_handler) lbs_get_scan,	/* SIOCGIWSCAN */
2300 2301
	(iw_handler) lbs_mesh_set_essid,/* SIOCSIWESSID */
	(iw_handler) lbs_mesh_get_essid,/* SIOCGIWESSID */
2302 2303 2304 2305
	(iw_handler) NULL,		/* SIOCSIWNICKN */
	(iw_handler) mesh_get_nick,	/* SIOCGIWNICKN */
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319
	(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 */
2320 2321
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2322 2323 2324 2325 2326 2327
	(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 */
2328 2329
	(iw_handler) NULL,		/* SIOCSIWPMKSA */
};
2330 2331 2332 2333
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,
2334
};
2335 2336

struct iw_handler_def mesh_handler_def = {
2337
	.num_standard	= ARRAY_SIZE(mesh_wlan_handler),
2338
	.standard	= (iw_handler *) mesh_wlan_handler,
2339
	.get_wireless_stats = lbs_get_wireless_stats,
2340
};
反馈
建议
客服 返回
顶部