mac80211: extend/document powersave API
authorJohannes Berg <johannes@sipsolutions.net>
Wed, 7 Jan 2009 17:28:20 +0000 (18:28 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 29 Jan 2009 20:59:58 +0000 (15:59 -0500)
This modifies hardware flags for powersave to support three different
flags:
 * IEEE80211_HW_SUPPORTS_PS - indicates general PS support
 * IEEE80211_HW_PS_NULLFUNC_STACK - indicates nullfunc sending in software
 * IEEE80211_HW_SUPPORTS_DYNAMIC_PS - indicates dynamic PS on the device

It also adds documentation for all this which explains how to set the
various flags.

Additionally, it fixes a few things:
 * a spot where && was used to test flags
 * enable CONF_PS only when associated again

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Documentation/DocBook/mac80211.tmpl
drivers/net/wireless/iwlwifi/iwl-core.c
drivers/net/wireless/rt2x00/rt2400pci.c
drivers/net/wireless/rt2x00/rt2500pci.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
include/net/mac80211.h
net/mac80211/mlme.c
net/mac80211/tx.c
net/mac80211/wext.c

index bdf908a6e545b1f10e3cc16d2130d9540c06dab4..8af6d9626878e0459c02052081c41bbc33c5d20d 100644 (file)
@@ -17,8 +17,7 @@
     </authorgroup>
 
     <copyright>
-      <year>2007</year>
-      <year>2008</year>
+      <year>2007-2009</year>
       <holder>Johannes Berg</holder>
     </copyright>
 
@@ -223,6 +222,11 @@ usage should require reading the full document.
 !Finclude/net/mac80211.h ieee80211_key_flags
     </chapter>
 
+    <chapter id="powersave">
+      <title>Powersave support</title>
+!Pinclude/net/mac80211.h Powersave support
+    </chapter>
+
     <chapter id="qos">
       <title>Multiple queues and QoS support</title>
       <para>TBD</para>
index 76315c30e6fcda6d6858712cff84ff2a8461cc8b..07c3870365bed5b573c0b709c2b60045b8a5b999 100644 (file)
@@ -805,7 +805,8 @@ int iwl_setup_mac(struct iwl_priv *priv)
        /* Tell mac80211 our characteristics */
        hw->flags = IEEE80211_HW_SIGNAL_DBM |
                    IEEE80211_HW_NOISE_DBM |
-                   IEEE80211_HW_AMPDU_AGGREGATION;
+                   IEEE80211_HW_AMPDU_AGGREGATION |
+                   IEEE80211_HW_SUPPORTS_PS;
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_STATION) |
                BIT(NL80211_IFTYPE_ADHOC);
index 9104113270d0cae1cb05a85810ef502fbde11eb4..ae8bfd6b59df3676b411a61af86272405388c08f 100644 (file)
@@ -1449,7 +1449,9 @@ static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
         * Initialize all hw fields.
         */
        rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
-                              IEEE80211_HW_SIGNAL_DBM;
+                              IEEE80211_HW_SIGNAL_DBM |
+                              IEEE80211_HW_SUPPORTS_PS |
+                              IEEE80211_HW_PS_NULLFUNC_STACK;
        rt2x00dev->hw->extra_tx_headroom = 0;
 
        SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
index ebcc4977092232fd97ef27dc603f6e1d1c0cba16..bca6798be153f45ade3d5b7e4ef723690345e605 100644 (file)
@@ -1749,7 +1749,9 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
         * Initialize all hw fields.
         */
        rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
-                              IEEE80211_HW_SIGNAL_DBM;
+                              IEEE80211_HW_SIGNAL_DBM |
+                              IEEE80211_HW_SUPPORTS_PS |
+                              IEEE80211_HW_PS_NULLFUNC_STACK;
 
        rt2x00dev->hw->extra_tx_headroom = 0;
 
index e992bad6464425fb968b07eec673bb36bbe2b5ea..27a6971df5790b1f186b3ab65bffe52f4fd60930 100644 (file)
@@ -1801,7 +1801,9 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        rt2x00dev->hw->flags =
            IEEE80211_HW_RX_INCLUDES_FCS |
            IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
-           IEEE80211_HW_SIGNAL_DBM;
+           IEEE80211_HW_SIGNAL_DBM |
+           IEEE80211_HW_SUPPORTS_PS |
+           IEEE80211_HW_PS_NULLFUNC_STACK;
 
        rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE;
 
index 82d35a5a4aa78a7927d5bb84e8b8b638c320171b..549480a963e92034b197b48f34e34ef4161754b6 100644 (file)
@@ -2556,7 +2556,9 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
         */
        rt2x00dev->hw->flags =
            IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
-           IEEE80211_HW_SIGNAL_DBM;
+           IEEE80211_HW_SIGNAL_DBM |
+           IEEE80211_HW_SUPPORTS_PS |
+           IEEE80211_HW_PS_NULLFUNC_STACK;
        rt2x00dev->hw->extra_tx_headroom = 0;
 
        SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
index 2b70c01b55e93a962f5e007e3c3991f580b583a7..849220236c7d0bec488cd56bb0829a285e9b10c8 100644 (file)
@@ -2077,7 +2077,9 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
         */
        rt2x00dev->hw->flags =
            IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
-           IEEE80211_HW_SIGNAL_DBM;
+           IEEE80211_HW_SIGNAL_DBM |
+           IEEE80211_HW_SUPPORTS_PS |
+           IEEE80211_HW_PS_NULLFUNC_STACK;
        rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE;
 
        SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
index 83ee8a212296ebbe6787c50488795d853bec9cbe..8a305bfdb87b09ed6b8e5f9dd3e3b3ba1814f379 100644 (file)
@@ -850,10 +850,15 @@ enum ieee80211_tkip_key_type {
  * @IEEE80211_HW_AMPDU_AGGREGATION:
  *     Hardware supports 11n A-MPDU aggregation.
  *
- * @IEEE80211_HW_NO_STACK_DYNAMIC_PS:
- *     Hardware which has dynamic power save support, meaning
- *     that power save is enabled in idle periods, and don't need support
- *     from stack.
+ * @IEEE80211_HW_SUPPORTS_PS:
+ *     Hardware has power save support (i.e. can go to sleep).
+ *
+ * @IEEE80211_HW_PS_NULLFUNC_STACK:
+ *     Hardware requires nullfunc frame handling in stack, implies
+ *     stack support for dynamic PS.
+ *
+ * @IEEE80211_HW_SUPPORTS_DYNAMIC_PS:
+ *     Hardware has support for dynamic PS.
  */
 enum ieee80211_hw_flags {
        IEEE80211_HW_RX_INCLUDES_FCS                    = 1<<1,
@@ -866,7 +871,9 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_NOISE_DBM                          = 1<<8,
        IEEE80211_HW_SPECTRUM_MGMT                      = 1<<9,
        IEEE80211_HW_AMPDU_AGGREGATION                  = 1<<10,
-       IEEE80211_HW_NO_STACK_DYNAMIC_PS                = 1<<11,
+       IEEE80211_HW_SUPPORTS_PS                        = 1<<11,
+       IEEE80211_HW_PS_NULLFUNC_STACK                  = 1<<12,
+       IEEE80211_HW_SUPPORTS_DYNAMIC_PS                = 1<<13,
 };
 
 /**
@@ -1052,6 +1059,42 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw,
  * handler is software decryption with wrap around of iv16.
  */
 
+/**
+ * DOC: Powersave support
+ *
+ * mac80211 has support for various powersave implementations.
+ *
+ * First, it can support hardware that handles all powersaving by
+ * itself, such hardware should simply set the %IEEE80211_HW_SUPPORTS_PS
+ * hardware flag. In that case, it will be told about the desired
+ * powersave mode depending on the association status, and the driver
+ * must take care of sending nullfunc frames when necessary, i.e. when
+ * entering and leaving powersave mode. The driver is required to look at
+ * the AID in beacons and signal to the AP that it woke up when it finds
+ * traffic directed to it. This mode supports dynamic PS by simply
+ * enabling/disabling PS.
+ *
+ * Additionally, such hardware may set the %IEEE80211_HW_SUPPORTS_DYNAMIC_PS
+ * flag to indicate that it can support dynamic PS mode itself (see below).
+ *
+ * Other hardware designs cannot send nullfunc frames by themselves and also
+ * need software support for parsing the TIM bitmap. This is also supported
+ * by mac80211 by combining the %IEEE80211_HW_SUPPORTS_PS and
+ * %IEEE80211_HW_PS_NULLFUNC_STACK flags. The hardware is of course still
+ * required to pass up beacons. Additionally, in this case, mac80211 will
+ * wake up the hardware when multicast traffic is announced in the beacon.
+ *
+ * FIXME: I don't think we can be fast enough in software when we want to
+ *       receive multicast traffic?
+ *
+ * Dynamic powersave mode is an extension to normal powersave mode in which
+ * the hardware stays awake for a user-specified period of time after sending
+ * a frame so that reply frames need not be buffered and therefore delayed
+ * to the next wakeup. This can either be supported by hardware, in which case
+ * the driver needs to look at the @dynamic_ps_timeout hardware configuration
+ * value, or by the stack if all nullfunc handling is in the stack.
+ */
+
 /**
  * DOC: Frame filtering
  *
index 7709e7645671cf5cc884531071f10321921b3889..a1e683e305f0a7bc63b017232ac11db0cebccb85 100644 (file)
@@ -775,17 +775,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        bss_info_changed |= BSS_CHANGED_BASIC_RATES;
        ieee80211_bss_info_change_notify(sdata, bss_info_changed);
 
-       if (local->powersave &&
-                       !(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS)) {
-               if (local->hw.conf.dynamic_ps_timeout > 0)
+       if (local->powersave) {
+               if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) &&
+                   local->hw.conf.dynamic_ps_timeout > 0) {
                        mod_timer(&local->dynamic_ps_timer, jiffies +
                                  msecs_to_jiffies(
                                        local->hw.conf.dynamic_ps_timeout));
-               else {
-                       ieee80211_send_nullfunc(local, sdata, 1);
+               } else {
+                       if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+                               ieee80211_send_nullfunc(local, sdata, 1);
                        conf->flags |= IEEE80211_CONF_PS;
-                       ieee80211_hw_config(local,
-                                           IEEE80211_CONF_CHANGE_PS);
+                       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
                }
        }
 
@@ -1779,16 +1779,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
                                 elems.wmm_param_len);
 
-       if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS)) {
+       if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK &&
+           local->hw.conf.flags & IEEE80211_CONF_PS) {
                directed_tim = check_tim(&elems, ifsta->aid, &is_mc);
 
                if (directed_tim || is_mc) {
-                       if (local->hw.conf.flags && IEEE80211_CONF_PS) {
-                               local->hw.conf.flags &= ~IEEE80211_CONF_PS;
-                               ieee80211_hw_config(local,
-                                               IEEE80211_CONF_CHANGE_PS);
-                               ieee80211_send_nullfunc(local, sdata, 0);
-                       }
+                       local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+                       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+                       ieee80211_send_nullfunc(local, sdata, 0);
                }
        }
 
@@ -2694,9 +2692,10 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
        if (local->hw.conf.flags & IEEE80211_CONF_PS)
                return;
 
-       ieee80211_send_nullfunc(local, sdata, 1);
-       local->hw.conf.flags |= IEEE80211_CONF_PS;
+       if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+               ieee80211_send_nullfunc(local, sdata, 1);
 
+       local->hw.conf.flags |= IEEE80211_CONF_PS;
        ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
 }
 
index b18a7269011946d7f566e51389ffa097eef5033e..cd6bc87eec737eddb1bc01a69d51c65a773d6fdc 100644 (file)
@@ -1295,7 +1295,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
                return 0;
        }
 
-       if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) &&
+       if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
            local->hw.conf.dynamic_ps_timeout > 0) {
                if (local->hw.conf.flags & IEEE80211_CONF_PS) {
                        ieee80211_stop_queues_by_reason(&local->hw,
index 3f2db0bda46ca94d82431404dcf7b2996f923708..1e5b29bdb3a77b33e480ffa24c31eefbf8d73cc3 100644 (file)
@@ -837,6 +837,9 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
        int ret = 0, timeout = 0;
        bool ps;
 
+       if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
+               return -EOPNOTSUPP;
+
        if (sdata->vif.type != NL80211_IFTYPE_STATION)
                return -EINVAL;
 
@@ -862,32 +865,37 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
        if (wrq->flags & IW_POWER_TIMEOUT)
                timeout = wrq->value / 1000;
 
-set:
+ set:
        if (ps == local->powersave && timeout == conf->dynamic_ps_timeout)
                return ret;
 
        local->powersave = ps;
        conf->dynamic_ps_timeout = timeout;
 
-       if (local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) {
+       if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
                ret = ieee80211_hw_config(local,
                                          IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
-       } else if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
-               if (conf->dynamic_ps_timeout > 0)
-                       mod_timer(&local->dynamic_ps_timer, jiffies +
-                                 msecs_to_jiffies(conf->dynamic_ps_timeout));
-               else {
-                       if (local->powersave) {
+
+       if (!(sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED))
+               return ret;
+
+       if (conf->dynamic_ps_timeout > 0 &&
+           !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
+               mod_timer(&local->dynamic_ps_timer, jiffies +
+                         msecs_to_jiffies(conf->dynamic_ps_timeout));
+       } else {
+               if (local->powersave) {
+                       if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
                                ieee80211_send_nullfunc(local, sdata, 1);
-                               conf->flags |= IEEE80211_CONF_PS;
-                               ret = ieee80211_hw_config(local,
-                                               IEEE80211_CONF_CHANGE_PS);
-                       } else {
-                               conf->flags &= ~IEEE80211_CONF_PS;
-                               ret = ieee80211_hw_config(local,
-                                               IEEE80211_CONF_CHANGE_PS);
+                       conf->flags |= IEEE80211_CONF_PS;
+                       ret = ieee80211_hw_config(local,
+                                       IEEE80211_CONF_CHANGE_PS);
+               } else {
+                       conf->flags &= ~IEEE80211_CONF_PS;
+                       ret = ieee80211_hw_config(local,
+                                       IEEE80211_CONF_CHANGE_PS);
+                       if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
                                ieee80211_send_nullfunc(local, sdata, 0);
-                       }
                }
        }