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

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

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


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

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

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

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

65 66
	for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
		rc = &priv->region_channel[j];
67 68 69 70 71 72 73 74 75 76 77 78 79 80

		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)
81
		lbs_deb_wext("lbs_find_cfp_by_band_and_channel: can't find "
82
		       "cfp by band %d / channel %d\n", band, channel);
83 84 85 86 87 88 89

	return cfp;
}

/**
 *  @brief Find the channel frequency power info with specific frequency
 *
90
 *  @param priv 	A pointer to struct lbs_private structure
91 92 93 94
 *  @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.
 */
95
static struct chan_freq_power *find_cfp_by_band_and_freq(
96
	struct lbs_private *priv,
97 98
	u8 band,
	u32 freq)
99 100 101 102 103
{
	struct chan_freq_power *cfp = NULL;
	struct region_channel *rc;
	int i, j;

104 105
	for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
		rc = &priv->region_channel[j];
106 107 108 109 110 111 112 113 114 115 116 117 118 119

		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)
120 121
		lbs_deb_wext("find_cfp_by_band_and_freql: can't find cfp by "
		       "band %d / freq %d\n", band, freq);
122 123 124 125 126

	return cfp;
}

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

136 137
	if ((priv->connect_status != LBS_CONNECTED) &&
		(priv->mesh_connect_status != LBS_CONNECTED))
138
		memcpy(rates, lbs_bg_rates, MAX_RATES);
139
	else
140
		memcpy(rates, priv->curbssparams.rates, MAX_RATES);
141

142
	lbs_deb_leave(LBS_DEB_WEXT);
143 144
}

145
static int lbs_get_name(struct net_device *dev, struct iw_request_info *info,
146 147 148
			 char *cwrq, char *extra)
{

149
	lbs_deb_enter(LBS_DEB_WEXT);
150

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

154
	lbs_deb_leave(LBS_DEB_WEXT);
155 156 157
	return 0;
}

158
static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
159 160
			 struct iw_freq *fwrq, char *extra)
{
161
	struct lbs_private *priv = dev->ml_priv;
162 163
	struct chan_freq_power *cfp;

164
	lbs_deb_enter(LBS_DEB_WEXT);
165

166
	cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
167
					   priv->channel);
168 169

	if (!cfp) {
170
		if (priv->channel)
171
			lbs_deb_wext("invalid channel %d\n",
172
			       priv->channel);
173 174 175 176 177 178
		return -EINVAL;
	}

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

179 180
	lbs_deb_wext("freq %u\n", fwrq->m);
	lbs_deb_leave(LBS_DEB_WEXT);
181 182 183
	return 0;
}

184
static int lbs_get_wap(struct net_device *dev, struct iw_request_info *info,
185 186
			struct sockaddr *awrq, char *extra)
{
187
	struct lbs_private *priv = dev->ml_priv;
188

189
	lbs_deb_enter(LBS_DEB_WEXT);
190

191 192
	if (priv->connect_status == LBS_CONNECTED) {
		memcpy(awrq->sa_data, priv->curbssparams.bssid, ETH_ALEN);
193 194 195 196 197
	} else {
		memset(awrq->sa_data, 0, ETH_ALEN);
	}
	awrq->sa_family = ARPHRD_ETHER;

198
	lbs_deb_leave(LBS_DEB_WEXT);
199 200 201
	return 0;
}

202
static int lbs_set_nick(struct net_device *dev, struct iw_request_info *info,
203 204
			 struct iw_point *dwrq, char *extra)
{
205
	struct lbs_private *priv = dev->ml_priv;
206

207
	lbs_deb_enter(LBS_DEB_WEXT);
208 209 210 211 212 213 214 215 216

	/*
	 * Check the size of the string
	 */

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

217 218 219 220
	mutex_lock(&priv->lock);
	memset(priv->nodename, 0, sizeof(priv->nodename));
	memcpy(priv->nodename, extra, dwrq->length);
	mutex_unlock(&priv->lock);
221

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

226
static int lbs_get_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
	dwrq->length = strlen(priv->nodename);
	memcpy(extra, priv->nodename, dwrq->length);
235
	extra[dwrq->length] = '\0';
236

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

239
	lbs_deb_leave(LBS_DEB_WEXT);
240 241 242
	return 0;
}

243 244 245
static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info,
			 struct iw_point *dwrq, char *extra)
{
246
	struct lbs_private *priv = dev->ml_priv;
247 248 249 250 251

	lbs_deb_enter(LBS_DEB_WEXT);

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

252
	if (priv->mesh_connect_status == LBS_CONNECTED) {
253 254
		strncpy(extra, "Mesh", 12);
		extra[12] = '\0';
255
		dwrq->length = strlen(extra);
256 257 258 259
	}

	else {
		extra[0] = '\0';
260
		dwrq->length = 0;
261 262 263 264 265
	}

	lbs_deb_leave(LBS_DEB_WEXT);
	return 0;
}
266

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

274
	lbs_deb_enter(LBS_DEB_WEXT);
275

276 277
	if (vwrq->disabled)
		val = MRVDRV_RTS_MAX_VALUE;
278

279
	if (val > MRVDRV_RTS_MAX_VALUE) /* min rts value is 0 */
280 281 282
		return -EINVAL;

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

284
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
285 286 287
	return ret;
}

288
static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info,
289 290
			struct iw_param *vwrq, char *extra)
{
291
	struct lbs_private *priv = dev->ml_priv;
292 293
	int ret = 0;
	u16 val = 0;
294

295
	lbs_deb_enter(LBS_DEB_WEXT);
296

297
	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, &val);
298 299
	if (ret)
		goto out;
300

301
	vwrq->value = val;
302
	vwrq->disabled = val > MRVDRV_RTS_MAX_VALUE; /* min rts value is 0 */
303 304
	vwrq->fixed = 1;

305 306 307
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
308 309
}

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

317
	lbs_deb_enter(LBS_DEB_WEXT);
318

319 320 321 322 323
	if (vwrq->disabled)
		val = MRVDRV_FRAG_MAX_VALUE;

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

325
	ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, (u16) val);
326 327

	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
328 329 330
	return ret;
}

331
static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info,
332 333
			 struct iw_param *vwrq, char *extra)
{
334
	struct lbs_private *priv = dev->ml_priv;
335 336
	int ret = 0;
	u16 val = 0;
337

338
	lbs_deb_enter(LBS_DEB_WEXT);
339

340
	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, &val);
341 342
	if (ret)
		goto out;
343

344 345 346
	vwrq->value = val;
	vwrq->disabled = ((val < MRVDRV_FRAG_MIN_VALUE)
			  || (val > MRVDRV_FRAG_MAX_VALUE));
347 348
	vwrq->fixed = 1;

349 350
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
351 352 353
	return ret;
}

354
static int lbs_get_mode(struct net_device *dev,
355 356
			 struct iw_request_info *info, u32 * uwrq, char *extra)
{
357
	struct lbs_private *priv = dev->ml_priv;
358

359
	lbs_deb_enter(LBS_DEB_WEXT);
360

361
	*uwrq = priv->mode;
362

363
	lbs_deb_leave(LBS_DEB_WEXT);
364 365 366
	return 0;
}

367 368 369 370 371 372
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);

373
	*uwrq = IW_MODE_REPEAT;
374 375 376 377 378

	lbs_deb_leave(LBS_DEB_WEXT);
	return 0;
}

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

387
	lbs_deb_enter(LBS_DEB_WEXT);
388

389 390 391 392 393 394 395
	if (!priv->radio_on) {
		lbs_deb_wext("tx power off\n");
		vwrq->value = 0;
		vwrq->disabled = 1;
		goto out;
	}

396
	ret = lbs_get_tx_power(priv, &curlevel, NULL, NULL);
397 398
	if (ret)
		goto out;
399

400 401
	lbs_deb_wext("tx power level %d dbm\n", curlevel);
	priv->txpower_cur = curlevel;
402

403
	vwrq->value = curlevel;
404
	vwrq->fixed = 1;
405 406
	vwrq->disabled = 0;
	vwrq->flags = IW_TXPOW_DBM;
407

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

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

420
	lbs_deb_enter(LBS_DEB_WEXT);
421

422 423 424 425 426
        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 */
427 428
#define TX_RETRY_MIN 0
#define TX_RETRY_MAX 14
429 430
	if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX)
		return -EINVAL;
431

432 433 434 435 436 437 438
	/* 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 */
439

440 441 442 443 444 445
	if (llimit) {
		ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_LONG_RETRY_LIMIT,
				       llimit);
		if (ret)
			goto out;
	}
446

447 448 449 450 451
	if (slimit) {
		/* txretrycount follows the short retry limit */
		priv->txretrycount = slimit;
		ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_SHORT_RETRY_LIMIT,
				       slimit);
452 453
		if (ret)
			goto out;
454 455
	}

456 457 458
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
459 460
}

461
static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info,
462 463
			  struct iw_param *vwrq, char *extra)
{
464
	struct lbs_private *priv = dev->ml_priv;
465
	int ret = 0;
466
	u16 val = 0;
467

468 469
	lbs_deb_enter(LBS_DEB_WEXT);

470
	vwrq->disabled = 0;
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486

	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;
487
		/* Subtract 1 to convert try count to retry count */
488 489
		vwrq->value = val - 1;
		vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
490 491
	}

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

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
 */
536
static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
537 538 539
			  struct iw_point *dwrq, char *extra)
{
	int i, j;
540
	struct lbs_private *priv = dev->ml_priv;
541 542
	struct iw_range *range = (struct iw_range *)extra;
	struct chan_freq_power *cfp;
543
	u8 rates[MAX_RATES + 1];
544

545
	lbs_deb_enter(LBS_DEB_WEXT);
546 547 548 549 550 551 552 553

	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));
554
	copy_active_data_rates(priv, rates);
555 556 557
	range->num_bitrates = strnlen(rates, IW_MAX_BITRATES);
	for (i = 0; i < range->num_bitrates; i++)
		range->bitrate[i] = rates[i] * 500000;
558
	range->num_bitrates = i;
559
	lbs_deb_wext("IW_MAX_BITRATES %d, num_bitrates %d\n", IW_MAX_BITRATES,
560 561 562
	       range->num_bitrates);

	range->num_frequency = 0;
563 564 565

	range->scan_capa = IW_SCAN_CAPA_ESSID;

H
Holger Schurig 已提交
566 567 568
	for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
	     && (j < ARRAY_SIZE(priv->region_channel)); j++) {
		cfp = priv->region_channel[j].CFP;
569
		for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
H
Holger Schurig 已提交
570 571 572 573 574
		     && priv->region_channel[j].valid
		     && cfp
		     && (i < priv->region_channel[j].nrcfp); i++) {
			range->freq[range->num_frequency].i =
			    (long)cfp->channel;
575
			range->freq[range->num_frequency].m =
H
Holger Schurig 已提交
576
			    (long)cfp->freq * 100000;
577
			range->freq[range->num_frequency].e = 1;
H
Holger Schurig 已提交
578
			cfp++;
579 580 581 582
			range->num_frequency++;
		}
	}

583
	lbs_deb_wext("IW_MAX_FREQUENCIES %d, num_frequency %d\n",
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
	       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;

609 610 611 612
	/*
	 * Right now we support only "iwconfig ethX power on|off"
	 */
	range->pm_capa = IW_POWER_ON;
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645

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

646
	/* Setup the supported power level ranges */
647
	memset(range->txpower, 0, sizeof(range->txpower));
648 649 650 651
	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;
652 653 654 655 656 657

	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;

658
	if (priv->fwcapinfo & FW_CAPINFO_WPA) {
659 660 661 662 663 664
		range->enc_capa =   IW_ENC_CAPA_WPA
		                  | IW_ENC_CAPA_WPA2
		                  | IW_ENC_CAPA_CIPHER_TKIP
		                  | IW_ENC_CAPA_CIPHER_CCMP;
	}

665
	lbs_deb_leave(LBS_DEB_WEXT);
666 667 668
	return 0;
}

669
static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
670 671
			  struct iw_param *vwrq, char *extra)
{
672
	struct lbs_private *priv = dev->ml_priv;
673
	int ret = 0;
674

675
	lbs_deb_enter(LBS_DEB_WEXT);
676

677
	if (!(priv->fwcapinfo & FW_CAPINFO_PS)) {
678 679 680 681 682 683
		if (vwrq->disabled)
			return 0;
		else
			return -EINVAL;
	}

684 685 686 687 688
	/* PS is currently supported only in Infrastructure mode
	 * Remove this check if it is to be supported in IBSS mode also
	 */

	if (vwrq->disabled) {
689 690
		priv->psmode = LBS802_11POWERMODECAM;
		if (priv->psstate != PS_STATE_FULL_POWER) {
691
			lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
692 693 694 695 696 697
		}

		return 0;
	}

	if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
698 699
		lbs_deb_wext(
		       "setting power timeout is not supported\n");
700 701
		return -EINVAL;
	} else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
		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;
		}
750 751
	}

752
	if (priv->psmode != LBS802_11POWERMODECAM) {
753 754 755
		return 0;
	}

756
	priv->psmode = LBS802_11POWERMODEMAX_PSP;
757

758
	if (priv->connect_status == LBS_CONNECTED) {
759
		lbs_ps_sleep(priv, CMD_OPTION_WAITFORRSP);
760 761
	}

762
	lbs_deb_leave(LBS_DEB_WEXT);
763

764 765 766
	return 0;
}

767
static int lbs_get_power(struct net_device *dev, struct iw_request_info *info,
768 769
			  struct iw_param *vwrq, char *extra)
{
770
	struct lbs_private *priv = dev->ml_priv;
771

772
	lbs_deb_enter(LBS_DEB_WEXT);
773 774

	vwrq->value = 0;
775 776 777
	vwrq->flags = 0;
	vwrq->disabled = priv->psmode == LBS802_11POWERMODECAM
		|| priv->connect_status == LBS_DISCONNECTED;
778

779
	lbs_deb_leave(LBS_DEB_WEXT);
780 781 782
	return 0;
}

783
static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
784 785 786 787 788 789 790 791 792
{
	enum {
		POOR = 30,
		FAIR = 60,
		GOOD = 80,
		VERY_GOOD = 90,
		EXCELLENT = 95,
		PERFECT = 100
	};
793
	struct lbs_private *priv = dev->ml_priv;
794 795 796
	u32 rssi_qual;
	u32 tx_qual;
	u32 quality = 0;
797
	int ret, stats_valid = 0;
798 799
	u8 rssi;
	u32 tx_retries;
800
	struct cmd_ds_802_11_get_log log;
801

802
	lbs_deb_enter(LBS_DEB_WEXT);
803

804
	priv->wstats.status = priv->mode;
805 806

	/* If we're not associated, all quality values are meaningless */
807 808
	if ((priv->connect_status != LBS_CONNECTED) &&
	    (priv->mesh_connect_status != LBS_CONNECTED))
809 810 811 812
		goto out;

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

816
	if (priv->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
817 818 819
		priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
	} else {
		priv->wstats.qual.noise =
820
		    CAL_NF(priv->NF[TYPE_BEACON][TYPE_NOAVG]);
821 822
	}

823 824
	lbs_deb_wext("signal level %#x\n", priv->wstats.qual.level);
	lbs_deb_wext("noise %#x\n", priv->wstats.qual.noise);
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841

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

844 845
	memset(&log, 0, sizeof(log));
	log.hdr.size = cpu_to_le16(sizeof(log));
846 847 848
	ret = lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log);
	if (ret)
		goto out;
849 850

	tx_retries = le32_to_cpu(log.retry);
851 852 853 854 855 856 857 858 859 860 861 862 863 864 865

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

866
	priv->wstats.discard.code = le32_to_cpu(log.wepundecryptable);
867
	priv->wstats.discard.retries = tx_retries;
868
	priv->wstats.discard.misc = le32_to_cpu(log.ackfailure);
869 870

	/* Calculate quality */
871
	priv->wstats.qual.qual = min_t(u8, quality, 100);
872 873 874 875
	priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
	stats_valid = 1;

	/* update stats asynchronously for future calls */
876
	ret = lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
877
					0, 0, NULL);
878 879
	if (ret)
		lbs_pr_err("RSSI command failed\n");
880 881 882 883 884 885 886 887 888 889 890 891
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;
	}

892
	lbs_deb_leave(LBS_DEB_WEXT);
893 894 895 896 897
	return &priv->wstats;


}

898
static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info,
899 900
		  struct iw_freq *fwrq, char *extra)
{
901
	int ret = -EINVAL;
902
	struct lbs_private *priv = dev->ml_priv;
903
	struct chan_freq_power *cfp;
904
	struct assoc_request * assoc_req;
905

906
	lbs_deb_enter(LBS_DEB_WEXT);
907

908 909
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
910 911 912 913
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}
914

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

919
		cfp = find_cfp_by_band_and_freq(priv, 0, f);
920
		if (!cfp) {
921
			lbs_deb_wext("invalid freq %ld\n", f);
922
			goto out;
923 924 925
		}

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

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

934
	cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m);
935 936
	if (!cfp) {
		goto out;
937 938
	}

939 940 941
	assoc_req->channel = fwrq->m;
	ret = 0;

942
out:
943 944
	if (ret == 0) {
		set_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags);
945
		lbs_postpone_association_work(priv);
946
	} else {
947
		lbs_cancel_association_work(priv);
948
	}
949
	mutex_unlock(&priv->lock);
950 951 952

	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
953 954
}

955 956 957 958
static int lbs_mesh_set_freq(struct net_device *dev,
			     struct iw_request_info *info,
			     struct iw_freq *fwrq, char *extra)
{
959
	struct lbs_private *priv = dev->ml_priv;
960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
	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;
	}

989
	if (fwrq->m != priv->channel) {
990 991
		lbs_deb_wext("mesh channel change forces eth disconnect\n");
		if (priv->mode == IW_MODE_INFRA)
992 993 994
			lbs_cmd_80211_deauthenticate(priv,
						     priv->curbssparams.bssid,
						     WLAN_REASON_DEAUTH_LEAVING);
995
		else if (priv->mode == IW_MODE_ADHOC)
996
			lbs_adhoc_stop(priv);
997
	}
998
	lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, fwrq->m);
999
	lbs_update_channel(priv);
1000 1001 1002 1003 1004 1005 1006
	ret = 0;

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

1007
static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
1008 1009
		  struct iw_param *vwrq, char *extra)
{
1010
	struct lbs_private *priv = dev->ml_priv;
1011
	u8 new_rate = 0;
1012 1013
	int ret = -EINVAL;
	u8 rates[MAX_RATES + 1];
1014

1015
	lbs_deb_enter(LBS_DEB_WEXT);
1016

1017
	lbs_deb_wext("vwrq->value %d\n", vwrq->value);
1018 1019 1020 1021
	lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);

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

1023
	/* Auto rate? */
1024 1025 1026
	priv->enablehwauto = !vwrq->fixed;

	if (vwrq->value == -1)
1027
		priv->cur_rate = 0;
1028
	else {
1029 1030
		if (vwrq->value % 100000)
			goto out;
1031

1032 1033 1034
		new_rate = vwrq->value / 500000;
		priv->cur_rate = new_rate;
		/* the rest is only needed for lbs_set_data_rate() */
1035
		memset(rates, 0, sizeof(rates));
1036
		copy_active_data_rates(priv, rates);
1037 1038 1039 1040
		if (!memchr(rates, new_rate, sizeof(rates))) {
			lbs_pr_alert("fixed data rate 0x%X out of range\n",
				new_rate);
			goto out;
1041
		}
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
		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;
1054 1055
	}

1056 1057 1058 1059 1060 1061
	/* 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);
1062

1063
out:
1064
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1065 1066 1067
	return ret;
}

1068
static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info,
1069 1070
		  struct iw_param *vwrq, char *extra)
{
1071
	struct lbs_private *priv = dev->ml_priv;
1072

1073
	lbs_deb_enter(LBS_DEB_WEXT);
1074

1075 1076
	if (priv->connect_status == LBS_CONNECTED) {
		vwrq->value = priv->cur_rate * 500000;
1077

1078
		if (priv->enablehwauto)
1079 1080 1081 1082
			vwrq->fixed = 0;
		else
			vwrq->fixed = 1;

1083
	} else {
1084 1085
		vwrq->fixed = 0;
		vwrq->value = 0;
1086 1087
	}

1088
	lbs_deb_leave(LBS_DEB_WEXT);
1089 1090 1091
	return 0;
}

1092
static int lbs_set_mode(struct net_device *dev,
1093 1094 1095
		  struct iw_request_info *info, u32 * uwrq, char *extra)
{
	int ret = 0;
1096
	struct lbs_private *priv = dev->ml_priv;
1097 1098
	struct assoc_request * assoc_req;

1099
	lbs_deb_enter(LBS_DEB_WEXT);
1100

1101 1102 1103
	if (   (*uwrq != IW_MODE_ADHOC)
	    && (*uwrq != IW_MODE_INFRA)
	    && (*uwrq != IW_MODE_AUTO)) {
1104
		lbs_deb_wext("Invalid mode: 0x%x\n", *uwrq);
1105 1106
		ret = -EINVAL;
		goto out;
1107 1108
	}

1109 1110
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1111 1112
	if (!assoc_req) {
		ret = -ENOMEM;
1113
		lbs_cancel_association_work(priv);
1114
	} else {
1115
		assoc_req->mode = *uwrq;
1116
		set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
1117
		lbs_postpone_association_work(priv);
1118
		lbs_deb_wext("Switching to mode: 0x%x\n", *uwrq);
1119
	}
1120
	mutex_unlock(&priv->lock);
1121

1122
out:
1123
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
	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
 */
1137
static int lbs_get_encode(struct net_device *dev,
1138 1139 1140
			   struct iw_request_info *info,
			   struct iw_point *dwrq, u8 * extra)
{
1141
	struct lbs_private *priv = dev->ml_priv;
1142 1143
	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;

1144
	lbs_deb_enter(LBS_DEB_WEXT);
1145

1146
	lbs_deb_wext("flags 0x%x, index %d, length %d, wep_tx_keyidx %d\n",
1147
	       dwrq->flags, index, dwrq->length, priv->wep_tx_keyidx);
1148 1149 1150 1151

	dwrq->flags = 0;

	/* Authentication method */
1152
	switch (priv->secinfo.auth_mode) {
1153
	case IW_AUTH_ALG_OPEN_SYSTEM:
1154 1155 1156
		dwrq->flags = IW_ENCODE_OPEN;
		break;

1157 1158
	case IW_AUTH_ALG_SHARED_KEY:
	case IW_AUTH_ALG_LEAP:
1159 1160 1161 1162 1163 1164 1165 1166 1167
		dwrq->flags = IW_ENCODE_RESTRICTED;
		break;
	default:
		dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN;
		break;
	}

	memset(extra, 0, 16);

1168
	mutex_lock(&priv->lock);
1169 1170 1171

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

1174 1175 1176 1177
	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;
1178 1179 1180 1181

		dwrq->flags |= (index + 1);
		/* Return WEP enabled */
		dwrq->flags &= ~IW_ENCODE_DISABLED;
1182 1183
	} else if ((priv->secinfo.WPAenabled)
		   || (priv->secinfo.WPA2enabled)) {
1184 1185
		/* return WPA enabled */
		dwrq->flags &= ~IW_ENCODE_DISABLED;
1186
		dwrq->flags |= IW_ENCODE_NOKEY;
1187 1188 1189 1190
	} else {
		dwrq->flags |= IW_ENCODE_DISABLED;
	}

1191
	mutex_unlock(&priv->lock);
1192

1193
	lbs_deb_wext("key: %02x:%02x:%02x:%02x:%02x:%02x, keylen %d\n",
1194 1195 1196
	       extra[0], extra[1], extra[2],
	       extra[3], extra[4], extra[5], dwrq->length);

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

1199
	lbs_deb_leave(LBS_DEB_WEXT);
1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212
	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
 */
1213
static int lbs_set_wep_key(struct assoc_request *assoc_req,
1214 1215 1216 1217 1218
			    const char *key_material,
			    u16 key_length,
			    u16 index,
			    int set_tx_key)
{
1219
	int ret = 0;
1220
	struct enc_key *pkey;
1221

1222
	lbs_deb_enter(LBS_DEB_WEXT);
1223 1224 1225

	/* Paranoid validation of key index */
	if (index > 3) {
1226 1227
		ret = -EINVAL;
		goto out;
1228 1229 1230 1231
	}

	/* validate max key length */
	if (key_length > KEY_LEN_WEP_104) {
1232 1233
		ret = -EINVAL;
		goto out;
1234 1235 1236 1237 1238
	}

	pkey = &assoc_req->wep_keys[index];

	if (key_length > 0) {
1239
		memset(pkey, 0, sizeof(struct enc_key));
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
		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) {
1251 1252 1253
			lbs_deb_wext("key not set, so cannot enable it\n");
			ret = -EINVAL;
			goto out;
1254 1255 1256 1257
		}
		assoc_req->wep_tx_keyidx = index;
	}

1258
	assoc_req->secinfo.wep_enabled = 1;
1259

1260 1261 1262
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286
}

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;

1287 1288
	lbs_deb_enter(LBS_DEB_WEXT);

1289
	/* Set Open System auth mode */
1290
	assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1291 1292

	/* Clear WEP keys and mark WEP as disabled */
1293
	assoc_req->secinfo.wep_enabled = 0;
1294 1295 1296 1297 1298
	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);
1299 1300 1301 1302 1303 1304 1305 1306

	lbs_deb_leave(LBS_DEB_WEXT);
}

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

1307
	memset(&assoc_req->wpa_mcast_key, 0, sizeof (struct enc_key));
1308 1309 1310
	assoc_req->wpa_mcast_key.flags = KEY_INFO_WPA_MCAST;
	set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);

1311
	memset(&assoc_req->wpa_unicast_key, 0, sizeof (struct enc_key));
1312 1313 1314 1315 1316 1317 1318 1319
	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);
1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330
}

/**
 *  @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
 */
1331
static int lbs_set_encode(struct net_device *dev,
1332 1333 1334 1335
		    struct iw_request_info *info,
		    struct iw_point *dwrq, char *extra)
{
	int ret = 0;
1336
	struct lbs_private *priv = dev->ml_priv;
1337 1338 1339
	struct assoc_request * assoc_req;
	u16 is_default = 0, index = 0, set_tx_key = 0;

1340
	lbs_deb_enter(LBS_DEB_WEXT);
1341

1342 1343
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1344 1345 1346 1347 1348 1349 1350
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}

	if (dwrq->flags & IW_ENCODE_DISABLED) {
		disable_wep (assoc_req);
1351
		disable_wpa (assoc_req);
1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365
		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.
	 */
1366
	if (!assoc_req->secinfo.wep_enabled || (dwrq->length == 0 && !is_default))
1367 1368
		set_tx_key = 1;

1369
	ret = lbs_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key);
1370 1371 1372 1373 1374 1375 1376 1377 1378
	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) {
1379
		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1380
	} else if (dwrq->flags & IW_ENCODE_OPEN) {
1381
		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1382 1383 1384 1385 1386
	}

out:
	if (ret == 0) {
		set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1387
		lbs_postpone_association_work(priv);
1388
	} else {
1389
		lbs_cancel_association_work(priv);
1390
	}
1391
	mutex_unlock(&priv->lock);
1392

1393
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405
	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
 */
1406
static int lbs_get_encodeext(struct net_device *dev,
1407 1408 1409 1410 1411
			      struct iw_request_info *info,
			      struct iw_point *dwrq,
			      char *extra)
{
	int ret = -EINVAL;
1412
	struct lbs_private *priv = dev->ml_priv;
1413 1414 1415
	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
	int index, max_key_len;

1416
	lbs_deb_enter(LBS_DEB_WEXT);
1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427

	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 {
1428
		index = priv->wep_tx_keyidx;
1429 1430
	}

R
Roel Kluin 已提交
1431
	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
1432
	    ext->alg != IW_ENCODE_ALG_WEP) {
1433
		if (index != 0 || priv->mode != IW_MODE_INFRA)
1434 1435 1436 1437 1438 1439
			goto out;
	}

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

1440 1441 1442
	if (   !priv->secinfo.wep_enabled
	    && !priv->secinfo.WPAenabled
	    && !priv->secinfo.WPA2enabled) {
1443 1444 1445 1446 1447 1448
		ext->alg = IW_ENCODE_ALG_NONE;
		ext->key_len = 0;
		dwrq->flags |= IW_ENCODE_DISABLED;
	} else {
		u8 *key = NULL;

1449 1450 1451
		if (   priv->secinfo.wep_enabled
		    && !priv->secinfo.WPAenabled
		    && !priv->secinfo.WPA2enabled) {
1452
			/* WEP */
1453
			ext->alg = IW_ENCODE_ALG_WEP;
1454 1455 1456 1457 1458
			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)) {
1459
			/* WPA */
1460
			struct enc_key * pkey = NULL;
1461

1462 1463 1464 1465 1466 1467
			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;
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480

			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;
			}
1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498
		} 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:
1499
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511
	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
 */
1512
static int lbs_set_encodeext(struct net_device *dev,
1513 1514 1515 1516 1517
			      struct iw_request_info *info,
			      struct iw_point *dwrq,
			      char *extra)
{
	int ret = 0;
1518
	struct lbs_private *priv = dev->ml_priv;
1519 1520 1521 1522
	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
	int alg = ext->alg;
	struct assoc_request * assoc_req;

1523
	lbs_deb_enter(LBS_DEB_WEXT);
1524

1525 1526
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1527 1528 1529 1530 1531 1532 1533
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}

	if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) {
		disable_wep (assoc_req);
1534
		disable_wpa (assoc_req);
1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546
	} 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.
		 */
1547
		if (   !assoc_req->secinfo.wep_enabled
1548 1549 1550 1551 1552
		    || (dwrq->length == 0 && !is_default)
		    || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY))
			set_tx_key = 1;

		/* Copy key to driver */
1553
		ret = lbs_set_wep_key(assoc_req, ext->key, ext->key_len, index,
1554 1555 1556 1557 1558
					set_tx_key);
		if (ret)
			goto out;

		if (dwrq->flags & IW_ENCODE_RESTRICTED) {
1559
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1560
		} else if (dwrq->flags & IW_ENCODE_OPEN) {
1561
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1562 1563 1564 1565 1566 1567 1568 1569 1570
		}

		/* 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)) {
1571
		struct enc_key * pkey;
1572 1573 1574 1575 1576 1577

		/* 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))) {
1578
				lbs_deb_wext("invalid size %d for key of alg "
1579
				       "type %d\n",
1580 1581 1582 1583 1584 1585
				       ext->key_len,
				       alg);
				ret = -EINVAL;
				goto out;
		}

1586
		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
1587
			pkey = &assoc_req->wpa_mcast_key;
1588 1589
			set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
		} else {
1590
			pkey = &assoc_req->wpa_unicast_key;
1591 1592
			set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
		}
1593

1594
		memset(pkey, 0, sizeof (struct enc_key));
1595 1596
		memcpy(pkey->key, ext->key, ext->key_len);
		pkey->len = ext->key_len;
1597 1598
		if (pkey->len)
			pkey->flags |= KEY_INFO_WPA_ENABLED;
1599

1600
		/* Do this after zeroing key structure */
1601 1602 1603 1604 1605 1606
		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
			pkey->flags |= KEY_INFO_WPA_MCAST;
		} else {
			pkey->flags |= KEY_INFO_WPA_UNICAST;
		}

1607
		if (alg == IW_ENCODE_ALG_TKIP) {
1608
			pkey->type = KEY_TYPE_ID_TKIP;
1609
		} else if (alg == IW_ENCODE_ALG_CCMP) {
1610
			pkey->type = KEY_TYPE_ID_AES;
1611
		}
1612 1613 1614 1615 1616 1617 1618 1619 1620

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

1621 1622 1623
		/* Only disable wep if necessary: can't waste time here. */
		if (priv->mac_control & CMD_ACT_MAC_WEP_ENABLE)
			disable_wep(assoc_req);
1624 1625 1626
	}

out:
1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640
	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);
1641
	} else {
1642
		lbs_cancel_association_work(priv);
1643
	}
1644
	mutex_unlock(&priv->lock);
1645

1646
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1647 1648 1649 1650
	return ret;
}


1651
static int lbs_set_genie(struct net_device *dev,
1652 1653 1654 1655
			  struct iw_request_info *info,
			  struct iw_point *dwrq,
			  char *extra)
{
1656
	struct lbs_private *priv = dev->ml_priv;
1657 1658 1659
	int ret = 0;
	struct assoc_request * assoc_req;

1660
	lbs_deb_enter(LBS_DEB_WEXT);
1661

1662 1663
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678
	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 {
1679
		memset(&assoc_req->wpa_ie[0], 0, sizeof(priv->wpa_ie));
1680 1681 1682 1683 1684 1685
		assoc_req->wpa_ie_len = 0;
	}

out:
	if (ret == 0) {
		set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags);
1686
		lbs_postpone_association_work(priv);
1687
	} else {
1688
		lbs_cancel_association_work(priv);
1689
	}
1690
	mutex_unlock(&priv->lock);
1691

1692
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1693 1694 1695
	return ret;
}

1696
static int lbs_get_genie(struct net_device *dev,
1697 1698 1699 1700
			  struct iw_request_info *info,
			  struct iw_point *dwrq,
			  char *extra)
{
1701
	int ret = 0;
1702
	struct lbs_private *priv = dev->ml_priv;
1703

1704
	lbs_deb_enter(LBS_DEB_WEXT);
1705

1706
	if (priv->wpa_ie_len == 0) {
1707
		dwrq->length = 0;
1708
		goto out;
1709 1710
	}

1711
	if (dwrq->length < priv->wpa_ie_len) {
1712 1713
		ret = -E2BIG;
		goto out;
1714 1715
	}

1716 1717
	dwrq->length = priv->wpa_ie_len;
	memcpy(extra, &priv->wpa_ie[0], priv->wpa_ie_len);
1718

1719 1720 1721
out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
1722 1723 1724
}


1725
static int lbs_set_auth(struct net_device *dev,
1726 1727 1728 1729
			 struct iw_request_info *info,
			 struct iw_param *dwrq,
			 char *extra)
{
1730
	struct lbs_private *priv = dev->ml_priv;
1731 1732 1733 1734
	struct assoc_request * assoc_req;
	int ret = 0;
	int updated = 0;

1735
	lbs_deb_enter(LBS_DEB_WEXT);
1736

1737 1738
	mutex_lock(&priv->lock);
	assoc_req = lbs_get_association_request(priv);
1739 1740 1741 1742 1743 1744
	if (!assoc_req) {
		ret = -ENOMEM;
		goto out;
	}

	switch (dwrq->flags & IW_AUTH_INDEX) {
1745 1746
	case IW_AUTH_PRIVACY_INVOKED:
	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
1747 1748 1749
	case IW_AUTH_TKIP_COUNTERMEASURES:
	case IW_AUTH_CIPHER_PAIRWISE:
	case IW_AUTH_CIPHER_GROUP:
1750
	case IW_AUTH_DROP_UNENCRYPTED:
1751 1752 1753 1754 1755
		/*
		 * libertas does not use these parameters
		 */
		break;

1756 1757 1758 1759 1760
	case IW_AUTH_KEY_MGMT:
		assoc_req->secinfo.key_mgmt = dwrq->value;
		updated = 1;
		break;

1761 1762 1763 1764
	case IW_AUTH_WPA_VERSION:
		if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) {
			assoc_req->secinfo.WPAenabled = 0;
			assoc_req->secinfo.WPA2enabled = 0;
1765
			disable_wpa (assoc_req);
1766 1767 1768
		}
		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) {
			assoc_req->secinfo.WPAenabled = 1;
1769
			assoc_req->secinfo.wep_enabled = 0;
1770
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1771 1772 1773
		}
		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) {
			assoc_req->secinfo.WPA2enabled = 1;
1774
			assoc_req->secinfo.wep_enabled = 0;
1775
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1776 1777 1778 1779 1780 1781
		}
		updated = 1;
		break;

	case IW_AUTH_80211_AUTH_ALG:
		if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) {
1782
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
1783
		} else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) {
1784
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1785
		} else if (dwrq->value & IW_AUTH_ALG_LEAP) {
1786
			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_LEAP;
1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798
		} 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;
1799
				assoc_req->secinfo.wep_enabled = 0;
1800
				assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
1801 1802 1803 1804
			}
		} else {
			assoc_req->secinfo.WPAenabled = 0;
			assoc_req->secinfo.WPA2enabled = 0;
1805
			disable_wpa (assoc_req);
1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818
		}
		updated = 1;
		break;

	default:
		ret = -EOPNOTSUPP;
		break;
	}

out:
	if (ret == 0) {
		if (updated)
			set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
1819
		lbs_postpone_association_work(priv);
1820
	} else if (ret != -EOPNOTSUPP) {
1821
		lbs_cancel_association_work(priv);
1822
	}
1823
	mutex_unlock(&priv->lock);
1824

1825
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1826 1827 1828
	return ret;
}

1829
static int lbs_get_auth(struct net_device *dev,
1830 1831 1832 1833
			 struct iw_request_info *info,
			 struct iw_param *dwrq,
			 char *extra)
{
1834
	int ret = 0;
1835
	struct lbs_private *priv = dev->ml_priv;
1836

1837
	lbs_deb_enter(LBS_DEB_WEXT);
1838 1839

	switch (dwrq->flags & IW_AUTH_INDEX) {
1840 1841 1842 1843
	case IW_AUTH_KEY_MGMT:
		dwrq->value = priv->secinfo.key_mgmt;
		break;

1844 1845
	case IW_AUTH_WPA_VERSION:
		dwrq->value = 0;
1846
		if (priv->secinfo.WPAenabled)
1847
			dwrq->value |= IW_AUTH_WPA_VERSION_WPA;
1848
		if (priv->secinfo.WPA2enabled)
1849 1850 1851 1852 1853 1854
			dwrq->value |= IW_AUTH_WPA_VERSION_WPA2;
		if (!dwrq->value)
			dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED;
		break;

	case IW_AUTH_80211_AUTH_ALG:
1855
		dwrq->value = priv->secinfo.auth_mode;
1856 1857 1858
		break;

	case IW_AUTH_WPA_ENABLED:
1859
		if (priv->secinfo.WPAenabled && priv->secinfo.WPA2enabled)
1860 1861 1862 1863
			dwrq->value = 1;
		break;

	default:
1864
		ret = -EOPNOTSUPP;
1865 1866
	}

1867 1868
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
1869 1870 1871
}


1872
static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
1873 1874 1875
		   struct iw_param *vwrq, char *extra)
{
	int ret = 0;
1876
	struct lbs_private *priv = dev->ml_priv;
1877
	s16 dbm = (s16) vwrq->value;
1878

1879
	lbs_deb_enter(LBS_DEB_WEXT);
1880 1881

	if (vwrq->disabled) {
1882
		lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 0);
1883
		goto out;
1884 1885
	}

1886
	if (vwrq->fixed == 0) {
1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901
		/* 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;
1902 1903 1904 1905 1906 1907 1908 1909
		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;
		}
1910

1911 1912
		/* Validate requested power level against firmware allowed
		 * levels */
1913 1914 1915 1916
		if (priv->txpower_min && (dbm < priv->txpower_min)) {
			ret = -EINVAL;
			goto out;
		}
1917

1918 1919 1920 1921
		if (priv->txpower_max && (dbm > priv->txpower_max)) {
			ret = -EINVAL;
			goto out;
		}
1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933
		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;
1934
	}
1935

1936 1937 1938 1939 1940 1941
	/* 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;
	}
1942

1943
	lbs_deb_wext("txpower set %d dBm\n", dbm);
1944

1945
	ret = lbs_set_tx_power(priv, dbm);
1946

1947
out:
1948
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
1949 1950 1951
	return ret;
}

1952
static int lbs_get_essid(struct net_device *dev, struct iw_request_info *info,
1953 1954
		   struct iw_point *dwrq, char *extra)
{
1955
	struct lbs_private *priv = dev->ml_priv;
1956

1957 1958
	lbs_deb_enter(LBS_DEB_WEXT);

1959 1960 1961 1962 1963 1964 1965 1966
	/*
	 * Note : if dwrq->flags != 0, we should get the relevant SSID from
	 * the SSID list...
	 */

	/*
	 * Get the current SSID
	 */
1967 1968 1969 1970
	if (priv->connect_status == LBS_CONNECTED) {
		memcpy(extra, priv->curbssparams.ssid,
		       priv->curbssparams.ssid_len);
		extra[priv->curbssparams.ssid_len] = '\0';
1971 1972
	} else {
		memset(extra, 0, 32);
1973
		extra[priv->curbssparams.ssid_len] = '\0';
1974 1975 1976 1977 1978
	}
	/*
	 * If none, we may want to get the one that was set
	 */

1979
	dwrq->length = priv->curbssparams.ssid_len;
1980 1981 1982

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

1983
	lbs_deb_leave(LBS_DEB_WEXT);
1984 1985 1986
	return 0;
}

1987
static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
1988 1989
		   struct iw_point *dwrq, char *extra)
{
1990
	struct lbs_private *priv = dev->ml_priv;
1991
	int ret = 0;
1992
	u8 ssid[IEEE80211_MAX_SSID_LEN];
1993
	u8 ssid_len = 0;
1994
	struct assoc_request * assoc_req;
1995
	int in_ssid_len = dwrq->length;
1996
	DECLARE_SSID_BUF(ssid_buf);
1997

1998
	lbs_deb_enter(LBS_DEB_WEXT);
1999

2000 2001 2002 2003 2004
	if (!priv->radio_on) {
		ret = -EINVAL;
		goto out;
	}

2005
	/* Check the size of the string */
2006
	if (in_ssid_len > IEEE80211_MAX_SSID_LEN) {
2007 2008 2009 2010
		ret = -E2BIG;
		goto out;
	}

2011
	memset(&ssid, 0, sizeof(ssid));
2012

2013
	if (!dwrq->flags || !in_ssid_len) {
2014 2015 2016
		/* "any" SSID requested; leave SSID blank */
	} else {
		/* Specific SSID requested */
2017 2018
		memcpy(&ssid, extra, in_ssid_len);
		ssid_len = in_ssid_len;
2019 2020
	}

2021 2022 2023 2024
	if (!ssid_len) {
		lbs_deb_wext("requested any SSID\n");
	} else {
		lbs_deb_wext("requested SSID '%s'\n",
2025
		             print_ssid(ssid_buf, ssid, ssid_len));
2026
	}
2027 2028

out:
2029
	mutex_lock(&priv->lock);
2030 2031
	if (ret == 0) {
		/* Get or create the current association request */
2032
		assoc_req = lbs_get_association_request(priv);
2033 2034 2035 2036
		if (!assoc_req) {
			ret = -ENOMEM;
		} else {
			/* Copy the SSID to the association request */
2037
			memcpy(&assoc_req->ssid, &ssid, IEEE80211_MAX_SSID_LEN);
2038
			assoc_req->ssid_len = ssid_len;
2039
			set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
2040
			lbs_postpone_association_work(priv);
2041 2042 2043 2044 2045
		}
	}

	/* Cancel the association request if there was an error */
	if (ret != 0) {
2046
		lbs_cancel_association_work(priv);
2047 2048
	}

2049
	mutex_unlock(&priv->lock);
2050

2051
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
2052 2053 2054
	return ret;
}

2055 2056 2057 2058
static int lbs_mesh_get_essid(struct net_device *dev,
			      struct iw_request_info *info,
			      struct iw_point *dwrq, char *extra)
{
2059
	struct lbs_private *priv = dev->ml_priv;
2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076

	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)
{
2077
	struct lbs_private *priv = dev->ml_priv;
2078 2079 2080 2081
	int ret = 0;

	lbs_deb_enter(LBS_DEB_WEXT);

2082 2083 2084 2085 2086
	if (!priv->radio_on) {
		ret = -EINVAL;
		goto out;
	}

2087
	/* Check the size of the string */
2088
	if (dwrq->length > IEEE80211_MAX_SSID_LEN) {
2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101
		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;
	}

2102
	lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
2103
			priv->channel);
2104 2105 2106 2107 2108
 out:
	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
	return ret;
}

2109 2110 2111 2112 2113 2114 2115 2116 2117
/**
 *  @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
 */
2118
static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
2119 2120
		 struct sockaddr *awrq, char *extra)
{
2121
	struct lbs_private *priv = dev->ml_priv;
2122 2123 2124
	struct assoc_request * assoc_req;
	int ret = 0;

2125
	lbs_deb_enter(LBS_DEB_WEXT);
2126

2127 2128 2129
	if (!priv->radio_on)
		return -EINVAL;

2130 2131 2132
	if (awrq->sa_family != ARPHRD_ETHER)
		return -EINVAL;

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

2135
	mutex_lock(&priv->lock);
2136 2137

	/* Get or create the current association request */
2138
	assoc_req = lbs_get_association_request(priv);
2139
	if (!assoc_req) {
2140
		lbs_cancel_association_work(priv);
2141 2142 2143 2144 2145
		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);
2146
		lbs_postpone_association_work(priv);
2147 2148
	}

2149
	mutex_unlock(&priv->lock);
2150 2151 2152 2153 2154 2155 2156

	return ret;
}

/*
 * iwconfig settable callbacks
 */
2157
static const iw_handler lbs_handler[] = {
2158
	(iw_handler) NULL,	/* SIOCSIWCOMMIT */
2159
	(iw_handler) lbs_get_name,	/* SIOCGIWNAME */
2160 2161
	(iw_handler) NULL,	/* SIOCSIWNWID */
	(iw_handler) NULL,	/* SIOCGIWNWID */
2162 2163 2164 2165
	(iw_handler) lbs_set_freq,	/* SIOCSIWFREQ */
	(iw_handler) lbs_get_freq,	/* SIOCGIWFREQ */
	(iw_handler) lbs_set_mode,	/* SIOCSIWMODE */
	(iw_handler) lbs_get_mode,	/* SIOCGIWMODE */
2166 2167 2168
	(iw_handler) NULL,	/* SIOCSIWSENS */
	(iw_handler) NULL,	/* SIOCGIWSENS */
	(iw_handler) NULL,	/* SIOCSIWRANGE */
2169
	(iw_handler) lbs_get_range,	/* SIOCGIWRANGE */
2170 2171 2172 2173 2174 2175 2176 2177
	(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 */
2178 2179
	(iw_handler) lbs_set_wap,	/* SIOCSIWAP */
	(iw_handler) lbs_get_wap,	/* SIOCGIWAP */
2180 2181
	(iw_handler) NULL,	/* SIOCSIWMLME */
	(iw_handler) NULL,	/* SIOCGIWAPLIST - deprecated */
2182 2183 2184 2185 2186 2187
	(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 */
2188 2189
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203
	(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 */
2204 2205
	(iw_handler) NULL,	/* -- hole -- */
	(iw_handler) NULL,	/* -- hole -- */
2206 2207 2208 2209 2210 2211
	(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 */
2212 2213 2214
	(iw_handler) NULL,		/* SIOCSIWPMKSA */
};

2215 2216
static const iw_handler mesh_wlan_handler[] = {
	(iw_handler) NULL,	/* SIOCSIWCOMMIT */
2217
	(iw_handler) lbs_get_name,	/* SIOCGIWNAME */
2218 2219
	(iw_handler) NULL,	/* SIOCSIWNWID */
	(iw_handler) NULL,	/* SIOCGIWNWID */
2220
	(iw_handler) lbs_mesh_set_freq,	/* SIOCSIWFREQ */
2221
	(iw_handler) lbs_get_freq,	/* SIOCGIWFREQ */
2222 2223 2224 2225 2226
	(iw_handler) NULL,		/* SIOCSIWMODE */
	(iw_handler) mesh_wlan_get_mode,	/* SIOCGIWMODE */
	(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 2236 2237 2238 2239
	(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 */
2240 2241
	(iw_handler) lbs_set_scan,	/* SIOCSIWSCAN */
	(iw_handler) lbs_get_scan,	/* SIOCGIWSCAN */
2242 2243
	(iw_handler) lbs_mesh_set_essid,/* SIOCSIWESSID */
	(iw_handler) lbs_mesh_get_essid,/* SIOCGIWESSID */
2244 2245 2246 2247
	(iw_handler) NULL,		/* SIOCSIWNICKN */
	(iw_handler) mesh_get_nick,	/* SIOCGIWNICKN */
	(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
	(iw_handler) NULL,		/* SIOCSIWPMKSA */
};
2272 2273 2274 2275
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,
2276
};
2277 2278

struct iw_handler_def mesh_handler_def = {
2279
	.num_standard	= ARRAY_SIZE(mesh_wlan_handler),
2280
	.standard	= (iw_handler *) mesh_wlan_handler,
2281
	.get_wireless_stats = lbs_get_wireless_stats,
2282
};