Beispiel #1
0
static void
scan_curchan_task(void *arg, int pending)
{
	struct ieee80211_scan_state *ss = arg;
	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
	struct ieee80211com *ic = ss->ss_ic;
	struct ieee80211_channel *chan;
	unsigned long maxdwell;
	int scandone;

	IEEE80211_LOCK(ic);
end:
	scandone = (ss->ss_next >= ss->ss_last) ||
	    (ss_priv->ss_iflags & ISCAN_CANCEL) != 0;

	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
	    "%s: loop start; scandone=%d\n",
	    __func__,
	    scandone);

	if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) ||
	    (ss_priv->ss_iflags & ISCAN_ABORT) ||
	     ieee80211_time_after(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) {
		ss_priv->ss_iflags &= ~ISCAN_RUNNING;
		scan_end(ss, scandone);
		return;
	} else
		ss_priv->ss_iflags |= ISCAN_RUNNING;

	chan = ss->ss_chans[ss->ss_next++];

	/*
	 * Watch for truncation due to the scan end time.
	 */
	if (ieee80211_time_after(ticks + ss->ss_maxdwell, ss_priv->ss_scanend))
		maxdwell = ss_priv->ss_scanend - ticks;
	else
		maxdwell = ss->ss_maxdwell;

	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
	    "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n",
	    __func__,
	    ieee80211_chan2ieee(ic, ic->ic_curchan),
	    ieee80211_channel_type_char(ic->ic_curchan),
	    ieee80211_chan2ieee(ic, chan),
	    ieee80211_channel_type_char(chan),
	    (ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
		(chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ?
		"active" : "passive",
	    ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell));

	/*
	 * Potentially change channel and phy mode.
	 */
	ic->ic_curchan = chan;
	ic->ic_rt = ieee80211_get_ratetable(chan);
	IEEE80211_UNLOCK(ic);
	/*
	 * Perform the channel change and scan unlocked so the driver
	 * may sleep. Once set_channel returns the hardware has
	 * completed the channel change.
	 */
	ic->ic_set_channel(ic);
	ieee80211_radiotap_chan_change(ic);

	/*
	 * Scan curchan.  Drivers for "intelligent hardware"
	 * override ic_scan_curchan to tell the device to do
	 * the work.  Otherwise we manage the work ourselves;
	 * sending a probe request (as needed), and arming the
	 * timeout to switch channels after maxdwell ticks.
	 *
	 * scan_curchan should only pause for the time required to
	 * prepare/initiate the hardware for the scan (if at all).
	 */
	ic->ic_scan_curchan(ss, maxdwell);
	IEEE80211_LOCK(ic);

	/* XXX scan state can change! Re-validate scan state! */

	ss_priv->ss_chanmindwell = ticks + ss->ss_mindwell;
	/* clear mindwell lock and initial channel change flush */
	ss_priv->ss_iflags &= ~ISCAN_REP;

	if (ss_priv->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)) {
		taskqueue_cancel_timeout(ic->ic_tq, &ss_priv->ss_scan_curchan,
		    NULL);
		goto end;
	}

	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: waiting\n",
	    __func__);
	IEEE80211_UNLOCK(ic);
}
Beispiel #2
0
static void
scan_task(void *arg, int pending)
{
#define	ISCAN_REP	(ISCAN_MINDWELL | ISCAN_DISCARD)
	struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
	struct ieee80211vap *vap = ss->ss_vap;
	struct ieee80211com *ic = ss->ss_ic;
	struct ieee80211_channel *chan;
	unsigned long maxdwell, scanend;
	int scandone = 0;

	IEEE80211_LOCK(ic);
	if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
	    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)) {
		/* Cancelled before we started */
		goto done;
	}

	if (ss->ss_next == ss->ss_last) {
		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
			"%s: no channels to scan\n", __func__);
		goto done;
	}

	if (vap->iv_opmode == IEEE80211_M_STA &&
	    vap->iv_state == IEEE80211_S_RUN) {
		if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
			/* Enable station power save mode */
			ieee80211_sta_pwrsave(vap, 1);
			/*
			 * Use an 1ms delay so the null data frame has a chance
			 * to go out.
			 * XXX Should use M_TXCB mechanism to eliminate this.
			 */
			cv_timedwait(&SCAN_PRIVATE(ss)->ss_scan_cv,
			    IEEE80211_LOCK_OBJ(ic), hz / 1000);
			if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)
				goto done;
		}
	}

	scanend = ticks + SCAN_PRIVATE(ss)->ss_duration;
	IEEE80211_UNLOCK(ic);
	ic->ic_scan_start(ic);		/* notify driver */
	IEEE80211_LOCK(ic);

	for (;;) {
		scandone = (ss->ss_next >= ss->ss_last) ||
		    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0;
		if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) ||
		    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) ||
		     time_after(ticks + ss->ss_mindwell, scanend))
			break;

		chan = ss->ss_chans[ss->ss_next++];

		/*
		 * Watch for truncation due to the scan end time.
		 */
		if (time_after(ticks + ss->ss_maxdwell, scanend))
			maxdwell = scanend - ticks;
		else
			maxdwell = ss->ss_maxdwell;

		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
		    "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n",
		    __func__,
		    ieee80211_chan2ieee(ic, ic->ic_curchan),
		        channel_type(ic->ic_curchan),
		    ieee80211_chan2ieee(ic, chan), channel_type(chan),
		    (ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
			(chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ?
			"active" : "passive",
		    ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell));

		/*
		 * Potentially change channel and phy mode.
		 */
		ic->ic_curchan = chan;
		ic->ic_rt = ieee80211_get_ratetable(chan);
		IEEE80211_UNLOCK(ic);
		/*
		 * Perform the channel change and scan unlocked so the driver
		 * may sleep. Once set_channel returns the hardware has
		 * completed the channel change.
		 */
		ic->ic_set_channel(ic);
		ieee80211_radiotap_chan_change(ic);

		/*
		 * Scan curchan.  Drivers for "intelligent hardware"
		 * override ic_scan_curchan to tell the device to do
		 * the work.  Otherwise we manage the work outselves;
		 * sending a probe request (as needed), and arming the
		 * timeout to switch channels after maxdwell ticks.
		 *
		 * scan_curchan should only pause for the time required to
		 * prepare/initiate the hardware for the scan (if at all), the
		 * below condvar is used to sleep for the channels dwell time
		 * and allows it to be signalled for abort.
		 */
		ic->ic_scan_curchan(ss, maxdwell);
		IEEE80211_LOCK(ic);

		SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell;
		/* clear mindwell lock and initial channel change flush */
		SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;

		if ((SCAN_PRIVATE(ss)->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)))
			continue;

		/* Wait to be signalled to scan the next channel */
		cv_wait(&SCAN_PRIVATE(ss)->ss_scan_cv, IEEE80211_LOCK_OBJ(ic));
	}
	if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)
		goto done;

	IEEE80211_UNLOCK(ic);
	ic->ic_scan_end(ic);		/* notify driver */
	IEEE80211_LOCK(ic);

	/*
	 * Record scan complete time.  Note that we also do
	 * this when canceled so any background scan will
	 * not be restarted for a while.
	 */
	if (scandone)
		ic->ic_lastscan = ticks;
	/* return to the bss channel */
	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
	    ic->ic_curchan != ic->ic_bsschan) {
		ieee80211_setupcurchan(ic, ic->ic_bsschan);
		IEEE80211_UNLOCK(ic);
		ic->ic_set_channel(ic);
		ieee80211_radiotap_chan_change(ic);
		IEEE80211_LOCK(ic);
	}
	/* clear internal flags and any indication of a pick */
	SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
	ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;

	/*
	 * If not canceled and scan completed, do post-processing.
	 * If the callback function returns 0, then it wants to
	 * continue/restart scanning.  Unfortunately we needed to
	 * notify the driver to end the scan above to avoid having
	 * rx frames alter the scan candidate list.
	 */
	if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 &&
	    !ss->ss_ops->scan_end(ss, vap) &&
	    (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
	    time_before(ticks + ss->ss_mindwell, scanend)) {
		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
		    "%s: done, restart "
		    "[ticks %u, dwell min %lu scanend %lu]\n",
		    __func__,
		    ticks, ss->ss_mindwell, scanend);
		ss->ss_next = 0;	/* reset to begining */
		if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
			vap->iv_stats.is_scan_active++;
		else
			vap->iv_stats.is_scan_passive++;

		ss->ss_ops->scan_restart(ss, vap);	/* XXX? */
		ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
		IEEE80211_UNLOCK(ic);
		return;
	}

	/* past here, scandone is ``true'' if not in bg mode */
	if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
		scandone = 1;

	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
	    "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n",
	    __func__, scandone ? "done" : "stopped",
	    ticks, ss->ss_mindwell, scanend);

	/*
	 * Clear the SCAN bit first in case frames are
	 * pending on the station power save queue.  If
	 * we defer this then the dispatch of the frames
	 * may generate a request to cancel scanning.
	 */
done:
	ic->ic_flags &= ~IEEE80211_F_SCAN;
	/*
	 * Drop out of power save mode when a scan has
	 * completed.  If this scan was prematurely terminated
	 * because it is a background scan then don't notify
	 * the ap; we'll either return to scanning after we
	 * receive the beacon frame or we'll drop out of power
	 * save mode because the beacon indicates we have frames
	 * waiting for us.
	 */
	if (scandone) {
		ieee80211_sta_pwrsave(vap, 0);
		if (ss->ss_next >= ss->ss_last) {
			ieee80211_notify_scan_done(vap);
			ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
		}
	}
	SCAN_PRIVATE(ss)->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT);
	ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
	IEEE80211_UNLOCK(ic);
#undef ISCAN_REP
}