Merge madwifi-dfs back to trunk: this change is only about DFS, excluding

DFS pulse analysis. Untested.


git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@4076 0192ed92-7a03-0410-a25b-9323aeb14dbd
This commit is contained in:
benoit 2009-07-11 17:20:58 +00:00
parent 5b3bc6b37c
commit 1b019e9b0d
17 changed files with 1293 additions and 542 deletions

View File

@ -288,16 +288,11 @@ static void ath_set_txcont_rate(struct ieee80211com *ic, unsigned int new_rate);
static void ath_dfs_cac_completed(unsigned long);
static void ath_interrupt_dfs_cac(struct ath_softc *sc, const char *reason);
static inline int ath_chan_unavail(struct ath_softc *sc);
#define ath_cac_running_dbgmsg(_sc) \
_ath_cac_running_dbgmsg((_sc), __func__)
#define ath_chan_unavail_dbgmsg(_sc) \
_ath_chan_unavail_dbgmsg((_sc), __func__)
static inline int _ath_cac_running_dbgmsg(struct ath_softc *sc,
const char *func);
static inline int _ath_chan_unavail_dbgmsg(struct ath_softc *sc,
const char *func);
/* DFS testing functions */
#define ath_dfs_can_transmit_dbgmsg(_sc) \
_ath_dfs_can_transmit_dbgmsg((_sc), __func__)
#define ath_dfs_can_transmit_csaie_dbgmsg(_sc) \
_ath_dfs_can_transmit_csaie_dbgmsg((_sc), __func__)
/* 802.11h DFS testing functions */
static int ath_get_dfs_testmode(struct ieee80211com *);
@ -310,7 +305,9 @@ static void ath_set_dfs_excl_period(struct ieee80211com *,
static unsigned int ath_get_dfs_cac_time(struct ieee80211com *);
static void ath_set_dfs_cac_time(struct ieee80211com *, unsigned int seconds);
static unsigned int ath_test_radar(struct ieee80211com *);
static void ath_radar_detected(struct ieee80211com *ic, const char *cause,
int switchChanRequested, u_int8_t switchChan);
static unsigned int ath_dump_hal_map(struct ieee80211com *ic);
static u_int32_t ath_get_clamped_maxtxpower(struct ath_softc *sc);
@ -458,6 +455,56 @@ MODULE_PARM_DESC(ieee80211_debug, "Load-time 802.11 debug output enable");
(bssid)[0] |= (((id) << 2) | 0x02); \
} while (0)
/* Internal functions to set/clear CHANNEL_DFS_CLEAR/CHANNEL_INTERFERENCE
* flags */
static inline void _ath_set_dfs_clear(struct ath_softc *sc, HAL_CHANNEL *channel, int val)
{
if (val) {
channel->privFlags |= CHANNEL_DFS_CLEAR;
DPRINTF(sc, ATH_DEBUG_DOTH,
"Set CHANNEL_DFS_CLEAR flag on channel %4u MHz\n",
channel->channel);
} else {
channel->privFlags &= ~CHANNEL_DFS_CLEAR;
DPRINTF(sc, ATH_DEBUG_DOTH,
"Clear CHANNEL_DFS_CLEAR flag on channel %4u MHz\n",
channel->channel);
}
}
static inline void _ath_set_dfs_interference(struct ath_softc *sc, HAL_CHANNEL *channel, int val)
{
if (val) {
channel->privFlags |= CHANNEL_INTERFERENCE;
DPRINTF(sc, ATH_DEBUG_DOTH,
"Set CHANNEL_INTERFERENCE flag on channel %4u MHz\n",
channel->channel);
} else {
channel->privFlags &= ~CHANNEL_INTERFERENCE;
DPRINTF(sc, ATH_DEBUG_DOTH,
"Clear CHANNEL_INTERFERENCE flag on channel %4u MHz\n",
channel->channel);
}
}
/* Set/clear CHANNEL_DFS_CLEAR/CHANNEL_INTERFERENCE flags for the operating channel */
static void ath_set_dfs_clear(struct ieee80211com *ic, int val)
{
struct net_device *dev = ic->ic_dev;
struct ath_softc *sc = netdev_priv(dev);
_ath_set_dfs_clear(sc, &sc->sc_curchan, val);
}
static void ath_set_dfs_interference(struct ieee80211com *ic, int val)
{
struct net_device *dev = ic->ic_dev;
struct ath_softc *sc = netdev_priv(dev);
_ath_set_dfs_interference(sc, &sc->sc_curchan, val);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
static const struct net_device_ops ath_netdev_ops = {
.ndo_open = ath_init,
@ -798,6 +845,9 @@ ath_attach(u_int16_t devid, struct net_device *dev, HAL_BUS_TAG tag)
sc->sc_dfs_cac_period = ATH_DFS_WAIT_MIN_PERIOD;
sc->sc_dfs_excl_period = ATH_DFS_AVOID_MIN_PERIOD;
_ath_set_dfs_clear(sc, &sc->sc_curchan, 0);
_ath_set_dfs_interference(sc, &sc->sc_curchan, 0);
/* initialize radar stuff */
ath_rp_init(sc);
@ -832,7 +882,7 @@ ath_attach(u_int16_t devid, struct net_device *dev, HAL_BUS_TAG tag)
#else
dev->netdev_ops = &ath_netdev_ops;
#endif
dev->watchdog_timeo = 5 * HZ;
dev->watchdog_timeo = HZ;
dev->tx_queue_len = ATH_TXBUF - ATH_TXBUF_MGT_RESERVED;
#ifdef USE_HEADERLEN_RESV
dev->hard_header_len += sizeof(struct ieee80211_qosframe) +
@ -1021,7 +1071,6 @@ ath_attach(u_int16_t devid, struct net_device *dev, HAL_BUS_TAG tag)
ic->ic_vap_create = ath_vap_create;
ic->ic_vap_delete = ath_vap_delete;
ic->ic_test_radar = ath_test_radar;
ic->ic_dump_hal_map = ath_dump_hal_map;
ic->ic_set_dfs_testmode = ath_set_dfs_testmode;
@ -1053,6 +1102,13 @@ ath_attach(u_int16_t devid, struct net_device *dev, HAL_BUS_TAG tag)
ic->ic_set_dfs_excl_period = ath_set_dfs_excl_period;
ic->ic_get_dfs_excl_period = ath_get_dfs_excl_period;
/* DFS flag manipulation */
ic->ic_set_dfs_clear = ath_set_dfs_clear;
ic->ic_set_dfs_interference = ath_set_dfs_interference;
/* DFS radar detection handling */
ic->ic_radar_detected = ath_radar_detected;
if (register_netdev(dev)) {
EPRINTF(sc, "Unable to register device\n");
goto bad3;
@ -1187,6 +1243,49 @@ ath_detach(struct net_device *dev)
return 0;
}
/* Return the hardware TSF in usec */
static u_int64_t
ath_vap_get_tsf(struct ieee80211vap *vap)
{
struct ath_softc *sc = netdev_priv(vap->iv_ic->ic_dev);
struct ath_hal *ah = sc->sc_ah;
return ath_hal_gettsf64(ah);
}
/* Return the next TBTT in TU for the specified VAP. Currently, it does not
* take into account staggered beacons */
static u_int32_t
ath_vap_get_nexttbtt(struct ieee80211vap *vap)
{
struct ath_softc *sc = netdev_priv(vap->iv_ic->ic_dev);
struct ath_hal *ah = sc->sc_ah;
u_int32_t now_tu, nexttbtt;
/* Prevent a divide by zero to occur */
if (vap->iv_bss->ni_intval == 0) {
DPRINTF(sc, ATH_DEBUG_ANY,
"BUG: vap->iv_bss->ni_intval is 0\n");
return 0;
}
/* Calculate the closest TBTT that is > now_tu. */
now_tu = IEEE80211_TSF_TO_TU(ath_hal_gettsf64(ah));
nexttbtt = sc->sc_nexttbtt + roundup_s(
now_tu + 1 - sc->sc_nexttbtt, vap->iv_bss->ni_intval);
if (nexttbtt != sc->sc_nexttbtt) {
DPRINTF(sc, ATH_DEBUG_DOTH,
"now_tu:%u nexttbtt:%u corrected to %u [bintval:%u]\n",
now_tu, sc->sc_nexttbtt, nexttbtt,
vap->iv_bss->ni_intval);
}
return nexttbtt;
}
static struct ieee80211vap *
ath_vap_create(struct ieee80211com *ic, const char *name,
int opmode, int flags, struct net_device *mdev)
@ -1299,6 +1398,9 @@ ath_vap_create(struct ieee80211com *ic, const char *name,
vap->iv_comp_set = ath_comp_set;
#endif
vap->iv_get_tsf = ath_vap_get_tsf;
vap->iv_get_nexttbtt = ath_vap_get_nexttbtt;
/* Let rate control register proc entries for the VAP */
if (sc->sc_rc->ops->dynamic_proc_register)
sc->sc_rc->ops->dynamic_proc_register(vap);
@ -1742,33 +1844,74 @@ static HAL_BOOL ath_hw_reset(struct ath_softc *sc, HAL_OPMODE opmode,
* - Channel Availability Check is not done or a radar has been detected
*/
static int
ath_chan_unavail(struct ath_softc *sc)
ath_dfs_can_transmit(struct ath_softc *sc)
{
return timer_pending(&sc->sc_dfs_cac_timer) ||
((sc->sc_curchan.privFlags & CHANNEL_DFS) &&
(sc->sc_curchan.privFlags & CHANNEL_INTERFERENCE));
struct ieee80211com *ic = &sc->sc_ic;
if ((sc->sc_curchan.privFlags & CHANNEL_DFS) &&
(ic->ic_flags & IEEE80211_F_DOTH)) {
/* DFS is required on the current channel. We can transmit
* only after CAC and if no radar has been detected */
return ((sc->sc_curchan.privFlags & CHANNEL_DFS_CLEAR) &&
(sc->sc_curchan.privFlags & CHANNEL_INTERFERENCE)==0);
} else {
/* DFS is not required on the current channel */
return 1;
}
}
/* Returns true if we can transmit frames containing CSA IE */
static int
ath_dfs_can_transmit_csaie(struct ath_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
if ((sc->sc_curchan.privFlags & CHANNEL_DFS) &&
(ic->ic_flags & IEEE80211_F_DOTH)) {
/* DFS is required on the current channel. We can transmit
* only after CAC even if radar has been detected */
return (sc->sc_curchan.privFlags & CHANNEL_DFS_CLEAR);
} else {
/* DFS is not required on the current channel */
return 1;
}
}
static inline int
_ath_cac_running_dbgmsg(struct ath_softc *sc, const char *func)
_ath_dfs_can_transmit_dbgmsg(struct ath_softc *sc, const char *func)
{
int b = timer_pending(&sc->sc_dfs_cac_timer);
if (b)
DPRINTF(sc, ATH_DEBUG_DOTH,
"%s: Invoked a transmit function during DFS "
"channel availability check!\n", func);
int b = ath_dfs_can_transmit(sc);
if (!b) {
if ((sc->sc_curchan.privFlags & CHANNEL_DFS_CLEAR) == 0) {
DPRINTF(sc, ATH_DEBUG_DOTH,
"%s: Invoked a transmit function before "
"end of DFS channel availability check\n",
func);
}
if (sc->sc_curchan.privFlags & CHANNEL_INTERFERENCE) {
DPRINTF(sc, ATH_DEBUG_DOTH,
"%s: Invoked a transmit function while radar "
"has been detected\n", func);
}
}
return b;
}
static inline int
_ath_chan_unavail_dbgmsg(struct ath_softc *sc, const char *func)
_ath_dfs_can_transmit_csaie_dbgmsg(struct ath_softc *sc, const char *func)
{
int b = ath_chan_unavail(sc);
if (b)
DPRINTF(sc, ATH_DEBUG_DOTH,
"%s: Invoked a transmit function during DFS "
"channel availability check OR while radar "
"interference is detected!\n", func);
int b = ath_dfs_can_transmit_csaie(sc);
if (!b) {
if ((sc->sc_curchan.privFlags & CHANNEL_DFS_CLEAR)==0) {
DPRINTF(sc, ATH_DEBUG_BEACON,
"%s: Invoked a transmit function before "
"end of DFS channel availability check\n",
func);
}
}
return b;
}
@ -2052,7 +2195,7 @@ ath_intr_process_rx_descriptors(struct ath_softc *sc, int *pneedmark, u_int64_t
/* If we are supposed to be not listening or
* transmitting, don't do uapsd triggers */
if (!ath_cac_running_dbgmsg(sc)) {
if (ath_dfs_can_transmit_dbgmsg(sc)) {
/* make sure the frame is QoS data/null */
/* NB: with current sub-type definitions, the
* IEEE80211_FC0_SUBTYPE_QOS check, below,
@ -2410,13 +2553,8 @@ ath_intr(int irq, void *dev_id, struct pt_regs *regs)
ATH_SCHEDULE_TQUEUE(&sc->sc_rxorntq, &needmark);
} else {
if (status & HAL_INT_SWBA) {
struct ieee80211vap *vap;
u_int32_t hw_tsftu = IEEE80211_TSF_TO_TU(hw_tsf);
/* Updates sc_nexttbtt */
vap = TAILQ_FIRST(&sc->sc_ic.ic_vaps);
sc->sc_nexttbtt += vap->iv_bss->ni_intval;
DPRINTF(sc, ATH_DEBUG_BEACON,
"HAL_INT_SWBA at "
"hw_tsf=%10llx nexttbtt_tsf=%10llx "
@ -2429,7 +2567,7 @@ ath_intr(int irq, void *dev_id, struct pt_regs *regs)
* Handle beacon transmission directly; deferring
* this is too slow to meet timing constraints
* under load. */
if (!timer_pending(&sc->sc_dfs_cac_timer))
if (ath_dfs_can_transmit_csaie_dbgmsg(sc))
ath_beacon_send(sc, &needmark, hw_tsf);
else {
sc->sc_beacons = 0;
@ -2482,7 +2620,7 @@ ath_intr(int irq, void *dev_id, struct pt_regs *regs)
}
if (status & HAL_INT_BMISS) {
sc->sc_stats.ast_bmiss++;
if (!timer_pending(&sc->sc_dfs_cac_timer))
if (ath_dfs_can_transmit_csaie_dbgmsg(sc))
ATH_SCHEDULE_TQUEUE(&sc->sc_bmisstq, &needmark);
else {
sc->sc_beacons = 0;
@ -2816,7 +2954,6 @@ ath_reset(struct net_device *dev)
c = ic->ic_curchan;
sc->sc_curchan.channel = c->ic_freq;
sc->sc_curchan.channelFlags = ath_chan2flags(c);
sc->sc_curchan.privFlags = 0;
ath_hal_intrset(ah, 0); /* disable interrupts */
ath_draintxq(sc); /* stop xmit side */
@ -2949,8 +3086,14 @@ ath_tx_txqaddbuf(struct ath_softc *sc, struct ieee80211_node *ni,
ds += bf->bf_numdescff;
#endif
if (ath_cac_running_dbgmsg(sc))
return;
if (!ath_dfs_can_transmit_csaie_dbgmsg(sc)) {
/* This message spots errors in the driver, so it's printed
* whenever any debug options is enabled and it does not
* prevent from sending. This message is normal if you are
* doing packet injection, since it bypass DFS rules */
DPRINTF(sc, ATH_DEBUG_ANY,
"%s: WE ARE SENDING PACKETS UNDER CAC!\n", __func__);
}
/* Insert the frame on the outbound list and
* pass it on to the hardware. */
@ -3382,6 +3525,8 @@ ath_hardstart(struct sk_buff *__skb, struct net_device *dev)
STAILQ_INIT(&bf_head);
/* We send injected packets before checking DFS rules.
* It means that packet injection bypasses DFS rules. */
if (SKB_CB(skb)->flags & M_RAW) {
bf = ath_take_txbuf(sc);
if (bf == NULL) {
@ -3394,6 +3539,25 @@ ath_hardstart(struct sk_buff *__skb, struct net_device *dev)
return NETDEV_TX_OK;
}
/* If we are under CAC or have detected a radar, we simply drop (and
* free) frames. This check is done after processing injected
* packets */
if (!ath_dfs_can_transmit_dbgmsg(sc)) {
/* No need to print a warning or error messages here since we
* know that ath_hardstart() is invoked directly or indirectly
* by the linux network stack and that all packets needs to be
* dropped without exception. */
goto hardstart_fail;
}
ni = SKB_CB(skb)->ni; /* NB: always passed down by 802.11 layer */
if (ni == NULL) {
/* NB: this happens if someone marks the underlying device up */
DPRINTF(sc, ATH_DEBUG_XMIT,
"Dropping; No node in skb control block!\n");
goto hardstart_fail;
}
#ifdef ATH_SUPERG_FF
if (M_FLAG_GET(skb, M_UAPSD)) {
/* bypass FF handling */
@ -3680,6 +3844,14 @@ ath_mgtstart(struct ieee80211com *ic, struct sk_buff *skb)
error = -ENETDOWN;
goto bad;
}
if (!ath_dfs_can_transmit_csaie_dbgmsg(sc)) {
DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_DOTH,
"Dropping; we are under radar\n");
error = 0;
goto bad;
}
/*
* Grab a TX buffer and associated resources.
*/
@ -4227,7 +4399,8 @@ ath_calcrxfilter(struct ath_softc *sc)
if (sc->sc_nmonvaps > 0)
rfilt |= (HAL_RX_FILTER_CONTROL | HAL_RX_FILTER_BEACON |
HAL_RX_FILTER_PROBEREQ | HAL_RX_FILTER_PROM);
if (sc->sc_curchan.privFlags & CHANNEL_DFS)
if ((sc->sc_curchan.privFlags & CHANNEL_DFS) &&
(ic->ic_flags & IEEE80211_F_DOTH))
rfilt |= (HAL_RX_FILTER_PHYERR | HAL_RX_FILTER_PHYRADAR);
return rfilt;
}
@ -5023,11 +5196,7 @@ ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap, int *needmar
return NULL;
}
if (ath_chan_unavail_dbgmsg(sc)) {
DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
"Skipping VAP when DFS requires radio silence\n");
return NULL;
}
/* If we detected a radar, we still need to send beacons with CSA IE */
#ifdef ATH_SUPERG_XR
if (vap->iv_flags & IEEE80211_F_XR) {
@ -5220,8 +5389,8 @@ ath_beacon_send(struct ath_softc *sc, int *needmark, uint64_t hw_tsf)
u_int32_t bfaddr = 0;
u_int32_t n_beacon;
if (ath_chan_unavail_dbgmsg(sc))
return;
/* Once a radar has been detected, we need to send beacons with CSA
* IE */
/*
* Check if the previous beacon has gone out. If
@ -6987,7 +7156,7 @@ static void ath_grppoll_start(struct ieee80211vap *vap, int pollcount)
if (sc->sc_xrgrppoll)
return;
if (ath_cac_running_dbgmsg(sc))
if (!ath_dfs_can_transmit_csaie_dbgmsg(sc))
return;
memset(&rates, 0, sizeof(rates));
@ -8502,7 +8671,7 @@ ath_tx_timeout(struct net_device *dev)
struct ath_softc *sc = netdev_priv(dev);
int i;
if (ath_chan_unavail(sc))
if (!ath_dfs_can_transmit_dbgmsg(sc))
return;
DPRINTF(sc, ATH_DEBUG_WATCHDOG, "%sRUNNING. sc is %svalid.\n",
@ -8769,18 +8938,22 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
/* Stop any pending channel calibrations or availability check if we
* are really changing channels. maybe a turbo mode switch only. */
if (hchan.channel != sc->sc_curchan.channel)
if (!sc->sc_dfs_testmode && timer_pending(&sc->sc_dfs_cac_timer))
if (!sc->sc_dfs_testmode &&
timer_pending(&sc->sc_dfs_cac_timer))
ath_interrupt_dfs_cac(sc,
"Channel change interrupted DFS wait.");
/* Need a DFS channel availability check? We do if ... */
dfs_cac_needed = IEEE80211_IS_MODE_DFS_MASTER(ic->ic_opmode) &&
(hchan.channel != sc->sc_curchan.channel ||
/* the scan wasn't already done */
(0 == (sc->sc_curchan.privFlags & CHANNEL_DFS_CLEAR))) &&
/* CAC has not been done and we are not under radar */
(0 == (sc->sc_curchan.privFlags & (CHANNEL_DFS_CLEAR|CHANNEL_INTERFERENCE)))) &&
/* the new channel requires DFS protection */
ath_radar_is_dfs_required(sc, &hchan) &&
(ic->ic_flags & IEEE80211_F_DOTH);
/* IEEE 802.11h is required */
(ic->ic_flags & IEEE80211_F_DOTH) &&
/* CAC is not already started */
(!timer_pending(&sc->sc_dfs_cac_timer));
channel_change_required = hchan.channel != sc->sc_curchan.channel ||
hchan.channelFlags != sc->sc_curchan.channelFlags ||
@ -8844,19 +9017,24 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
sc->sc_curchan.channelFlags),
tv.tv_sec, (long)tv.tv_usec);
/* set the timeout to normal */
dev->watchdog_timeo = 120 * HZ;
dev->watchdog_timeo = (sc->sc_dfs_cac_period + 1) * HZ;
/* Disable beacons and beacon miss interrupts */
sc->sc_beacons = 0;
sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
ath_hal_intrset(ah, sc->sc_imask);
/* Reset CHANNEL_INTERFERENCE and CHANNEL_DFS_CLEAR
* after a channel change */
_ath_set_dfs_clear(sc, &sc->sc_curchan, 0);
_ath_set_dfs_interference(sc, &sc->sc_curchan, 0);
/* Enter DFS wait period */
mod_timer(&sc->sc_dfs_cac_timer,
jiffies + (sc->sc_dfs_cac_period * HZ));
}
/*
* re configure beacons when it is a turbo mode switch.
* HW seems to turn off beacons during turbo mode switch.
* FIXME : HW seems to turn off beacons after a call to
* ath_hal_reset().
*/
if (sc->sc_beacons && !timer_pending(&sc->sc_dfs_cac_timer))
ath_beacon_config(sc, NULL);
@ -8917,7 +9095,7 @@ ath_calibrate(unsigned long arg)
(TAILQ_FIRST(&ic->ic_vaps)->iv_opmode !=
IEEE80211_M_WDS) &&
!txcont_was_active &&
!timer_pending(&sc->sc_dfs_cac_timer)) {
!timer_pending(&sc->sc_dfs_cac_timer)) {
sc->sc_beacons = 1;
}
@ -9281,7 +9459,7 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
/* if it is a DFS channel and has not been checked for radar
* do not let the 80211 state machine to go to RUN state. */
if (timer_pending(&sc->sc_dfs_cac_timer) &&
if (0 && timer_pending(&sc->sc_dfs_cac_timer) &&
IEEE80211_IS_MODE_DFS_MASTER(vap->iv_opmode)) {
DPRINTF(sc, ATH_DEBUG_STATE | ATH_DEBUG_DOTH,
"VAP -> DFSWAIT_PENDING \n");
@ -9349,7 +9527,8 @@ done:
#endif
bad:
netif_wake_queue(dev);
dev->watchdog_timeo = (timer_pending(&sc->sc_dfs_cac_timer) ? 120 : 5) * HZ; /* set the timeout to normal */
dev->watchdog_timeo = (timer_pending(&sc->sc_dfs_cac_timer) ?
(sc->sc_dfs_cac_period + 1) * HZ : HZ); /* set the timeout to normal */
return error;
}
@ -9368,20 +9547,13 @@ ath_dfs_cac_completed(unsigned long data )
struct ieee80211vap *vap ;
struct timeval tv;
if (!timer_pending(&sc->sc_dfs_cac_timer)) {
DPRINTF(sc, ATH_DEBUG_DOTH, "DFS wait timer "
"expired, but the driver didn't think we "
"were in dfswait. Somebody forgot to "
"delete the DFS wait timer.\n");
return;
}
if (!sc->sc_dfs_testmode) {
do_gettimeofday(&tv);
DPRINTF(sc, ATH_DEBUG_STATE | ATH_DEBUG_DOTH,
"DFS wait %s! - Channel: %u Time: "
"%ld.%06ld\n",
(sc->sc_curchan.privFlags & CHANNEL_DFS) ?
((sc->sc_curchan.privFlags & CHANNEL_DFS) &&
(ic->ic_flags & IEEE80211_F_DOTH)) ?
"completed" : "not applicable",
ieee80211_mhz2ieee(sc->sc_curchan.channel,
sc->sc_curchan.channelFlags),
@ -9395,7 +9567,8 @@ ath_dfs_cac_completed(unsigned long data )
"wait timer.\n");
return;
}
if (0 == (sc->sc_curchan.privFlags & CHANNEL_DFS)) {
if (!((sc->sc_curchan.privFlags & CHANNEL_DFS) &&
(ic->ic_flags & IEEE80211_F_DOTH))) {
DPRINTF(sc, ATH_DEBUG_DOTH, "DFS wait "
"timer expired but the current "
"channel does not require DFS. "
@ -9406,7 +9579,7 @@ ath_dfs_cac_completed(unsigned long data )
}
DPRINTF(sc, ATH_DEBUG_DOTH, "Driver is now MARKING "
"channel as CHANNEL_DFS_CLEAR.\n");
sc->sc_curchan.privFlags |= CHANNEL_DFS_CLEAR;
_ath_set_dfs_clear(sc, &sc->sc_curchan, 1);
ath_chan_change(sc, ic->ic_curchan);
/* restart each VAP that was pending... */
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
@ -9442,7 +9615,7 @@ ath_dfs_cac_completed(unsigned long data )
if (sc->sc_beacons) {
ath_beacon_config(sc, NULL);
}
dev->watchdog_timeo = 5 * HZ; /* restore normal timeout */
dev->watchdog_timeo = HZ; /* restore normal timeout */
} else {
do_gettimeofday(&tv);
if (sc->sc_dfs_testmode) {
@ -9760,11 +9933,44 @@ ath_getchannels(struct net_device *dev, u_int cc,
/* Correct the DFS flags to account for problems with DFS in
* older binary HALs returning the wrong answers for FCC... */
ath_radar_correct_dfs_flags(sc, c);
/* Before the completion of CAC, no frames shall be
transmitted. After the completion of CAC, frames shall be
transmitted when the frame contains a CSA IE (a channel
change announcement) OR no radar has been detected on the
channel. The exemption for CSA IE allows us to inform other
nodes to stop transmitting and [optionally] change
channels.
Transmission is allowed according to the following table:
/ CHANNEL_DFS_CLEAR
| / CHANNEL_INTERFERENCE
+---+---+ Transmission state
| 0 | 0 | Do not transmit
| 0 | 1 | Do not transmit
| 1 | 0 | Transmit
| 1 | 1 | Do not transmit except frame with CSA IE
+---+---+
CHANNEL_DFS_CLEAR (CAC has been successful):
+ initial value : clear
+ set at the end of a CAC w/o radar (ath_dfs_cac_completed)
- clear at the end of the channel shutdown (TBD)
+ or on channel change (side effect of ath_chan_set)
CHANNEL_INTERFERENCE (a radar has been detected):
+ initial value : clear
+ set when a radar is detected (ath_radar_detected)
- or when a CSA IE with Mode=1 is received (TBD)
- clear at the end of the non-occupancy period (TBD)
+ or on channel change (side effect of ath_chan_set) */
/* Force re-check.
* XXX: Unclear whether regs say you can avoid the channel
* availability check if you've already performed it on the
* channel within some more brief interval. */
c->privFlags &= ~CHANNEL_DFS_CLEAR;
c->privFlags &= ~(CHANNEL_DFS_CLEAR|CHANNEL_INTERFERENCE);
/* Initialize all fields of ieee80211_channel here */
@ -9800,11 +10006,11 @@ ath_getchannels(struct net_device *dev, u_int cc,
(c->channelFlags & CHANNEL_CW_INT ?
" CF_CW_INTERFERENCE" : ""),
/* undocumented */
(c->channelFlags & 0x0004 ?
" CF & (1 << 2)" : ""),
(c->channelFlags & IEEE80211_CHAN_INDOOR ?
" CF_INDOOR" : ""),
/* undocumented */
(c->channelFlags & 0x0008 ?
" CF & (1 << 3)" : ""),
(c->channelFlags & IEEE80211_CHAN_OUTDOOR ?
" CF_OUTDOOR" : ""),
/* Turbo channel */
(c->channelFlags & CHANNEL_TURBO ?
" CF_TURBO" : ""),
@ -12021,24 +12227,6 @@ ath_get_dfs_excl_period(struct ieee80211com *ic)
return sc->sc_dfs_excl_period;
}
/* This is called by a private ioctl (iwpriv) to simulate radar by directly
* invoking the ath_radar_detected function even though we are outside of
* interrupt context. */
static unsigned int
ath_test_radar(struct ieee80211com *ic)
{
struct net_device *dev = ic->ic_dev;
struct ath_softc *sc = netdev_priv(dev);
if ((ic->ic_flags & IEEE80211_F_DOTH) && (sc->sc_curchan.privFlags & CHANNEL_DFS))
ath_radar_detected(sc, "ath_test_radar from user space");
else
DPRINTF(sc, ATH_DEBUG_DOTH, "Channel %u MHz is not marked "
"for CHANNEL_DFS. Radar test not performed!\n",
sc->sc_curchan.channel);
return 0;
}
/* This is called by a private ioctl (iwpriv) to dump the HAL obfuscation table */
static unsigned int
ath_dump_hal_map(struct ieee80211com *ic)
@ -12083,21 +12271,35 @@ ath_interrupt_dfs_cac(struct ath_softc *sc, const char *reason)
* trunk and it's left here because it may be used as the basis for
* implementing AP requested mute tests in station mode later. */
void
ath_radar_detected(struct ath_softc *sc, const char *cause)
static void
ath_radar_detected(struct ieee80211com *ic, const char *cause,
int switchChanRequested, u_int8_t switchChan)
{
struct ath_hal *ah = sc->sc_ah;
struct ieee80211com *ic = &sc->sc_ic;
struct net_device *dev = ic->ic_dev;
struct ath_softc *sc = netdev_priv(dev);
struct ath_hal *ah = sc->sc_ah;
struct ieee80211_channel ichan;
struct timeval tv;
if (!switchChanRequested)
switchChan = 0;
DPRINTF(sc, ATH_DEBUG_DOTH,
"Radar detected on channel:%u cause: %s%s\n",
"Radar detected on channel:%u [%s] %s channel %u%s\n",
sc->sc_curchan.channel,
cause,
sc->sc_radar_ignored ? " (ignored)" : "");
switchChanRequested ? "requesting" : "random",
switchChan,
sc->sc_radar_ignored || !(ic->ic_flags & IEEE80211_F_DOTH) ?
" (ignored)" : "");
if (sc->sc_radar_ignored) {
/* flag set through :
* echo 1 > /proc/sys/dev/wifi0/radar_ignored
* or :
* sysctl -w dev.wifi0.radar_ignored=1
*/
if (sc->sc_radar_ignored || !(ic->ic_flags & IEEE80211_F_DOTH)) {
/* debug message already printed above */
return;
}
@ -12138,9 +12340,18 @@ ath_radar_detected(struct ath_softc *sc, const char *cause)
"-- Time: %ld.%06ld\n",
ichan.ic_ieee, ichan.ic_freq, ichan.ic_flags,
tv.tv_sec, (long)tv.tv_usec);
/* do nothing if we already detected a radar, avoid
* re-entrancy. FIXME : we should use a lock to avoid
* race condition. */
if (sc->sc_curchan.privFlags & CHANNEL_INTERFERENCE) {
DPRINTF(sc, ATH_DEBUG_DOTH,
"DFS already marked, ignoring\n");
return ;
}
/* Mark the channel */
sc->sc_curchan.privFlags &= ~CHANNEL_DFS_CLEAR;
sc->sc_curchan.privFlags |= CHANNEL_INTERFERENCE;
_ath_set_dfs_interference(sc, &sc->sc_curchan, 1);
/* notify 80211 layer so it can change channels... */
ieee80211_mark_dfs(ic, &ichan);
}
@ -12194,14 +12405,14 @@ ath_return_txbuf_locked(struct ath_softc *sc, struct ath_buf **bf)
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
if (netif_queue_stopped(sc->sc_dev) &&
(ath_get_buffers_available(sc) > ATH_TXBUF_MGT_RESERVED) &&
(!ath_chan_unavail(sc))) {
(!ath_dfs_can_transmit(sc))) {
DPRINTF(sc, ATH_DEBUG_TXBUF | ATH_DEBUG_RESET,
"Waking device queue with %d available buffers.\n",
ath_get_buffers_available(sc));
netif_wake_queue(sc->sc_dev);
}
#if 0
else if (ath_chan_unavail(sc)) {
else if (ath_dfs_can_transmit(sc)) {
DPRINTF(sc, (ATH_DEBUG_TXBUF | ATH_DEBUG_RESET |
ATH_DEBUG_DOTH),
"Not waking device queue. Channel "

View File

@ -235,11 +235,10 @@ int ath_radar_update(struct ath_softc *sc)
/* Update the DFS flags (as a sanity check) */
if (ath_radar_correct_dfs_flags(sc, &sc->sc_curchan))
DPRINTF(sc, ATH_DEBUG_DOTH, "%s: %s: channel required "
"corrections to private flags.\n",
SC_DEV_NAME(sc), __func__);
required = ath_radar_is_dfs_required(sc, &sc->sc_curchan) &&
(ic->ic_flags & IEEE80211_F_DOTH);
DPRINTF(sc, ATH_DEBUG_DOTH, "channel required "
"corrections to private flags.\n");
required = ((sc->sc_curchan.privFlags & CHANNEL_DFS) &&
(ic->ic_flags & IEEE80211_F_DOTH));
/* configure radar pulse detector register using default values, but do
* not toggle the enable bit. XXX: allow tweaking?? */
ath_radar_set_params(sc, NULL);
@ -296,12 +295,46 @@ int ath_radar_update(struct ath_softc *sc)
return (required == ath_radar_is_enabled(sc));
}
/* Update channel's DFS flags based upon whether DFS is required. Return
* true if the value was repaired. */
static
int ath_radar_is_indoor_channel(HAL_CHANNEL *hchan)
{
/* Warning : we use hardcoded values here suited for France */
if ((hchan->channel >= 2412) && (hchan->channel <= 2472))
return 1;
if ((hchan->channel >= 5150) && (hchan->channel <= 5350))
return 1;
if ((hchan->channel >= 5470) && (hchan->channel <= 5725))
return 1;
return 0;
}
static
int ath_radar_is_outdoor_channel(HAL_CHANNEL *hchan)
{
/* Warning : we use hardcoded values here suited for France */
if ((hchan->channel >= 2412) && (hchan->channel <= 2472))
return 1;
if ((hchan->channel >= 5470) && (hchan->channel <= 5725))
return 1;
return 0;
}
/* Update channel's DFS flags based upon whether DFS is required. Return true
* if the value was repaired. It also add flags to know if a channel can be
* used indoor or outdoor or both. Those flags have been added and made
* compatible with HAL flags (as defined in <hal/ah.h> */
#define CHANNEL_INDOOR 0x00004
#define CHANNEL_OUTDOOR 0x00008
int ath_radar_correct_dfs_flags(struct ath_softc *sc, HAL_CHANNEL *hchan)
{
u_int32_t old_channelFlags = hchan->channelFlags;
u_int32_t old_privFlags = hchan->privFlags;
u_int8_t old_privFlags = hchan->privFlags;
int changed;
if (ath_radar_is_dfs_required(sc, hchan)) {
hchan->channelFlags |= CHANNEL_PASSIVE;
hchan->privFlags |= CHANNEL_DFS;
@ -309,8 +342,21 @@ int ath_radar_correct_dfs_flags(struct ath_softc *sc, HAL_CHANNEL *hchan)
hchan->channelFlags &= ~CHANNEL_PASSIVE;
hchan->privFlags &= ~CHANNEL_DFS;
}
return ((old_privFlags != hchan->privFlags) ||
(old_channelFlags != hchan->channelFlags));
changed = ((old_privFlags != hchan->privFlags) ||
(old_channelFlags != hchan->channelFlags));
hchan->channelFlags &= ~(CHANNEL_INDOOR | CHANNEL_OUTDOOR);
if (ath_radar_is_indoor_channel(hchan)) {
hchan->channelFlags |= CHANNEL_INDOOR;
}
if (ath_radar_is_outdoor_channel(hchan)) {
hchan->channelFlags |= CHANNEL_OUTDOOR;
}
return changed;
}
/* Returns true if DFS is required for the regulatory domain, country and
@ -1254,6 +1300,7 @@ static const char *get_longpulse_desc(int lp)
static HAL_BOOL rp_analyze(struct ath_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
HAL_BOOL radar = 0;
struct ath_rp *pulse;
@ -1487,30 +1534,30 @@ static HAL_BOOL rp_analyze(struct ath_softc *sc)
#ifdef ATH_RADAR_LONG_PULSE
} else {
DPRINTF(sc, ATH_DEBUG_DOTHPULSES,
"%s: Sample contains data matching %s\n",
DEV_NAME(sc->sc_dev),
"Sample contains data matching %s\n",
get_longpulse_desc(best_lp_bc));
}
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
ath_rp_print(sc, 0 /* analyzed pulses only */ );
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
"%s: ========================================\n",
DEV_NAME(sc->sc_dev));
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
"%s: ==END RADAR SAMPLE======================\n",
DEV_NAME(sc->sc_dev));
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
"%s: ========================================\n",
DEV_NAME(sc->sc_dev));
DPRINTF(sc, ATH_DEBUG_DOTHPULSES,
"========================================\n");
DPRINTF(sc, ATH_DEBUG_DOTHPULSES,
"==END RADAR SAMPLE======================\n");
DPRINTF(sc, ATH_DEBUG_DOTHPULSES,
"========================================\n");
}
#ifdef ATH_RADAR_LONG_PULSE
if (!best_lp_bc)
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
ath_radar_detected(sc, radar_patterns[best_index].name);
ic->ic_radar_detected(ic,
radar_patterns[best_index].name,
0, 0);
#ifdef ATH_RADAR_LONG_PULSE
else
ath_radar_detected(sc, get_longpulse_desc(best_lp_bc));
ic->ic_radar_detected(ic,
get_longpulse_desc(best_lp_bc),
0, 0);
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
}
return radar;

View File

@ -846,7 +846,9 @@ struct ath_softc {
* received pulses */
int sc_radar_ignored; /* if set, we ignored all
* detected radars */
u_int32_t sc_nexttbtt;
u_int32_t sc_nexttbtt; /* TBTT following the next SWBA, updated only
* by ath_beacon_config() to avoid race
* conditions */
u_int64_t sc_last_tsf;
};
@ -1013,6 +1015,5 @@ void ath_sysctl_unregister(void);
"MadWifi" : \
DEV_NAME(_v->iv_ic->ic_dev))
void ath_radar_detected(struct ath_softc *sc, const char *message);
#endif /* _DEV_ATH_ATHVAR_H */

View File

@ -154,7 +154,7 @@ struct ieee80211_channel {
#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */
#define IEEE80211_CHAN_ANYC ((struct ieee80211_channel *)IEEE80211_CHAN_ANY)
#define IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT 0
#define IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT 1
#define IEEE80211_DEFAULT_CHANCHANGE_TBTT_COUNT 3
#define IEEE80211_RADAR_TEST_MUTE_CHAN 36 /* Move to channel 36 for mute test */

View File

@ -361,6 +361,11 @@ ieee80211_ifattach(struct ieee80211com *ic)
ieee80211_expire_dfs_excl_timer;
ic->ic_dfs_excl_timer.data = (unsigned long) ic;
/* Initialize CSA related variables. */
init_timer(&ic->ic_csa_timer);
ic->ic_csa_timer.data = (unsigned long)ic;
ic->ic_csa_timer.function = ieee80211_doth_switch_channel_tmr;
ieee80211_crypto_attach(ic);
ieee80211_node_attach(ic);
ieee80211_power_attach(ic);
@ -521,8 +526,6 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct net_device *dev,
vap->iv_opmode = opmode;
IEEE80211_INIT_TQUEUE(&vap->iv_stajoin1tq, ieee80211_sta_join1_tasklet, vap);
vap->iv_chanchange_count = 0;
/* Enable various functionality by default, if we're capable. */
#ifdef ATH_WME /* Not yet the default */
if (vap->iv_caps & IEEE80211_C_WME)
@ -870,19 +873,14 @@ ieee80211_dfs_action(struct ieee80211com *ic) {
/* It is not scanning, but waiting for ath driver to move the
* vap to RUN. */
}
/* Check the scan results using only cached results */
if (!(ieee80211_check_scan(vap, IEEE80211_SCAN_NOSSID |
IEEE80211_SCAN_KEEPMODE |
IEEE80211_SCAN_USECACHE, 0,
vap->iv_des_nssid, vap->iv_des_ssid,
ieee80211_scan_dfs_action))) {
/* No channel was found, so call the scan action with no
* result. */
ieee80211_scan_dfs_action(vap, NULL);
}
ieee80211_scan_dfs_action(vap);
}
void
/* Check if some Non-Occupancy Period timer have expired and update flags
* accordingly */
static void
ieee80211_expire_excl_restrictions(struct ieee80211com *ic)
{
struct ieee80211_channel *c = NULL;
@ -904,6 +902,12 @@ ieee80211_expire_excl_restrictions(struct ieee80211com *ic)
c->ic_ieee, c->ic_freq, tv_now.tv_sec,
tv_now.tv_usec);
c->ic_flags &= ~IEEE80211_CHAN_RADAR;
ic->ic_chan_non_occupy[i].tv_sec = 0;
ic->ic_chan_non_occupy[i].tv_usec = 0;
/* FIXME : should we use ic_curchan or ic_bsschan ? */
if (c == ic->ic_curchan) {
ic->ic_set_dfs_interference(ic, 0);
}
} else {
if_printf(dev,
"Channel %3d (%4d MHz) is still "
@ -960,7 +964,7 @@ ieee80211_update_dfs_excl_timer(struct ieee80211com *ic)
}
}
/* Periodically expire radar avoidance marks. */
/* Timer callback : periodically expire radar avoidance marks. */
static void
ieee80211_expire_dfs_excl_timer(unsigned long data)
{
@ -977,17 +981,12 @@ ieee80211_expire_dfs_excl_timer(unsigned long data)
/* Go through and clear any interference flag we have, if we
* just got it cleared up for us */
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
/* We need to check for the special value
IEEE80211_CHAN_ANYC before using vap->iv_des_chan
since it will cause a kernel panic */
if ((vap->iv_state == IEEE80211_S_RUN) &&
((vap->iv_opmode == IEEE80211_M_HOSTAP) ||
(vap->iv_opmode == IEEE80211_M_IBSS)) &&
/* Operating on channel other than desired. */
(vap->iv_des_chan != IEEE80211_CHAN_ANYC) &&
(vap->iv_des_chan->ic_freq > 0) &&
(vap->iv_des_chan->ic_freq !=
ic->ic_bsschan->ic_freq)) {
IEEE80211_IS_MODE_DFS_MASTER(vap->iv_opmode) &&
/* Desired channel is really defined */
(vap->iv_des_chan != NULL) &&
(vap->iv_des_chan != IEEE80211_CHAN_ANYC)) {
struct ieee80211_channel *des_chan =
ieee80211_find_channel(ic,
vap->iv_des_chan->
@ -1016,11 +1015,25 @@ ieee80211_expire_dfs_excl_timer(unsigned long data)
ic_freq,
vap->iv_des_chan->
ic_flags);
ic->ic_chanchange_chan =
des_chan->ic_ieee;
ic->ic_chanchange_tbtt =
IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT;
ic->ic_flags |= IEEE80211_F_CHANSWITCH;
/* Directly call ic_set_channel() in
* order to start CAC if we are not
* really changing channel (it happens
* at the end of the Non-Occupancy
* Period for instance */
if (des_chan == ic->ic_bsschan) {
ic->ic_curchan = ic->ic_bsschan;
ic->ic_set_channel(ic);
} else {
ieee80211_start_new_csa(vap,
IEEE80211_CSA_CAN_STOP_TX,
des_chan,
IEEE80211_DEFAULT_CHANCHANGE_TBTT_COUNT,
0);
}
} else if (ieee80211_msg_is_reported(vap,
IEEE80211_MSG_DOTH)) {
/* Find the desired channel in
@ -1066,7 +1079,7 @@ ieee80211_mark_dfs(struct ieee80211com *ic, struct ieee80211_channel *ichan)
struct net_device *dev = ic->ic_dev;
struct timeval tv_now;
unsigned int excl_period = ic->ic_get_dfs_excl_period(ic);
int i;
int i, was_on_radar;
do_gettimeofday(&tv_now);
if_printf(dev, "Radar found on channel %3d (%4d MHz) -- "
@ -1074,6 +1087,11 @@ ieee80211_mark_dfs(struct ieee80211com *ic, struct ieee80211_channel *ichan)
ichan->ic_ieee, ichan->ic_freq,
tv_now.tv_sec, tv_now.tv_usec);
if (IEEE80211_IS_MODE_DFS_MASTER(ic->ic_opmode)) {
/* Check if the current channel is already under radar before
* setting radar flag */
was_on_radar = IEEE80211_IS_CHAN_RADAR(ic->ic_curchan);
/* Mark the channel in the ic_chan list */
if (ic->ic_flags_ext & IEEE80211_FEXT_MARKDFS) {
if_printf(dev, "Marking channel %3d (%4d MHz) in "
@ -1116,14 +1134,27 @@ ieee80211_mark_dfs(struct ieee80211com *ic, struct ieee80211_channel *ichan)
return;
}
if (ic->ic_curchan->ic_freq == c->ic_freq) {
if_printf(dev, "%s: Invoking "
/* If current channel is already marked for
* radar, do nothing. It prevents a new CSA
* process to start */
if (was_on_radar) {
if_printf(dev,
"%s: current channel "
"already under radar\n",
__func__);
} else {
if_printf(dev, "%s: Invoking "
"ieee80211_dfs_action "
"(%d, 0x%x)\n",
__func__, ichan->ic_freq,
ichan->ic_flags);
/* The current channel has been marked. We
* need to move away from it. */
ieee80211_dfs_action(ic);
/* The current channel has been
* marked. We need to move away from
* it. */
ieee80211_dfs_action(ic);
}
} else
if_printf(dev,
"Unexpected channel frequency! "
@ -1136,12 +1167,20 @@ ieee80211_mark_dfs(struct ieee80211com *ic, struct ieee80211_channel *ichan)
ic->ic_curchan->ic_ieee,
ic->ic_curchan->ic_freq);
} else {
struct ieee80211vap *vap;
/* Change to a radar free 11a channel for dfstesttime
* seconds. */
ic->ic_chanchange_chan = IEEE80211_RADAR_TEST_MUTE_CHAN;
ic->ic_chanchange_tbtt =
IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT;
ic->ic_flags |= IEEE80211_F_CHANSWITCH;
* seconds.
* Start a channel switch on all available VAPs. */
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
c = ieee80211_doth_findchan(
vap,
IEEE80211_RADAR_TEST_MUTE_CHAN);
ieee80211_start_new_csa(vap, IEEE80211_CSA_CAN_STOP_TX,
c, IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT, 0);
}
if_printf(dev,
"Mute test - markdfs is off, we are "
"in hostap mode, found radar on "

View File

@ -419,6 +419,22 @@ struct ieee80211_ie_csa {
u_int8_t csa_count; /* TBTTs until Channel Switch happens */
} __packed;
/* for Action Category. Table 19a in 802.11h $7.3.1.11 */
#define IEEE80211_ACTION_SPECTRUM_MANAGEMENT 0
/* for Spectrum Management Actions. Table 20e in 802.11h $7.4.1 */
#define IEEE80211_ACTION_S_MEASUREMENT_REQUEST 0
#define IEEE80211_ACTION_S_MEASUREMENT_REPORT 1
#define IEEE80211_ACTION_S_TPC_REQUEST 2
#define IEEE80211_ACTION_S_TPC_REPORT 3
#define IEEE80211_ACTION_S_CHANSWITCHANN 4
/* for csa_mode. It must be either 0 or 1. 1 means that the receiver shall stop
* sending until CS. 0 imposes no requirement. See 7.3.2.20 */
#define IEEE80211_CSA_CAN_STOP_TX 0
#define IEEE80211_CSA_MUST_STOP_TX 1
/* minimal Channel Switch Count in the initial announcement */
#define IEEE80211_CSA_PROTECTION_PERIOD 3

View File

@ -286,56 +286,28 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
int len_changed = 0;
u_int8_t *frm;
u_int16_t capinfo;
IEEE80211_LOCK_IRQ(ic);
/* Check if we need to change channel right now */
/* Check if channel switch is over, in which case we recompute beacon
* content */
if ((ic->ic_flags & IEEE80211_F_DOTH) &&
!(ic->ic_flags & IEEE80211_F_CHANSWITCH) &&
(vap->iv_flags & IEEE80211_F_CHANSWITCH)) {
struct ieee80211_channel *c =
ieee80211_doth_findchan(vap, ic->ic_chanchange_chan);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: reinit beacon\n", __func__);
if (!vap->iv_chanchange_count && !c) {
vap->iv_flags &= ~IEEE80211_F_CHANSWITCH;
ic->ic_flags &= ~IEEE80211_F_CHANSWITCH;
} else if (vap->iv_chanchange_count &&
((!ic->ic_chanchange_tbtt) ||
(vap->iv_chanchange_count == ic->ic_chanchange_tbtt))) {
u_int8_t *frm;
vap->iv_flags &= ~IEEE80211_F_CHANSWITCH;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: reinit beacon\n", __func__);
skb_pull(skb, sizeof(struct ieee80211_frame));
skb_trim(skb, 0);
frm = skb->data;
skb_put(skb, ieee80211_beacon_init(ni, bo, frm) - frm);
skb_push(skb, sizeof(struct ieee80211_frame));
/* NB: ic_bsschan is in the DSPARMS beacon IE, so must
* set this prior to the beacon re-init, below. */
if (c == NULL) {
/* Requested channel invalid; drop the channel
* switch announcement and do nothing. */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: find channel failure\n", __func__);
} else
ic->ic_bsschan = c;
skb_pull(skb, sizeof(struct ieee80211_frame));
skb_trim(skb, 0);
frm = skb->data;
skb_put(skb, ieee80211_beacon_init(ni, bo, frm) - frm);
skb_push(skb, sizeof(struct ieee80211_frame));
vap->iv_chanchange_count = 0;
vap->iv_flags &= ~IEEE80211_F_CHANSWITCH;
ic->ic_flags &= ~IEEE80211_F_CHANSWITCH;
/* NB: Only for the first VAP to get here, and when we
* have a valid channel to which to change. */
if (c && (ic->ic_curchan != c)) {
ic->ic_curchan = c;
ic->ic_set_channel(ic);
}
len_changed = 1;
}
len_changed = 1;
}
/* XXX faster to recalculate entirely or just changes? */
@ -480,12 +452,13 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
*is_dtim = (tie->tim_count == 0);
}
/* Whenever we want to switch to a new channel, we need to follow the
* following steps:
/* Whenever we want to switch to a new channel, we just need to call
* ieee80211_start_new_csa() which will set :
*
* - iv_chanchange_count= number of beacon intervals elapsed (0)
* - ic_chanchange_tbtt = number of beacon intervals before switching
* - ic_chanchange_chan = IEEE channel number after switching
* - ic_csa_mode = IEEE80211_CSA_{CAN|MUST}_STOP_TX
* - ic_csa_chan = pointer to struct ieee80211_channel
* - ic_csa_expires_tu = switch time in TU
* - mod_timer(&ic_csa_timer, expires) = switch time in jiffies
* - ic_flags |= IEEE80211_F_CHANSWITCH */
if (IEEE80211_IS_MODE_BEACON(vap->iv_opmode)) {
@ -494,13 +467,9 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
(ic->ic_flags & IEEE80211_F_CHANSWITCH)) {
struct ieee80211_ie_csa *csa_ie =
(struct ieee80211_ie_csa *)bo->bo_chanswitch;
u_int32_t now_tu, nexttbtt;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: Sending 802.11h chanswitch IE: "
"%d/%d\n", __func__,
ic->ic_chanchange_chan,
ic->ic_chanchange_tbtt);
if (!vap->iv_chanchange_count) {
if (!(vap->iv_flags & IEEE80211_F_CHANSWITCH)) {
vap->iv_flags |= IEEE80211_F_CHANSWITCH;
/* copy out trailer to open up a slot */
@ -512,10 +481,6 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
csa_ie->csa_id = IEEE80211_ELEMID_CHANSWITCHANN;
/* fixed length */
csa_ie->csa_len = sizeof(*csa_ie) - 2;
/* STA shall transmit no further frames */
csa_ie->csa_mode = 1;
csa_ie->csa_chan = ic->ic_chanchange_chan;
csa_ie->csa_count = ic->ic_chanchange_tbtt;
/* update the trailer lens */
bo->bo_chanswitch_trailerlen += sizeof(*csa_ie);
@ -529,13 +494,43 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
* may manage memory */
skb_put(skb, sizeof(*csa_ie));
len_changed = 1;
} else if (csa_ie->csa_count)
csa_ie->csa_count--;
}
now_tu = IEEE80211_TSF_TO_TU(vap->iv_get_tsf(vap));
nexttbtt = vap->iv_get_nexttbtt(vap);
csa_ie->csa_mode = ic->ic_csa_mode;
csa_ie->csa_chan = ic->ic_csa_chan->ic_ieee;
vap->iv_chanchange_count++;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: CHANSWITCH IE, change in %d TBTT\n",
__func__, csa_ie->csa_count);
"%s: now_tu:%ld nexttbtt_tu:%ld "
"=> expires_tu:%ld\n",
__func__, now_tu, nexttbtt,
ic->ic_csa_expires_tu);
if (ic->ic_csa_expires_tu > nexttbtt) {
csa_ie->csa_count =
(ic->ic_csa_expires_tu - nexttbtt)
/ vap->iv_bss->ni_intval;
} else {
csa_ie->csa_count = 0;
}
/* Since beaconing is a shared process in IBSS mode,
* we send Action frames as well. */
if (vap->iv_opmode == IEEE80211_M_IBSS) {
ieee80211_send_csa_frame(vap,
csa_ie->csa_mode,
csa_ie->csa_chan,
csa_ie->csa_count);
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: Sending beacon frame with "
"CSA IE: %u/%u/%u\n", __func__,
csa_ie->csa_mode,
csa_ie->csa_chan,
csa_ie->csa_count);
}
#ifdef ATH_SUPERG_XR
if (vap->iv_flags & IEEE80211_F_XRUPDATE) {

View File

@ -2690,55 +2690,47 @@ ieee80211_doth_findchan(struct ieee80211vap *vap, u_int8_t chan)
return c;
}
/* This function is called at the end of the Channel Shutdown procedure, just
* before switching to the new channel (if any) */
static void
ieee80211_doth_cancel_cs(struct ieee80211vap *vap)
ieee80211_doth_cancel_cs(struct ieee80211com *ic)
{
del_timer(&vap->iv_csa_timer);
if (vap->iv_csa_jiffies)
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"channel switch canceled (was: ""to %3d "
"(%4d MHz) in %u TBTT, mode %u)\n",
vap->iv_csa_chan->ic_ieee,
vap->iv_csa_chan->ic_freq,
vap->iv_csa_count, vap->iv_csa_mode);
vap->iv_csa_jiffies = 0;
del_timer(&ic->ic_csa_timer);
ic->ic_flags &= ~IEEE80211_F_CHANSWITCH;
ic->ic_set_dfs_clear(ic, 0);
}
static void
ieee80211_doth_switch_channel(struct ieee80211vap *vap)
ieee80211_doth_switch_channel(struct ieee80211com *ic)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211vap *vap;
u_int32_t now_tu;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: Channel switch to %3d (%4d MHz) NOW!\n",
__func__,
vap->iv_csa_chan->ic_ieee,
vap->iv_csa_chan->ic_freq);
#if 0
/* XXX does not belong here? */
/* XXX doesn't stop management frames */
/* XXX who restarts the queue? */
/* NB: for now, error here is non-catastrophic.
* in the future we may need to ensure we
* stop xmit on this channel.
*/
netif_stop_queue(ic->ic_dev);
#endif
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
now_tu = IEEE80211_TSF_TO_TU(vap->iv_get_tsf(vap));
vap->iv_csa_jiffies = 0; /* suppress "cancel" msg */
ieee80211_doth_cancel_cs(vap);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: Channel switch to %3d (%4d MHz) NOW! "
"(now_tu:%lu)\n", __func__,
ic->ic_csa_chan->ic_ieee,
ic->ic_csa_chan->ic_freq, now_tu);
}
ic->ic_curchan = ic->ic_bsschan = vap->iv_csa_chan;
ieee80211_doth_cancel_cs(ic);
ic->ic_curchan = ic->ic_bsschan = ic->ic_csa_chan;
ic->ic_set_channel(ic);
}
static void
void
ieee80211_doth_switch_channel_tmr(unsigned long arg)
{
struct ieee80211vap *vap = (struct ieee80211vap *)arg;
ieee80211_doth_switch_channel(vap);
struct ieee80211com *ic = (struct ieee80211com *)arg;
ieee80211_doth_switch_channel(ic);
}
/* This function is called when we received an action frame or a beacon frame
* containing a CSA IE. */
static int
ieee80211_parse_csaie(struct ieee80211_node *ni, u_int8_t *frm,
const struct ieee80211_frame *wh)
@ -2747,19 +2739,35 @@ ieee80211_parse_csaie(struct ieee80211_node *ni, u_int8_t *frm,
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c;
struct ieee80211_ie_csa *csa_ie = (struct ieee80211_ie_csa *)frm;
int subtype;
if (!frm) {
/* we had CS underway but now we got Beacon without CSA IE */
/* XXX abuse? */
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
IEEE80211_DPRINTF(
vap, IEEE80211_MSG_DOTH,
"%s: Receiving %s%s%s frame with CSA IE: %u/%u/%u\n",
__func__,
subtype == IEEE80211_FC0_SUBTYPE_BEACON ? "beacon" : "",
subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP ? "probe response" : "",
subtype == IEEE80211_FC0_SUBTYPE_ACTION ? "action" : "",
csa_ie->csa_mode,
csa_ie->csa_chan,
csa_ie->csa_count);
if ((ic->ic_flags & IEEE80211_F_DOTH) == 0) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: channel switch is scheduled, but we got "
"Beacon without CSA IE!\n", __func__);
ieee80211_doth_cancel_cs(vap);
"%s: Ignored CSA IE since 802.11h "
"support is disabled\n", __func__);
return 0;
}
if (csa_ie->csa_id != IEEE80211_ELEMID_CHANSWITCHANN) {
IEEE80211_DISCARD_IE(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH,
wh, "channel switch", "invalid element ID %u",
csa_ie->csa_id);
return -1;
}
if (csa_ie->csa_len != 3) {
IEEE80211_DISCARD_IE(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH,
@ -2768,6 +2776,15 @@ ieee80211_parse_csaie(struct ieee80211_node *ni, u_int8_t *frm,
return -1;
}
if ((csa_ie->csa_mode != IEEE80211_CSA_CAN_STOP_TX) &&
(csa_ie->csa_mode != IEEE80211_CSA_MUST_STOP_TX)) {
IEEE80211_DISCARD_IE(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH,
wh, "channel switch", "invalid CSA mode %u",
csa_ie->csa_mode);
return -1;
}
if (isclr(ic->ic_chan_avail, csa_ie->csa_chan)) {
IEEE80211_DISCARD_IE(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH,
@ -2785,112 +2802,17 @@ ieee80211_parse_csaie(struct ieee80211_node *ni, u_int8_t *frm,
return -1;
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: channel switch to %u in %u tbtt (mode %u) announced\n",
__func__, csa_ie->csa_chan, csa_ie->csa_count,
csa_ie->csa_mode);
ieee80211_start_new_csa(vap, csa_ie->csa_mode, c, csa_ie->csa_count,
subtype == IEEE80211_FC0_SUBTYPE_BEACON);
if (vap->iv_csa_jiffies) {
/* CSA was received recently */
if (c != vap->iv_csa_chan) {
/* XXX abuse? */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: channel switch channel "
"changed from %3d (%4d MHz) to %u!\n",
__func__,
vap->iv_csa_chan->ic_ieee,
vap->iv_csa_chan->ic_freq,
csa_ie->csa_chan);
if (vap->iv_csa_count > IEEE80211_CSA_PROTECTION_PERIOD)
ieee80211_doth_cancel_cs(vap);
return 0;
}
if (csa_ie->csa_mode != vap->iv_csa_mode) {
/* Can be abused, but with no (to little) impact. */
/* CS mode change has no influence on our actions since
* we don't respect cs modes at all (yet). Complain and
* forget. */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: channel switch mode changed from "
"%u to %u!\n", __func__,
vap->iv_csa_mode, csa_ie->csa_mode);
}
if (csa_ie->csa_count >= vap->iv_csa_count) {
/* XXX abuse? what for? */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: channel switch count didn't "
"decrease (%u -> %u)!\n", __func__,
vap->iv_csa_count, csa_ie->csa_count);
return 0;
}
{
u_int32_t elapsed = IEEE80211_JIFFIES_TO_TU(
jiffies - vap->iv_csa_jiffies);
u_int32_t cnt_diff = vap->iv_csa_count -
csa_ie->csa_count;
u_int32_t expected = ni->ni_intval * cnt_diff;
int32_t delta = elapsed - expected;
if (delta < 0)
delta = -delta;
if (delta > IEEE80211_CSA_SANITY_THRESHOLD) {
/* XXX abuse? for now, it's safer to cancel CS
* than to follow it blindly */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: %u.%02u bintvals elapsed, "
"but count dropped by %u (delta"
" = %u TUs)\n", __func__,
elapsed / ni->ni_intval,
elapsed * 100 / ni->ni_intval
% 100, cnt_diff, delta);
ieee80211_doth_cancel_cs(vap);
return 0;
}
}
vap->iv_csa_count = csa_ie->csa_count;
mod_timer(&vap->iv_csa_timer, jiffies +
IEEE80211_TU_TO_JIFFIES(vap->iv_csa_count
* ni->ni_intval + 10));
} else {
/* CSA wasn't received recently, so this is the first one in
* the sequence. */
#if 0
/* Needed for DFS / FCC ... */
if (csa_ie->csa_count < IEEE80211_CSA_PROTECTION_PERIOD) {
IEEE80211_DISCARD_IE(vap,
IEEE80211_MSG_ELEMID |
IEEE80211_MSG_DOTH,
wh, "channel switch",
"initial announcement: channel switch"
" would occur too soon (in %u tbtt)",
csa_ie->csa_count);
return 0;
}
#endif
vap->iv_csa_mode = csa_ie->csa_mode;
vap->iv_csa_count = csa_ie->csa_count;
vap->iv_csa_chan = c;
vap->iv_csa_timer.function = ieee80211_doth_switch_channel_tmr;
vap->iv_csa_timer.data = (unsigned long)vap;
mod_timer(&vap->iv_csa_timer, jiffies + IEEE80211_TU_TO_JIFFIES(
vap->iv_csa_count * ni->ni_intval + 10));
/* This is an extension to 802.11h. When we receive a CSA IE with
* Mode=1, then we treat it like a "remote" radar detected event. This
* is needed to effectively stop transmitting */
if (csa_ie->csa_mode == IEEE80211_CSA_MUST_STOP_TX) {
ic->ic_radar_detected(ic, "remote radar from CSA IE",
1, csa_ie->csa_chan);
}
vap->iv_csa_jiffies = jiffies;
if (vap->iv_csa_count <= 1)
ieee80211_doth_switch_channel(vap);
return 0;
}
@ -3216,6 +3138,20 @@ ieee80211_recv_mgmt(struct ieee80211vap *vap,
else
IEEE80211_NODE_STAT(ni, rx_proberesp);
/* According to 802.11h 11.6 p47 : if a STA with
* dot11SpectrumManagementRequired set to TRUE receives a
* Beacon or Probe Response frames with the Spectrum
* Management bit set to 1, then we behave the same way as if
* Channel Availability Check is done */
if ((ic->ic_flags & IEEE80211_F_DOTH) &&
(scan.capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"Received an enabling signal from "
MAC_FMT "\n", MAC_ADDR(wh->i_addr2));
ic->ic_set_dfs_clear(ic, 1);
}
/*
* When operating in station mode, check for state updates.
* Be careful to ignore beacons received while doing a
@ -3327,7 +3263,7 @@ ieee80211_recv_mgmt(struct ieee80211vap *vap,
ni->ni_flags &= ~IEEE80211_NODE_UAPSD;
if (scan.ath != NULL)
ieee80211_parse_athParams(ni, scan.ath);
if (scan.csa != NULL || vap->iv_csa_jiffies)
if (scan.csa != NULL)
ieee80211_parse_csaie(ni, scan.csa, wh);
if (scan.tim != NULL) {
/*
@ -3374,6 +3310,16 @@ ieee80211_recv_mgmt(struct ieee80211vap *vap,
ieee80211_bg_scan(vap);
return 0;
}
/* ieee80211_parse_csaie() needs to be called in IBSS mode as
* well. We filter on the IBSSID */
if ((vap->iv_opmode == IEEE80211_M_IBSS) &&
(scan.capinfo & IEEE80211_CAPINFO_IBSS) &&
IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid)) {
if (scan.csa != NULL)
ieee80211_parse_csaie(ni,scan.csa,wh);
}
/*
* If scanning, just pass information to the scan module.
*/
@ -4098,6 +4044,40 @@ ieee80211_recv_mgmt(struct ieee80211vap *vap,
}
break;
}
case IEEE80211_FC0_SUBTYPE_ACTION: {
unsigned char cat, act;
/* we only parse Action frame from our BSSID */
if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid)) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
wh->i_addr3, NULL,
"%s", "not to bss");
vap->iv_stats.is_rx_wrongbss ++;
return 0;
}
/* parse the Category field */
switch (cat = *frm++) {
case IEEE80211_ACTION_SPECTRUM_MANAGEMENT:
switch (act = *frm++) {
case IEEE80211_ACTION_S_CHANSWITCHANN:
ieee80211_parse_csaie(ni, frm, wh);
break;
default:
IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh,
"mgt", "action frame category 0x%x, "
"action 0x%x not handled", cat, act);
break;
}
break;
default:
IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "mgt",
"action frame category 0x%x not handled", cat);
break;
}
break;
}
default:
IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "mgt",
"subtype 0x%x not handled", subtype);

View File

@ -426,6 +426,136 @@ proc_read_nodes(struct ieee80211vap *vap, char *buf, int space)
return (p - buf);
}
static int
proc_doth_print(struct ieee80211vap *vap, char *buf, int space)
{
struct ieee80211com *ic = vap->iv_ic;
char *p = buf;
struct ieee80211_channel *channel;
int i;
char str[50];
for (i = 0; i < ic->ic_nchans; i++) {
/* Assume each lines needs 500 bytes max */
if (buf + space < p + 500)
break;
channel = &ic->ic_channels[i];
if ((ic->ic_chan_non_occupy[i].tv_sec == 0) &&
(ic->ic_chan_non_occupy[i].tv_usec) == 0) {
str[0] = 0; /* empty string */
} else {
sprintf(str, " End: %ld.%06ld",
ic->ic_chan_non_occupy[i].tv_sec,
(long)ic->ic_chan_non_occupy[i].tv_usec);
}
p += sprintf(p,
"Channel %3d (%4d MHz) : %s %s %s%s%s%s\n",
channel->ic_ieee,
channel->ic_freq,
isset(ic->ic_chan_active, channel->ic_ieee) ?
" Active" : "Inactive",
IEEE80211_IS_CHAN_PASSIVE(channel) ? " Dfs":"NoDfs",
IEEE80211_IS_CHAN_RADAR(channel) ? " Radar":"NoRadar",
IEEE80211_IS_CHAN_INDOOR(channel) ? " Indoor" : "",
IEEE80211_IS_CHAN_OUTDOOR(channel) ? " Outdoor" : "",
str);
}
return (p - buf);
}
static int
proc_doth_state_print(struct ieee80211vap *vap, char *buf, int space)
{
struct ieee80211com *ic = vap->iv_ic;
char *p = buf;
struct net_device *dev = ic->ic_dev;
struct ath_softc *sc = netdev_priv(dev);
p += sprintf(p,
"sc_curchan: --- (%4d MHz)\n",
sc->sc_curchan.channel);
p += sprintf(p,
" CHANNEL_DFS:%d\n"
" CHANNEL_DFS_CLEAR:%d\n"
" CHANNEL_INTERFERENCE:%d\n"
" CAC in progress:%d\n",
sc->sc_curchan.privFlags & CHANNEL_DFS ? 1 : 0,
sc->sc_curchan.privFlags & CHANNEL_DFS_CLEAR ? 1 : 0,
sc->sc_curchan.privFlags & CHANNEL_INTERFERENCE ? 1 : 0,
timer_pending(&sc->sc_dfs_cac_timer));
p += sprintf(p,
"ic_curchan: %3d (%4d MHz)\n"
" IEEE80211_F_DOTH:%d\n"
" IEEE80211_IS_CHAN_RADAR:%d\n",
ic->ic_curchan->ic_ieee,
ic->ic_curchan->ic_freq,
ic->ic_flags & IEEE80211_F_DOTH ? 1 : 0,
IEEE80211_IS_CHAN_RADAR(ic->ic_curchan) ? 1 : 0);
return (p - buf);
}
static int
proc_iv_bss_print(struct ieee80211vap *vap, char *buf, int space)
{
char *p = buf;
const struct ieee80211_node *ni = vap->iv_bss;
p += sprintf(p, "vap:%p vap->iv_bss: %p\n",
vap, ni);
if (ni == NULL)
return (p - buf);
p += sprintf(p, "ni_macaddr: " MAC_FMT "\n"
"ni_bssid: " MAC_FMT "\n"
"ni_tstamp: 0x%llx us\n"
"ni_intval: %u TU\n"
"ni_capinfo: 0x%x%s%s%s%s%s%s%s%s%s%s%s%s\n",
MAC_ADDR(ni->ni_macaddr),
MAC_ADDR(ni->ni_bssid),
le64_to_cpu(ni->ni_tstamp.tsf),
ni->ni_intval,
ni->ni_capinfo,
ni->ni_capinfo & IEEE80211_CAPINFO_ESS ? " ESS" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_IBSS ? " IBSS" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_CF_POLLABLE ?
" CF_POLLABLE" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_CF_POLLREQ ?
" CF_POLLREQ" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY ?
" PRIVACY" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE ?
" SHORT_PREAMBLE" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_PBCC ? " PBCC" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_CHNL_AGILITY ?
" CHANNEL_AGILITY" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT ?
" SPECTRUM_MGMT" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME ?
" SHORT_SLOTTIME" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_RSN ? " RSN" : "",
ni->ni_capinfo & IEEE80211_CAPINFO_DSSSOFDM ?
"DSSSOFDM" : "");
if (ni->ni_chan == NULL) {
p += sprintf(p, "ni_chan: NULL\n");
} else if (ni->ni_chan == IEEE80211_CHAN_ANYC) {
p += sprintf(p, "ni_chan: ANY\n");
} else {
p += sprintf(p,
"ni_chan: Frequency:%4u MHz Channel:%3u\n",
ni->ni_chan->ic_freq, ni->ni_chan->ic_ieee);
}
return (p - buf);
}
static ssize_t
proc_ieee80211_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
{
@ -448,11 +578,9 @@ proc_ieee80211_read(struct file *file, char __user *buf, size_t len, loff_t *off
}
static int
proc_ieee80211_open(struct inode *inode, struct file *file)
proc_common_open(struct inode *inode, struct file *file)
{
struct proc_ieee80211_priv *pv = NULL;
struct proc_dir_entry *dp = PDE(inode);
struct ieee80211vap *vap = dp->data;
struct proc_ieee80211_priv *pv;
if (!(file->private_data = kzalloc(sizeof(struct proc_ieee80211_priv),
GFP_KERNEL)))
@ -474,11 +602,83 @@ proc_ieee80211_open(struct inode *inode, struct file *file)
memset(pv->rbuf, 0, MAX_PROC_IEEE80211_SIZE);
pv->max_wlen = MAX_PROC_IEEE80211_SIZE;
pv->max_rlen = MAX_PROC_IEEE80211_SIZE;
return 0;
}
static int
proc_ieee80211_open(struct inode *inode, struct file *file)
{
struct proc_ieee80211_priv *pv = NULL;
struct proc_dir_entry *dp = PDE(inode);
struct ieee80211vap *vap = dp->data;
int result;
result = proc_common_open(inode, file);
if (result != 0)
return result;
/* now read the data into the buffer */
pv = (struct proc_ieee80211_priv *) file->private_data;
pv->rlen = proc_read_nodes(vap, pv->rbuf, MAX_PROC_IEEE80211_SIZE);
return 0;
}
static int
proc_doth_open(struct inode *inode, struct file *file)
{
struct proc_ieee80211_priv *pv = NULL;
struct proc_dir_entry *dp = PDE(inode);
struct ieee80211vap *vap = dp->data;
int result;
result = proc_common_open(inode, file);
if (result != 0)
return result;
/* now read the data into the buffer */
pv = (struct proc_ieee80211_priv *) file->private_data;
pv->rlen = proc_doth_print(vap, pv->rbuf, MAX_PROC_IEEE80211_SIZE);
return 0;
}
static int
proc_doth_state_open(struct inode *inode, struct file *file)
{
struct proc_ieee80211_priv *pv = NULL;
struct proc_dir_entry *dp = PDE(inode);
struct ieee80211vap *vap = dp->data;
int result;
result = proc_common_open(inode, file);
if (result != 0)
return result;
/* now read the data into the buffer */
pv = (struct proc_ieee80211_priv *) file->private_data;
pv->rlen = proc_doth_state_print(vap,
pv->rbuf, MAX_PROC_IEEE80211_SIZE);
return 0;
}
static int
proc_iv_bss_open(struct inode *inode, struct file *file)
{
struct proc_ieee80211_priv *pv = NULL;
struct proc_dir_entry *dp = PDE(inode);
struct ieee80211vap *vap = dp->data;
int result;
result = proc_common_open(inode, file);
if (result != 0)
return result;
/* now read the data into the buffer */
pv = (struct proc_ieee80211_priv *) file->private_data;
pv->rlen = proc_iv_bss_print(vap, pv->rbuf, MAX_PROC_IEEE80211_SIZE);
return 0;
}
static ssize_t
proc_ieee80211_write(struct file *file, const char __user *buf, size_t len, loff_t *offset)
{
@ -523,6 +723,27 @@ static struct file_operations proc_ieee80211_ops = {
.release = proc_ieee80211_close,
};
static struct file_operations proc_doth_ops = {
.read = proc_ieee80211_read,
.write = proc_ieee80211_write,
.open = proc_doth_open,
.release = proc_ieee80211_close,
};
static struct file_operations proc_doth_state_ops = {
.read = proc_ieee80211_read,
.write = proc_ieee80211_write,
.open = proc_doth_state_open,
.release = proc_ieee80211_close,
};
static struct file_operations proc_iv_bss_ops = {
.read = proc_ieee80211_read,
.write = proc_ieee80211_write,
.open = proc_iv_bss_open,
.release = proc_ieee80211_close,
};
#ifdef IEEE80211_DEBUG
static int
IEEE80211_SYSCTL_DECL(ieee80211_sysctl_debug, ctl, write, filp, buffer,
@ -808,6 +1029,9 @@ ieee80211_virtfs_latevattach(struct ieee80211vap *vap)
/* Create a proc entry listing the associated stations */
ieee80211_proc_vcreate(vap, &proc_ieee80211_ops, "associated_sta");
ieee80211_proc_vcreate(vap, &proc_doth_ops, "doth");
ieee80211_proc_vcreate(vap, &proc_doth_state_ops, "doth_state");
ieee80211_proc_vcreate(vap, &proc_iv_bss_ops, "iv_bss");
/* Recreate any other proc entries that have been registered */
if (vap->iv_proc) {

View File

@ -235,6 +235,17 @@ ieee80211_hardstart(struct sk_buff *skb, struct net_device *dev)
ieee80211_parent_queue_xmit(skb);
return NETDEV_TX_OK;
}
/* If we have detected a radar on the current channel, or another node
* told us to stop transmitting, we no longer transmit. Note : we
* still allow a monitor interface to transmit */
if (IEEE80211_IS_CHAN_RADAR(ic->ic_curchan) &&
(ic->ic_flags & IEEE80211_F_DOTH)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: dropping data since we are under radar\n",
__func__);
goto bad;
}
/* Cancel any running BG scan */
ieee80211_cancel_scan(vap);
@ -394,7 +405,8 @@ ieee80211_send_setup(struct ieee80211vap *vap,
* reference (and potentially freeing up any associated storage).
*/
static void
ieee80211_mgmt_output(struct ieee80211_node *ni, struct sk_buff *skb, int type)
ieee80211_mgmt_output(struct ieee80211_node *ni, struct sk_buff *skb, int type,
const u_int8_t da[IEEE80211_ADDR_LEN])
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
@ -408,7 +420,7 @@ ieee80211_mgmt_output(struct ieee80211_node *ni, struct sk_buff *skb, int type)
skb_push(skb, sizeof(struct ieee80211_frame));
ieee80211_send_setup(vap, ni, wh,
IEEE80211_FC0_TYPE_MGT | type,
vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid);
vap->iv_myaddr, da, vap->iv_bssid);
/* XXX power management */
if ((SKB_CB(skb)->flags & M_LINK0) != 0 && ni->ni_challenge != NULL) {
@ -1746,6 +1758,147 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
return 0;
}
/* Start a new Channel Switch process. It will first check if there is already
* one Channel Switch process running and if so, will determine which one will
* run. This function must be the only function setting IEEE80211_F_CHANSWITCH
* in ic_flags.
*
* is_beacon_frame : true if the csa_count comes from a beacon frame we just
* received. */
void
ieee80211_start_new_csa(struct ieee80211vap *vap,
u_int8_t csa_mode,
struct ieee80211_channel *csa_chan,
u_int8_t csa_count,
int is_beacon_frame)
{
struct ieee80211com *ic = vap->iv_ic;
u_int32_t now_tu, nexttbtt, expires_tu;
unsigned long now, expires;
/* 802.11h 7.3.2.20 : A value of 1 indicates that the switch will
* occur immediately before the next TBTT. A value of 0 indicates that
* the switch will occur at any time after the frame containing the
* element is transmitted. */
now_tu = IEEE80211_TSF_TO_TU(vap->iv_get_tsf(vap));
now = jiffies;
if (csa_count == 0) {
expires_tu = now_tu;
expires = now;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: now:%d count:%d => expires:%d\n",
__func__, now, csa_count, expires);
} else {
/* csa_count includes the current frame if it is a beacon
* frame. */
if (is_beacon_frame)
csa_count --;
/* Compute the closest nexttbtt, next time a beacon for this
* VAP will be sent. */
nexttbtt = vap->iv_get_nexttbtt(vap);
/* Compute ic_csa_expires_tu = nexttbtt + csa_count *
* ni_intval. */
expires_tu = nexttbtt + csa_count * vap->iv_bss->ni_intval;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: now_tu:%ld nexttbtt_tu:%ld "
"=> expires_tu:%ld\n",
__func__, now_tu, nexttbtt,
expires_tu);
/* Convert to jiffies, including a margin. */
expires = now +
IEEE80211_TU_TO_JIFFIES(expires_tu - now_tu - 10) - 1;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: now:%d count:%d => expires:%d\n",
__func__, now, csa_count, expires);
}
/* If we have a CS in progress, we ignore this new CSA IE if the
* channel switch time is later than the current one. */
if ((ic->ic_flags & IEEE80211_F_CHANSWITCH) &&
(expires_tu > ic->ic_csa_expires_tu)) {
/* We do not ignore csa_mode if it says we must stop sending
* right now. */
if (ic->ic_csa_mode == IEEE80211_CSA_CAN_STOP_TX &&
csa_mode == IEEE80211_CSA_MUST_STOP_TX) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: Updating CSA mode\n",
__func__);
ic->ic_csa_mode = csa_mode;
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: Ignored CSA IE since a sooner "
"channel switch is scheduled\n", __func__);
return;
}
ic->ic_csa_mode = csa_mode;
ic->ic_csa_chan = csa_chan;
ic->ic_csa_expires_tu = expires_tu;
mod_timer(&ic->ic_csa_timer, expires);
ic->ic_flags |= IEEE80211_F_CHANSWITCH;
}
/* Send a broadcast CSA frame, announcing the new channel. References are from
* IEEE 802.11h-2003. CSA frame format is an "Action" frame (Type: 00, Subtype:
* 1101, see 7.1.3.1.2)
*
* [1] Category : 0, Spectrum Management, 7.3.1.11
* [1] Action : 4, Channel Switch Announcement, 7.4.1 and 7.4.1.5
* [1] Element ID : 37, Channel Switch Announcement, 7.3.2
* [1] Length : 3, 7.3.2.20
* [1] Channel Switch Mode : 1, stop transmission immediately
* [1] New Channel Number
* [1] Channel Switch Count in TBTT : 0, immediate channel switch
*
* csa_mode : IEEE80211_CSA_MANDATORY / IEEE80211_CSA_ADVISORY
* csa_chan : new IEEE channel number
* csa_tbtt : TBTT until Channel Switch happens */
void
ieee80211_send_csa_frame(struct ieee80211vap *vap,
u_int8_t csa_mode,
u_int8_t csa_chan,
u_int8_t csa_count)
{
struct ieee80211_node *ni = vap->iv_bss;
struct ieee80211com *ic = ni->ni_ic;
struct sk_buff *skb;
const int frm_len = 7;
u_int8_t *frm;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: Sending action frame with CSA IE: %u/%u/%u\n",
__func__, csa_mode, csa_chan, csa_count);
skb = ieee80211_getmgtframe(&frm, frm_len);
if (skb == NULL) {
IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni,
"%s: cannot get buf; size %u", __func__, frm_len);
vap->iv_stats.is_tx_nobuf++;
return ;
}
*frm++ = IEEE80211_ACTION_SPECTRUM_MANAGEMENT; /* Category */
*frm++ = IEEE80211_ACTION_S_CHANSWITCHANN; /* Spectrum Management */
*frm++ = IEEE80211_ELEMID_CHANSWITCHANN;
*frm++ = 3;
*frm++ = csa_mode;
*frm++ = csa_chan;
*frm++ = csa_count;
ieee80211_mgmt_output(ieee80211_ref_node(ni), skb,
IEEE80211_FC0_SUBTYPE_ACTION,
ic->ic_dev->broadcast);
}
/*
* Send a management frame. The node is for the destination (or ic_bss
* when in station mode). Nodes other than ic_bss have their reference
@ -2181,7 +2334,8 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg)
/* NOTREACHED */
}
ieee80211_mgmt_output(ieee80211_ref_node(ni), skb, type);
ieee80211_mgmt_output(ieee80211_ref_node(ni), skb, type,
ni->ni_macaddr);
if (timer)
mod_timer(&vap->iv_mgtsend, jiffies + timer * HZ);
return 0;

View File

@ -130,7 +130,6 @@ ieee80211_proto_vattach(struct ieee80211vap *vap)
init_timer(&vap->iv_mgtsend);
init_timer(&vap->iv_xrvapstart);
init_timer(&vap->iv_swbmiss);
init_timer(&vap->iv_csa_timer);
vap->iv_mgtsend.function = ieee80211_tx_timeout;
vap->iv_mgtsend.data = (unsigned long) vap;
@ -1405,7 +1404,13 @@ __ieee80211_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int
if (arg != 0)
ieee80211_scan_assoc_fail(ic,
vap->iv_bss->ni_macaddr, arg);
if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
/* ic_roaming is relevant to STA mode only. Since DFS
* CAC does a SCAN -> SCAN transition, this code was
* causing a spurious scan that was stopping DFS CAC
* altogether */
if (vap->iv_opmode == IEEE80211_M_STA &&
ic->ic_roaming == IEEE80211_ROAMING_AUTO)
ieee80211_check_scan(vap,
IEEE80211_SCAN_ACTIVE,
IEEE80211_SCAN_FOREVER,
@ -1466,7 +1471,8 @@ __ieee80211_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
ieee80211_sta_leave(ni);
if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
if (vap->iv_opmode == IEEE80211_M_STA &&
ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
/* try to reauth */
IEEE80211_SEND_MGMT(ni,
IEEE80211_FC0_SUBTYPE_AUTH, 1);

View File

@ -77,6 +77,12 @@ int ieee80211_hardstart(struct sk_buff *, struct net_device *);
void ieee80211_parent_queue_xmit(struct sk_buff *);
int ieee80211_send_nulldata(struct ieee80211_node *);
int ieee80211_send_qosnulldata(struct ieee80211_node *, int);
void ieee80211_start_new_csa(struct ieee80211vap *vap, u_int8_t csa_mode,
struct ieee80211_channel *csa_chan, u_int8_t csa_count,
int is_beacon_frame);
void ieee80211_send_csa_frame(struct ieee80211vap *vap, u_int8_t csa_mode,
u_int8_t csa_chan, u_int8_t csa_count);
void ieee80211_doth_switch_channel_tmr(unsigned long arg);
int ieee80211_send_mgmt(struct ieee80211_node *, int, int);
int ieee80211_send_probereq(struct ieee80211_node *,
const u_int8_t sa[IEEE80211_ADDR_LEN],

View File

@ -969,114 +969,143 @@ ieee80211_scan_flush(struct ieee80211com *ic)
}
}
/* Check if a channel is usable for a DFS channel switch */
static int
ieee80211_dfs_is_channel_usable(struct ieee80211com *ic,
struct ieee80211_channel *channel)
{
u_int16_t curChanBandFlags, curChanOutdoorFlags;
/*
* Criteria for the new frequency:
* - it must be different from the current frequency
* - it must not have radar detected
* - it must be an active channel
* - it must be in the same band (2.4Ghz/5Ghz)
* - it must be suitable for indoor/outdoor use according to what the
* user selected
*/
curChanBandFlags = ic->ic_bsschan->ic_flags &
(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ);
curChanOutdoorFlags = ic->ic_country_outdoor ?
IEEE80211_CHAN_OUTDOOR : IEEE80211_CHAN_INDOOR;
return ((channel->ic_freq != ic->ic_bsschan->ic_freq) &&
(!IEEE80211_IS_CHAN_RADAR(channel) &&
(ic->ic_flags & IEEE80211_F_DOTH)) &&
(isset(ic->ic_chan_active, channel->ic_ieee)) &&
(channel->ic_flags & curChanBandFlags) &&
(channel->ic_flags & curChanOutdoorFlags));
}
/*
* Execute radar channel change. This is called when a radar/dfs
* signal is detected. AP mode only. Return 1 on success, 0 on
* failure
* Execute radar channel change. This is called when a radar signal is
* detected. AP/IBSS mode only. Return 1 on success, 0 on failure
*/
int
ieee80211_scan_dfs_action(struct ieee80211vap *vap,
const struct ieee80211_scan_entry *se)
void
ieee80211_scan_dfs_action(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *new_channel = NULL;
int chanStart, i, count;
if (!IEEE80211_IS_MODE_DFS_MASTER(vap->iv_opmode))
return 0;
if (se != NULL) {
new_channel = se->se_chan;
if (new_channel != NULL) {
if (!IEEE80211_IS_MODE_DFS_MASTER(vap->iv_opmode)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: called but not in DFS master mode\n",
__func__);
return;
}
/* Do nothing if a channel switch is already in progress */
if (ic->ic_flags & IEEE80211_F_CHANSWITCH) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: channel switch already in progress\n",
__func__);
return;
}
if ((ic->ic_curchan != NULL) &&
(ic->ic_curchan != IEEE80211_CHAN_ANYC)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: ic_curchan is %3d (%4d MHz)\n",
__func__, ic->ic_curchan->ic_ieee,
ic->ic_curchan->ic_freq);
}
if ((ic->ic_bsschan != NULL) &&
(ic->ic_bsschan != IEEE80211_CHAN_ANYC)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: ic_bsschan is %3d (%4d MHz)\n",
__func__, ic->ic_bsschan->ic_ieee,
ic->ic_bsschan->ic_freq);
}
/* According to FCC/ETSI rules on uniform spreading, we shall select a
* channel out of the list of usable channels so that the probability
* of selecting a given channel shall be the same for all channels
* (reference: ETSI 301 893 v1.3.1 $4.6.2.5.1 */
/* First, we count the usable channels */
count = 0;
for (i = 0; i < ic->ic_nchans; i++) {
if (ieee80211_dfs_is_channel_usable(
ic, &ic->ic_channels[i])) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: new channel found in scan cache\n",
__func__);
"%s: usable channel %3d "
"(%4d MHz)\n",
__func__,
ic->ic_channels[i].ic_ieee,
ic->ic_channels[i].ic_freq);
count ++;
}
} else {
/* No channel was found via scan module, means no good scanlist
* was found */
int chanStart, i, count;
u_int32_t curChanFlags;
}
if ((ic->ic_curchan != NULL) &&
(ic->ic_curchan != IEEE80211_CHAN_ANYC)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: ic_curchan is %3d (%4d MHz)\n",
__func__, ic->ic_curchan->ic_ieee,
ic->ic_curchan->ic_freq);
}
if (count != 0) {
/* Next, we pickup a random usable channel */
chanStart = jiffies % count;
if ((ic->ic_bsschan != NULL) &&
(ic->ic_bsschan != IEEE80211_CHAN_ANYC)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: ic_bsschan is %3d (%4d MHz)\n",
__func__, ic->ic_bsschan->ic_ieee,
ic->ic_bsschan->ic_freq);
}
/* According to FCC/ETSI rules on uniform spreading, we shall
* select a channel out of the list of usable channels so that
* the probability of selecting a given channel shall be the
* same for all channels (reference: ETSI 301 893 v1.3.1
* $4.6.2.5.1 */
/* First, we count the usable channels */
count = 0;
curChanFlags = (ic->ic_bsschan->ic_flags) &
~(IEEE80211_CHAN_RADAR);
for (i = 0; i < ic->ic_nchans; i++) {
if ((ic->ic_channels[i].ic_ieee !=
ic->ic_bsschan->ic_ieee) &&
(ic->ic_channels[i].ic_flags == curChanFlags)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: usable channel %3d "
"(%4d MHz)\n",
__func__,
ic->ic_channels[i].ic_ieee,
ic->ic_channels[i].ic_freq);
count ++;
}
}
if (count != 0) {
/* Next, we pickup a random usable channel */
chanStart = jiffies % count;
count = 0;
for (i = 0; i < ic->ic_nchans; i++) {
if ((ic->ic_channels[i].ic_ieee !=
ic->ic_bsschan->ic_ieee) &&
(ic->ic_channels[i].ic_flags ==
curChanFlags)) {
if (count++ == chanStart) {
new_channel =
&ic->ic_channels[i];
break;
}
/* must be the same formula as above */
if (ieee80211_dfs_is_channel_usable(
ic, &ic->ic_channels[i])) {
if (count++ == chanStart) {
new_channel =
&ic->ic_channels[i];
break;
}
}
}
if (new_channel != NULL)
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: new random channel found %3d "
"(%4d MHz)\n", __func__,
new_channel->ic_ieee,
new_channel->ic_freq);
}
if (!new_channel) {
/* Search for the first channel with no radar detected */
int n = 0;
for (n = 0; n < ic->ic_nchans; n++) {
if (!IEEE80211_IS_CHAN_RADAR(&ic->ic_channels[n])) {
new_channel = &ic->ic_channels[n];
break;
}
if (new_channel == NULL) {
/* We found no channel to switch to (for instance, all
* available channels are under Non-Occupancy Period). In this
* case, we still need to send an action frame and beacon with
* CSA IE to tell other nodes to stop their transmission, in
* order to meet the Channel Closing Transmission Time
* requirement of FCC/ETSI */
if ((ic->ic_bsschan != NULL) &&
(ic->ic_bsschan != IEEE80211_CHAN_ANYC)) {
new_channel = ic->ic_bsschan;
}
if (new_channel != NULL)
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: new non-radar channel found\n",
__func__);
}
if (new_channel != NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: new channel found %3d "
"(%4d MHz)\n", __func__,
new_channel->ic_ieee,
new_channel->ic_freq);
/* send a CSA frame immediately */
ieee80211_send_csa_frame(vap,
IEEE80211_CSA_MUST_STOP_TX,
new_channel->ic_ieee,
IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT);
/* A suitable scan entry was found, so change channels */
if (vap->iv_state == IEEE80211_S_RUN) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
@ -1085,10 +1114,9 @@ ieee80211_scan_dfs_action(struct ieee80211vap *vap,
new_channel->ic_ieee,
new_channel->ic_freq);
ic->ic_chanchange_chan = new_channel->ic_ieee;
ic->ic_chanchange_tbtt =
IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT;
ic->ic_flags |= IEEE80211_F_CHANSWITCH;
ieee80211_start_new_csa(vap,
IEEE80211_CSA_MUST_STOP_TX, new_channel,
IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT, 0);
} else {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
@ -1108,9 +1136,6 @@ ieee80211_scan_dfs_action(struct ieee80211vap *vap,
/* A suitable scan entry was not found */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: new channel not found\n", __func__);
return 0;
}
return 1;
}
EXPORT_SYMBOL(ieee80211_scan_dfs_action);

View File

@ -96,7 +96,7 @@ int ieee80211_check_scan(struct ieee80211vap *, int, u_int, u_int,
int ieee80211_bg_scan(struct ieee80211vap *);
void ieee80211_cancel_scan(struct ieee80211vap *);
int ieee80211_scan_dfs_action(struct ieee80211vap *, const struct ieee80211_scan_entry *);
void ieee80211_scan_dfs_action(struct ieee80211vap *);
struct ieee80211_scanparams;
void ieee80211_add_scan(struct ieee80211vap *, const struct ieee80211_scanparams *,

View File

@ -828,7 +828,8 @@ pick_channel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap,
continue;
/* Verify channel is not marked for non-occupancy */
if (IEEE80211_IS_CHAN_RADAR(c->chan))
if (IEEE80211_IS_CHAN_RADAR(c->chan) &&
(ic->ic_flags & IEEE80211_F_DOTH))
continue;
/* Do not select 802.11a ST if mode is specified and is not
@ -853,6 +854,12 @@ pick_channel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap,
/* break the loop as the subsequent chans won't be
* better */
break;
if (!IEEE80211_ARE_CHANS_SAME_MODE(c->chan,
ic->ic_bsschan))
/* break the loop as the subsequent chans won't be
* better */
break;
}
if (sta_assoc != 0) {

View File

@ -234,12 +234,6 @@ struct ieee80211vap {
u_int iv_bgscanintvl; /* bg scan min interval */
u_int iv_scanvalid; /* scan cache valid threshold */
struct ieee80211_roam iv_roam; /* sta-mode roaming state */
unsigned long iv_csa_jiffies; /* last csa recv jiffies */
u_int8_t iv_csa_count; /* last csa count */
struct ieee80211_channel *iv_csa_chan; /* last csa channel */
u_int8_t iv_csa_mode; /* last csa mode */
struct timer_list iv_csa_timer; /* csa timer */
u_int32_t *iv_aid_bitmap; /* association id map */
u_int16_t iv_max_aid;
u_int16_t iv_sta_assoc; /* stations associated */
@ -282,7 +276,6 @@ struct ieee80211vap {
struct ieee80211vap *iv_xrvap; /* pointer to XR VAP , if XR is enabled */
u_int16_t iv_xrbcnwait; /* SWBA count incremented until it reaches XR_BECON_FACTOR */
struct timer_list iv_xrvapstart; /* timer to start xr */
u_int8_t iv_chanchange_count; /* 11h counter for channel change */
int iv_mcast_rate; /* Multicast rate (Kbps) */
const struct ieee80211_aclator *iv_acl; /* aclator glue */
@ -297,6 +290,8 @@ struct ieee80211vap {
struct ieee80211_spy iv_spy; /* IWSPY support */
struct ieee80211_app_ie app_ie[IEEE80211_APPIE_NUM_OF_FRAME]; /* app-specified IEs by frame type */
u_int32_t app_filter; /* filters which management frames are forwarded to app */
u_int64_t (*iv_get_tsf)(struct ieee80211vap *);
u_int32_t (*iv_get_nexttbtt)(struct ieee80211vap *);
};
/* Debug functions need the definition of struct ieee80211vap because iv_debug
@ -437,8 +432,13 @@ struct ieee80211com {
* know value until change to channel and detect).
*/
u_int8_t ic_curchanmaxpwr;
u_int8_t ic_chanchange_tbtt;
u_int8_t ic_chanchange_chan;
/* To handle Channel Switch Announcements, only valid if ic_flags has
* IEEE80211_F_CHANSWITCH set. */
u_int8_t ic_csa_mode;
struct ieee80211_channel * ic_csa_chan;
u_int32_t ic_csa_expires_tu;
struct timer_list ic_csa_timer;
/* Global debug flags applicable to all VAPs */
int ic_debug;
@ -497,9 +497,6 @@ struct ieee80211com {
void (*ic_set_dfs_testmode)(struct ieee80211com *, int);
int (*ic_get_dfs_testmode)(struct ieee80211com *);
/* inject a fake radar signal -- used while on a 802.11h DFS channels */
unsigned int (*ic_test_radar)(struct ieee80211com *);
/* dump HAL */
unsigned int (*ic_dump_hal_map)(struct ieee80211com *);
@ -511,6 +508,14 @@ struct ieee80211com {
void (*ic_set_dfs_excl_period)(struct ieee80211com *, unsigned int);
unsigned int (*ic_get_dfs_excl_period)(struct ieee80211com *);
/* DFS flag manipulation */
void (*ic_set_dfs_clear)(struct ieee80211com *, int);
void (*ic_set_dfs_interference)(struct ieee80211com *, int);
/* DFS radar detection handling */
void (*ic_radar_detected)(struct ieee80211com *, const char *cause,
int switchChanRequested, u_int8_t switchChan);
/* Set coverage class */
void (*ic_set_coverageclass)(struct ieee80211com *);

View File

@ -74,7 +74,8 @@
#define RESCAN 1
static void
pre_announced_chanswitch(struct net_device *dev, u_int32_t channel, u_int32_t tbtt);
pre_announced_chanswitch(struct net_device *dev,
struct ieee80211_channel *channel, u_int8_t csa_count);
static int
preempt_scan(struct net_device *dev, int max_grace, int max_wait)
@ -686,6 +687,8 @@ ieee80211_ioctl_siwfreq(struct net_device *dev, struct iw_request_info *info,
else
i = freq->m;
/* Compute vap->iv_des_chan according to channel number i. "iwconfig
* ath0 channel auto" gives i = -1. 0 & -1 means "auto" channel */
if ((i != 0) && (i != -1)) {
if (i > IEEE80211_CHAN_MAX)
return -EINVAL;
@ -726,18 +729,11 @@ ieee80211_ioctl_siwfreq(struct net_device *dev, struct iw_request_info *info,
if (vap->iv_opmode == IEEE80211_M_HOSTAP)
return -EINVAL;
}
if ((vap->iv_state == IEEE80211_S_RUN) && (c == vap->iv_des_chan))
return 0; /* no change, return */
/* Don't allow to change to channel with radar found */
if (IEEE80211_IS_CHAN_RADAR(c))
return -EINVAL;
/*
* Mark desired channel and if running force a
* radio change.
*/
vap->iv_des_chan = c;
if (vap->iv_des_chan == c)
return 0; /* no change, return */
/* Mark desired channel and if running force a radio
* change. */
vap->iv_des_chan = c;
} else {
/*
* Interpret channel 0 to mean "no desired channel";
@ -745,9 +741,20 @@ ieee80211_ioctl_siwfreq(struct net_device *dev, struct iw_request_info *info,
* channel.
*/
if (vap->iv_des_chan == IEEE80211_CHAN_ANYC)
return 0;
return 0; /* no change, return */
vap->iv_des_chan = IEEE80211_CHAN_ANYC;
}
/* Here, vap->iv_des_chan is correctly initialized */
/* Don't allow to change immediately to a channel where radar has been
* found */
if ((vap->iv_des_chan != NULL) &&
(vap->iv_des_chan != IEEE80211_CHAN_ANYC)) {
if (IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan) &&
(ic->ic_flags & IEEE80211_F_DOTH))
return -EBUSY;
}
#if 0
if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
int mode = ieee80211_chan2mode(vap->iv_des_chan);
@ -755,28 +762,32 @@ ieee80211_ioctl_siwfreq(struct net_device *dev, struct iw_request_info *info,
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 if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
/* Need to use channel switch announcement on beacon if we are
* up and running. We use ic_set_channel directly if we are
* "running" but not "up". Otherwise, iv_des_chan will take
* effect when we are transitioned to RUN state later. */
if (IS_UP(vap->iv_dev) &&
(0 == (vap->iv_des_chan->ic_flags & CHANNEL_DFS))) {
pre_announced_chanswitch(dev,
ieee80211_chan2ieee(ic, vap->iv_des_chan),
IEEE80211_DEFAULT_CHANCHANGE_TBTT_COUNT);
}
else if (vap->iv_state == IEEE80211_S_RUN) {
if ((vap->iv_opmode == IEEE80211_M_MONITOR) ||
(vap->iv_opmode == IEEE80211_M_WDS)) {
if ((vap->iv_des_chan != NULL) &&
(vap->iv_des_chan != IEEE80211_CHAN_ANYC)) {
/* Monitor and wds modes can switch directly. */
ic->ic_curchan = vap->iv_des_chan;
ic->ic_set_channel(ic);
if (vap->iv_state == IEEE80211_S_RUN) {
ic->ic_set_channel(ic);
}
}
} else if (IEEE80211_IS_MODE_DFS_MASTER(vap->iv_opmode) &&
(ic->ic_flags & IEEE80211_F_DOTH)) {
if ((vap->iv_des_chan != NULL) &&
(vap->iv_des_chan != IEEE80211_CHAN_ANYC)) {
/* Need to use channel switch announcement on beacon
* if we are up and running. Use ic_set_channel
* directly if we are "running" but not "up".
* Otherwise, use iv_des_chan which will take effect
* when we are transitioned to RUN state later. */
if (IS_UP(vap->iv_dev)) {
pre_announced_chanswitch(dev, vap->iv_des_chan,
IEEE80211_DEFAULT_CHANCHANGE_TBTT_COUNT);
} else if (vap->iv_state == IEEE80211_S_RUN) {
ic->ic_curchan = vap->iv_des_chan;
ic->ic_set_channel(ic);
}
}
} else {
/* Need to go through the state machine in case we need
@ -1568,6 +1579,8 @@ ieee80211_ioctl_hal_map(struct net_device *dev, struct iw_request_info *info,
return 0;
}
/* iwpriv wlan0 doth_radar X simulate a radar detection where X is either 0 to
* switch to a random channel or the IEEE channel to switch to */
static int
ieee80211_ioctl_radar(struct net_device *dev, struct iw_request_info *info,
@ -1576,9 +1589,20 @@ ieee80211_ioctl_radar(struct net_device *dev, struct iw_request_info *info,
int *params = (int *)extra;
struct ieee80211vap *vap = netdev_priv(dev);
struct ieee80211com *ic = vap->iv_ic;
if (!(ic->ic_flags & IEEE80211_F_DOTH))
return 0;
params[0] = ic->ic_test_radar(ic);
int switchChanRequested;
u_int8_t switchChan;
if (params[0] != 0) {
switchChanRequested = 1;
switchChan = params[0];
} else {
switchChanRequested = 0;
switchChan = 0;
}
ic->ic_radar_detected(ic, "local radar from user space",
switchChanRequested, switchChan);
return 0;
}
@ -4446,13 +4470,18 @@ ieee80211_ioctl_getstainfo(struct net_device *dev, struct iwreq *iwr)
}
static void
pre_announced_chanswitch(struct net_device *dev, u_int32_t channel, u_int32_t tbtt) {
pre_announced_chanswitch(struct net_device *dev,
struct ieee80211_channel *channel,
u_int8_t csa_count)
{
struct ieee80211vap *vap = netdev_priv(dev);
struct ieee80211com *ic = vap->iv_ic;
/* now flag the beacon update to include the channel switch IE */
ic->ic_flags |= IEEE80211_F_CHANSWITCH;
ic->ic_chanchange_chan = channel;
ic->ic_chanchange_tbtt = tbtt;
ieee80211_send_csa_frame(vap, IEEE80211_CSA_CAN_STOP_TX,
channel->ic_ieee, csa_count);
/* Now flag the beacon update to include the channel switch IE. */
ieee80211_start_new_csa(vap, IEEE80211_CSA_CAN_STOP_TX,
channel, csa_count, 0);
}
static int
@ -4462,11 +4491,17 @@ ieee80211_ioctl_chanswitch(struct net_device *dev, struct iw_request_info *info,
struct ieee80211vap *vap = netdev_priv(dev);
struct ieee80211com *ic = vap->iv_ic;
unsigned int *param = (unsigned int *)extra;
struct ieee80211_channel *c;
if (!(ic->ic_flags & IEEE80211_F_DOTH))
return 0;
pre_announced_chanswitch(dev, param[0], param[1]);
c = ieee80211_doth_findchan(vap, param[0]);
if ((c != NULL) && (c != IEEE80211_CHAN_ANYC)) {
pre_announced_chanswitch(dev, c, param[1]);
} else {
return -EINVAL;
}
return 0;
}
@ -5254,7 +5289,7 @@ static const struct iw_priv_args ieee80211_priv_args[] = {
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwmmparams" },
{ IEEE80211_IOCTL_RADAR,
0, 0, "doth_radar" },
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "doth_radar" },
{ IEEE80211_IOCTL_HALMAP,
0, 0, "dump_hal_map" },
/*