Begin fleshing out support for net80211 provided (legacy) sleep management.

This transitions the VAP in and out of SLEEP state based on:

* whether there's been an active transmission in the last (hardcoded) 500ms;
* whether the TIM from the AP indicates there is data available.

It uses the beacon reception to trigger the active traffic check.
This way there's no further timer running to wake up the CPU
from its own sleep states.

Right now the VAP isn't woken up for multicast traffic - mostly because
the only NIC I plan on doing this for right will auto wakeup and stay
awake for multicast traffic indicated in the TIM.  So I don't have
to manually keep the hardware awake.

This doesn't do anything if the NIC doesn't advertise it implements
the new SWSLEEP capability AND if the VAP doesn't have powersave
enabled.

It also doesn't do much with ath(4) as it doesn't currently implement
the SLEEP state.

Tested:

* AR5416, STA mode (with local ath(4) changes)
This commit is contained in:
Adrian Chadd 2014-04-24 01:39:53 +00:00
parent 3914f0881e
commit 902840a5db
3 changed files with 132 additions and 5 deletions

View File

@ -555,3 +555,89 @@ ieee80211_sta_pwrsave(struct ieee80211vap *vap, int enable)
ieee80211_send_nulldata(ieee80211_ref_node(ni));
}
}
/*
* Handle being notified that we have data available for us in a TIM/ATIM.
*
* This may schedule a transition from _SLEEP -> _RUN if it's appropriate.
*/
void
ieee80211_sta_tim_notify(struct ieee80211vap *vap, int set)
{
/*
* Schedule the driver state change. It'll happen at some point soon.
* Since the hardware shouldn't know that we're running just yet
* (and thus tell the peer that we're awake before we actually wake
* up said hardware), we leave the actual node state transition
* up to the transition to RUN.
*
* XXX TODO: verify that the transition to RUN will wake up the
* BSS node!
*/
IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, "%s: TIM=%d\n", __func__, set);
IEEE80211_LOCK(vap->iv_ic);
if (set == 1 && vap->iv_state == IEEE80211_S_SLEEP) {
ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
}
IEEE80211_UNLOCK(vap->iv_ic);
}
/*
* Timer check on whether the VAP has had any transmit activity.
*
* This may schedule a transition from _RUN -> _SLEEP if it's appropriate.
*/
void
ieee80211_sta_ps_timer_check(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
/* XXX lock assert */
/* For no, only do this in STA mode */
if (! (vap->iv_caps & IEEE80211_C_SWSLEEP))
goto out;
if (vap->iv_opmode != IEEE80211_M_STA)
goto out;
/* If we're not at run state, bail */
if (vap->iv_state != IEEE80211_S_RUN)
goto out;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER,
"%s: lastdata=%llu, ticks=%llu\n",
__func__, (unsigned long long) ic->ic_lastdata,
(unsigned long long) ticks);
/* If powersave is disabled on the VAP, don't bother */
if (! (vap->iv_flags & IEEE80211_F_PMGTON))
goto out;
/* If we've done any data within our idle interval, bail */
/* XXX hard-coded to one second for now, ew! */
if (time_after(ic->ic_lastdata + 500, ticks))
goto out;
/*
* Signify we're going into power save and transition the
* node to powersave.
*/
if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
vap->iv_sta_ps(vap, 1);
/*
* XXX The driver has to handle the fact that we're going
* to sleep but frames may still be transmitted;
* hopefully it and/or us will do the right thing and mark any
* transmitted frames with PWRMGT set to 1.
*/
ieee80211_new_state_locked(vap, IEEE80211_S_SLEEP, 0);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER,
"%s: time delta=%d msec\n", __func__,
(int) ticks_to_msecs(ticks - ic->ic_lastdata));
out:
return;
}

View File

@ -79,6 +79,9 @@ int ieee80211_node_psq_age(struct ieee80211_node *);
int ieee80211_pwrsave(struct ieee80211_node *, struct mbuf *);
void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
void ieee80211_sta_pwrsave(struct ieee80211vap *, int enable);
void ieee80211_sta_tim_notify(struct ieee80211vap *vap, int set);
void ieee80211_sta_ps_timer_check(struct ieee80211vap *vap);
/* XXX what's this? */
void ieee80211_power_poll(struct ieee80211com *);
#endif /* _NET80211_IEEE80211_POWER_H_ */

View File

@ -234,6 +234,7 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
switch (ostate) {
case IEEE80211_S_SLEEP:
/* XXX wakeup */
/* XXX driver hook to wakeup the hardware? */
case IEEE80211_S_RUN:
IEEE80211_SEND_MGMT(ni,
IEEE80211_FC0_SUBTYPE_DISASSOC,
@ -403,6 +404,7 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
break;
case IEEE80211_S_SLEEP:
/* Wake up from sleep */
vap->iv_sta_ps(vap, 0);
break;
default:
@ -430,9 +432,11 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
ieee80211_node_authorize(ni);
/*
* Fake association when joining an existing bss.
*
* Don't do this if we're doing SLEEP->RUN.
*/
if (ic->ic_newassoc != NULL)
ic->ic_newassoc(vap->iv_bss, ostate != IEEE80211_S_RUN);
if (ic->ic_newassoc != NULL && ostate != IEEE80211_S_SLEEP)
ic->ic_newassoc(vap->iv_bss, (ostate != IEEE80211_S_RUN));
break;
case IEEE80211_S_CSA:
if (ostate != IEEE80211_S_RUN)
@ -1312,6 +1316,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
vap->iv_stats.is_beacon_bad++;
return;
}
/*
* Count frame now that we know it's to be processed.
*/
@ -1381,23 +1386,48 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
}
if (scan.quiet)
ic->ic_set_quiet(ni, scan.quiet);
if (scan.tim != NULL) {
struct ieee80211_tim_ie *tim =
(struct ieee80211_tim_ie *) scan.tim;
#if 0
/*
* XXX Check/debug this code; see if it's about
* the right time to force the VAP awake if we
* receive a frame destined for us?
*/
int aid = IEEE80211_AID(ni->ni_associd);
int ix = aid / NBBY;
int min = tim->tim_bitctl &~ 1;
int max = tim->tim_len + min - 4;
if ((tim->tim_bitctl&1) ||
/*
* Only do this for unicast traffic in the TIM
* The multicast traffic notification for
* the scan notification stuff should occur
* differently.
*/
if (min <= ix && ix <= max &&
isset(tim->tim_bitmap - min, aid)) {
ieee80211_sta_tim_notify(vap, 1);
ic->ic_lastdata = ticks;
}
/*
* XXX TODO: do a separate notification
* for the multicast bit being set.
*/
#if 0
if ((tim->tim_bitctl & 1) ||
(min <= ix && ix <= max &&
isset(tim->tim_bitmap - min, aid))) {
/*
* XXX Do not let bg scan kick off
* we are expecting data.
*/
ieee80211_sta_tim_notify(vap, 1);
ic->ic_lastdata = ticks;
vap->iv_sta_ps(vap, 0);
// XXX not yet?
// vap->iv_sta_ps(vap, 0);
}
#endif
ni->ni_dtim_count = tim->tim_count;
@ -1445,6 +1475,14 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
ieee80211_bg_scan(vap, 0);
}
/*
* Put the station to sleep if we haven't seen
* traffic in a while.
*/
IEEE80211_LOCK(ic);
ieee80211_sta_ps_timer_check(vap);
IEEE80211_UNLOCK(ic);
/*
* If we've had a channel width change (eg HT20<->HT40)
* then schedule a delayed driver notification.