On Wed, Jan 23, 2013 at 2:19 AM, Marco Porsch <[email protected]> wrote:
> Configure the HW for PS mode if the local mesh PS parameters
> allow so.
>
> Expose a callback ieee80211_mps_init for drivers to register
> mesh powersave ops:
> - hw_doze - put the radio to sleep now
> - hw_wakeup - wake the radio up for frame RX
> These ops may be extended in the future to allow drivers/HW to
> implement mesh PS themselves. (The current design goal was to
> concentrate mesh PS routines in mac80211 to keep driver
> modifications minimal.
>
> Track the beacon timing information of peers we are in PS mode
> towards. Set a per-STA hrtimer which will trigger a wakeup right
> before the peer's next TBTT.
> Also use the same hrtimer to go to sleep mode after not
> receiving a beacon in a defined time margin. In this case
> calculate the next TBTT and increase the margin.
>
> For mesh Awake Windows wakeup on SWBA (beacon_get_tim) and start
> a timer which triggers a hw_doze call on expiry.
>
> Signed-off-by: Marco Porsch <[email protected]>
> ---
> include/net/mac80211.h | 34 +++++
> net/mac80211/ieee80211_i.h | 9 +-
> net/mac80211/mesh.c | 17 +++
> net/mac80211/mesh.h | 17 +++
> net/mac80211/mesh_plink.c | 1 +
> net/mac80211/mesh_ps.c | 357
> ++++++++++++++++++++++++++++++++++++++++++++
> net/mac80211/sta_info.c | 4 +
> net/mac80211/sta_info.h | 13 ++
> net/mac80211/tx.c | 2 +
> 9 files changed, 453 insertions(+), 1 deletion(-)
>
> diff --git a/include/net/mac80211.h b/include/net/mac80211.h
> index 23daed3..ca6979d 100644
> --- a/include/net/mac80211.h
> +++ b/include/net/mac80211.h
> @@ -3952,6 +3952,40 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif
> *vif, u16 ba_rx_bitmap,
> */
> void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn);
>
> +/**
> + * struct ieee80211_mps_ops - callbacks from mac80211 to driver for mesh PS
> + *
> + * This structure contains callbacks that the driver has to or may handle for
> + * mesh powersave.
> + * TODO Add further callbacks for HW that performs certain mesh PS tasks on
> its
> + * own (e.g. assign list of STA to track).
> + *
> + * @hw_doze: put the radio to doze state to conserve power
> + * @hw_wakeup: wake up the radio to receive frames again
> + */
> +struct ieee80211_mps_ops {
> + void (*hw_doze)(struct ieee80211_hw *hw);
> + void (*hw_wakeup)(struct ieee80211_hw *hw);
> +};
> +
> +#ifdef CONFIG_MAC80211_MESH
> +/**
> + * ieee80211_mps_init - register driver callbacks for mesh PS
> + *
> + * @hw: the hardware
> + * @ops: callbacks for this device
> + *
> + * called by driver on mesh interface add/remove
> + * TODO add HW capability flags
> + */
> +int ieee80211_mps_init(struct ieee80211_hw *hw,
> + const struct ieee80211_mps_ops *ops);
> +#else
> +static inline int ieee80211_mps_init(struct ieee80211_hw *hw,
> + const struct ieee80211_mps_ops *ops)
> +{ return 0; }
> +#endif
> +
> /* Rate control API */
>
> /**
> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
> index e08b4c0..c08f423 100644
> --- a/net/mac80211/ieee80211_i.h
> +++ b/net/mac80211/ieee80211_i.h
> @@ -629,6 +629,8 @@ struct ieee80211_if_mesh {
> int ps_peers_deep_sleep;
> struct ps_data ps;
> atomic_t num_mpsp; /* counts both owner and recipient independently */
> + struct timer_list awake_window_end_timer;
> + bool in_awake_window;
> };
>
> #ifdef CONFIG_MAC80211_MESH
> @@ -1126,7 +1128,7 @@ struct ieee80211_local {
> bool pspolling;
> bool offchannel_ps_enabled;
> /*
> - * PS can only be enabled when we have exactly one managed
> + * managed mode PS can only be enabled when we have exactly one
> managed
> * interface (and monitors) in PS, this then points there.
> */
> struct ieee80211_sub_if_data *ps_sdata;
> @@ -1146,6 +1148,11 @@ struct ieee80211_local {
>
> int user_power_level; /* in dBm, for all interfaces */
>
> + /* mesh power save */
> + bool mps_enabled;
> + bool mps_hw_doze;
> + const struct ieee80211_mps_ops *mps_ops;
> +
> enum ieee80211_smps_mode smps_mode;
>
> struct work_struct restart_work;
> diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
> index 8ce5d60..740d035 100644
> --- a/net/mac80211/mesh.c
> +++ b/net/mac80211/mesh.c
> @@ -803,6 +803,7 @@ void ieee80211_mesh_rx_queued_mgmt(struct
> ieee80211_sub_if_data *sdata,
>
> void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
> {
> + struct ieee80211_local *local = sdata->local;
> struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
>
> if (ifmsh->preq_queue_len &&
> @@ -824,6 +825,19 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data
> *sdata)
>
> if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags))
> mesh_sync_adjust_tbtt(sdata);
> +
> + if (test_and_clear_bit(MESH_WORK_PS_HW_CONF, &ifmsh->wrkq_flags))
> + ieee80211_mps_hw_conf(local);
> +
> + /* in case both fired simultaneously, wakeup overrides doze */
> + if (test_bit(MESH_WORK_PS_DOZE, &ifmsh->wrkq_flags) &&
> + test_bit(MESH_WORK_PS_WAKEUP, &ifmsh->wrkq_flags))
> + clear_bit(MESH_WORK_PS_DOZE, &ifmsh->wrkq_flags);
> +
> + if (test_and_clear_bit(MESH_WORK_PS_WAKEUP, &ifmsh->wrkq_flags))
> + ieee80211_mps_wakeup(local);
> + else if (test_and_clear_bit(MESH_WORK_PS_DOZE, &ifmsh->wrkq_flags))
> + ieee80211_mps_doze(local);
> }
>
> void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
> @@ -863,6 +877,9 @@ void ieee80211_mesh_init_sdata(struct
> ieee80211_sub_if_data *sdata)
> setup_timer(&ifmsh->mesh_path_root_timer,
> ieee80211_mesh_path_root_timer,
> (unsigned long) sdata);
> + setup_timer(&ifmsh->awake_window_end_timer,
> + ieee80211_mps_awake_window_end,
> + (unsigned long) sdata);
> INIT_LIST_HEAD(&ifmsh->preq_queue.list);
> skb_queue_head_init(&ifmsh->ps.bc_buf);
> spin_lock_init(&ifmsh->mesh_preq_queue_lock);
> diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
> index fa1423e..ce35b78 100644
> --- a/net/mac80211/mesh.h
> +++ b/net/mac80211/mesh.h
> @@ -58,6 +58,10 @@ enum mesh_path_flags {
> * @MESH_WORK_ROOT: the mesh root station needs to send a frame
> * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to
> other
> * mesh nodes
> + * @MESH_WORK_PS_HW_CONF: configure hardware according to the link-specific
> + * mesh power modes
> + * @MESH_WORK_PS_DOZE: put the hardware to sleep after checking all
> conditions
> + * @MESH_WORK_PS_WAKEUP: wakeup the hardware immediately
> */
> enum mesh_deferred_task_flags {
> MESH_WORK_HOUSEKEEPING,
> @@ -65,6 +69,9 @@ enum mesh_deferred_task_flags {
> MESH_WORK_GROW_MPP_TABLE,
> MESH_WORK_ROOT,
> MESH_WORK_DRIFT_ADJUST,
> + MESH_WORK_PS_HW_CONF,
> + MESH_WORK_PS_DOZE,
> + MESH_WORK_PS_WAKEUP,
> };
>
> /**
> @@ -258,6 +265,16 @@ void ieee80211_mpsp_trigger_process(struct ieee80211_hdr
> *hdr,
> struct sta_info *sta, bool tx, bool
> acked);
> void ieee80211_mps_frame_release(struct sta_info *sta,
> struct ieee802_11_elems *elems);
> +void ieee80211_mps_hw_conf(struct ieee80211_local *local);
> +void ieee80211_mps_sta_tbtt_update(struct sta_info *sta,
> + struct ieee80211_mgmt *mgmt,
> + struct ieee80211_tim_ie *tim,
> + u64 tsf);
> +enum hrtimer_restart ieee80211_mps_sta_tbtt_timer(struct hrtimer *timer);
> +void ieee80211_mps_awake_window_start(struct ieee80211_sub_if_data *sdata);
> +void ieee80211_mps_awake_window_end(unsigned long data);
> +void ieee80211_mps_doze(struct ieee80211_local *local);
> +void ieee80211_mps_wakeup(struct ieee80211_local *local);
>
> /* Mesh paths */
> int mesh_nexthop_lookup(struct sk_buff *skb,
> diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
> index af6fbfd..f41b4bb 100644
> --- a/net/mac80211/mesh_plink.c
> +++ b/net/mac80211/mesh_plink.c
> @@ -419,6 +419,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data
> *sdata,
> ifmsh->sync_ops->rx_bcn(sta, mgmt, elems, rx_status, tsf);
>
> ieee80211_mps_frame_release(sta, elems);
> + ieee80211_mps_sta_tbtt_update(sta, mgmt, elems->tim, tsf);
> out:
> rcu_read_unlock();
> }
> diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c
> index 788b935..d7fffd9 100644
> --- a/net/mac80211/mesh_ps.c
> +++ b/net/mac80211/mesh_ps.c
> @@ -9,12 +9,32 @@
>
> #include "mesh.h"
> #include "wme.h"
> +#include <linux/export.h>
> +#include <linux/hrtimer.h>
>
> +/*
> + * time to wakeup before and stay awake after peer TBTT until beacon receipt.
> + * required to cope with stack delay and HW wakeup time before TBTT and
> delayed
> + * beacons after TBTT
> + * TODO adjust this value for different hardware or make it adaptive
> + */
> +#define TBTT_MARGIN 5000 /* in us units */
> +
> +
> +static inline void mps_queue_work(struct ieee80211_sub_if_data *sdata,
> + enum mesh_deferred_task_flags flag)
> +{
> + set_bit(flag, &sdata->u.mesh.wrkq_flags);
> + ieee80211_queue_work(&sdata->local->hw, &sdata->work);
> +}
>
> static inline bool test_and_set_mpsp_flag(struct sta_info *sta,
> enum ieee80211_sta_info_flags flag)
> {
> if (!test_and_set_sta_flag(sta, flag)) {
> + if (sta->sdata->local->mps_enabled &&
> + atomic_read(&sta->sdata->u.mesh.num_mpsp) == 0)
> + mps_queue_work(sta->sdata, MESH_WORK_PS_WAKEUP);
> atomic_inc(&sta->sdata->u.mesh.num_mpsp);
> return false;
> }
> @@ -26,6 +46,9 @@ static inline bool test_and_clear_mpsp_flag(struct sta_info
> *sta,
> {
> if (test_and_clear_sta_flag(sta, flag)) {
> atomic_dec(&sta->sdata->u.mesh.num_mpsp);
> + if (sta->sdata->local->mps_enabled &&
> + atomic_read(&sta->sdata->u.mesh.num_mpsp) == 0)
> + mps_queue_work(sta->sdata, MESH_WORK_PS_DOZE);
> return true;
> }
> return false;
> @@ -148,6 +171,8 @@ void ieee80211_mps_local_status_update(struct
> ieee80211_sub_if_data *sdata)
>
> ifmsh->ps_peers_light_sleep = light_sleep_cnt;
> ifmsh->ps_peers_deep_sleep = deep_sleep_cnt;
> +
> + mps_queue_work(sdata, MESH_WORK_PS_HW_CONF);
> }
>
> /**
> @@ -605,3 +630,335 @@ void ieee80211_mps_frame_release(struct sta_info *sta,
> else
> mps_frame_deliver(sta, 1);
> }
> +
> +
> +/* mesh PS driver configuration and doze scheduling */
> +
> +static bool mps_hw_conf_check(struct ieee80211_local *local)
> +{
> + struct ieee80211_sub_if_data *sdata;
> + struct ieee80211_if_mesh *ifmsh;
> + bool enable = true;
> +
> + if (!local->mps_ops)
> + return false;
> +
> + mutex_lock(&local->iflist_mtx);
> + list_for_each_entry(sdata, &local->interfaces, list) {
> + if (!ieee80211_sdata_running(sdata))
> + continue;
> +
> + /* If an AP or any other non-mesh vif is found, disable PS */
> + if (ieee80211_sdata_running(sdata) &&
> + sdata->vif.type != NL80211_IFTYPE_MESH_POINT) {
> + enable = false;
> + break;
> + }
> +
> + ifmsh = &sdata->u.mesh;
> +
> + /*
> + * check for non-peer power mode, check for links in active
> + * mode. Assume a valid power mode is set for each established
> + * peer link
> + */
> + if (ifmsh->nonpeer_pm == NL80211_MESH_POWER_ACTIVE ||
> + ifmsh->ps_peers_light_sleep + ifmsh->ps_peers_deep_sleep
> + < atomic_read(&ifmsh->estab_plinks)) {
> + enable = false;
> + break;
> + }
> + }
> + mutex_unlock(&local->iflist_mtx);
> +
> + return enable;
> +}
> +
> +/**
> + * mps_hw_conf_sta_prepare - mark peers to catch beacon once before first
> doze
> + */
> +static void mps_hw_conf_sta_prepare(struct ieee80211_local *local)
> +{
> + struct sta_info *sta;
> +
> + mutex_lock(&local->sta_mtx);
> + list_for_each_entry(sta, &local->sta_list, list) {
> + if (!ieee80211_vif_is_mesh(&sta->sdata->vif) ||
> + !ieee80211_sdata_running(sta->sdata) ||
> + sta->plink_state != NL80211_PLINK_ESTAB)
> + continue;
> + else
> + set_sta_flag(sta, WLAN_STA_MPS_WAIT_FOR_BEACON);
> + }
> + mutex_unlock(&local->sta_mtx);
> +}
> +
> +/**
> + * ieee80211_mps_hw_conf - check conditions for mesh PS and configure driver
> + *
> + * @local: local interface data
> + */
> +void ieee80211_mps_hw_conf(struct ieee80211_local *local)
> +{
> + bool enable;
> +
> + enable = mps_hw_conf_check(local);
> +
> + if (local->mps_enabled == enable)
> + return;
> +
> + if (enable) {
> + mps_hw_conf_sta_prepare(local);
> + local->hw.conf.flags |= IEEE80211_CONF_PS;
> + } else {
> + local->hw.conf.flags &= ~IEEE80211_CONF_PS;
> + }
> +
> + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
> + local->mps_enabled = enable;
> +}
> +
> +static void mps_sta_tbtt_set_timer(struct sta_info *sta,
> + struct ieee80211_tim_ie *tim,
> + u64 tsf_local)
> +{
> + u64 tsf_peer;
> + int skip = 1;
> + u32 nexttbtt_interval;
> + ktime_t now;
> +
> + /* simple Deep Sleep implementation: only wake up for DTIM beacons */
> + if (sta->local_pm == NL80211_MESH_POWER_DEEP_SLEEP &&
> + tim->dtim_count == 0)
> + skip = tim->dtim_period;
Still need to schedule for the next DTIM if this isn't a DTIM beacon so:
skip = tim->dtim_count ? tim->dtim_count : tim->dtim_period; ?
> + /*
> + * determine time to peer TBTT (TSF % beacon_interval = 0).
> + * This approach is robust to delayed beacons.
> + */
> + tsf_peer = tsf_local + sta->t_offset;
> + nexttbtt_interval = sta->beacon_interval -
> + do_div(tsf_peer, sta->beacon_interval * skip);
> + now = hrtimer_cb_get_time(&sta->mps_beacon_timer);
> +
> + mps_dbg(sta->sdata, "updating %pM next TBTT in %dus (%llus awake)\n",
> + sta->sta.addr, nexttbtt_interval,
> + (long long) ktime_to_us(ktime_sub(now, sta->tbtt_wakeup)));
> +
> + sta->tbtt_wakeup = ktime_add_us(now, nexttbtt_interval - TBTT_MARGIN);
> + sta->tbtt_miss = ktime_add_us(now, nexttbtt_interval + TBTT_MARGIN);
> +
> + hrtimer_start(&sta->mps_beacon_timer, sta->tbtt_wakeup,
> + HRTIMER_MODE_ABS);
> +}
> +
> +/**
> + * ieee80211_mps_sta_tbtt_update - update peer beacon wakeup schedule
> + *
> + * @sta: mesh STA
> + * @mgmt: beacon frame
> + * @tim: TIM IE of beacon frame
> + * @tsf_local: current HW TSF
> + */
> +void ieee80211_mps_sta_tbtt_update(struct sta_info *sta,
> + struct ieee80211_mgmt *mgmt,
> + struct ieee80211_tim_ie *tim,
> + u64 tsf_local)
> +{
> + struct ieee80211_sub_if_data *sdata = sta->sdata;
> +
> + if (!sdata->local->mps_enabled ||
> + sta->plink_state != NL80211_PLINK_ESTAB)
> + return;
> +
> + hrtimer_cancel(&sta->mps_beacon_timer);
> + clear_sta_flag(sta, WLAN_STA_MPS_WAIT_FOR_BEACON);
> +
> + sta->beacon_interval = le16_to_cpu(mgmt->u.beacon.beacon_int) * 1024;
> + /* pending multicasts after DTIM beacon? TODO reset after RX */
> + if (tim->bitmap_ctrl & 0x01)
> + set_sta_flag(sta, WLAN_STA_MPS_WAIT_FOR_CAB);
> + else
> + clear_sta_flag(sta, WLAN_STA_MPS_WAIT_FOR_CAB);
> +
> + mps_sta_tbtt_set_timer(sta, tim, tsf_local);
> +
> + mps_queue_work(sdata, MESH_WORK_PS_DOZE);
> +}
> +
> +/**
> + * ieee80211_mps_sta_tbtt_timer - hrtimer callback for mesh PS doze/wakeup
> + *
> + * Used for both waking up before TBTT and resuming doze in case the beacon
> + * is not received on time.
> + * XXX what lock should be used here? hrtimer callbacks are hard IRQ context
> + */
> +enum hrtimer_restart ieee80211_mps_sta_tbtt_timer(struct hrtimer *timer)
> +{
> + /*
> + * This STA is valid because the timer is canceled on STA removal
> + * after having made sure it cannot be armed (by deleting the plink.)
> + */
> + struct sta_info *sta = container_of(timer, struct sta_info,
> + mps_beacon_timer);
> + struct ieee80211_sub_if_data *sdata = sta->sdata;
> +
> + if (!sdata->local->mps_enabled ||
> + sta->plink_state != NL80211_PLINK_ESTAB)
> + return HRTIMER_NORESTART;
> +
> + if (!test_sta_flag(sta, WLAN_STA_MPS_WAIT_FOR_BEACON)) {
> + mps_dbg(sdata, "wakeup for %pM (margin %dus)\n",
> + sta->sta.addr, TBTT_MARGIN);
> +
> + set_sta_flag(sta, WLAN_STA_MPS_WAIT_FOR_BEACON);
> + hrtimer_set_expires(&sta->mps_beacon_timer, sta->tbtt_miss);
> +
> + mps_queue_work(sdata, MESH_WORK_PS_WAKEUP);
> + } else {
> + mps_dbg(sdata, "beacon miss %pM\n", sta->sta.addr);
> +
> + clear_sta_flag(sta, WLAN_STA_MPS_WAIT_FOR_BEACON);
> +
> + /* increase the margin for each beacon miss TODO deep sleep */
> + sta->tbtt_wakeup = ktime_add_us(sta->tbtt_wakeup,
> + sta->beacon_interval - TBTT_MARGIN);
> + sta->tbtt_miss = ktime_add_us(sta->tbtt_miss,
> + sta->beacon_interval + TBTT_MARGIN);
> + hrtimer_set_expires(&sta->mps_beacon_timer, sta->tbtt_wakeup);
> +
> + mps_queue_work(sdata, MESH_WORK_PS_DOZE);
> + }
> +
> + return HRTIMER_RESTART;
> +}
> +
> +/**
> + * ieee80211_mps_awake_window_start - start Awake Window on SWBA
> + *
> + * @sdata: local mesh subif
> + */
> +void ieee80211_mps_awake_window_start(struct ieee80211_sub_if_data *sdata)
> +{
> + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
> +
> + if (!sdata->local->mps_enabled)
> + return;
> +
> + mps_dbg(sdata, "awake window start (%dTU)\n",
> + ifmsh->mshcfg.dot11MeshAwakeWindowDuration);
> +
> + ifmsh->in_awake_window = true;
> + mod_timer(&ifmsh->awake_window_end_timer, jiffies + usecs_to_jiffies(
> + ifmsh->mshcfg.dot11MeshAwakeWindowDuration * 1024));
> +
> + mps_queue_work(sdata, MESH_WORK_PS_WAKEUP);
> +}
> +
> +/**
> + * ieee80211_mps_awake_window_end - timer callback for end of Awake Window
> + */
> +void ieee80211_mps_awake_window_end(unsigned long data)
> +{
> + struct ieee80211_sub_if_data *sdata = (void *) data;
> + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
> +
> + mps_dbg(sdata, "awake window end\n");
> +
> + ifmsh->in_awake_window = false;
> +
> + if (!sdata->local->mps_enabled)
> + return;
> +
> + mps_queue_work(sdata, MESH_WORK_PS_DOZE);
> +}
> +
> +static bool mps_doze_check_vif(struct ieee80211_local *local)
> +{
> + struct ieee80211_sub_if_data *sdata;
> + bool allow = true;
> +
> + mutex_lock(&local->iflist_mtx);
> + list_for_each_entry(sdata, &local->interfaces, list) {
> + if (!ieee80211_sdata_running(sdata))
> + continue;
> +
> + if (!ieee80211_vif_is_mesh(&sdata->vif) ||
> + sdata->u.mesh.in_awake_window ||
Can't you just check if the awake_window_end timer is queued and get
rid of this bool?
> + atomic_read(&sdata->u.mesh.num_mpsp)) {
> + allow = false;
> + break;
> + }
> + }
> + mutex_unlock(&local->iflist_mtx);
> +
> + return allow;
> +}
> +
> +static bool mps_doze_check_sta(struct ieee80211_local *local)
> +{
> + struct sta_info *sta;
> + bool allow = true;
> +
> + mutex_lock(&local->sta_mtx);
> + list_for_each_entry(sta, &local->sta_list, list) {
> + if (!ieee80211_vif_is_mesh(&sta->sdata->vif) ||
> + !ieee80211_sdata_running(sta->sdata) ||
> + sta->plink_state != NL80211_PLINK_ESTAB) {
> + continue;
> + } else if (test_sta_flag(sta, WLAN_STA_MPS_WAIT_FOR_BEACON) ||
> + test_sta_flag(sta, WLAN_STA_MPS_WAIT_FOR_CAB)) {
> + allow = false;
> + break;
> + }
> + }
> + mutex_unlock(&local->sta_mtx);
> +
> + return allow;
> +}
> +
> +/**
> + * ieee80211_mps_doze - check conditions and trigger radio doze state
> + *
> + * @local: local interface data
> + */
> +void ieee80211_mps_doze(struct ieee80211_local *local)
> +{
> + if (!local->mps_enabled ||
> + local->mps_hw_doze ||
> + !mps_doze_check_vif(local) ||
> + !mps_doze_check_sta(local))
> + return;
> +
> + local->mps_hw_doze = true;
Only set this if local->mps_ops exist?
> + if (local->mps_ops)
> + local->mps_ops->hw_doze(&local->hw);
> +}
> +
> +/**
> + * ieee80211_mps_wakeup - trigger radio wakeup immediately
> + *
> + * @local: local interface data
> + */
> +void ieee80211_mps_wakeup(struct ieee80211_local *local)
> +{
> + if (!local->mps_hw_doze)
> + return;
> +
> + local->mps_hw_doze = false;
> + if (local->mps_ops)
> + local->mps_ops->hw_wakeup(&local->hw);
> +}
> +
> +int ieee80211_mps_init(struct ieee80211_hw *hw,
> + const struct ieee80211_mps_ops *ops)
> +{
> + struct ieee80211_local *local = hw_to_local(hw);
> +
> + local->mps_ops = ops;
> + if (!ops)
> + local->mps_enabled = false;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(ieee80211_mps_init);
> diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
> index 3d447a1..3e30788 100644
> --- a/net/mac80211/sta_info.c
> +++ b/net/mac80211/sta_info.c
> @@ -142,6 +142,7 @@ static void cleanup_single_sta(struct sta_info *sta)
> mesh_accept_plinks_update(sdata);
> mesh_plink_deactivate(sta);
> del_timer_sync(&sta->plink_timer);
> + hrtimer_cancel(&sta->mps_beacon_timer);
> }
> #endif
>
> @@ -385,6 +386,9 @@ struct sta_info *sta_info_alloc(struct
> ieee80211_sub_if_data *sdata,
> #ifdef CONFIG_MAC80211_MESH
> sta->plink_state = NL80211_PLINK_LISTEN;
> init_timer(&sta->plink_timer);
> + hrtimer_init(&sta->mps_beacon_timer, CLOCK_MONOTONIC,
> + HRTIMER_MODE_REL);
> + sta->mps_beacon_timer.function = ieee80211_mps_sta_tbtt_timer;
> #endif
>
> return sta;
> diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
> index 5a1deba..3bd1f96 100644
> --- a/net/mac80211/sta_info.h
> +++ b/net/mac80211/sta_info.h
> @@ -16,6 +16,7 @@
> #include <linux/average.h>
> #include <linux/etherdevice.h>
> #include "key.h"
> +#include <linux/hrtimer.h>
>
> /**
> * enum ieee80211_sta_info_flags - Stations flags
> @@ -58,6 +59,8 @@
> * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid.
> * @WLAN_STA_MPSP_OWNER: local STA is owner of a mesh Peer Service Period.
> * @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP.
> + * @WLAN_STA_MPS_WAIT_FOR_BEACON: STA beacon is imminent
> + * @WLAN_STA_MPS_WAIT_FOR_CAB: STA multicast frames are imminent
> */
> enum ieee80211_sta_info_flags {
> WLAN_STA_AUTH,
> @@ -82,6 +85,8 @@ enum ieee80211_sta_info_flags {
> WLAN_STA_TOFFSET_KNOWN,
> WLAN_STA_MPSP_OWNER,
> WLAN_STA_MPSP_RECIPIENT,
> + WLAN_STA_MPS_WAIT_FOR_BEACON,
> + WLAN_STA_MPS_WAIT_FOR_CAB,
> };
>
> #define ADDBA_RESP_INTERVAL HZ
> @@ -289,6 +294,10 @@ struct sta_ampdu_mlme {
> * @local_pm: local link-specific power save mode
> * @peer_pm: peer-specific power save mode towards local STA
> * @nonpeer_pm: STA power save mode towards non-peer neighbors
> + * @beacon_interval: beacon interval of neighbor STA (in us)
> + * @mps_beacon_timer: timer to trigger wakeup and sleep events for beacons RX
> + * @tbtt_wakeup: absolute time to wakeup for this peer beacon
> + * @tbtt_miss: absolute time to give up waiting for this peer beacon
> * @debugfs: debug filesystem info
> * @dead: set to true when sta is unlinked
> * @uploaded: set to true when sta is uploaded to the driver
> @@ -390,6 +399,10 @@ struct sta_info {
> enum nl80211_mesh_power_mode local_pm;
> enum nl80211_mesh_power_mode peer_pm;
> enum nl80211_mesh_power_mode nonpeer_pm;
> + u32 beacon_interval;
> + struct hrtimer mps_beacon_timer;
> + ktime_t tbtt_wakeup;
> + ktime_t tbtt_miss;
> #endif
>
> #ifdef CONFIG_MAC80211_DEBUGFS
> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
> index 1890441..e09f597 100644
> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -2494,6 +2494,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct
> ieee80211_hw *hw,
> pr_err("o11s: couldn't add ies!\n");
> goto out;
> }
> +
> + ieee80211_mps_awake_window_start(sdata);
> } else {
> WARN_ON(1);
> goto out;
> --
> 1.7.9.5
>
> _______________________________________________
> Devel mailing list
> [email protected]
> http://lists.open80211s.org/cgi-bin/mailman/listinfo/devel
--
Thomas
_______________________________________________
Devel mailing list
[email protected]
http://lists.open80211s.org/cgi-bin/mailman/listinfo/devel