Enscript Output

extractedLnx/linux-2.6.38/drivers/staging/brcm80211/sys/wlc_mac80211.c__wlc_ioctl.c

static int
_wlc_ioctl(struct wlc_info *wlc, int cmd, void *arg, int len,
	   struct wlc_if *wlcif)
{
	int val, *pval;
	bool bool_val;
	int bcmerror;
	d11regs_t *regs;
	uint i;
	struct scb *nextscb;
	bool ta_ok;
	uint band;
	rw_reg_t *r;
	wlc_bsscfg_t *bsscfg;
	struct osl_info *osh;
	wlc_bss_info_t *current_bss;

	/* update bsscfg pointer */
	bsscfg = NULL;		/* XXX: Hack bsscfg to be size one and use this globally */
	current_bss = NULL;

	/* initialize the following to get rid of compiler warning */
	nextscb = NULL;
	ta_ok = false;
	band = 0;
	r = NULL;

	/* If the device is turned off, then it's not "removed" */
	if (!wlc->pub->hw_off && DEVICEREMOVED(wlc)) {
		WL_ERROR("wl%d: %s: dead chip\n", wlc->pub->unit, __func__);
		wl_down(wlc->wl);
		return BCME_ERROR;
	}

	ASSERT(!(wlc->pub->hw_off && wlc->pub->up));

	/* default argument is generic integer */
	pval = arg ? (int *)arg:NULL;

	/* This will prevent the misaligned access */
	if (pval && (u32) len >= sizeof(val))
		bcopy(pval, &val, sizeof(val));
	else
		val = 0;

	/* bool conversion to avoid duplication below */
	bool_val = val != 0;

	if (cmd != WLC_SET_CHANNEL)
		WL_NONE("WLC_IOCTL: cmd %d val 0x%x (%d) len %d\n",
			cmd, (uint)val, val, len);

	bcmerror = 0;
	regs = wlc->regs;
	osh = wlc->osh;

	/* A few commands don't need any arguments; all the others do. */
	switch (cmd) {
	case WLC_UP:
	case WLC_OUT:
	case WLC_DOWN:
	case WLC_DISASSOC:
	case WLC_RESTART:
	case WLC_REBOOT:
	case WLC_START_CHANNEL_QA:
	case WLC_INIT:
		break;

	default:
		if ((arg == NULL) || (len <= 0)) {
			WL_ERROR("wl%d: %s: Command %d needs arguments\n",
				 wlc->pub->unit, __func__, cmd);
			bcmerror = BCME_BADARG;
			goto done;
		}
	}

	switch (cmd) {

#if defined(BCMDBG)
	case WLC_GET_MSGLEVEL:
		*pval = wl_msg_level;
		break;

	case WLC_SET_MSGLEVEL:
		wl_msg_level = val;
		break;
#endif

	case WLC_GET_INSTANCE:
		*pval = wlc->pub->unit;
		break;

	case WLC_GET_CHANNEL:{
			channel_info_t *ci = (channel_info_t *) arg;

			ASSERT(len > (int)sizeof(ci));

			ci->hw_channel =
			    CHSPEC_CHANNEL(WLC_BAND_PI_RADIO_CHANSPEC);
			ci->target_channel =
			    CHSPEC_CHANNEL(wlc->default_bss->chanspec);
			ci->scan_channel = 0;

			break;
		}

	case WLC_SET_CHANNEL:{
			chanspec_t chspec = CH20MHZ_CHSPEC(val);

			if (val < 0 || val > MAXCHANNEL) {
				bcmerror = BCME_OUTOFRANGECHAN;
				break;
			}

			if (!wlc_valid_chanspec_db(wlc->cmi, chspec)) {
				bcmerror = BCME_BADCHAN;
				break;
			}

			if (!wlc->pub->up && IS_MBAND_UNLOCKED(wlc)) {
				if (wlc->band->bandunit !=
				    CHSPEC_WLCBANDUNIT(chspec))
					wlc->bandinit_pending = true;
				else
					wlc->bandinit_pending = false;
			}

			wlc->default_bss->chanspec = chspec;
			/* wlc_BSSinit() will sanitize the rateset before using it.. */
			if (wlc->pub->up && !wlc->pub->associated &&
			    (WLC_BAND_PI_RADIO_CHANSPEC != chspec)) {
				wlc_set_home_chanspec(wlc, chspec);
				wlc_suspend_mac_and_wait(wlc);
				wlc_set_chanspec(wlc, chspec);
				wlc_enable_mac(wlc);
			}
			break;
		}

#if defined(BCMDBG)
	case WLC_GET_UCFLAGS:
		if (!wlc->pub->up) {
			bcmerror = BCME_NOTUP;
			break;
		}

		/* optional band is stored in the second integer of incoming buffer */
		band =
		    (len <
		     (int)(2 * sizeof(int))) ? WLC_BAND_AUTO : ((int *)arg)[1];

		/* bcmerror checking */
		bcmerror = wlc_iocregchk(wlc, band);
		if (bcmerror)
			break;

		if (val >= MHFMAX) {
			bcmerror = BCME_RANGE;
			break;
		}

		*pval = wlc_bmac_mhf_get(wlc->hw, (u8) val, WLC_BAND_AUTO);
		break;

	case WLC_SET_UCFLAGS:
		if (!wlc->pub->up) {
			bcmerror = BCME_NOTUP;
			break;
		}

		/* optional band is stored in the second integer of incoming buffer */
		band =
		    (len <
		     (int)(2 * sizeof(int))) ? WLC_BAND_AUTO : ((int *)arg)[1];

		/* bcmerror checking */
		bcmerror = wlc_iocregchk(wlc, band);
		if (bcmerror)
			break;

		i = (u16) val;
		if (i >= MHFMAX) {
			bcmerror = BCME_RANGE;
			break;
		}

		wlc_mhf(wlc, (u8) i, 0xffff, (u16) (val >> NBITS(u16)),
			WLC_BAND_AUTO);
		break;

	case WLC_GET_SHMEM:
		ta_ok = true;

		/* optional band is stored in the second integer of incoming buffer */
		band =
		    (len <
		     (int)(2 * sizeof(int))) ? WLC_BAND_AUTO : ((int *)arg)[1];

		/* bcmerror checking */
		bcmerror = wlc_iocregchk(wlc, band);
		if (bcmerror)
			break;

		if (val & 1) {
			bcmerror = BCME_BADADDR;
			break;
		}

		*pval = wlc_read_shm(wlc, (u16) val);
		break;

	case WLC_SET_SHMEM:
		ta_ok = true;

		/* optional band is stored in the second integer of incoming buffer */
		band =
		    (len <
		     (int)(2 * sizeof(int))) ? WLC_BAND_AUTO : ((int *)arg)[1];

		/* bcmerror checking */
		bcmerror = wlc_iocregchk(wlc, band);
		if (bcmerror)
			break;

		if (val & 1) {
			bcmerror = BCME_BADADDR;
			break;
		}

		wlc_write_shm(wlc, (u16) val,
			      (u16) (val >> NBITS(u16)));
		break;

	case WLC_R_REG:	/* MAC registers */
		ta_ok = true;
		r = (rw_reg_t *) arg;
		band = WLC_BAND_AUTO;

		if (len < (int)(sizeof(rw_reg_t) - sizeof(uint))) {
			bcmerror = BCME_BUFTOOSHORT;
			break;
		}

		if (len >= (int)sizeof(rw_reg_t))
			band = r->band;

		/* bcmerror checking */
		bcmerror = wlc_iocregchk(wlc, band);
		if (bcmerror)
			break;

		if ((r->byteoff + r->size) > sizeof(d11regs_t)) {
			bcmerror = BCME_BADADDR;
			break;
		}
		if (r->size == sizeof(u32))
			r->val =
			    R_REG(osh,
				  (u32 *)((unsigned char *)(unsigned long)regs +
					      r->byteoff));
		else if (r->size == sizeof(u16))
			r->val =
			    R_REG(osh,
				  (u16 *)((unsigned char *)(unsigned long)regs +
					      r->byteoff));
		else
			bcmerror = BCME_BADADDR;
		break;

	case WLC_W_REG:
		ta_ok = true;
		r = (rw_reg_t *) arg;
		band = WLC_BAND_AUTO;

		if (len < (int)(sizeof(rw_reg_t) - sizeof(uint))) {
			bcmerror = BCME_BUFTOOSHORT;
			break;
		}

		if (len >= (int)sizeof(rw_reg_t))
			band = r->band;

		/* bcmerror checking */
		bcmerror = wlc_iocregchk(wlc, band);
		if (bcmerror)
			break;

		if (r->byteoff + r->size > sizeof(d11regs_t)) {
			bcmerror = BCME_BADADDR;
			break;
		}
		if (r->size == sizeof(u32))
			W_REG(osh,
			      (u32 *)((unsigned char *)(unsigned long) regs +
					  r->byteoff), r->val);
		else if (r->size == sizeof(u16))
			W_REG(osh,
			      (u16 *)((unsigned char *)(unsigned long) regs +
					  r->byteoff), r->val);
		else
			bcmerror = BCME_BADADDR;
		break;
#endif				/* BCMDBG */

	case WLC_GET_TXANT:
		*pval = wlc->stf->txant;
		break;

	case WLC_SET_TXANT:
		bcmerror = wlc_stf_ant_txant_validate(wlc, (s8) val);
		if (bcmerror < 0)
			break;

		wlc->stf->txant = (s8) val;

		/* if down, we are done */
		if (!wlc->pub->up)
			break;

		wlc_suspend_mac_and_wait(wlc);

		wlc_stf_phy_txant_upd(wlc);
		wlc_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec);

		wlc_enable_mac(wlc);

		break;

	case WLC_GET_ANTDIV:{
			u8 phy_antdiv;

			/* return configured value if core is down */
			if (!wlc->pub->up) {
				*pval = wlc->stf->ant_rx_ovr;

			} else {
				if (wlc_phy_ant_rxdiv_get
				    (wlc->band->pi, &phy_antdiv))
					*pval = (int)phy_antdiv;
				else
					*pval = (int)wlc->stf->ant_rx_ovr;
			}

			break;
		}
	case WLC_SET_ANTDIV:
		/* values are -1=driver default, 0=force0, 1=force1, 2=start1, 3=start0 */
		if ((val < -1) || (val > 3)) {
			bcmerror = BCME_RANGE;
			break;
		}

		if (val == -1)
			val = ANT_RX_DIV_DEF;

		wlc->stf->ant_rx_ovr = (u8) val;
		wlc_phy_ant_rxdiv_set(wlc->band->pi, (u8) val);
		break;

	case WLC_GET_RX_ANT:{	/* get latest used rx antenna */
			u16 rxstatus;

			if (!wlc->pub->up) {
				bcmerror = BCME_NOTUP;
				break;
			}

			rxstatus = R_REG(wlc->osh, &wlc->regs->phyrxstatus0);
			if (rxstatus == 0xdead || rxstatus == (u16) -1) {
				bcmerror = BCME_ERROR;
				break;
			}
			*pval = (rxstatus & PRXS0_RXANT_UPSUBBAND) ? 1 : 0;
			break;
		}

#if defined(BCMDBG)
	case WLC_GET_UCANTDIV:
		if (!wlc->clk) {
			bcmerror = BCME_NOCLK;
			break;
		}

		*pval =
		    (wlc_bmac_mhf_get(wlc->hw, MHF1, WLC_BAND_AUTO) &
		     MHF1_ANTDIV);
		break;

	case WLC_SET_UCANTDIV:{
			if (!wlc->pub->up) {
				bcmerror = BCME_NOTUP;
				break;
			}

			/* if multiband, band must be locked */
			if (IS_MBAND_UNLOCKED(wlc)) {
				bcmerror = BCME_NOTBANDLOCKED;
				break;
			}

			/* 4322 supports antdiv in phy, no need to set it to ucode */
			if (WLCISNPHY(wlc->band)
			    && D11REV_IS(wlc->pub->corerev, 16)) {
				WL_ERROR("wl%d: can't set ucantdiv for 4322\n",
					 wlc->pub->unit);
				bcmerror = BCME_UNSUPPORTED;
			} else
				wlc_mhf(wlc, MHF1, MHF1_ANTDIV,
					(val ? MHF1_ANTDIV : 0), WLC_BAND_AUTO);
			break;
		}
#endif				/* defined(BCMDBG) */

	case WLC_GET_SRL:
		*pval = wlc->SRL;
		break;

	case WLC_SET_SRL:
		if (val >= 1 && val <= RETRY_SHORT_MAX) {
			int ac;
			wlc->SRL = (u16) val;

			wlc_bmac_retrylimit_upd(wlc->hw, wlc->SRL, wlc->LRL);

			for (ac = 0; ac < AC_COUNT; ac++) {
				WLC_WME_RETRY_SHORT_SET(wlc, ac, wlc->SRL);
			}
			wlc_wme_retries_write(wlc);
		} else
			bcmerror = BCME_RANGE;
		break;

	case WLC_GET_LRL:
		*pval = wlc->LRL;
		break;

	case WLC_SET_LRL:
		if (val >= 1 && val <= 255) {
			int ac;
			wlc->LRL = (u16) val;

			wlc_bmac_retrylimit_upd(wlc->hw, wlc->SRL, wlc->LRL);

			for (ac = 0; ac < AC_COUNT; ac++) {
				WLC_WME_RETRY_LONG_SET(wlc, ac, wlc->LRL);
			}
			wlc_wme_retries_write(wlc);
		} else
			bcmerror = BCME_RANGE;
		break;

	case WLC_GET_CWMIN:
		*pval = wlc->band->CWmin;
		break;

	case WLC_SET_CWMIN:
		if (!wlc->clk) {
			bcmerror = BCME_NOCLK;
			break;
		}

		if (val >= 1 && val <= 255) {
			wlc_set_cwmin(wlc, (u16) val);
		} else
			bcmerror = BCME_RANGE;
		break;

	case WLC_GET_CWMAX:
		*pval = wlc->band->CWmax;
		break;

	case WLC_SET_CWMAX:
		if (!wlc->clk) {
			bcmerror = BCME_NOCLK;
			break;
		}

		if (val >= 255 && val <= 2047) {
			wlc_set_cwmax(wlc, (u16) val);
		} else
			bcmerror = BCME_RANGE;
		break;

	case WLC_GET_RADIO:	/* use mask if don't want to expose some internal bits */
		*pval = wlc->pub->radio_disabled;
		break;

	case WLC_SET_RADIO:{	/* 32 bits input, higher 16 bits are mask, lower 16 bits are value to
				 * set
				 */
			u16 radiomask, radioval;
			uint validbits =
			    WL_RADIO_SW_DISABLE | WL_RADIO_HW_DISABLE;
			mbool new = 0;

			radiomask = (val & 0xffff0000) >> 16;
			radioval = val & 0x0000ffff;

			if ((radiomask == 0) || (radiomask & ~validbits)
			    || (radioval & ~validbits)
			    || ((radioval & ~radiomask) != 0)) {
				WL_ERROR("SET_RADIO with wrong bits 0x%x\n",
					 val);
				bcmerror = BCME_RANGE;
				break;
			}

			new =
			    (wlc->pub->radio_disabled & ~radiomask) | radioval;
			wlc->pub->radio_disabled = new;

			wlc_radio_hwdisable_upd(wlc);
			wlc_radio_upd(wlc);
			break;
		}

	case WLC_GET_PHYTYPE:
		*pval = WLC_PHYTYPE(wlc->band->phytype);
		break;

#if defined(BCMDBG)
	case WLC_GET_KEY:
		if ((val >= 0) && (val < WLC_MAX_WSEC_KEYS(wlc))) {
			wl_wsec_key_t key;

			wsec_key_t *src_key = wlc->wsec_keys[val];

			if (len < (int)sizeof(key)) {
				bcmerror = BCME_BUFTOOSHORT;
				break;
			}

			memset((char *)&key, 0, sizeof(key));
			if (src_key) {
				key.index = src_key->id;
				key.len = src_key->len;
				bcopy(src_key->data, key.data, key.len);
				key.algo = src_key->algo;
				if (WSEC_SOFTKEY(wlc, src_key, bsscfg))
					key.flags |= WL_SOFT_KEY;
				if (src_key->flags & WSEC_PRIMARY_KEY)
					key.flags |= WL_PRIMARY_KEY;

				bcopy(src_key->ea.octet, key.ea.octet,
				      ETH_ALEN);
			}

			bcopy((char *)&key, arg, sizeof(key));
		} else
			bcmerror = BCME_BADKEYIDX;
		break;
#endif				/* defined(BCMDBG) */

	case WLC_SET_KEY:
		bcmerror =
		    wlc_iovar_op(wlc, "wsec_key", NULL, 0, arg, len, IOV_SET,
				 wlcif);
		break;

	case WLC_GET_KEY_SEQ:{
			wsec_key_t *key;

			if (len < DOT11_WPA_KEY_RSC_LEN) {
				bcmerror = BCME_BUFTOOSHORT;
				break;
			}

			/* Return the key's tx iv as an EAPOL sequence counter.
			 * This will be used to supply the RSC value to a supplicant.
			 * The format is 8 bytes, with least significant in seq[0].
			 */

			key = WSEC_KEY(wlc, val);
			if ((val >= 0) && (val < WLC_MAX_WSEC_KEYS(wlc)) &&
				(key != NULL)) {
				u8 seq[DOT11_WPA_KEY_RSC_LEN];
				u16 lo;
				u32 hi;
				/* group keys in WPA-NONE (IBSS only, AES and TKIP) use a global TXIV */
				if ((bsscfg->WPA_auth & WPA_AUTH_NONE) &&
				    is_zero_ether_addr(key->ea.octet)) {
					lo = bsscfg->wpa_none_txiv.lo;
					hi = bsscfg->wpa_none_txiv.hi;
				} else {
					lo = key->txiv.lo;
					hi = key->txiv.hi;
				}

				/* format the buffer, low to high */
				seq[0] = lo & 0xff;
				seq[1] = (lo >> 8) & 0xff;
				seq[2] = hi & 0xff;
				seq[3] = (hi >> 8) & 0xff;
				seq[4] = (hi >> 16) & 0xff;
				seq[5] = (hi >> 24) & 0xff;
				seq[6] = 0;
				seq[7] = 0;

				bcopy((char *)seq, arg, sizeof(seq));
			} else {
				bcmerror = BCME_BADKEYIDX;
			}
			break;
		}

	case WLC_GET_CURR_RATESET:{
			wl_rateset_t *ret_rs = (wl_rateset_t *) arg;
			wlc_rateset_t *rs;

			if (bsscfg->associated)
				rs = &current_bss->rateset;
			else
				rs = &wlc->default_bss->rateset;

			if (len < (int)(rs->count + sizeof(rs->count))) {
				bcmerror = BCME_BUFTOOSHORT;
				break;
			}

			/* Copy only legacy rateset section */
			ret_rs->count = rs->count;
			bcopy(&rs->rates, &ret_rs->rates, rs->count);
			break;
		}

	case WLC_GET_RATESET:{
			wlc_rateset_t rs;
			wl_rateset_t *ret_rs = (wl_rateset_t *) arg;

			memset(&rs, 0, sizeof(wlc_rateset_t));
			wlc_default_rateset(wlc, (wlc_rateset_t *) &rs);

			if (len < (int)(rs.count + sizeof(rs.count))) {
				bcmerror = BCME_BUFTOOSHORT;
				break;
			}

			/* Copy only legacy rateset section */
			ret_rs->count = rs.count;
			bcopy(&rs.rates, &ret_rs->rates, rs.count);
			break;
		}

	case WLC_SET_RATESET:{
			wlc_rateset_t rs;
			wl_rateset_t *in_rs = (wl_rateset_t *) arg;

			if (len < (int)(in_rs->count + sizeof(in_rs->count))) {
				bcmerror = BCME_BUFTOOSHORT;
				break;
			}

			if (in_rs->count > WLC_NUMRATES) {
				bcmerror = BCME_BUFTOOLONG;
				break;
			}

			memset(&rs, 0, sizeof(wlc_rateset_t));

			/* Copy only legacy rateset section */
			rs.count = in_rs->count;
			bcopy(&in_rs->rates, &rs.rates, rs.count);

			/* merge rateset coming in with the current mcsset */
			if (N_ENAB(wlc->pub)) {
				if (bsscfg->associated)
					bcopy(&current_bss->rateset.mcs[0],
					      rs.mcs, MCSSET_LEN);
				else
					bcopy(&wlc->default_bss->rateset.mcs[0],
					      rs.mcs, MCSSET_LEN);
			}

			bcmerror = wlc_set_rateset(wlc, &rs);

			if (!bcmerror)
				wlc_ofdm_rateset_war(wlc);

			break;
		}

	case WLC_GET_BCNPRD:
		if (BSSCFG_STA(bsscfg) && bsscfg->BSS && bsscfg->associated)
			*pval = current_bss->beacon_period;
		else
			*pval = wlc->default_bss->beacon_period;
		break;

	case WLC_SET_BCNPRD:
		/* range [1, 0xffff] */
		if (val >= DOT11_MIN_BEACON_PERIOD
		    && val <= DOT11_MAX_BEACON_PERIOD) {
			wlc->default_bss->beacon_period = (u16) val;
		} else
			bcmerror = BCME_RANGE;
		break;

	case WLC_GET_DTIMPRD:
		if (BSSCFG_STA(bsscfg) && bsscfg->BSS && bsscfg->associated)
			*pval = current_bss->dtim_period;
		else
			*pval = wlc->default_bss->dtim_period;
		break;

	case WLC_SET_DTIMPRD:
		/* range [1, 0xff] */
		if (val >= DOT11_MIN_DTIM_PERIOD
		    && val <= DOT11_MAX_DTIM_PERIOD) {
			wlc->default_bss->dtim_period = (u8) val;
		} else
			bcmerror = BCME_RANGE;
		break;

#ifdef SUPPORT_PS
	case WLC_GET_PM:
		*pval = wlc->PM;
		break;

	case WLC_SET_PM:
		if ((val >= PM_OFF) && (val <= PM_MAX)) {
			wlc->PM = (u8) val;
			if (wlc->pub->up) {
			}
			/* Change watchdog driver to align watchdog with tbtt if possible */
			wlc_watchdog_upd(wlc, PS_ALLOWED(wlc));
		} else
			bcmerror = BCME_ERROR;
		break;
#endif				/* SUPPORT_PS */

#ifdef SUPPORT_PS
#ifdef BCMDBG
	case WLC_GET_WAKE:
		if (AP_ENAB(wlc->pub)) {
			bcmerror = BCME_NOTSTA;
			break;
		}
		*pval = wlc->wake;
		break;

	case WLC_SET_WAKE:
		if (AP_ENAB(wlc->pub)) {
			bcmerror = BCME_NOTSTA;
			break;
		}

		wlc->wake = val ? true : false;

		/* if down, we're done */
		if (!wlc->pub->up)
			break;

		/* apply to the mac */
		wlc_set_ps_ctrl(wlc);
		break;
#endif				/* BCMDBG */
#endif				/* SUPPORT_PS */

	case WLC_GET_REVINFO:
		bcmerror = wlc_get_revision_info(wlc, arg, (uint) len);
		break;

	case WLC_GET_AP:
		*pval = (int)AP_ENAB(wlc->pub);
		break;

	case WLC_GET_ATIM:
		if (bsscfg->associated)
			*pval = (int)current_bss->atim_window;
		else
			*pval = (int)wlc->default_bss->atim_window;
		break;

	case WLC_SET_ATIM:
		wlc->default_bss->atim_window = (u32) val;
		break;

	case WLC_GET_PKTCNTS:{
			get_pktcnt_t *pktcnt = (get_pktcnt_t *) pval;
			if (WLC_UPDATE_STATS(wlc))
				wlc_statsupd(wlc);
			pktcnt->rx_good_pkt = WLCNTVAL(wlc->pub->_cnt->rxframe);
			pktcnt->rx_bad_pkt = WLCNTVAL(wlc->pub->_cnt->rxerror);
			pktcnt->tx_good_pkt =
			    WLCNTVAL(wlc->pub->_cnt->txfrmsnt);
			pktcnt->tx_bad_pkt =
			    WLCNTVAL(wlc->pub->_cnt->txerror) +
			    WLCNTVAL(wlc->pub->_cnt->txfail);
			if (len >= (int)sizeof(get_pktcnt_t)) {
				/* Be backward compatible - only if buffer is large enough  */
				pktcnt->rx_ocast_good_pkt =
				    WLCNTVAL(wlc->pub->_cnt->rxmfrmocast);
			}
			break;
		}

#ifdef SUPPORT_HWKEY
	case WLC_GET_WSEC:
		bcmerror =
		    wlc_iovar_op(wlc, "wsec", NULL, 0, arg, len, IOV_GET,
				 wlcif);
		break;

	case WLC_SET_WSEC:
		bcmerror =
		    wlc_iovar_op(wlc, "wsec", NULL, 0, arg, len, IOV_SET,
				 wlcif);
		break;

	case WLC_GET_WPA_AUTH:
		*pval = (int)bsscfg->WPA_auth;
		break;

	case WLC_SET_WPA_AUTH:
		/* change of WPA_Auth modifies the PS_ALLOWED state */
		if (BSSCFG_STA(bsscfg)) {
			bsscfg->WPA_auth = (u16) val;
		} else
			bsscfg->WPA_auth = (u16) val;
		break;
#endif				/* SUPPORT_HWKEY */

	case WLC_GET_BANDLIST:
		/* count of number of bands, followed by each band type */
		*pval++ = NBANDS(wlc);
		*pval++ = wlc->band->bandtype;
		if (NBANDS(wlc) > 1)
			*pval++ = wlc->bandstate[OTHERBANDUNIT(wlc)]->bandtype;
		break;

	case WLC_GET_BAND:
		*pval = wlc->bandlocked ? wlc->band->bandtype : WLC_BAND_AUTO;
		break;

	case WLC_GET_PHYLIST:
		{
			unsigned char *cp = arg;
			if (len < 3) {
				bcmerror = BCME_BUFTOOSHORT;
				break;
			}

			if (WLCISNPHY(wlc->band)) {
				*cp++ = 'n';
			} else if (WLCISLCNPHY(wlc->band)) {
				*cp++ = 'c';
			} else if (WLCISSSLPNPHY(wlc->band)) {
				*cp++ = 's';
			}
			*cp = '\0';
			break;
		}

	case WLC_GET_SHORTSLOT:
		*pval = wlc->shortslot;
		break;

	case WLC_GET_SHORTSLOT_OVERRIDE:
		*pval = wlc->shortslot_override;
		break;

	case WLC_SET_SHORTSLOT_OVERRIDE:
		if ((val != WLC_SHORTSLOT_AUTO) &&
		    (val != WLC_SHORTSLOT_OFF) && (val != WLC_SHORTSLOT_ON)) {
			bcmerror = BCME_RANGE;
			break;
		}

		wlc->shortslot_override = (s8) val;

		/* shortslot is an 11g feature, so no more work if we are
		 * currently on the 5G band
		 */
		if (BAND_5G(wlc->band->bandtype))
			break;

		if (wlc->pub->up && wlc->pub->associated) {
			/* let watchdog or beacon processing update shortslot */
		} else if (wlc->pub->up) {
			/* unassociated shortslot is off */
			wlc_switch_shortslot(wlc, false);
		} else {
			/* driver is down, so just update the wlc_info value */
			if (wlc->shortslot_override == WLC_SHORTSLOT_AUTO) {
				wlc->shortslot = false;
			} else {
				wlc->shortslot =
				    (wlc->shortslot_override ==
				     WLC_SHORTSLOT_ON);
			}
		}

		break;

	case WLC_GET_LEGACY_ERP:
		*pval = wlc->include_legacy_erp;
		break;

	case WLC_SET_LEGACY_ERP:
		if (wlc->include_legacy_erp == bool_val)
			break;

		wlc->include_legacy_erp = bool_val;

		if (AP_ENAB(wlc->pub) && wlc->clk) {
			wlc_update_beacon(wlc);
			wlc_update_probe_resp(wlc, true);
		}
		break;

	case WLC_GET_GMODE:
		if (wlc->band->bandtype == WLC_BAND_2G)
			*pval = wlc->band->gmode;
		else if (NBANDS(wlc) > 1)
			*pval = wlc->bandstate[OTHERBANDUNIT(wlc)]->gmode;
		break;

	case WLC_SET_GMODE:
		if (!wlc->pub->associated)
			bcmerror = wlc_set_gmode(wlc, (u8) val, true);
		else {
			bcmerror = BCME_ASSOCIATED;
			break;
		}
		break;

	case WLC_GET_GMODE_PROTECTION:
		*pval = wlc->protection->_g;
		break;

	case WLC_GET_PROTECTION_CONTROL:
		*pval = wlc->protection->overlap;
		break;

	case WLC_SET_PROTECTION_CONTROL:
		if ((val != WLC_PROTECTION_CTL_OFF) &&
		    (val != WLC_PROTECTION_CTL_LOCAL) &&
		    (val != WLC_PROTECTION_CTL_OVERLAP)) {
			bcmerror = BCME_RANGE;
			break;
		}

		wlc_protection_upd(wlc, WLC_PROT_OVERLAP, (s8) val);

		/* Current g_protection will sync up to the specified control alg in watchdog
		 * if the driver is up and associated.
		 * If the driver is down or not associated, the control setting has no effect.
		 */
		break;

	case WLC_GET_GMODE_PROTECTION_OVERRIDE:
		*pval = wlc->protection->g_override;
		break;

	case WLC_SET_GMODE_PROTECTION_OVERRIDE:
		if ((val != WLC_PROTECTION_AUTO) &&
		    (val != WLC_PROTECTION_OFF) && (val != WLC_PROTECTION_ON)) {
			bcmerror = BCME_RANGE;
			break;
		}

		wlc_protection_upd(wlc, WLC_PROT_G_OVR, (s8) val);

		break;

	case WLC_SET_SUP_RATESET_OVERRIDE:{
			wlc_rateset_t rs, new;

			/* copyin */
			if (len < (int)sizeof(wlc_rateset_t)) {
				bcmerror = BCME_BUFTOOSHORT;
				break;
			}
			bcopy((char *)arg, (char *)&rs, sizeof(wlc_rateset_t));

			/* check for bad count value */
			if (rs.count > WLC_NUMRATES) {
				bcmerror = BCME_BADRATESET;	/* invalid rateset */
				break;
			}

			/* this command is only appropriate for gmode operation */
			if (!(wlc->band->gmode ||
			      ((NBANDS(wlc) > 1)
			       && wlc->bandstate[OTHERBANDUNIT(wlc)]->gmode))) {
				bcmerror = BCME_BADBAND;	/* gmode only command when not in gmode */
				break;
			}

			/* check for an empty rateset to clear the override */
			if (rs.count == 0) {
				memset(&wlc->sup_rates_override, 0,
				      sizeof(wlc_rateset_t));
				break;
			}

			/* validate rateset by comparing pre and post sorted against 11g hw rates */
			wlc_rateset_filter(&rs, &new, false, WLC_RATES_CCK_OFDM,
					   RATE_MASK, BSS_N_ENAB(wlc, bsscfg));
			wlc_rate_hwrs_filter_sort_validate(&new,
							   &cck_ofdm_rates,
							   false,
							   wlc->stf->txstreams);
			if (rs.count != new.count) {
				bcmerror = BCME_BADRATESET;	/* invalid rateset */
				break;
			}

			/* apply new rateset to the override */
			bcopy((char *)&new, (char *)&wlc->sup_rates_override,
			      sizeof(wlc_rateset_t));

			/* update bcn and probe resp if needed */
			if (wlc->pub->up && AP_ENAB(wlc->pub)
			    && wlc->pub->associated) {
				wlc_update_beacon(wlc);
				wlc_update_probe_resp(wlc, true);
			}
			break;
		}

	case WLC_GET_SUP_RATESET_OVERRIDE:
		/* this command is only appropriate for gmode operation */
		if (!(wlc->band->gmode ||
		      ((NBANDS(wlc) > 1)
		       && wlc->bandstate[OTHERBANDUNIT(wlc)]->gmode))) {
			bcmerror = BCME_BADBAND;	/* gmode only command when not in gmode */
			break;
		}
		if (len < (int)sizeof(wlc_rateset_t)) {
			bcmerror = BCME_BUFTOOSHORT;
			break;
		}
		bcopy((char *)&wlc->sup_rates_override, (char *)arg,
		      sizeof(wlc_rateset_t));

		break;

	case WLC_GET_PRB_RESP_TIMEOUT:
		*pval = wlc->prb_resp_timeout;
		break;

	case WLC_SET_PRB_RESP_TIMEOUT:
		if (wlc->pub->up) {
			bcmerror = BCME_NOTDOWN;
			break;
		}
		if (val < 0 || val >= 0xFFFF) {
			bcmerror = BCME_RANGE;	/* bad value */
			break;
		}
		wlc->prb_resp_timeout = (u16) val;
		break;

	case WLC_GET_KEY_PRIMARY:{
			wsec_key_t *key;

			/* treat the 'val' parm as the key id */
			key = WSEC_BSS_DEFAULT_KEY(bsscfg);
			if (key != NULL) {
				*pval = key->id == val ? true : false;
			} else {
				bcmerror = BCME_BADKEYIDX;
			}
			break;
		}

	case WLC_SET_KEY_PRIMARY:{
			wsec_key_t *key, *old_key;

			bcmerror = BCME_BADKEYIDX;

			/* treat the 'val' parm as the key id */
			for (i = 0; i < WSEC_MAX_DEFAULT_KEYS; i++) {
				key = bsscfg->bss_def_keys[i];
				if (key != NULL && key->id == val) {
					old_key = WSEC_BSS_DEFAULT_KEY(bsscfg);
					if (old_key != NULL)
						old_key->flags &=
						    ~WSEC_PRIMARY_KEY;
					key->flags |= WSEC_PRIMARY_KEY;
					bsscfg->wsec_index = i;
					bcmerror = BCME_OK;
				}
			}
			break;
		}

#ifdef BCMDBG
	case WLC_INIT:
		wl_init(wlc->wl);
		break;
#endif

	case WLC_SET_VAR:
	case WLC_GET_VAR:{
			char *name;
			/* validate the name value */
			name = (char *)arg;
			for (i = 0; i < (uint) len && *name != '\0';
			     i++, name++)
				;

			if (i == (uint) len) {
				bcmerror = BCME_BUFTOOSHORT;
				break;
			}
			i++;	/* include the null in the string length */

			if (cmd == WLC_GET_VAR) {
				bcmerror =
				    wlc_iovar_op(wlc, arg,
						 (void *)((s8 *) arg + i),
						 len - i, arg, len, IOV_GET,
						 wlcif);
			} else
				bcmerror =
				    wlc_iovar_op(wlc, arg, NULL, 0,
						 (void *)((s8 *) arg + i),
						 len - i, IOV_SET, wlcif);

			break;
		}

	case WLC_SET_WSEC_PMK:
		bcmerror = BCME_UNSUPPORTED;
		break;

#if defined(BCMDBG)
	case WLC_CURRENT_PWR:
		if (!wlc->pub->up)
			bcmerror = BCME_NOTUP;
		else
			bcmerror = wlc_get_current_txpwr(wlc, arg, len);
		break;
#endif

	case WLC_LAST:
		WL_ERROR("%s: WLC_LAST\n", __func__);
	}
 done:

	if (bcmerror) {
		if (VALID_BCMERROR(bcmerror))
			wlc->pub->bcmerror = bcmerror;
		else {
			bcmerror = 0;
		}

	}
	/* BMAC_NOTE: for HIGH_ONLY driver, this seems being called after RPC bus failed */
	/* In hw_off condition, IOCTLs that reach here are deemed safe but taclear would
	 * certainly result in getting -1 for register reads. So skip ta_clear altogether
	 */
	if (!(wlc->pub->hw_off))
		ASSERT(wlc_bmac_taclear(wlc->hw, ta_ok) || !ta_ok);

	return bcmerror;
}

Generated by GNU enscript 1.6.4.