diff options
author | adrian <adrian@FreeBSD.org> | 2015-08-23 01:17:52 +0000 |
---|---|---|
committer | adrian <adrian@FreeBSD.org> | 2015-08-23 01:17:52 +0000 |
commit | 2af8300f0a0bd96ae7c076d410b2e6e808060d5a (patch) | |
tree | 01064c872b4c5dba07790a3b4d62db81faec81ab /sys/net80211 | |
parent | 6129bf3e3a3f2de61c8d5f0f59b95cf9d3096dd4 (diff) | |
download | FreeBSD-src-2af8300f0a0bd96ae7c076d410b2e6e808060d5a.zip FreeBSD-src-2af8300f0a0bd96ae7c076d410b2e6e808060d5a.tar.gz |
Reset the channel to the first available channel if the interface
is configured on a channel that isn't valid in the new operating mode.
This isn't strictly true - it should find the first channel that is
available for the given operating mode.
However, I think defaulting to the first channel is fine - it's typically
available for all modes.
If someone would like to correctly implement this feature - try to
find a channel that is valid for the given operating mode and error
out if we can't find one.
This prevents various NICs (eg wpi(4)) from throwing a firmware error.
Tested:
* ath(4), STA/AP mode
* iwn(4), STA/adhoc mode
PR: kern/202502
Submitted by: Andriy Voskoboinyk <s3erios@gmail.com>
Diffstat (limited to 'sys/net80211')
-rw-r--r-- | sys/net80211/ieee80211_proto.c | 40 |
1 files changed, 40 insertions, 0 deletions
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 01c60e1..548daca 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -1215,6 +1215,41 @@ ieee80211_waitfor_parent(struct ieee80211com *ic) } /* + * Check to see whether the current channel needs reset. + * + * Some devices don't handle being given an invalid channel + * in their operating mode very well (eg wpi(4) will throw a + * firmware exception.) + * + * Return 0 if we're ok, 1 if the channel needs to be reset. + * + * See PR kern/202502. + */ +static int +ieee80211_start_check_reset_chan(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + if ((vap->iv_opmode == IEEE80211_M_IBSS && + IEEE80211_IS_CHAN_NOADHOC(ic->ic_curchan)) || + (vap->iv_opmode == IEEE80211_M_HOSTAP && + IEEE80211_IS_CHAN_NOHOSTAP(ic->ic_curchan))) + return (1); + return (0); +} + +/* + * Reset the curchan to a known good state. + */ +static void +ieee80211_start_reset_chan(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + ic->ic_curchan = &ic->ic_channels[0]; +} + +/* * Start a vap running. If this is the first vap to be * set running on the underlying device then we * automatically bring the device up. @@ -1248,6 +1283,11 @@ ieee80211_start_locked(struct ieee80211vap *vap) */ if (ic->ic_nrunning++ == 0 && (parent->if_drv_flags & IFF_DRV_RUNNING) == 0) { + + /* reset the channel to a known good channel */ + if (ieee80211_start_check_reset_chan(vap)) + ieee80211_start_reset_chan(vap); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, "%s: up parent %s\n", __func__, parent->if_xname); |