mirror of https://github.com/proski/madwifi
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:
parent
5b3bc6b37c
commit
1b019e9b0d
425
ath/if_ath.c
425
ath/if_ath.c
|
@ -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 "
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 *,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 *);
|
||||
|
||||
|
|
|
@ -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" },
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue