On Wed, Oct 17, 2012 at 07:23:29PM -0700, [email protected] wrote:
> From: Marco Porsch <[email protected]>
> 
> According to IEEE802.11-2012 a mesh STA shall indicate the current
> power mode in transmitted frames. This differs for individually addressed
> frames and group addressed frames as well as whether the frames are
> addressed to peers or non-peers.
> A mesh STA shall indicate its link-specific power mode with the Power
> Management field in the Frame Control field and the Mesh Power Save
> Level field in the QoS Control field in all individually addressed
> Mesh Data frames and QoS Null frames.
> The Power Management field set to 1 and the Mesh Power Save Level
> subfield set to 0 indicate that the mesh STA is operating in light sleep
> mode. The Power Management field set to 0 and the mesh Power Save
> Level subfield set to 1 indicate that the mesh STA is operating in
> deep sleep mode. The Mesh Power Save Level subfield is reserved, if
> the Power Management subfield is set to 0.
> 
> A peer-specific mesh power mode transition is indicated by an
> individually addressed QoS Null frame to the respective peer.
> 
> In frames transmitted to non-peer STA and in management frames the
> non-peer mesh power mode is indicated in the Power Management field
> in the Frame Control field.
> In group addressed Mesh QoS Data frames the Mesh Power Save Level field
> in the QoS Control field indicates whether the local STA has any deep
> sleep peers.
> 
> Signed-off-by: Marco Porsch <[email protected]>
> Signed-off-by: Ivan Bezyazychnyy <[email protected]>
> Signed-off-by: Mike Krinkin <[email protected]>
> Signed-off-by: Max Filippov <[email protected]>
> ---
>  include/linux/ieee80211.h   |    3 +
>  net/mac80211/mesh.h         |    3 +
>  net/mac80211/mesh_hwmp.c    |    2 +
>  net/mac80211/mesh_pathtbl.c |    3 +
>  net/mac80211/mesh_ps.c      |  127 
> +++++++++++++++++++++++++++++++++++++++++++
>  net/mac80211/rx.c           |    3 +
>  net/mac80211/tx.c           |    2 +
>  7 files changed, 143 insertions(+)
> 
> diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
> index 2385119..26fefe5 100644
> --- a/include/linux/ieee80211.h
> +++ b/include/linux/ieee80211.h
> @@ -149,6 +149,9 @@
>  /* Mesh Control 802.11s */
>  #define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT  0x0100
>  
> +/* mesh power save level subfield mask */
> +#define IEEE80211_QOS_CTL_MESH_PS_LEVEL      0x0200
> +
>  /* U-APSD queue for WMM IEs sent by AP */
>  #define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD    (1<<7)
>  #define IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK       0x0f
> diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
> index a0d02d8..e4c45e1 100644
> --- a/net/mac80211/mesh.h
> +++ b/net/mac80211/mesh.h
> @@ -261,9 +261,12 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data 
> *sdata);
>  void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
>  void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh);
>  struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method);
> +struct sk_buff *ieee80211_mesh_null_get(struct sta_info *sta);
>  void ieee80211_local_ps_update(struct ieee80211_sub_if_data *sdata);
>  void ieee80211_set_local_ps_mode(struct sta_info *sta,
>                                enum nl80211_mesh_power_mode pm, u32 delay);
> +void ieee80211_set_mesh_ps_fields(struct ieee80211_sub_if_data *sdata,
> +                               struct ieee80211_hdr *hdr);
>  
>  /* Mesh paths */
>  int mesh_nexthop_lookup(struct sk_buff *skb,
> diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
> index 47aeee2..c3edda6 100644
> --- a/net/mac80211/mesh_hwmp.c
> +++ b/net/mac80211/mesh_hwmp.c
> @@ -205,6 +205,7 @@ static void prepare_frame_for_deferred_tx(struct 
> ieee80211_sub_if_data *sdata,
>               struct sk_buff *skb)
>  {
>       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +     struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
>  
>       skb_set_mac_header(skb, 0);
>       skb_set_network_header(skb, 0);
> @@ -216,6 +217,7 @@ static void prepare_frame_for_deferred_tx(struct 
> ieee80211_sub_if_data *sdata,
>  
>       info->control.vif = &sdata->vif;
>       ieee80211_set_qos_hdr(sdata, skb);
> +     ieee80211_set_mesh_ps_fields(sdata, hdr);
>  }
>  
>  /**
> diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
> index aa74981..f4a424f 100644
> --- a/net/mac80211/mesh_pathtbl.c
> +++ b/net/mac80211/mesh_pathtbl.c
> @@ -212,6 +212,9 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, 
> struct sta_info *sta)
>               hdr = (struct ieee80211_hdr *) skb->data;
>               memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN);
>               memcpy(hdr->addr2, mpath->sdata->vif.addr, ETH_ALEN);
> +
> +             /* now we can set the link-specific PS fields */
> +             ieee80211_set_mesh_ps_fields(sta->sdata, hdr);
>       }
>  
>       spin_unlock_irqrestore(&mpath->frame_queue.lock, flags);
> diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c
> index 511ca5d..15b3abe 100644
> --- a/net/mac80211/mesh_ps.c
> +++ b/net/mac80211/mesh_ps.c
> @@ -10,6 +10,72 @@
>  #include "mesh.h"
>  
>  /**
> + * ieee80211_mesh_null_get - create pre-addressed QoS Null frame
> + *
> + * Returns the created sk_buff
> + *
> + * @sta: mesh STA
> + */
> +struct sk_buff *ieee80211_mesh_null_get(struct sta_info *sta)
> +{
> +     struct ieee80211_sub_if_data *sdata = sta->sdata;
> +     struct ieee80211_local *local = sdata->local;
> +     struct ieee80211_hdr *nullfunc; /* use 4addr header */
> +     struct sk_buff *skb;
> +     int size = sizeof(*nullfunc);
> +     __le16 fc;
> +
> +     skb = dev_alloc_skb(local->hw.extra_tx_headroom + size + 2);
> +     if (!skb)
> +             return NULL;
> +     skb_reserve(skb, local->hw.extra_tx_headroom);
> +
> +     nullfunc = (void *) skb_put(skb, size);

Why are you casting to a void *?

> +
> +     fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
> +                      IEEE80211_STYPE_QOS_NULLFUNC);
> +     ieee80211_fill_mesh_addresses(nullfunc, &fc,
> +                                   sta->sta.addr,
> +                                   sdata->vif.addr);
> +     nullfunc->frame_control = fc;
> +     nullfunc->duration_id = 0;
> +     /* HWMP will not see this frame -> set addr 1 immediately */
> +     memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
> +
> +     /* append QoS control field (missing in ieee80211_hdr) */
> +     skb_put(skb, 2);
> +
> +     return skb;
> +}
> +
> +/**
> + * ieee80211_send_mesh_null - send a QoS Null to peer to indicate power mode
> + *
> + * @sta: mesh STA to inform
> + */
> +static void ieee80211_send_mesh_null(struct sta_info *sta)
> +{
> +     struct sk_buff *skb;
> +
> +     skb = ieee80211_mesh_null_get(sta);
> +     if (!skb)
> +             return;
> +
> +     mps_dbg(sta->sdata, "announcing peer-specific power mode to %pM\n",
> +             sta->sta.addr);
> +
> +     /* don't unintentionally start a PSP */
> +     if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
> +             __le16 *qc = (__le16 *) ieee80211_get_qos_ctl(
> +                             (struct ieee80211_hdr *) skb->data);
> +
> +             *qc |= cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
> +     }
> +
> +     ieee80211_tx_skb(sta->sdata, skb);
> +}
> +
> +/**
>   * ieee80211_local_ps_update - keep track of link-specific PS modes
>   *
>   * @sdata: local mesh subif
> @@ -132,3 +198,64 @@ void ieee80211_set_local_ps_mode(struct sta_info *sta,
>  
>       ieee80211_local_ps_update(sdata);
>  }
> +
> +static enum nl80211_mesh_power_mode
> +ieee80211_get_local_ps_mode(struct ieee80211_sub_if_data *sdata,
> +                         struct ieee80211_hdr *hdr)
> +{
> +     enum nl80211_mesh_power_mode pm = NL80211_MESH_POWER_ACTIVE;
> +     struct sta_info *sta;
> +
> +     if (is_unicast_ether_addr(hdr->addr1) &&
> +         ieee80211_is_data_qos(hdr->frame_control)) {
> +             /* power mode is link-specific */
> +             rcu_read_lock();
> +             sta = sta_info_get(sdata, hdr->addr1);
> +             if (sta) {
> +                     if (sta->plink_state == NL80211_PLINK_ESTAB)
> +                             pm = sta->local_ps_mode;
> +                     else
> +                             pm = sdata->u.mesh.nonpeer_ps_mode;
> +             }
> +             rcu_read_unlock();
> +     } else {
> +             pm = sdata->u.mesh.nonpeer_ps_mode;
> +     }
> +
> +     return pm;
> +}
> +
> +/**
> + * ieee80211_set_mesh_ps_fields - set mesh PS fields in FC (and QoS Control)
> + *
> + * @sdata: local mesh subif
> + * @hdr: 802.11 frame header
> + *
> + * see IEEE802.11-2012 8.2.4.1.7 and 8.2.4.5.11
> + */
> +void ieee80211_set_mesh_ps_fields(struct ieee80211_sub_if_data *sdata,
> +                               struct ieee80211_hdr *hdr)
> +{
> +     enum nl80211_mesh_power_mode pm;
> +     __le16 *qc;
> +
> +     pm = ieee80211_get_local_ps_mode(sdata, hdr);
> +
> +     if (pm == NL80211_MESH_POWER_ACTIVE)
> +             hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_PM);
> +     else
> +             hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
> +
> +     if (!ieee80211_is_data_qos(hdr->frame_control))
> +             return;
> +
> +     qc = (__le16 *) ieee80211_get_qos_ctl(hdr);
> +
> +     if ((is_unicast_ether_addr(hdr->addr1) &&
> +          pm == NL80211_MESH_POWER_DEEP_SLEEP) ||
> +         (is_multicast_ether_addr(hdr->addr1) &&
> +          sdata->u.mesh.deep_sleep_peers > 0))
> +             *qc |= cpu_to_le16(IEEE80211_QOS_CTL_MESH_PS_LEVEL);
> +     else
> +             *qc &= cpu_to_le16(~IEEE80211_QOS_CTL_MESH_PS_LEVEL);
> +}
> diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
> index 0573b1b..8cedf22 100644
> --- a/net/mac80211/rx.c
> +++ b/net/mac80211/rx.c
> @@ -1986,6 +1986,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
>               return RX_DROP_MONITOR;
>       }
>  
> +     /* mesh power mode is link-specific -> update when forwarding */
> +     ieee80211_set_mesh_ps_fields(sdata, fwd_hdr);
> +
>       IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames);
>       ieee80211_add_pending_skb(local, fwd_skb);
>   out:
> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
> index d7c3ead..033c21d 100644
> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -1495,6 +1495,8 @@ void ieee80211_xmit(struct ieee80211_sub_if_data 
> *sdata, struct sk_buff *skb)
>       }
>  
>       ieee80211_set_qos_hdr(sdata, skb);
> +     if (ieee80211_vif_is_mesh(&sdata->vif))
> +             ieee80211_set_mesh_ps_fields(sdata, hdr);

Were you not able to call this later in the tx_handlers where we look up
the sta_info again anyway? You should already have the sta pointer in
the forwarding path as well.

>       ieee80211_tx(sdata, skb, false);
>       rcu_read_unlock();
>  }
> -- 
> 1.7.9.5
> 
_______________________________________________
Devel mailing list
[email protected]
http://lists.open80211s.org/cgi-bin/mailman/listinfo/devel

Reply via email to