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