nl80211: prohibit mixing 'any' and regular wowlan triggers
authorJohannes Berg <johannes.berg@intel.com>
Sun, 1 Mar 2015 07:10:13 +0000 (09:10 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 4 Mar 2015 09:34:11 +0000 (10:34 +0100)
If the device supports waking up on 'any' signal - i.e. it continues
operating as usual and wakes up the host on pretty much anything that
happens, then it makes no sense to also configure the more restricted
WoWLAN mode where the device operates more autonomously but also in a
more restricted fashion.

Currently only cw2100 supports both 'any' and other triggers, but it
seems to be broken as it doesn't configure anything to the device, so
we can't currently get into a situation where both even can correctly
be configured. This is about to change (Intel devices are going to
support both and have different behaviour depending on configuration)
so make sure the conflicting modes cannot be configured.

(It seems that cw2100 advertises 'any' and 'disconnect' as a means of
saying that's what it will always do, but that isn't really the way
this API was meant to be used nor does it actually mean anything as
'any' always implies 'disconnect' already, and the driver doesn't
change device configuration in any way depending on the settings.)

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/uapi/linux/nl80211.h
net/wireless/nl80211.c

index 90c5aeb3cca75176811a26ca414faa7deb7ecd23..37e7f39441e5e6c01472a077eb80fafcc077a392 100644 (file)
@@ -3708,6 +3708,8 @@ struct nl80211_pattern_support {
  * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put
  *     the chip into a special state -- works best with chips that have
  *     support for low-power operation already (flag)
+ *     Note that this mode is incompatible with all of the others, if
+ *     any others are even supported by the device.
  * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect
  *     is detected is implementation-specific (flag)
  * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed
index 01874628ae00fd75229cfc17107c9f1bc49ac979..07cef3d7653eeae7ec79ad1d0d907cb154f2ea10 100644 (file)
@@ -9105,6 +9105,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
        const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan;
        int err, i;
        bool prev_enabled = rdev->wiphy.wowlan_config;
+       bool regular = false;
 
        if (!wowlan)
                return -EOPNOTSUPP;
@@ -9132,12 +9133,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
                        return -EINVAL;
                new_triggers.disconnect = true;
+               regular = true;
        }
 
        if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
                if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
                        return -EINVAL;
                new_triggers.magic_pkt = true;
+               regular = true;
        }
 
        if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
@@ -9147,24 +9150,28 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
                        return -EINVAL;
                new_triggers.gtk_rekey_failure = true;
+               regular = true;
        }
 
        if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
                if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
                        return -EINVAL;
                new_triggers.eap_identity_req = true;
+               regular = true;
        }
 
        if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
                if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
                        return -EINVAL;
                new_triggers.four_way_handshake = true;
+               regular = true;
        }
 
        if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
                if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
                        return -EINVAL;
                new_triggers.rfkill_release = true;
+               regular = true;
        }
 
        if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
@@ -9173,6 +9180,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                int rem, pat_len, mask_len, pkt_offset;
                struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
 
+               regular = true;
+
                nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
                                    rem)
                        n_patterns++;
@@ -9234,6 +9243,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) {
+               regular = true;
                err = nl80211_parse_wowlan_tcp(
                        rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION],
                        &new_triggers);
@@ -9242,6 +9252,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) {
+               regular = true;
                err = nl80211_parse_wowlan_nd(
                        rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT],
                        &new_triggers);
@@ -9249,6 +9260,17 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                        goto error;
        }
 
+       /* The 'any' trigger means the device continues operating more or less
+        * as in its normal operation mode and wakes up the host on most of the
+        * normal interrupts (like packet RX, ...)
+        * It therefore makes little sense to combine with the more constrained
+        * wakeup trigger modes.
+        */
+       if (new_triggers.any && regular) {
+               err = -EINVAL;
+               goto error;
+       }
+
        ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
        if (!ntrig) {
                err = -ENOMEM;