On Wed, Oct 17, 2012 at 07:23:31PM -0700, [email protected] wrote:
> From: Marco Porsch <[email protected]>
>
> According to IEEE802.11-2012 a mesh peering is always associated between
> two mesh STAs. Both mesh STAs have their own mesh power mode for the
> mesh peering. This power mode is called the link-specific power mode.
> The peer_ps_mode field has been added to the sta_info structure to
> represent the peer's link-specific power mode towards the local station.
> The nonpeer_ps_mode field has been added to represent the peer's power
> mode towards all non-peer stations.
>
> The peer's link-specific power modes are tracked from the Power Management
> field in the Frame Control field and the Mesh Power Save Level field
> in the QoS Control field. Peer and non-peer modes are tracked independently
> from respective frames. This allows fast reconfiguration after a peering
> change.
>
> 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 | 10 +++++++
> net/mac80211/mesh.h | 4 +++
> net/mac80211/mesh_ps.c | 73
> +++++++++++++++++++++++++++++++++++++++++++++
> net/mac80211/rx.c | 27 +++++++++++++++++
> net/mac80211/sta_info.h | 4 +++
> 5 files changed, 118 insertions(+)
>
> diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
> index 26fefe5..3048774 100644
> --- a/include/linux/ieee80211.h
> +++ b/include/linux/ieee80211.h
> @@ -571,6 +571,16 @@ static inline int ieee80211_is_first_frag(__le16
> seq_ctrl)
> return (seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0;
> }
>
> +/**
> + * ieee80211_has_qos_pm - check Power Save Level in QoS control
> + * @qc - QoS control bytes in little-endian byteorder
> + */
> +
Extra newline.
> +static inline int ieee80211_has_qos_pm(__le16 qc)
> +{
> + return (qc & cpu_to_le16(IEEE80211_QOS_CTL_MESH_PS_LEVEL)) != 0;
The 1st conditional alone should be sufficient?
> +}
> +
> struct ieee80211s_hdr {
> u8 flags;
> u8 ttl;
> diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
> index 22c3fd6..b342933 100644
> --- a/net/mac80211/mesh.h
> +++ b/net/mac80211/mesh.h
> @@ -268,6 +268,10 @@ 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);
> +void ieee80211_set_peer_ps_mode(struct sta_info *sta,
> + struct ieee80211_hdr *hdr);
> +void ieee80211_set_nonpeer_ps_mode(struct sta_info *sta,
> + struct ieee80211_hdr *hdr);
>
> /* Mesh paths */
> int mesh_nexthop_lookup(struct sk_buff *skb,
> diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c
> index 15b3abe..5ddf7be 100644
> --- a/net/mac80211/mesh_ps.c
> +++ b/net/mac80211/mesh_ps.c
> @@ -259,3 +259,76 @@ void ieee80211_set_mesh_ps_fields(struct
> ieee80211_sub_if_data *sdata,
> else
> *qc &= cpu_to_le16(~IEEE80211_QOS_CTL_MESH_PS_LEVEL);
> }
> +
> +/**
> + * ieee80211_set_peer_ps_mode - track the neighbor mesh STA's peer-specific
> + * power mode towards the local STA
> + *
> + * @sta: STA info to update
> + * @hdr: IEEE 802.11 QoS Header
> + */
> +void ieee80211_set_peer_ps_mode(struct sta_info *sta,
> + struct ieee80211_hdr *hdr)
> +{
> + enum nl80211_mesh_power_mode pm;
> + __le16 *qc = (__le16 *) ieee80211_get_qos_ctl(hdr);
> + static const char *modes[] = {
> + [NL80211_MESH_POWER_ACTIVE] = "active",
> + [NL80211_MESH_POWER_LIGHT_SLEEP] = "light sleep",
> + [NL80211_MESH_POWER_DEEP_SLEEP] = "deep sleep",
> + };
> +
> + BUG_ON(!ieee80211_is_data_qos(hdr->frame_control) ||
> + is_multicast_ether_addr(hdr->addr1));
BUG_ON(1) crashes the kernel. Can't we just WARN_ON_ONCE() then return?
> + if (ieee80211_has_pm(hdr->frame_control)) {
> + if (ieee80211_has_qos_pm(*qc))
> + pm = NL80211_MESH_POWER_DEEP_SLEEP;
> + else
> + pm = NL80211_MESH_POWER_LIGHT_SLEEP;
> + } else {
> + pm = NL80211_MESH_POWER_ACTIVE;
> + }
> +
> + if (sta->peer_ps_mode == pm)
> + return;
> +
> + mps_dbg(sta->sdata, "STA %pM enters %s mode\n",
> + sta->sta.addr, modes[pm]);
> +
> + sta->peer_ps_mode = pm;
> +
> + ieee80211_sta_ps_update(sta);
> +}
> +
> +/**
> + * ieee80211_set_nonpeer_ps_mode - track the neighbor mesh STA's
> + * power mode towards non-peer STA
> + *
> + * @sta: STA info to update
> + * @hdr: IEEE 802.11 (QoS) Header
> + */
> +void ieee80211_set_nonpeer_ps_mode(struct sta_info *sta,
> + struct ieee80211_hdr *hdr)
> +{
> + enum nl80211_mesh_power_mode pm;
> + static const char *modes[] = {
> + [NL80211_MESH_POWER_ACTIVE] = "active",
> + [NL80211_MESH_POWER_DEEP_SLEEP] = "deep sleep",
> + };
> +
> + if (ieee80211_has_pm(hdr->frame_control))
> + pm = NL80211_MESH_POWER_DEEP_SLEEP;
> + else
> + pm = NL80211_MESH_POWER_ACTIVE;
> +
> + if (sta->nonpeer_ps_mode == pm)
> + return;
> +
> + mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %s\n",
> + sta->sta.addr, modes[pm]);
> +
> + sta->nonpeer_ps_mode = pm;
> +
> + ieee80211_sta_ps_update(sta);
> +}
> diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
> index 8cedf22..b88e420 100644
> --- a/net/mac80211/rx.c
> +++ b/net/mac80211/rx.c
> @@ -1350,6 +1350,33 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data
> *rx)
> }
>
> /*
> + * Test Power Managment field of frame control (PW) and
> + * mesh power save level subfield of QoS control field (PSL)
> + *
> + * | PM | PSL| Mesh Power Mode |
> + * +----+----+-----------------+
> + * | 0 |Rsrv| Active |
> + * | 1 | 0 | Light |
> + * | 1 | 1 | Deep |
> + */
> + if (ieee80211_vif_is_mesh(&rx->sdata->vif)) {
> + if (is_unicast_ether_addr(hdr->addr1) &&
> + ieee80211_is_data_qos(hdr->frame_control)) {
> + /*
> + * individually addressed QoS Data/Null frames contain
> + * peer's link-specific PS mode towards the local STA
> + */
> + ieee80211_set_peer_ps_mode(sta, hdr);
> + } else {
> + /*
> + * can only determine non-peer PS mode
> + * (see IEEE802.11-2012 8.2.4.1.7)
> + */
> + ieee80211_set_nonpeer_ps_mode(sta, hdr);
> + }
> + }
> +
> + /*
> * Drop (qos-)data::nullfunc frames silently, since they
> * are used only to control station power saving mode.
> */
> diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
> index e911527..5fdb9d3 100644
> --- a/net/mac80211/sta_info.h
> +++ b/net/mac80211/sta_info.h
> @@ -278,6 +278,8 @@ struct sta_ampdu_mlme {
> * @local_ps_mode: local link-specific power save mode
> * @local_ps_mode_delayed: temp. storage for delayed setting of local_ps_mode
> * @local_ps_mode_timer: timer for delayed setting of local_ps_mode
> + * @peer_ps_mode: peer's link-specific power save mode
> + * @nonpeer_ps_mode: STA's power save mode towards non-peer neighbors
> * @debugfs: debug filesystem info
> * @dead: set to true when sta is unlinked
> * @uploaded: set to true when sta is uploaded to the driver
> @@ -378,6 +380,8 @@ struct sta_info {
> enum nl80211_mesh_power_mode local_ps_mode;
> enum nl80211_mesh_power_mode local_ps_mode_delayed;
> struct timer_list local_ps_mode_timer;
> + enum nl80211_mesh_power_mode peer_ps_mode;
> + enum nl80211_mesh_power_mode nonpeer_ps_mode;
> #endif
>
> #ifdef CONFIG_MAC80211_DEBUGFS
> --
> 1.7.9.5
>
_______________________________________________
Devel mailing list
[email protected]
http://lists.open80211s.org/cgi-bin/mailman/listinfo/devel