static int
ieee80211_ioctl_siwfreq(struct net_device *dev, struct iw_request_info *info,
struct iw_freq *freq, char *extra)
{
struct ieee80211vap *vap = dev->priv;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c, *c2;
int i;
if (freq->e > 1)
return -EINVAL;
if (freq->e == 1)
i = (ic->ic_mhz2ieee)(ic, freq->m / 100000, 0);
else
i = freq->m;
if (i != 0) {
if (i > IEEE80211_CHAN_MAX)
return -EINVAL;
c = findchannel(ic, i, vap->iv_des_mode);
if (c == NULL) {
c = findchannel(ic, i, IEEE80211_MODE_AUTO);
if (c == NULL) /* no channel */
return -EINVAL;
}
/*
* Fine tune channel selection based on desired mode:
* if 11b is requested, find the 11b version of any
* 11g channel returned,
* if static turbo, find the turbo version of any
* 11a channel return,
* otherwise we should be ok with what we've got.
*/
switch (vap->iv_des_mode) {
case IEEE80211_MODE_11B:
if (IEEE80211_IS_CHAN_ANYG(c)) {
c2 = findchannel(ic, i, IEEE80211_MODE_11B);
/* NB: should not happen, =>'s 11g w/o 11b */
if (c2 != NULL)
c = c2;
}
break;
case IEEE80211_MODE_TURBO_A:
if (IEEE80211_IS_CHAN_A(c)) {
c2 = findchannel(ic, i, IEEE80211_MODE_TURBO_A);
if (c2 != NULL)
c = c2;
}
break;
default: /* NB: no static turboG */
break;
}
if (ieee80211_check_mode_consistency(ic,vap->iv_des_mode,c)) {
if (vap->iv_opmode == IEEE80211_M_HOSTAP)
return -EINVAL;
}
if (vap->iv_state == IEEE80211_S_RUN && c == ic->ic_bsschan)
return 0; /* no change, return */
/* Don't allow to change to channel with radar found */
if (c->ic_flags & IEEE80211_CHAN_RADAR)
return -EINVAL;
/*
* Mark desired channel and if running force a
* radio change.
*/
vap->iv_des_chan = c;
} else {
/*
* Intepret channel 0 to mean "no desired channel";
* otherwise there's no way to undo fixing the desired
* channel.
*/
if (vap->iv_des_chan == IEEE80211_CHAN_ANYC)
return 0;
vap->iv_des_chan = IEEE80211_CHAN_ANYC;
}
#if 0
if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
int mode = ieee80211_chan2mode(vap->iv_des_chan);
if (mode != ic->ic_curmode)
ieee80211_setmode(ic, mode);
}
#endif
if ((vap->iv_opmode == IEEE80211_M_MONITOR ||
vap->iv_opmode == IEEE80211_M_WDS) &&
vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
/*
* Monitor and wds modes can switch directly.
*/
ic->ic_curchan = vap->iv_des_chan;
if (vap->iv_state == IEEE80211_S_RUN) {
ic->ic_set_channel(ic);
}
} else {
/*
* Need to go through the state machine in case we need
* to reassociate or the like. The state machine will
* pickup the desired channel and avoid scanning.
*/
if (IS_UP_AUTO(vap))
ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
}
return 0;
}